Search code examples
c++bitmapbmpbitsetmonochrome

Create monochrome BMP from bitset


I could need some help to figure out how to feed the proc below. I need to write a monochrome BMP file. The code below (its from: How to Save monochrome Image as bmp in windows C++ ?) looks like to be able to do this. I'm now stuck on how to convert a std::bitset or preferably boost::dynamic_bitset into this byte* format. All of my attempts so far failed, I wasn't able to write something like an 8x8 checker pattern into the BMP. The proc creates the BMP and it is readable by Photoshop, but the content is a mess. So any suggestions how to solve this are appreciated!

Save1BppImage(byte* ImageData, const char* filename, long w, long h){

    int bitmap_dx = w; // Width of image
    int bitmap_dy = h; // Height of Image

    // create file
    std::ofstream file(filename, std::ios::binary | std::ios::trunc);
    if(!file) return;

    // save bitmap file headers
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER * infoHeader;
    infoHeader = (BITMAPINFOHEADER*) malloc(sizeof(BITMAPINFOHEADER) );
    RGBQUAD bl = {0,0,0,0};  //black color
    RGBQUAD wh = {0xff,0xff,0xff,0xff}; // white color


    fileHeader.bfType      = 0x4d42;
    fileHeader.bfSize      = 0;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER));

    infoHeader->biSize          = (sizeof(BITMAPINFOHEADER) );
    infoHeader->biWidth         = bitmap_dx;    
    infoHeader->biHeight        = bitmap_dy;
    infoHeader->biPlanes        = 1;
    infoHeader->biBitCount      = 1;
    infoHeader->biCompression   = BI_RGB; //no compression needed
    infoHeader->biSizeImage     = 0;
    infoHeader->biXPelsPerMeter = 0;
    infoHeader->biYPelsPerMeter = 0;
    infoHeader->biClrUsed       = 2;
    infoHeader->biClrImportant  = 2;

    file.write((char*)&fileHeader, sizeof(fileHeader)); //write bitmapfileheader
    file.write((char*)infoHeader, (sizeof(BITMAPINFOHEADER) )); //write bitmapinfoheader
    file.write((char*)&bl,sizeof(bl)); //write RGBQUAD for black
    file.write((char*)&wh,sizeof(wh)); //write RGBQUAD for white

    int bytes = (w/8) * h ; //for example for 32X64 image = (32/8)bytes X 64 = 256;

    file.write((const char*)ImageData, bytes);

    file.close();
}

-edit-

an naive approach of mine was something like this

    byte test[64];
for(unsigned int i=0; i<64; ++i)
    if(i % 2)
        test[i] = 0;
    else
        test[i] = 1;

Save1BppImage(test, "C:/bitmap.bmp", 8, 8);

Solution

  • The code you have is very close. Here are a few thoughts about where it might be off.

    The bfOffBits value must include the size of the palette.

    fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER)) + 2*sizeof(RGBQUAD);
    

    Some software may interpret 0 as white and 1 as black, regardless of what the palette says. Even though the file format allows you to go either way, you're better off specifying the palette in that order and inverting your bits if necessary.

    Each row of a bitmap will start on a 4-byte boundary. If your bitmap width isn't a multiple of 32, you're going to need some padding between each row.

    BMP files are ordered from the bottom row to the top row, which is backwards from the way most people organize their arrays.

    The last two recommendations are combined to look something like this:

    int bytes_in = (w + 7) / 8;
    int bytes_out = ((w + 31) / 32) * 4;
    const char * zeros[4] = {0, 0, 0, 0};
    for (int y = h - 1;  y >= 0;  --y)
    {
        file.write(((const char *)ImageData) + (y * bytes_in), bytes_in);
        if (bytes_out != bytes_in)
            file.write(zeros, bytes_out - bytes_in);
    }