【问题标题】:Creating 8bpp bitmap with GDI and saving it as a file使用 GDI 创建 8bpp 位图并将其保存为文件
【发布时间】:2013-01-29 07:53:32
【问题描述】:

我有一个创建 32bpp 位图的完美工作代码,我需要更改它以便创建 8bpp 位图。

这是创建 32bpp 位图的一段代码,在其中绘制,然后它创建一个位图文件并将其存储到字节向量中:

// prepare bitmap:
BYTE* bitmap_data = NULL;
HDC hDC = GetDC(NULL);
HDC memHDC = CreateCompatibleDC(hDC);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = desiredWidth;                 // desiredWidth is 800
bmi.bmiHeader.biHeight = desiredHeight;               // desiredHeight is 202
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = (((desiredWidth * bmi.bmiHeader.biBitCount + 31) & ~31) >> 3) * desiredHeight;
HBITMAP bitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&bitmap_data, NULL, NULL);
ReleaseDC(NULL, hDC);
DeleteDC(hDC);

... // drawing into bitmap

// prepare bitmap file header:
BITMAPFILEHEADER bf;
memset(&bf, 0, sizeof(BITMAPFILEHEADER));
bf.bfType = MAKEWORD('B', 'M');
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + bmi.bmiHeader.biSize;
bf.bfSize = bf.bfOffBits + bmi.bmiHeader.biSizeImage;

// write bitmap file into the vector:
std::vector<BYTE> bitmapData;
bitmapData.insert(bitmapData.end(), (BYTE*)&bf, ((BYTE*)&bf) + sizeof(BITMAPFILEHEADER));
bitmapData.insert(bitmapData.end(), (BYTE*)&bmi.bmiHeader, ((BYTE*)&bmi.bmiHeader) + sizeof(BITMAPINFOHEADER));
bitmapData.insert(bitmapData.end(), bitmap_data, bitmap_data + bmi.bmiHeader.biSizeImage);

然后将向量存储到文件中:

std::ofstream of("picture.bmp", std::ofstream::out | std::ofstream::binary);
of.write((char*)&bitmapData[0], bitmapData.size());
of.close();

这是输出图像:

我尝试过的:

第一步自然是将32 替换为8 在此行中:bmi.bmiHeader.biBitCount = 32; 导致图像填充为纯灰色。然后基于this answer我做了如下修改:

BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));

改成:

struct BITMAPINFO256 {
    BITMAPINFOHEADER bmiHeader;
    RGBQUAD bmiColors[256];
} bmi;
memset(&bmi, 0, sizeof(BITMAPINFO256));

在调用 CreateDIBSection 之前添加了这个循环:

for (UINT i = 0; i < 256; i++) {
    bmi.bmiColors[i].rgbRed   = i;
    bmi.bmiColors[i].rgbGreen = i;
    bmi.bmiColors[i].rgbBlue  = i;
}

bmi.bmiHeader被写入向量时,RGBQUAD数组被包括在内:所以sizeof(BITMAPINFO256)表示头部的大小。

新代码 (full code here) 产生以下输出:

为什么新图片是这样的?那里发生了什么事?我错过了什么?

任何帮助将不胜感激。

【问题讨论】:

  • 使用调试器并从 2x2 位图开始,这样您就不会迷失在像素中。
  • @HansPassant:如果您看不出发生这种情况的任何原因,您至少能猜到这里可能出现的问题吗?我被困住了,无法真正找出这里出了什么问题。我使用的函数和结构的文档也没有帮助,我也没有找到任何可以从中找到灵感的好的教程。

标签: c++ visual-c++ bitmap gdi bitmapimage


【解决方案1】:

看起来像是对齐问题。确保更新 BITMAPFILEHEADER 中的 bfOffBits,使其指向像素数据的第一个字节。 (如果你不改变它,那么它可能指向调色板的开头。)

换句话说,sizeof(RGBQUAD)*256 也应该在这里添加:

bf.bfOffBits = sizeof(BITMAPFILEHEADER) + bmi.bmiHeader.biSize;

还要确保第一条扫描线从 DWORD 边界开始。也就是说,它与文件开头的偏移量应该是四个字节的倍数。同样,每条扫描线都应填充为四个字节的倍数。 (如果你的宽度是很好的偶数,你可能看不到这些问题。在你的测试用例中有一个奇数宽度的图像很好。)

【讨论】:

  • 终于!谢谢,确实很好看。太糟糕了,我只能投票一次:)
  • @LihO,别担心,我为你添加了另一个。对不起,如果我自己的回答浪费​​了您的时间。
  • @MarkRansom:浪费了我寻找错误的时间。问题已经解决了,这才是最重要的。也许其他一些程序员将来也会处理同样的问题,他们会发现这很有用:)
  • 如果在 Stack Overflow 上花半小时可以节省我几个小时的调试时间,那绝对值得:)
【解决方案2】:

您需要指定您附加的调色板的大小。现在它为零,所以调色板显示为图像中的第一组像素。

bmi.bmiHeader.biClrUsed = 256;

【讨论】:

  • 不幸的是,这没有帮助。结果是一样的。
  • 该字段是可选的。如果将其保留为零,则假定它是位深度允许的完整大小。
【解决方案3】:

您需要为您的图像生成调色板。 32 位图像中的每个像素都存储为 8 位,用于 Alpha、Red、Green 和 Blue。 在 8 位图像中,每个像素中的值是调色板中的 8 位索引。

你的 for(i=0..255) { bmi.bmiColors[i].rgbRed = i; ....} 代码生成一个灰度调色板。

如果整个图像显示为灰色,则听起来像是对齐错误,根据内存,调色板图像的宽度必须是 4 的倍数。

尝试从 Paint 中保存 SMALL 256 色(又名 8 位图像)并在十六进制编辑器中进行比较。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-12
    • 2021-11-06
    • 2017-01-25
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    相关资源
    最近更新 更多