Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a bitmap stored as a BGRA array of bytes. This is the code I've been using to paint the bitmap:

CDC *dispDC = new CDC();
dispDC->CreateCompatibleDC(pDC);
CBitmap *dispBMP = new CBitmap();
dispBMP->CreateCompatibleBitmap(pDC, sourceImage->GetWidth(), sourceImage->GetHeight());
dispDC->SelectObject(this->dispBMP);

The actual copying of the pixels in the translatedImage array happens with this:

dispBMP->SetBitmapBits(sourceImage->GetArea() * 4, translatedImage);

Then after some more processing I call pDC->StretchBlt with dispDC as the source CDC. This works fine when logged in locally because the display is also set to 32bpp.

Once I log in with Remote Desktop, the display goes to 16bpp and the image is mangled. The culprit is SetBitmapBits; i.e. for it to work, I have to properly fill translatedImage with the 16bpp version of what I want to show. Rather than do this myself, I searched the documentation and found SetDIBits which sounds like it does what I want:

The SetDIBits function sets the pixels in a compatible bitmap (DDB) using the color data found in the specified DIB.

In my case, the DIB is the 32bpp RGBA array, and the DDB is dispBMP which I create with CreateCompatibleBitmap.

So instead of my call to SetBitmapBits, this is what I did:

BITMAPINFO info;
ZeroMemory(&info, sizeof(BITMAPINFO));
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biSizeImage = sourceImage->GetArea()*4;
info.bmiHeader.biWidth = sourceImage->GetWidth();
info.bmiHeader.biHeight = sourceImage->GetHeight();
info.bmiHeader.biClrUsed = 0;
int r = SetDIBits(pDC->GetSafeHdc(), (HBITMAP)dispBMP,
                  0, sourceImage->GetHeight(), translatedImage, 
                  &info, DIB_PAL_COLORS);

However, r is always zero and, naturally, I get nothing but black in my window. What is wrong with the code?

Have you checked input of SetDIBits, when you log in with Remote Desktop? For example, is it true, that dispBMP!=NULL in this case? – Ilya Aug 24, 2014 at 0:59

The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function.

In your example code you select it into device context after creating it, so presumably that's why SetDIBits is failing.

This is not the reason, apparently. I now have the SelectObject call just before the StretchBLT call with SetDIBits in between and still get black. SetDIBits returns zero and obviously does nothing because if I leave both it and the SetBitmapBits call, I get the proper image (until I go into remote desktop). – darda Sep 2, 2014 at 15:24

Ross Ridge was correct in pointing out the code order mistake. However, this didn't solve the problem.

The problem was in the parameters I was passing. I am new to C++ and MFC and often forget all the "operators" which can act on types to automatically convert them.

Previously I had this:

int r = SetDIBits(pDC->GetSafeHdc(), (HBITMAP)dispBMP,
              0, sourceImage->GetHeight(), translatedImage, 
              &info, DIB_PAL_COLORS);

The correct call is this:

int r = SetDIBits(*pDC, *dispBMP,
              0, sourceImage->GetHeight(), translatedImage, 
              &info, DIB_PAL_COLORS);

(Note I pass dereferenced pointers in the first two parameters.) Everything else was correct, including the counter-intuitive DIB_PAL_COLORS flag for a bitmap which has not palette.

After obviously missing some key points in the documentation I reread it and then found this which has sample code showing that I was simply passing the parameters incorrectly.

There was nothing wrong with the GetSafeHdc you were using, the problem was in casting the pointer to CBitmap dispBMP rather than the object itself *dispBMP. I'm curious why you made it a pointer in the first place rather than a local object? – Mark Ransom Sep 2, 2014 at 15:55 @MarkRansom: dispBMP is a class member variable because I have found in my experience with C that allocating memory ends up often time being very expensive in the types of application I work on (data processing). So since this is a new project starting from scratch I'm being very careful to reuse large memory objects like bitmaps and bitmap byte arrays. – darda Sep 3, 2014 at 19:04 That strategy isn't buying you much in this case. Do a sizeof(dispBMP) to see what I mean - it doesn't contain the bitmap, it holds a handle to the bitmap, which is allocated separately. – Mark Ransom Sep 3, 2014 at 19:18 @MarkRansom: right. So if I lose that handle I need to create a new bitmap every time. Isn't CreateCompatibleBitmap the one allocating the memory? I don't show it in my snippet, but in the actual code I only call that if I know the image changed size or if dispBMP is NULL. – darda Sep 4, 2014 at 21:53

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.