0

As it is descriped in the title. The code is shown as below

#include <Windows.h>
#include <string>

using namespace std;

#define WIDTH  90
#define HEIGHT 180

class Test {
public:
    static bool ShowWithBMP(string filename) {
        BITMAPFILEHEADER    bmfh;           // bitmap file header  
        BITMAPINFOHEADER    bmih;           // bitmap info header (windows)  

        const int OffBits = 54;

        bmfh.bfReserved1 = 0;
        bmfh.bfReserved2 = 0;
        bmfh.bfType = 0x4d42;   //"BM"
        bmfh.bfOffBits = OffBits;      
        bmfh.bfSize = WIDTH * HEIGHT * 3 + OffBits;

        memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
        bmih.biSize = 40;               
        bmih.biPlanes = 1;
        bmih.biSizeImage = 0;
        bmih.biBitCount = 24;
        bmih.biCompression = BI_RGB;
        bmih.biWidth = WIDTH;
        bmih.biHeight = HEIGHT;


        FILE* file = fopen(filename.c_str(), "w");
        fwrite((const void*)&bmfh, sizeof(BITMAPFILEHEADER), 1, file);
        fwrite((const void*)&bmih, sizeof(BITMAPINFOHEADER), 1, file);


        for (int row = 0; row < HEIGHT; ++row)
        {
            // RGB 24 BITS  
            for (int col = 0; col < WIDTH; ++col)
            {
                int index = row * WIDTH + col;
                RGBTRIPLE pix;

                pix.rgbtRed = 0;
                pix.rgbtGreen = 0;
                pix.rgbtBlue = 0; 
                fwrite((const void*)&pix, sizeof(RGBTRIPLE), 1, file);
            }
        }
        fclose(file);
        return true;
    }   
};

int main() {
    Test::ShowWithBMP("test.bmp");
    system("pause");
    return 0;
}

When I set WIDTH to 90 and HEIGHT to 180, the output file "test.bmp" seems to be a wrong file and I can't open it, but when set WIDTH to 100 and HEIGHT to 180, it becomes correct. What's wrong with it? Is there any limitation when I try to write a bmp file with C++?

Mills
  • 3
  • 1
  • 1
    Do not prefer `#define` for constants in `c++` instead prefer `const type name;` (e.g., `const int WIDTH = 90;`). Eventually, you probably won't want those to be constants anyway. – James Adkison Oct 30 '15 at 14:24
  • Possible duplicate of [Bitmap file header size](http://stackoverflow.com/questions/12336994/bitmap-file-header-size) – Jongware Oct 30 '15 at 14:27
  • Thanks a lot.I have corrected it. But what's the problem with the bmp file?I copy the code in the website, then I see the MSDN and rewrite it by myself, but it doesn't work..... T T – Mills Oct 30 '15 at 14:30

2 Answers2

2

From memory (about 20 years ago), BMP format contains in essence DIB format for the pixel data. Each pixel row needs to be a multiple of 4 bytes (i.e. a whole number of DWORDs); therefore you will need to ensure you have appropriate padding at the end of each row.

abligh
  • 24,573
  • 4
  • 47
  • 84
0

Here is your function with fixes that should work

static bool ShowWithBMP(string filename) {
    BITMAPFILEHEADER    bmfh;           // bitmap file header  
    BITMAPINFOHEADER    bmih;           // bitmap info header (windows)  

    const int OffBits = 54;

    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfType = 0x4d42;   //"BM"
    bmfh.bfOffBits = OffBits; 

    DWORD bitmapLineSize = ((WIDTH * 3 + 3) / 4) * 4; //size of a row in bytes (ceil(WIDTH * 3 / 4) * 4)
    bmfh.bfSize = bitmapLineSize * HEIGHT + OffBits;

    memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
    bmih.biSize = 40;               
    bmih.biPlanes = 1;
    bmih.biSizeImage = 0;
    bmih.biBitCount = 24;
    bmih.biCompression = BI_RGB;
    bmih.biWidth = WIDTH;
    bmih.biHeight = HEIGHT;

    DWORD paddingSize = bitmapLineSize - 3 * WIDTH;//extra 0 to 3 bytes per line to write

    FILE* file = fopen(filename.c_str(), "w");
    fwrite((const void*)&bmfh, sizeof(BITMAPFILEHEADER), 1, file);
    fwrite((const void*)&bmih, sizeof(BITMAPINFOHEADER), 1, file);


    for (int row = 0; row < HEIGHT; ++row)
    {
        // RGB 24 BITS  
        for (int col = 0; col < WIDTH; ++col)
        {
            int index = row * WIDTH + col;
            RGBTRIPLE pix;

            pix.rgbtRed = 0;
            pix.rgbtGreen = 0;
            pix.rgbtBlue = 0; 
            fwrite((const void*)&pix, sizeof(RGBTRIPLE), 1, file);
        }

        if (paddingSize > 0)
        {
            //write out the rest of the bitmap line filled with zeros
            DWORD padding = 0;
            fwrite((const void*)&padding, paddingSize, 1, file);
        }
    }
    fclose(file);
    return true;
}   
0kcats
  • 672
  • 5
  • 16
  • Thank you very much! It helps me a lot – Mills Oct 30 '15 at 14:49
  • You welcome. It is preferred to have the whole bitmap or a portion of it (N lines) allocated and you'd fill the memory buffer first then write it out to disk instead of writing out pixel by pixel. – 0kcats Oct 30 '15 at 15:45
  • fwrite has mechanism to save the data in buffer until the buffer is full or when use fflush or fclose, so I think both are ok X0 – Mills Oct 30 '15 at 18:36