C++ Saving a Bitmap File
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 ofchar*
that originally need to beconst 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;
}
user3865729
Updated on June 04, 2022Comments
-
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 almost 10 yearsOh ok so.... the CreateDISection returns a longpointer? would the following code be apropriate then: fclose(CreateDIBSection(blah blah...), 1, lImageSize, pFile);?
-
user3865729 almost 10 yearsand would you mind giving me an example of how CreateDIBSection() works?
-
user3865729 almost 10 yearswhat 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 almost 10 yearsDo 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...