Search code examples
c++winapigdi

NULL pointer for bmBits after converting a bit array to HBITMAP


I'm trying to convert a pixel array to a HBITMAP as instructed in this post: How to convert an Array of pixels to HBITMAP.

Basically, the array is converted to a HBITMAPand then verified by copied to the clipboard. However, when accessing the bit value from the BITMAP construct by its bmBits member, it returns NULL.

I might have something missing here. If a HBITMAP is created successfully, why we still get a NULL pointer to its bit values?

uint8 width = 160;
uint8 height = 120;

uint8* pixels = new uint8[160 * 120 * 4];
for (int i = 0; i < 160 * 120 * 4; i++){
    pixels[i] = (i % 4 == 1) * 255; // testing pixels
}

BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = width;
bmih.biHeight = -1 * height;
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 10;
bmih.biYPelsPerMeter = 10;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;

BITMAPINFO dbmi;
ZeroMemory(&dbmi, sizeof(dbmi));
dbmi.bmiHeader = bmih;
dbmi.bmiColors->rgbBlue = 0;
dbmi.bmiColors->rgbGreen = 0;
dbmi.bmiColors->rgbRed = 0;
dbmi.bmiColors->rgbReserved = 0;

HDC hdc = ::GetDC(NULL);

HBITMAP hbmp = CreateDIBitmap(hdc, &bmih, CBM_INIT, pixels, &dbmi, DIB_RGB_COLORS);
if (hbmp == NULL) {
    ::MessageBox(NULL, L"Could not load the desired image", L"Error", MB_OK);
    return NULL;
}

::ReleaseDC(NULL, hdc);

// a little test if everything is OK
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbmp); // I can verify the image by pasting
CloseClipboard();

// verify the bitmap
BITMAP bitmap;
::GetObject(hbmp, sizeof(BITMAP), &bitmap);
uint8* lpbits = (uint8*)bitmap.bmBits;

assert(lpbits != NULL); // Why this assertion failed??

// cleanup
// DeleteObject(hbmp);

Solution

  • The answer is in the GetObject() documentation:

    If hgdiobj is a handle to a bitmap created by calling CreateDIBSection, and the specified buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member of the BITMAP structure contained within the DIBSECTION will contain a pointer to the bitmap's bit values.

    If hgdiobj is a handle to a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling the GetDIBits() or GetBitmapBits() function.

    This means that bmBits is populated only when querying a DIB bitmap for a DIBSECTION structure, which contains a BITMAP amongst other things.

    You are querying a DDB bitmap for a BITMAP structure, so bmBits does not get populated, and you have to retrieve the pixel bits separately.