在Winapi中创建24位BITMAP

时间:2019-06-05 14:15:07

标签: windows winapi bitmap imagemagick hbitmap

我正在使用以下代码将ImageMagick图像转换为32位HBITMAP:

BITMAP bitmap;
std::memset(&bitmap, 0, sizeof(bitmap));

bitmap.bmType = 0;
bitmap.bmWidth = image->image()->columns;
bitmap.bmHeight = image->image()->rows;
bitmap.bmWidthBytes = 4 * bitmap.bmWidth;
bitmap.bmPlanes = 1;
bitmap.bmBitsPixel = 32;
bitmap.bmBits = NULL;

const size_t size = bitmap.bmWidthBytes * bitmap.bmHeight;
auto buffer = (HANDLE)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);

RGBQUAD *bitmap_bits = (RGBQUAD *) GlobalLock((HGLOBAL) buffer);
register RGBQUAD *q = bitmap_bits;

for (size_t y = 0; y < image->image()->rows; y++)
{
    register auto p = GetVirtualPixels(image->image(), 0, y, image->image()->columns, 1, exception);
    if (!p) break;

    for (size_t x = 0; x < image->image()->columns; x++)
    {
        q->rgbRed = ScaleQuantumToChar(GetPixelRed(image->image(), p));
        q->rgbGreen = ScaleQuantumToChar(GetPixelGreen(image->image(), p));
        q->rgbBlue = ScaleQuantumToChar(GetPixelBlue(image->image(), p));
        q->rgbReserved = 0;

        p += GetPixelChannels(image->image());
        q++;
    }
}

bitmap.bmBits = bitmap_bits;
HBITMAP hbmp = CreateBitmapIndirect(&bitmap);

效果很好,但是我想通过使用深度较小的图像来节省一些内存。不幸的是,我什至无法使其与24位图像一起使用。我修改了代码,使其看起来像这样:

BITMAP bitmap;
std::memset(&bitmap, 0, sizeof(bitmap));

bitmap.bmType = 0;
bitmap.bmWidth = image->image()->columns;
bitmap.bmHeight = image->image()->rows;
bitmap.bmWidthBytes = ((bitmap.bmWidth * 24 + 31) / 32) * 4;
bitmap.bmPlanes = 1;
bitmap.bmBitsPixel = 24;
bitmap.bmBits = NULL;

const size_t length = bitmap.bmWidthBytes * bitmap.bmHeight;
auto buffer = (HANDLE)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, length);

RGBTRIPLE *bitmap_bits = (RGBTRIPLE *) GlobalLock((HGLOBAL) buffer);
register RGBTRIPLE *q = bitmap_bits;

for (size_t y = 0; y < image->image()->rows; y++)
{
    register auto p = GetVirtualPixels(image->image(), 0, y, image->image()->columns, 1, exception);
    if (!p) break;

    for (size_t x = 0; x < image->image()->columns; x++)
    {
        q->rgbtRed = ScaleQuantumToChar(GetPixelRed(image->image(), p));
        q->rgbtGreen = ScaleQuantumToChar(GetPixelGreen(image->image(), p));
        q->rgbtBlue = ScaleQuantumToChar(GetPixelBlue(image->image(), p));

        p += GetPixelChannels(image->image());
        q++;
    }
}

bitmap.bmBits = bitmap_bits;
HBITMAP hbmp = CreateBitmapIndirect(&bitmap);

但是看来这段代码无法产生有效的位图。我在做什么错了?

1 个答案:

答案 0 :(得分:1)

您未考虑步幅/对齐方式。每行都需要DWORD对齐。

  

Calculating Surface Stride

     

在未压缩的位图中,步幅是指从一行像素的开始到下一行的开始所需的字节数。图像格式定义了图像的最小步幅。此外,图形硬件可能要求包含图像的表面有较大的跨度。   对于未压缩的RGB格式,最小跨度始终是图像宽度(以字节为单位),四舍五入到最接近的DWORD。您可以使用以下公式计算步幅:

     

stride = ((((biWidth * biBitCount) + 31) & ~31) >> 3)

您需要修复访问缓冲区中RGBTRIPLE的方式。

在“ x循环”之前,您应该执行类似q = (RGBTRIPLE*) (((char*)bitmap_bits) + (y * bitmap.bmWidthBytes));

的操作

CreateBitmapIndirect创建的DDB可能不是最佳选择,请创建DIB:

#define CalcStride(w, bpp) ( ((((w) * (bpp)) + 31) & ~31) >> 3 )
static void SetPixel24(UINT w, void*bits, UINT x, UINT y, COLORREF cr)
{
    RGBTRIPLE*p = ((RGBTRIPLE*) ( ((char*)bits) + (y * CalcStride(w, 24)) )) + x;
    p->rgbtRed = GetRValue(cr);
    p->rgbtGreen = GetGValue(cr);
    p->rgbtBlue = GetBValue(cr);
}
void Silly24BPPExample()
{
    HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, WC_STATIC, 0, WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_OVERLAPPEDWINDOW|SS_BITMAP|SS_REALSIZECONTROL, 0, 0, 99, 99, 0, 0, 0, 0);
    const INT w = 4, h = 4, bpp = 24;
    BITMAPINFO bi;
    ZeroMemory(&bi, sizeof(bi));
    BITMAPINFOHEADER&bih = bi.bmiHeader;
    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = w, bih.biHeight = -h;
    bih.biPlanes = 1, bih.biBitCount = bpp;
    bih.biCompression = BI_RGB;
    void*bits;
    HBITMAP hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
    for (UINT x = 0; x < w; ++x)
        for (UINT y = 0; y < h; ++y)
            SetPixel24(w, bits, x, y, RGB(255, 0, 0)); // All red
    SetPixel24(w, bits, 0, 0, RGB(0, 0, 255)); // except one blue
    SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBmp);
    for (MSG msg; IsWindow(hWnd) && GetMessage(&msg, 0, 0, 0); ) DispatchMessage(&msg);
    // DeleteObject(...)
}