Win32 BITMAPINFO 如何可靠地为 RGBQUAD 数组/颜色 table(bmiColors 字段)分配足够的内存?

Win32 BITMAPINFO how to reliably allocate enough memory for RGBQUAD array / color table (bmiColors field)?

我有一个 HBITMAP,我想从 HBITMAP 中获得完整的 BITMAPINFO 结构和相应的 RGBQUAD 数组,而不仅仅是 BITMAPINFOHEADER

这是BITMAPINFO的结构:

typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
} BITMAPINFO, *LPBITMAPINFO, *PBITMAPINFO;

我可以使用 GetDIBitsNULL 指针来引用位缓冲区,从而 只是 BITMAPINFOHEADER 进入我分配的 BITMAPINFO记忆.

我不知道的是如何可靠地根据 BITMAPINFOHEADER 数据计算 RGBQUAD 数组的大小,以便我可以分配足够的 space 允许 GetDIBits 存储整个数组。文档(以及我发现的许多代码示例)非常混乱,甚至在有关该主题的某些部分上存在冲突。

重新阅读文档、wiki、一些博客文章和其他来源(none 其中内部一致)这似乎是确定 Color Table 中元素数量的最佳方法(RGBQUAD数组):

对像素缓冲区使用 GetDIBitsNULL 值以获得 BITMAPINFO:

BITMAPINFO* pbinfo = ( BITMAPINFO* ) malloc( sizeof( BITMAPINFOHEADER ) );
...
pbinfo->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
GetDIBits( hScreenMemory, hBitmap, 0, 0, NULL, pbinfo, DIB_RGB_COLORS );

然后基于:

  1. pbinfo->bmiHeader.biBitCount
  2. pbinfo->bmiHeader.biClrUsed
  3. pbinfo->bmiHeader.biCompression

我们可以使用以下逻辑确定 RGBQUAD 个数组元素的数量:

DWORD ColorTableLength( BITMAPINFOHEADER* h ) {
  DWORD result        = 0;

  DWORD biClrUsed     = h->biClrUsed;
  WORD  biBitCount    = h->biBitCount;
  DWORD biCompression = h->biCompression;

  switch ( biBitCount ) {
    case 24:
      result = biClrUsed;
      break;
    case 16:
    case 32:
      if ( biCompression == BI_RGB )
        result = biClrUsed;
      else if ( biCompression == BI_BITFIELDS )
        result = 3;
      break;
    default: // for 0, 1, 2, 4, and 8
      if ( biClrUsed == 0 )
        result = ( 1 << biBitCount ); // 2^biBitCount
      else
        result = biClrUsed;
      break;
  }

  return result;
}

这是 "lowest common denominator",它与我在网上找到的许多资源和示例是一致的(但仍有可能并非对所有情况都适用)。

需要为RGBQUAD数组分配的字节数可以通过sizeof(RGBQUAD) * ColorTableLength( &... )计算得到。

在某些情况下,RGBQUAD 数组被 3 个 DWORD 替换,但是 DWORDRGBQUAD 的大小相同,因此哪个 sizeof 我们乘以 ColorTableLength 值。

我不确定为什么标准没有定义一个字段来明确说明为 Color Table / RGBQUAD array / RGB DWORDs 保留了多少字节但是看起来就是这样。

biClrUsed 字段应该有这个目的,但并非在所有情况下都如此。