c++ - 使用 GDI 创建 8bpp 位图并将其保存为文件

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

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

下面是创建 32bpp 位图、绘制到其中的代码,然后创建一个位图文件并将其存储到字节 vector 中:

// 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);

然后 vector 被存储到文件中:

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

这是输出图像:

32bpp image

我尝试过的:

第一步自然是将这一行中的 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被写入 vector 时,RGBQUAD数组被包含:因此sizeof(BITMAPINFO256)表示标题的大小。

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

8bpp image

为什么新图像看起来是这样的?那里发生了什么事?我错过了什么?

任何帮助将不胜感激。

最佳答案

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

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

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

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

关于c++ - 使用 GDI 创建 8bpp 位图并将其保存为文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14859138/

相关文章:

c++ - 我应该删除 C++ 类的字符串成员吗?

c++ - vcpkg安装包时编译失败如何编辑包文件?

visual-studio-2010 - Visual C++ 2010,如何找到失败的构建后步骤?

c# - 通过套接字在 C++ 和 C# 应用程序之间传输图像

c++ - Octave - 访问 octave_value_list 中的值

netbean 和 cygwin 的 C++ 编译错误?

c++ - 模板函数类型推导和运算符<<

c++ - 歧义时的类型名解析

java - 我如何在android中将位图转换为PDF格式

c# - 如何在 C# 中创建 256 兆像素的图像?