首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WinAPI/GDI:如何使用GetDIBits()为位图合成颜色表?

WinAPI/GDI:如何使用GetDIBits()为位图合成颜色表?
EN

Stack Overflow用户
提问于 2017-10-04 10:15:30
回答 2查看 1.9K关注 0票数 1

我发现很难理解GetDIBits()函数的MSDN站点下面的摘录:

如果BITMAPINFOHEADER,并且BITMAPINFO的位计数成员初始化为零,则GetDIBits将填充不带颜色表的结构或。此技术可用于查询位图属性。

问题1:“BITMAPINFO的位计数成员”是什么意思?这是不是意味着some_bmi.bmiHeader.biBitCount

问题2:"GetDIBits填充BITMAPINFOHEADER结构或没有颜色表的BITMAPCOREHEADER“是什么意思?有什么颜色的表格来填充这些结构?它们中似乎没有一个与颜色表相关的成员。是关于数组some_bmi.bmiColors吗?

问题3:是否有一种方法可以使用GetDIBits()来获取位图的颜色表(即数组映射到颜色的索引)?

编辑:

从目前为止的评论来看,把问题分解成更小的部分似乎没有效果。我会尝试另一种方法。

这就是我从刚开始引用的MSDN中引用的部分中所理解的:

假设函数调用为GetDIBits(hdc, hbmp, uStartScan, cScanLines, lpvBits, lpbi, uUsage);如果lpvBits为NULL,而lpvBits->bmiHeader.biBitCount初始化为零,则GetDIBits()只填充lpbi->bmiHeader,而lpbi->bmiColors不被修改。

这是理解它的正确方法吗?如果是这样的话,是否有一种方法可以让GetDIBits()填充lpbi->bmiColors,例如将lpvBits->bmiHeader.biBitCount初始化为位图的位深度?

我尝试测试问题1的假设如下,但是GetDIBits()在这种情况下失败了:

代码语言:javascript
复制
void test(HWND hWnd) {
    // get a memory DC
    HDC hdc = GetDC(hWnd);
    HDC hdcmem = CreateCompatibleDC(hdc); // has 1x1 mono bitmap selected 
                                          // into it initially by default
    // select a 16x16 mono bmp into it
    const int bmp_h = 16, bmp_w = 16;
    const int bits_per_px = 1;
    HBITMAP hbmp = CreateCompatibleBitmap(hdcmem, bmp_h, bmp_w); // 16x16 mono bitmap
    HGDIOBJ hOldBmp = SelectObject(hdcmem, hbmp);

    // initialize BITMAPINFO ptr
    // (make sure to allocate a buffer large enough for 2 RGBQUADs 
    // in case color table is retured by GetDIBits() call)
    const int bmi_buf_sz =
        sizeof(BITMAPINFO) + sizeof(RGBQUAD) * (1 << bits_per_px); // 2 + 1(extra) RGBQUADs allocated for pbmi->bimColors
    BYTE* p_bmi_buf = new BYTE[bmi_buf_sz];
    BITMAPINFO* pbmi = reinterpret_cast<BITMAPINFO*>(p_bmi_buf);
    ZeroMemory(pbmi, bmi_buf_sz);

    // populate BITMAPINFO
    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biBitCount = 1; // set to 1 just to see if GetDIBits()
                                    // fills in pbmi->bmiColors too
                                   // (when set to 0, only pbmi->bmiHeader is filled)
    if(!GetDIBits(hdcmem, hbmp,
                  0, (UINT)bmp_h,
                  NULL, pbmi, DIB_PAL_COLORS)) {
        MessageBox(hWnd, L"GetDIBits() failed!", NULL, MB_OK);
    }

    // clean-up
    delete[] p_bmi_buf;
    SelectObject(hdcmem, hOldBmp); // push hbmp out
    DeleteObject(hbmp);
    DeleteDC(hdcmem);
    ReleaseDC(hWnd, hdc);
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-05 01:47:00

获取颜色表的最简单方法是使用GetDibColorTable

代码语言:javascript
复制
HDC memdc = CreateCompatibleDC(NULL);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
int ncolors = 1 << bm.bmBitsPixel;
std::vector<RGBQUAD> rgb(ncolors);
if(ncolors == GetDIBColorTable(memdc, 0, ncolors, &rgb[0]))
{
    //success!
}
SelectObject(memdc, oldbmp);
DeleteDC(memdc);

回到您的问题:GetDIBits希望pbmi包含所有零( bmiHeader.biSize成员除外)。所以pbmi->bmiHeader.biBitCount应该是零。

代码语言:javascript
复制
//pbmi->bmiHeader.biBitCount = 1; <<= comment out this line

这应该是可行的;但是,正如文档中所述,这只会填充信息头,而不是颜色表。要获得颜色表,您必须再次调用GetDIBits,为dib位分配足够的资源。

此外,DIB_PAL_COLORS将返回调色板索引数组(我不确定您能用它做什么)。您可能需要使用DIB_RGB_COLORS标志,它将在出现颜色表时返回实际颜色。

使用从文件中加载的位图尝试这样做:

代码语言:javascript
复制
HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"8bit.bmp",
    IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);

BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);

//don't continue for hi color bitmaps
if(bm.bmBitsPixel > 8) return;

int ncolors = 1 << bm.bmBitsPixel;
HDC memdc = CreateCompatibleDC(NULL);
int bmpinfo_size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ncolors;
std::vector<BYTE> buf(bmpinfo_size);
BITMAPINFO* bmpinfo = (BITMAPINFO*)buf.data();
bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
if(!GetDIBits(memdc, hbitmap, 0, bm.bmHeight, NULL, bmpinfo, DIB_RGB_COLORS))
{
    DWORD err = GetLastError();
    //...
}

int dibsize = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight;
std::vector<BYTE> dib(dibsize);
if(!GetDIBits(memdc, hbitmap, 0, (UINT)bm.bmHeight, &dib[0], bmpinfo, DIB_RGB_COLORS))
{
    DWORD err = GetLastError();
    //...
}

现在,bmpinfo->bmiColors应该包含与前面显示的rgb数组相同的值。

BITMAPINFOBITMAPINFOHEADER之间的可能混淆

上述结构声明如下:

代码语言:javascript
复制
typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;

typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

因此BITMAPINFO没有biBitCount成员。但它确实有bmiHeader.biBitCount成员。

当您声明一个BITMAPINFOHEADER变量时,您必须设置biSize成员(这就是biSize的版本控制思想)。当您声明一个BITMAPINFO变量时,您必须确保它是BITMAPINFOHEADER

请注意,大多数情况下,您不必担心调色板。例如,LoadImage将返回一个兼容的位图(如果没有指定LR_CREATEDIBSECTION),您可以立即使用该位图。

票数 1
EN

Stack Overflow用户

发布于 2017-10-06 09:03:49

虽然接受的答案涵盖了细节,但这更多的是对执行部分中的问题的直接回答。

假设函数调用为GetDIBits(hdc, hbmp, uStartScan, cScanLines, lpvBits, lpbi, uUsage)

问题-1:

这是正确的。“BITMAPINFO的位计数成员”指的是lpbi->bmiHeader.biBitCount

问题2:

当调用GetDIBits()以获取DIB位(即使用非空lpvBits和适当初始化的lpbi->bmiHeader)时,lpbi->bmiColors也会被颜色表填充(如果位深度小于24 bpp)。不幸的是,这一点在该职能的文档中并不清楚。考虑到这一点,引用的部分的意思是,当lpvBits为NULL而lpbi->bmiHeader.biBitCount为零时,函数只填充lpbi->bmiHeader only不修改lpbi->bimColor(与调用函数以获取DIB位时不同)。

问题3:

您可以在lpbi->bmiColors中获得返回颜色表的函数(对于具有8-bbp或更少的位图),方法是使用非空lpvBits和适当初始化的lpbi->bmiHeader调用它。IOW,当您像往常一样调用函数获取DIB位时,它也会填充lpbi->bmiColors

编辑部分的问题:

如果lpvBits为NULL,而lpvBits->bmiHeader.biBitCount被初始化为零,则GetDIBits()只填充lpbi->bmiHeader,而lpbi->bmiColors不被修改。 这是理解它的正确方法吗?

是的,这是正确的。

如果是这样的话,是否有一种方法可以让GetDIBits()填充lpbi->bmiColors,例如将lpvBits->bmiHeader.biBitCount初始化为位图的位深度?

是的,有一种方法可以让函数返回颜色表,但正如在答复Q2中所解释的,仅将lpvBits->bmiHeader.biBitCount初始化为位图的位深度是不够的。必须对lpvBits->bmiHeader的所有成员进行适当的初始化,lpvBits必须为非空。这基本上与调用函数获取DIB位相同。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46562369

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档