C++ Saving a Bitmap File

16,893

Solution 1

lpBits refers to an array of bytes with size lImageSize.

Each byte of the array will contain a single color component, in this order: B, G and R: each pixel takes three bytes, one for each color component.

Please note that the code you posted doesn't take into consideration the 4 bytes alignment of each image's row. Each image's row must be aligned on a 4 bytes boundary, so the correct formula for lImageSize is:

lImageSize = h * ((w * 3 + 3) & 0xfffffffc);

You can create the lpbits by yourself:

lpbits = new BYTE[lImageSize];

or by using CreateDIBSection() as stated in the answer from Logicrat

Solution 2

Commenting the code:

// lpBits stand for long pointer bits

// szPathName : Specifies the pathname        -> the file path to save the image
// lpBits    : Specifies the bitmap bits      -> the buffer (content of the) image
// w    : Specifies the image width
// h    : Specifies the image height
bool SaveImage(char* szPathName, void* lpBits, int w, int h) {
    // Create a new file for writing
    FILE* pFile = fopen(szPathName, "wb"); // wb -> w: writable b: binary, open as writable and binary
    if (pFile == NULL) {
        return false;
    }

    BITMAPINFOHEADER BMIH;                         // BMP header
    BMIH.biSize = sizeof(BITMAPINFOHEADER);
    BMIH.biSizeImage = w * h * 3;
    // Create the bitmap for this OpenGL context
    BMIH.biSize = sizeof(BITMAPINFOHEADER);
    BMIH.biWidth = w;
    BMIH.biHeight = h;
    BMIH.biPlanes = 1;
    BMIH.biBitCount = 24;
    BMIH.biCompression = BI_RGB;
    BMIH.biSizeImage = w * h * 3;

    BITMAPFILEHEADER bmfh;                         // Other BMP header
    int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize;
    LONG lImageSize = BMIH.biSizeImage;
    LONG lFileSize = nBitsOffset + lImageSize;
    bmfh.bfType = 'B' + ('M' << 8);
    bmfh.bfOffBits = nBitsOffset;
    bmfh.bfSize = lFileSize;
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0;

    // Write the bitmap file header               // Saving the first header to file
    UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile);

    // And then the bitmap info header            // Saving the second header to file
    UINT nWrittenInfoHeaderSize = fwrite(&BMIH, 1, sizeof(BITMAPINFOHEADER), pFile);

    // Finally, write the image data itself
    //-- the data represents our drawing          // Saving the file content in lpBits to file
    UINT nWrittenDIBDataSize = fwrite(lpBits, 1, lImageSize, pFile);
    fclose(pFile); // closing the file.

    return true;
}

Some improvement to substitute the C code with C++:

The improvement were:

  • Using std::string instead of char* that originally need to be const char*
  • Using vector instead of void* (could be a problem in the original code, if the width and height provided was wrong or miscalculated the program will read invalid memory because there is no notion of size of lpBits. The content of the file not need to change when saving, adding const-correctness
  • Using std::ofstream instead of FILE.

Code:

// lpBits stand for long point bits

// szPathName : Specifies the pathname        -> the file path to save the image
// lpBits    : Specifies the bitmap bits      -> the buffer (content of the) image
// w    : Specifies the image width
// h    : Specifies the image height
bool SaveImage(const std::string& szPathName, const std::vector<char>& lpBits, int w, int h) {
    // Create a new file for writing
    std::ofstream pFile(szPathName, std::ios_base::binary);
    if (!pFile.is_open()) {
        return false;
    }

    BITMAPINFOHEADER bmih;
    bmih.biSize = sizeof(BITMAPINFOHEADER);
    bmih.biWidth = w;
    bmih.biHeight = h;
    bmih.biPlanes = 1;
    bmih.biBitCount = 24;
    bmih.biCompression = BI_RGB;
    bmih.biSizeImage = w * h * 3;

    BITMAPFILEHEADER bmfh;
    int nBitsOffset = sizeof(BITMAPFILEHEADER) + bmih.biSize;
    LONG lImageSize = bmih.biSizeImage;
    LONG lFileSize = nBitsOffset + lImageSize;
    bmfh.bfType = 'B' + ('M' << 8);
    bmfh.bfOffBits = nBitsOffset;
    bmfh.bfSize = lFileSize;
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0;

    // Write the bitmap file header
    pFile.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER));
    UINT nWrittenFileHeaderSize = pFile.tellp();

    // And then the bitmap info header
    pFile.write((const char*)&bmih, sizeof(BITMAPINFOHEADER));
    UINT nWrittenInfoHeaderSize = pFile.tellp();

    // Finally, write the image data itself
    //-- the data represents our drawing
    pFile.write(&lpBits[0], lpBits.size());
    UINT nWrittenDIBDataSize = pFile.tellp();
    pFile.close();

    return true;
}
Share:
16,893
user3865729
Author by

user3865729

Updated on June 04, 2022

Comments

  • user3865729
    user3865729 almost 2 years

    so what I am trying to do is to have my program take a screenshot and save it on the computer. The part of actually taking the screenshot I will program later, and I am first trying to solve the problem of how to actually save a bmp file on the computer. I found the following code that would help me out with that:

    // szPathName : Specifies the pathname
    
    // lpBits    : Specifies the bitmap bits
    
    // w    : Specifies the image width
    
    // h    : Specifies the image height
    
    bool SaveImage(char* szPathName, void* lpBits, int w, int h)
    
    {
    
        //Create a new file for writing
    
        FILE *pFile = fopen(szPathName, "wb");
    
        if(pFile == NULL)
    
        {
    
            return false;
    
        }
    
            BITMAPINFOHEADER BMIH;
    
            BMIH.biSize = sizeof(BITMAPINFOHEADER);
    
            BMIH.biSizeImage = w * h * 3;
    
            // Create the bitmap for this OpenGL context
    
            BMIH.biSize = sizeof(BITMAPINFOHEADER);
    
            BMIH.biWidth = w;
    
            BMIH.biHeight = h;
    
            BMIH.biPlanes = 1;
    
            BMIH.biBitCount = 24;
    
            BMIH.biCompression = BI_RGB;
    
            BMIH.biSizeImage = w * h* 3;
    
            BITMAPFILEHEADER bmfh;
    
            int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize;
    
            LONG lImageSize = BMIH.biSizeImage;
    
            LONG lFileSize = nBitsOffset + lImageSize;
    
            bmfh.bfType = 'B'+('M'<<8);
    
            bmfh.bfOffBits = nBitsOffset;
    
            bmfh.bfSize = lFileSize;
    
            bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
    
            //Write the bitmap file header
    
            UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1,
    
            sizeof(BITMAPFILEHEADER), pFile);
    
            //And then the bitmap info header
    
            UINT nWrittenInfoHeaderSize = fwrite(&BMIH,
    
            1, sizeof(BITMAPINFOHEADER), pFile);
    
            //Finally, write the image data itself
    
            //-- the data represents our drawing
    
            UINT nWrittenDIBDataSize =
    
            fwrite(lpBits, 1, lImageSize, pFile);
    
            fclose(pFile);
    
    
    
        return true;
    
    }
    

    So what is the issue.... Well I do not understand the varialbe IpBits. there is a brief explanation of the lpBits in the comments of the code (lpBits: Specifies the bitmap bits)... but I don't know what that actually means. I tried going into msdn and looking into the fopen and fclose functions since fclose is the function that will eventually use the lpbits that I pass to the SaveImage function.... and well it seemed that the lpBits variable in the fclose function varies depending on what variable was passed in the fopen function. I tried to find out what the "wb" of the fopen function means but was unsuccessful (even searching on msdn).

    QUESTION: in the case that I use "wb" as the second variable in the fopen function in my previous code, what exactly would the lpBits in the fclose function be? When I ask what exactly would it be, I mean... what type of variable is it (in the code it is placed as void* which basically allows it to be any variable) and I would appriciate any feedback you could give.

    Thanks guys!

  • user3865729
    user3865729 almost 10 years
    Oh ok so.... the CreateDISection returns a longpointer? would the following code be apropriate then: fclose(CreateDIBSection(blah blah...), 1, lImageSize, pFile);?
  • user3865729
    user3865729 almost 10 years
    and would you mind giving me an example of how CreateDIBSection() works?
  • user3865729
    user3865729 almost 10 years
    what modification would I have to make to fix my code? would it be just switching LONG lImageSize = BMIH.biSizeImage; with LONG lImageSize = h * ((w * 3 + 3) & 0xfffffffc);? Or possibly other lines of code as well or?
  • user3865729
    user3865729 almost 10 years
    Do you know of a function that could give you the array when a HBITMAP handle is passed to it? Thing is, I have the handle to the clipboard image (which was taken with a screenshot)... and I need that lpBits array to pass to the function stated in this thread...