4

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?

darda
  • 3,597
  • 6
  • 36
  • 49
  • 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 '14 at 00:59

2 Answers2

5

According to the documentation for SetDIBits:

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.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • 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 02 '14 at 15:24
4

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.

darda
  • 3,597
  • 6
  • 36
  • 49
  • 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 02 '14 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 03 '14 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 03 '14 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 04 '14 at 21:53