Create 8bpp bitmap with GDI and save it as file

I have a perfectly working code that creates a 32 bit bitmap and I need to modify it so that an 8bpp bitmap is generated.

Here's a piece of code that creates a 32bpp bitmap, pulls into it, then creates a bitmap file and saves it to a byte 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);

      

And later the vector is saved in 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 have tried:

The first step naturally replaced 32

with 8

in this line: bmi.bmiHeader.biBitCount = 32;

which resulted in the image being filled with solid gray. Then, based on this answer , I made the following changes:

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

      

changed to:

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

      

added this loop before getting called CreateDIBSection

:

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

      

and when bmi.bmiHeader

written to vector the array is included RGBQUAD

: therefore sizeof(BITMAPINFO256)

expresses the size of the header.

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

8bpp image

Why does the new image look like this? What is happening there? What am I missing?

Any help would be appreciated.

+2


source to share


3 answers


It looks like an alignment problem. Make sure you update bfOffBits in BITMAPFILEHEADER to point to the first byte of pixel data. (If you don't change it, it probably indicates the beginning of the palette.)

In other words, add here sizeof(RGBQUAD)*256

:



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

      

Also make sure the first scan line starts with a DWORD boundary. That is, its offset from the beginning of the file must be a multiple of four bytes. Likewise, each scan line must be padded to four bytes. (You may not see these problems if your widths are good even numbers. It's good to have an odd width image among your test cases.)

+3


source


You need to specify the size of the attached palette. It is now zero, so the palette is displayed as the first group of pixels in your image.



bmi.bmiHeader.biClrUsed = 256;

      

0


source


You need to create a palette for your image. Each pixel in a 32-bit image is stored as 8-bit for Alpha, Red, Green, and Blue. Where, as in an 8-bit image, the value in each pixel is an 8-bit index in the palette.

Your for (i = 0..255) {bmi.bmiColors [i] .rgbRed = i; ....} a gray scale is generated.

If the whole image comes out gray, then it sounds like an alignment error, from memory the width of the paletted image should be a multiple of 4.

Try to save the SMALL 256 color (aka 8-bit image) from Paint and compare in a hex editor.

0


source







All Articles