Reputation: 23
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!
Upvotes: 2
Views: 21727
Reputation: 4468
Study the Windows API CreateDIBSection()
. With this API, Windows will allocate the memory for the pixels you need. When it allocates the memory, it will give you the memory address in a long pointer. That is what "lpbits" refers to - a long pointer to the bits that were allocated.
The "wb" in fopen()
means "Write Binary". Without the "b" (i.e., if you use only a "w" in the second parameter), fopen
will open the file in text mode, which will cause the file to be written as though it were text, which may change the '\n' character in a system-dependent way.
Here is a constructor I use for class PixMapAny, which is primarily used for off-screen drawing but can also be used to read pixmaps.
PixMapAny::PixMapAny(int width, int height, int depth)
{
m_dc.CreateCompatibleDC(NULL);
m_width = width;
m_height = height;
m_depth = depth;
// The declaration of 'fake' creates a storage area big enough to
// contain a BITMAPINFO structure composed of a BITMAPINFOHEADER
// and a 256-element array of RGBQUAD values.
long fake[266];
LPBITMAPINFO pbmi = (LPBITMAPINFO) fake;
// Initialize the area to all zeros
for(int x = 0; x < 266; x++) fake[x] = 0;
// Fill in the header with the characteristics of the bitmap we want
// to write.
pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader);
pbmi->bmiHeader.biWidth = m_width;
pbmi->bmiHeader.biHeight = -m_height;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 24;
pbmi->bmiHeader.biCompression = BI_RGB;
// Tell the system to allocate room for the pixmap.
// 'ppvbits' receives a pointer to the pixmap memory.
m_dib = CreateDIBSection(m_dc.m_hDC, pbmi, DIB_RGB_COLORS, &m_ppvbits, NULL, 0);
// ____________________________________________________________________________
// Select the bitmap into the device context
m_prev = (CBitmap *) m_dc.SelectObject(m_dib);
// ____________________________________________________________________________
}
In this example, the height is negative because the rows in the pixmap will be ordered in a top-down fashion, i.e., the address of the top row is less than the address of the bottom row.
Once a pixmap has been constructed in this manner, copying into an area of an open windows is easy. Here, pDC is a pointer to the target window's device context, and x and y are coordinates within that window:
void PixMapAny::Blit(int x, int y, CDC * pDC)
{
pDC->BitBlt(x,y,m_width,m_height,&m_dc,0,0,SRCCOPY);
}
Upvotes: 0
Reputation: 4432
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:
std::string
instead of char*
that originally need to be const char*
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;
}
Upvotes: 2
Reputation: 4750
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
Upvotes: 4