2

I have a perfectly working code that creates 32bpp bitmap and I need to change it so that 8bpp bitmap is created.

Here's the piece of code that creates 32bpp bitmap, draws into it, then it creates a bitmap file and store it into the vector of bytes:

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

And later the vector is stored into the file:

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

and here's the output image:

32bpp image

What I've tried:

First step was naturally replacing 32 with 8 in this line: bmi.bmiHeader.biBitCount = 32; which resulted into image filled with solid grey colour. Then based on this answer I made following changes:

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

changed into:

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

added this loop right before CreateDIBSection is called:

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

and when the bmi.bmiHeader is being written into the vector, the RGBQUAD array is included: so sizeof(BITMAPINFO256) expresses the size of the header.

The new code (full code here) produces this output:

8bpp image

Why the new image looks that way? What's going on there? What am I missing?

Any help will be appreciated.

4

3 回答 3

3

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

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

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

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

于 2013-02-13T17:54:41.177 回答
0

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

bmi.bmiHeader.biClrUsed = 256;
于 2013-02-13T17:41:04.953 回答
0

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

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

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

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

于 2013-02-13T18:03:03.433 回答