如何将像素数组转换为HBITMAP

13

我有一个像素数组,需要将其转换为 HBITMAP 以便在窗口中显示。 我尝试使用 CreateDIBitmap() 但我没有 BMP 文件头。 我尝试根据 MSDN 文档手动构建它们,但这并没有起作用。

以下是我的代码:

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);

现在我得到了一个非NULL的hBitmap,这很好,但它总是显示黑色图像,就像它没有指向像素数组一样。 我使用以下代码进行了检查:

BITMAP qB;
GetObject(reinterpret_cast<HGDIOBJ>(hBitmap),sizeof(BITMAP),reinterpret_cast<LPVOID>(&qB));

实际上qB.bmBits是NULL。 问题是什么,如何解决?


我认为CreateDIBitmap不支持灰度图像。你可以创建一个包含256个灰度级的调色板,或者将图像转换为RGB三元组。 - john
好的,假设我这样做:char pixels[1601203]; 那么我就有了RGB图像。 - DanielHsH
我设置了bmih.biBitCount = 24; 但它仍然不起作用。 - DanielHsH
3个回答

9

我找到了如何做。 我们需要使用CreateDIBSection()而不是CreateDIBitmap() 下面是可行的代码

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));

对于灰度图像,应该使用循环将像素复制到DIB中,而不是使用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;
}  

注意:你可能需要将宽度填充为4个字节(32位整数)的倍数,而不是像我在示例中所做的那样使用16位WORD。 - DanielHsH
6
我认为在这里 void* bits = (void*)&(pixels[0]); 没有意义。应该改为:void* bits = NULL; - Mike Weir

5

你的帖子(答案)非常有帮助,但对我没有起作用,这里是带有小修正的代码:

    // creating input

    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.

    // at this point we have some input

    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;

    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 image", L"Error", MB_OK);
        return;
    }

    ::ReleaseDC(NULL, hdc);

    // a little test if everything is OK
    OpenClipboard(NULL);
    EmptyClipboard();
    SetClipboardData(CF_BITMAP, hbmp);
    CloseClipboard();

    // cleanup
    DeleteObject(hbmp);

你忘记在第5-6行用}关闭for循环了。 - Paul

1
这里的其他答案很有帮助,但我最终只用了一行代码就实现了它。
HBITMAP hBm = CreateBitmap(width,height,1,32,pixels); // 1 plane, 32 bits

希望对未来的读者有所帮助。
此外,如果您需要使用设备上下文,还有CreateCompatibleBitmapSetDIBits。在MSDN中,对于CreateDIBitmap,它说明:
调用fdwInit为CBM_INIT的CreateDIBitmap等同于调用CreateCompatibleBitmap函数以创建设备格式的DDB,然后调用SetDIBits函数将DIB位转换为DDB。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接