I have an array of pixels which I need to convert to HBITMAP in order to display it in a window. I tried to use CreateDIBitmap() but I don't have the BMP headers. I tried to construct them manually according to MSDN documentation but this didn't work.
Here how my code looks
HBITMAP hBitmap
char pixels[160*120]; // White grayscale image of size 160x120
memset(pixels,255,sizeof(pixels));
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = 160;
bmih.biHeight = -120;
bmih.biPlanes = 1;
bmih.biBitCount = 8;
bmih.biCompression = BI_RGB ;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 10;
bmih.biYPelsPerMeter = 10;
bmih.biClrUsed =0;
bmih.biClrImportant =0;
BITMAPINFO dbmi;
dbmi.bmiHeader = bmih;
dbmi.bmiColors->rgbBlue = 0;
dbmi.bmiColors->rgbGreen = 0;
dbmi.bmiColors->rgbRed = 0;
dbmi.bmiColors->rgbReserved = 0;
void* bits = (void*)&(pixels[0]);
hBitmap = CreateDIBitmap(localDC, &bmih, CBM_INIT, qB.bmBits, &dbmi, DIB_RGB_COLORS);
Now I get a non NULL hBitmap which is good but it shows always black image as if it doesn't point to the array of pixels. I checked it using the code
BITMAP qB;
GetObject(reinterpret_cast<HGDIOBJ>(hBitmap),sizeof(BITMAP),reinterpret_cast<LPVOID>(&qB));
And indeed qB.bmBits is NULL. What is the problem and how to fix it?
I found how to do it.
We need to use CreateDIBSection()
instead of CreateDIBitmap()
So here is the working code
HBITMAP hBitmap = NULL;
unsigned char pixels[160*120*3];
for (int i=0; i<160*120*3; i++){
pixels[i] = (i%4==1)*255; // An BGR (not RGB) 160x120 image.
}
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = 160;
bmih.biHeight = -120;
bmih.biPlanes = 1;
bmih.biBitCount = 24;
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;
void* bits = (void*)&(pixels[0]);
// Create DIB
hBitmap = CreateDIBSection(localDC, &dbmi, DIB_RGB_COLORS, &bits, NULL, 0);
if (hBitmap == NULL) {
::MessageBox(NULL, __T("Could not load the desired image image"), __T("Error"), MB_OK);
return;
}
// copy pixels into DIB.
memcpy(bits,pixels,sizeof(pixels));
For grey level images, copy the pixels to DIB in a loop instead of with memcpy()
#define INTENSITY unsigned char
INTENSITY* dest = (INTENSITY*)bits;
const INTENSITY* src = .. Put your char array of pixels;
for (int j=0; j<imageWidth; j++){
for (int i=0; i<imageHeight; i++, src++){
*dest++ = *src;
*dest++ = *src;
*dest++ = *src;
}
// Padd the line to round WORD.
if (imageWidth%2)
*dest++ = 0;
}
The other answers here are very helpful, but i ended up being able to achieve it with just 1 line.
HBITMAP hBm = CreateBitmap(width,height,1,32,pixels); // 1 plane, 32 bits
Hope it may be useful to any future readers.
Also, there's CreateCompatibleBitmap
and SetDIBits
incase you need to use a device context.
At MSDN for CreateDIBitmap
it states:
Calling CreateDIBitmap with fdwInit as CBM_INIT is equivalent to calling the CreateCompatibleBitmap function to create a DDB in the format of the device and then calling the SetDIBits function to translate the DIB bits to the DDB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With