获取图标的位图数据

  • 分两次使用GetDIBits(),以便于正确设置缓存的大小
  • 正确设置BITMAPINFO的大小,否则就会报堆栈溢出错误
ICONINFO info = { 0 };GetIconInfo(hIcon, &info)HDC bmp, maskbmp;bmp = CreateCompatibleDC(NULL);SelectObject(bmp, info.hbmColor);maskbmp = CreateCompatibleDC(NULL);SelectObject(maskbmp, info.hbmMask);BYTE* lpvBits = NULL;int nRet = 2;// 正确设置 BITMAPINFO 的大小,否则读取位图后头信息将无法存储BITMAPINFO bmpInfo = { 0 };bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);BITMAP bm;GetObject(info.hbmMask, sizeof(bm), &bm);int ncolors = 1 << bm.bmBitsPixel;int bmpinfo_size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ncolors;std::vector buf(bmpinfo_size);BITMAPINFO* bmpinfo = (BITMAPINFO*)buf.data();bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);// 第一次调用,获取位图数据需要的多大的缓冲区存储nRet = ::GetDIBits(maskbmp, info.hbmMask, 0, 0, NULL, bmpinfo, DIB_RGB_COLORS);// 设置正确的缓冲区存储位图数据lpvBits = new BYTE[bmpinfo->bmiHeader.biSizeImage];// 第二次调用,将位图数据存入缓冲区中nRet = ::GetDIBits(maskbmp, info.hbmMask, 0, nHeight, lpvBits, bmpinfo, DIB_RGB_COLORS);// 按照每行有32列的形式打印,注意bmp位图数据是倒向的,打印出来的图像是实际图像的逆向for (int i = 0; i bmiHeader.biSizeImage; i++){    if (i % 4 == 0) {        printf_s("\n");    }    BYTE tmp = lpvBits[i];    for (int j = 0; j < 8; j++)    {        bool res = tmp & 0x80;        tmp = tmp << 1;        printf_s("%d", res);    }}printf_s("\n");

使用注意

BITMAPINFOHEADER 结构后跟调色板条目或颜色掩码数组。 规则取决于 biCompression 的值。Link

  • 如果 biCompression 等于 BI_RGB并且 位图使用 8 bpp 或更少,则位图在 BITMAPINFOHEADER 结构之后立即具有颜色表。 颜色表由 RGBQUAD 值数组组成。 数组的大小由 biClrUsed 成员提供。 如果 biClrUsed 为零,则数组包含给定 bitdepth 的最大颜色数;即 2^biBitCount 颜色。
  • 如果 biCompression 是视频 FOURCC,则视频格式隐含颜色表的存在。 不应假定位深度为 8 bpp 或更少时存在颜色表。 但是,某些旧组件可能假定存在颜色表。 因此,如果要分配 BITMAPINFOHEADER 结构,建议在位深度为 8 bpp 或更少时为颜色表分配空间,即使不使用颜色表也是如此。
  • 请注意,如果位图使用颜色表或颜色掩码,则整个格式结构的大小 (BITMAPINFOHEADER 加上颜色信息) 不等于 sizeof(BITMAPINFOHEADER) 或 sizeof(BITMAPINFO)。 必须计算每个实例的实际大小。

根据Windows API官方介绍文档可以看出,使用GetDIBits()必须为BITMAPINFO计算实际的大小,否则从内存读取了较多内容,但是没有足够的空间去存放,在释放内存时就会出错,如堆栈错误或堆错误;当大于8bpp时,颜色表占用的内存空间剧增,机器内存很可能无法满足,因此不再建议使用此函数。