Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MFC BitBlt and SetDIBits vs. SetBitmapBits

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?

like image 631
darda Avatar asked Jan 11 '23 07:01

darda


2 Answers

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.

like image 87
Ross Ridge Avatar answered Jan 18 '23 23:01

Ross Ridge


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.

like image 34
darda Avatar answered Jan 18 '23 23:01

darda