c - GDI 单色位图在每次创建 HBITMAP 时翻转位

标签 c winapi bitmap gdi alpha

我一直在尝试从文件加载 16 位 (A1R5G5B5) BMP 并使用其 alpha channel 作为位掩码。除了过去一周一直困扰我的一个问题之外,我一切都运转良好。当我使用 CreateDIBitmap 创建具有字节缓冲区的 1 位 channel 时,创建的位图仅在第一次绘制时使用其所有位的反转。在下一次绘制时,这些位会正确翻转到提供的数据,并在之后的所有绘制中保持这种状态。这种行为非常奇怪,并且发生在所有 Windows 版本上,我已经追踪到它与 HDC 和可能的 CreateDIBitmap 的某种设置有关。我已经尝试了很多方法,包括将 HDC 前后的前景色和背景色设置为许多值,但我尝试过的所有操作仍然保持这种行为。

这是一个可以尝试的 POC:

BITMAPINFOHEADER bmih;
BITMAPINFO bmi;
HBITMAP mask;
PBYTE data;
PBYTE alpha;
SIZE dimension;
void WhenCreated() // WM_CREATE
{
    dimension.cx=3;
    dimension.cy=1;
    alpha=(PBYTE)malloc(1);
    data=(PBYTE)malloc(1);
    alpha[0]=0xA0; // 0b10100000
}
#define BIN_SCAPE(B,A) (B[0]&(1<<A))?1:0
void WhenPresenting(HDC H) // WM_PAINT
{
   printf(
       "ALPHA:\t%i %i %i\n",
       BIN_SCAPE(alpha,7),
       BIN_SCAPE(alpha,6),
       BIN_SCAPE(alpha,5)
    );
    HDC memory;
    HBITMAP matter;
    memory=CreateCompatibleDC(NULL);
    memset(&bmi,0x0,sizeof(BITMAPINFO));
    bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth=dimension.cx;
    bmi.bmiHeader.biHeight=dimension.cy;
    bmi.bmiHeader.biPlanes=1;
    bmi.bmiHeader.biBitCount=1;
    bmi.bmiHeader.biCompression=BI_RGB;
    memset(&bmih,0x0,sizeof(BITMAPINFOHEADER));
    bmih.biSize=sizeof(BITMAPINFOHEADER);
    bmih.biWidth=bmi.bmiHeader.biWidth;
    bmih.biHeight=bmi.bmiHeader.biHeight;
    mask=CreateDIBitmap(
        memory,
        &bmih,
        CBM_INIT,
        alpha,
        &bmi,
        DIB_RGB_COLORS
    );
    SelectObject(memory,mask);
    GetDIBits(memory,mask,0,1,data,&bmi,DIB_RGB_COLORS);
    printf(
       "DATA:\t%i %i %i\n",
       BIN_SCAPE(data,7),
       BIN_SCAPE(data,6),
       BIN_SCAPE(data,5)
     );
     StretchBlt(
         H,
         0,0,128,128,
         memory,
         0,0,dimension.cx,dimension.cy,
         SRCCOPY
     );
     DeleteDC(memory);
     DeleteObject(mask);
}

当程序加载时,显示的数据与给定的数据相反,后续绘制会导致数据符合控制台输出中提供的数据,肯定会发生位翻转。我的猜测是,提供的第一个 HDC 可能使用与第一个之后的所有调色板不同的调色板,这会导致此行为?

最佳答案

现在一切都有意义了,调色板正在发生变化。

"biBitCount member is less than 16, the biClrUsed member specifies the actual number of colors the graphics engine or device driver accesses." (from msdn)

如果您在 CreateDIBitmap 中使用颜色 HDC,您将得到黑色的颜色,并且该颜色会在每次重新绘制时发生变化,这会让您感到害怕,直到您明白这是因为您尚未为该颜色设置调色板HBITMAP 因为当每个 HDC 制作时,除非指定,否则其调色板是未定义的。您可以使用 SetDIBits,但如果您想在 CreateDIBitmap 期间完成它,请尝试以下操作:

PBITMAPINFO pbmi;
RGBQUAD palette[2];
{
// this will give you white (1) and black (0)
palette[0].rgbBlue=0x00;
palette[0].rgbGreen=0x00;
palette[0].rgbRed=0x00;
palette[1].rgbBlue=0xFF;
palette[1].rgbGreen=0xFF;
palette[1].rgbRed=0xFF;
// using a PBITMAPINFO in order to allocate room for palette
pbmi=(PBITMAPINFO)LocalAlloc(LPTR,sizeof(BITMAPINFO)+sizeof(RGBQUAD)*2); // this technically allocates an extra RGBQUAD
pbmi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth=dimension.cx;
pbmi->bmiHeader.biHeight=dimension.cy;
pbmi->bmiHeader.biPlanes=1;
pbmi->bmiHeader.biBitCount=1;
pbmi->bmiHeader.biCompression=BI_RGB;
pbmi->bmiHeader.biClrUsed=2; // palette is two colors long
pbmi->bmiHeader.biClrImportant=2;
memcpy(pbmi->bmiColors,palette,sizeof(RGBQUAD)*2);
mask=CreateDIBitmap(
    memory,
    &bmih,
    CBM_INIT,
    alpha,
    pbmi,
    DIB_RGB_COLORS
);
}

关于c - GDI 单色位图在每次创建 HBITMAP 时翻转位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29411436/

相关文章:

android - 位图不完全适合android中的矩形

C - 指针和引用

windows - 窗口定位导致 Windows 10 上窗口周围的空间

android - setImageViewBitmap 在 android 小部件中不起作用

c# - 将所有者设置为来自另一个流程表单的表单

windows - 如何在 Windows 中生成 OSD?

java - 如何在位图 Java 中创建文本周围的轮廓

c - 如何重新打开一个关闭的文件描述符

c++ - 如何链接到共享库的旧版本

c - 客户端和服务器之间的 OpenSSL 通信