PDA

View Full Version : CreateCompatibleBitmap fails in QPixmap



JimBrown
21st May 2007, 14:46
Hi,

The Qt app that I inherited at work has another problem. It creates a QPixmap from a QByteArray, and half of the time the QPixmap is null. The QByteArray is known good, and, the other half of the time, displays perfectly well.

Tracing through the code I found out that in qpixmap_win.cpp, the call to CreateCompatibleBitmap in QPixmap::init fails because the device context returned by qt_display_dc() is bad.

Is this a known issue? Can I force Qt to call GetDC somehow, or is there a better way to handle this?


Thanks!
Jim Brown

wysota
21st May 2007, 15:32
Do you use threads?

JimBrown
21st May 2007, 15:37
Do you use threads?

Thanks for the reply. No, it doesn't use threads.

wysota
21st May 2007, 15:42
Could you show us the code where you create the pixmap and code where you create the byte array?

JimBrown
21st May 2007, 15:50
Your thread question had me thinking, so i did a test. The app was failing at about 50% of the time on Friday after I had been working with it all week. This morning, after a reboot, it was not failing until I opened multiple instances of the app. When I close the other processes, the failure rate goes down.

Is there a shared resource among multiple instances?

JimBrown
21st May 2007, 16:06
Could you show us the code where you create the pixmap and code where you create the byte array?

Here is the code. I write the QByteArray buffer out to a file, and it is identical whether the pixmap is null or not. When I trace through the code execution, the only difference that I can see is in the call to to qt_display_dc() (defined in qapplication_win.cpp) in QPixmap::init. The HDC returned is bad when pixmap.isNull.




// -----------------------------------------------------------------
QString pgmHeader( QString( "P5\n%1 %2\n255\n" ).arg( imageWidth ).arg( imageHeight ) );
int pgmHeaderSize = pgmHeader.length();

QByteArray buf( pgmHeaderSize + numberOfImagePixels );
memcpy( buf.data(), pgmHeader.ascii(), pgmHeaderSize );
loadImageData( imageData, ucharptr( buf.data() + pgmHeaderSize ), numberOfImagePixels );

QPixmap pixmap(buf);
if ( pixmap.isNull() )
{
// report error
return -1;
}
// -----------------------------------------------------------------

marcel
21st May 2007, 17:55
About numberOfImagePixels - how many bytes per pixels does your image have? If only one byte than it's ok, but if not, when you're loading the image data, you must load numberOfImagePixels*bitDepth/8.

EDIT:
Does QPixmap have a QPixmap( QByteArray ) in Qt3? I know it has QPixmap( const char* ) but that is only for XPM data.

Have you tried using loadFromData?

JimBrown
21st May 2007, 18:42
Thanks for your reply.


About numberOfImagePixels - how many bytes per pixels does your image have? If only one byte than it's ok, but if not, when you're loading the image data, you must load numberOfImagePixels*bitDepth/8.


One byte per pixel. Good question; I too have thought about a wild pointer overwriting the DC handle.



EDIT:
Does QPixmap have a QPixmap( QByteArray ) in Qt3? I know it has QPixmap( const char* ) but that is only for XPM data.


Yes:


QPixmap ( const QByteArray& img_data );



Have you tried using loadFromData?


Yes. I have tried this code:



bool loadOk = pixmap.loadFromData(buf);
if (!loadOk)
{
// report error
return -1;
}


loadFromData always returns true, even when pixmap.isNull. The problem does not seem to be with the data or the functionality of QPixmap or QByteArray but rather with the DC. It may be true that some wild pointer is overwriting the DC handle, and it will take a lot of time to determine that. A temporary quick-fix would be to have pixmap reload the DC if I can do that. Otherwise, I'm at a loss as to how to proceed.

Thanks again for your reply.
Jim Brown

JimBrown
21st May 2007, 18:49
I still think that this is significant. What do you think?


The app was failing at about 50% of the time on Friday after I had been working with it all week. This morning, after a reboot, it was not failing until I opened multiple instances of the app. When I close the other processes, the failure rate goes down.

Is there a shared resource among multiple instances?

marcel
21st May 2007, 19:08
Yes, there has to be a reason. If not in the way you build the pixmap, then in Qt.
How fast are you loading the pixmaps?
I mean, can you estimate what is the interval between loading pixmaps?

JimBrown
21st May 2007, 19:36
It takes awhile; the user must select and open a file that the image data is in.

marcel
21st May 2007, 19:47
Hey, I just thought of something.
QByteArray::data does not return a (const char*), but (char*) and it is possible that QPixmap modifies this buffer inside.

Try using QByteArray::constData instead

JimBrown
21st May 2007, 20:08
I'm looking at the DC code inside of qapplication_win.cpp. It has:



Q_EXPORT HDC qt_display_dc() // get display DC
{
if ( !displayDC )
displayDC = GetDC( 0 );
return displayDC;
}


msdn says:


HDC GetDC(
HWND hWnd // handle to window
);
Parameters
hWnd
[in] Handle to the window whose DC is to be retrieved. If this value is NULL, GetDC retrieves the DC for the entire screen.

Return Values
If the function succeeds, the return value is a handle to the DC for the specified window's client area. If the function fails, the return value is NULL.


I put a breakpoint in qt_display_dc() for when displayDC is null. Notice that the arg to GetDC is zero. GetDC always returns a non-null value, but when I paste a bad DC into VisualStudio's debug memory window, it can't evaluate the address (again, when I have multiple instances of the app running). When the DC is good, I can see proper values at the address in the memory window.

I've seen this happen when the app first comes up, before I even open an image file. There's something else going wrong here...

marcel
21st May 2007, 20:39
I believe you're looking in the wrong place.
These kind of problems often come from memory corruptions, buffer overruns, etc.
I have seen that you're using a some (const char*)s and memcpy's in the code you posted and I tend to believe that you also use these in other parts of your code.

As I am sure you know, if they are not used correctly, they don't work. There is no middle way - you cannot use them just in part correctly.

So you could take a look again on all that.
Also, if you can, post the code where you're loading the image from disk.

Regards

JimBrown
29th May 2007, 15:24
I posted my problem to a microsoft vc developer's newsgroup and was directed to the thread below; thanks very much to Igor Tandetnik. The solution was to change the CreateCompatibleBitmap call to CreateDIBSection in QPixmap::init, and it works perfectly.

Thanks again to to all who responded to my request for help on Qt Centre Forum; you are most kind and generous with your knowledge and time.

-----------------------------------------------
From: microsoft.public.win32.programmer.gdi
Topic: Why does CreateCompatibleBitmap fail if there is 'enough' memory?

http://groups.google.com/group/microsoft.public.win32.programmer.gdi/browse_frm/thread/59a5dd42aaa5bb7c

-----------------------------------------------
Hi!

I've got 512MB Physical Memory, Win2000 and Visual Studio 6.0. When I start my program Windows Task Manager shows that MEM Usage is about 108MB. I'd like to create a big bitmap for flickerless animation. I call CreateCompatibleBitmap with very big values, for example CreateCompatibleBitmap(hdc,4000,4000), where hdc is a 32bpp Private Display Device Context. The function returns NULL, then GetLastError() returns 8, which means "Not enough storage is available to process this command". Then I call malloc(4000*4000*4) and it returns non-NULL, which shows that there's enough memory. The problem doesn't occur if I create smaller (for example 1000*1000) bitmap. I couldn't find any information concerning this problem on MSDN.

Laszlo
-----------------------------------------------

I'm just guessing here - but some of the bitmap commands attempt to allocate the memory in kernel storage for blitting efficiency.

DDBs are allocated by the driver for the reference DC. This allocation is made from paged pool. On a machine with that much memory, I would think your paged pool would be around 192 MB. From that, all such allocations (and anything else any drivers allocate) must come. So you can't have a bitmap which exceeds that size, and you can't have several bitmaps which collectively exceed that size.
On Terminal Server machines, this limit is exaggerated because of how that pool is shared. There, you'll see a limit of around 28Mb.
This is by design. If you require larger bitmaps in these scenarios, use DIBSections.

I guess David Solomon's books mentions that paged pool is a fixed region within kernel mode address space. The whole kernel mode address space is only 2 GB, shared by system kernel DLLs, pagining tables, kernel mode drivers, paged pool and non-paged pool. So it's quite crowded. The size of paged pool is limited by kernel mode address space.

-----------------------------------------------
-----------------------------------------------
Some correction to this information:

Device Compatible Bitmaps are created by the current display driver, if the driver supports them.

The display driver may allocate them out of video memory, and charge the session space only for a limited mapped view.

The most common implementation I've seen does map a whole chunck of video memory in session space, so, session-view space is the resource you compete against.

Other (display driver) implementations may choose to charge session pool, system paged pool or system non-paged pool.

In a TS Session, the space availabe for all device compatible bitmap in a session is limited by connection-negotiate parameters with the TS-Client.
-----------------------------------------------
-----------------------------------------------