I have the following code to create a bitmap:
//raw data
PBYTE firstPixel = (PBYTE)((PBYTE)AnsiBdbRecord) + sizeof(WINBIO_BDB_ANSI_381_RECORD);
// declare other bmp structures
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
RGBQUAD rq[256];
// create the grayscale palette
for (int i = 0; i<256; i++)
{
rq[i].rgbBlue = i;
rq[i].rgbGreen = i;
rq[i].rgbRed = i;
rq[i].rgbReserved = 0;
}
//RGBQUAD bl = { 0,0,0,0 }; //black color
//RGBQUAD wh = { 0xff,0xff,0xff,0xff }; // white color
// andinitialize them to zero
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
memset(&info, 0, sizeof(BITMAPINFOHEADER));
// fill the fileheader with data
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // + padding;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD);
// fill the infoheader
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = Width;
info.biHeight = Height;
info.biPlanes = 1; // we only have one bitplane
info.biBitCount = PixelDepth; // RGB mode is 24 bits
info.biCompression = BI_RGB;
info.biSizeImage = 0; // can be 0 for 24 bit images
info.biXPelsPerMeter = 0x0ec4; // paint and PSP use this values
info.biYPelsPerMeter = 0x0ec4;
info.biClrUsed = 0; // we are in RGB mode and have no palette
info.biClrImportant = 0; // all colors are importantenter code here
And I save it as follows:
HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// write file header
if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// write infoheader
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
//write rgbquad for black
if (WriteFile(file, &rq, sizeof(rq), &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// write image data
if (WriteFile(file, &firstPixel[0], imageSize, &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// and clean up
CloseHandle(file);
I think the above is the standard way of saving bitmap images. However, instead of saving the image, I want it to be available as BASE64 and pass it in a HTTP Post. Therefore, this question relates to this one, but I am having a lot of difficulties converting the bmp structure to BASE64. I have taken the BASE64 encoder from here, but I have no idea how to pass the BMPFILEHEADER, BMPINFOHEADER, RGBQUAD, and raw data structure as a parameter to the BASE64 encoder.
Any thoughts or pointers on how to combine the information I gathered?
Thanks to Roman Pustylnikov, I have gotten a bit farther already: I'm creating a struct like this:
struct ImageBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFOHEADER infobmp;
RGBQUAD rgb[256];
PBYTE bitmap;
};
Fill it as follows:
ImageBuffer capture;
capture.bfheader = bmfh;
capture.infobmp = info;
// create the grayscale palette
for (int i = 0; i<256; i++)
{
capture.rgb[i].rgbBlue = i;
capture.rgb[i].rgbGreen = i;
capture.rgb[i].rgbRed = i;
capture.rgb[i].rgbReserved = 0;
}
capture.bitmap = firstPixel;
And convert it as follows:
int totalSize = sizeof(capture.bfheader) + sizeof(capture.infobmp) + sizeof(capture.rgb) + imageSize;
std::string encodedImage = base64_encode(reinterpret_cast<const unsigned char*>(&capture), totalSize);
However, it gives me an invalid bitmap. Also, when I load the bitmap from disk (the one that is generated with writefile), I get a different base64 string. I use C# code to compare the two Base64 strings:
// generated base64 string
string test = "Put base64string generated from C++ here";
byte[] imageBytes = Convert.FromBase64String(test);
// generate the same string based on the actual bmp
byte[] data = File.ReadAllBytes(@"c:\successtest.bmp");
string original = Convert.ToBase64String(data);
The solution can be found in the latest update of Roman Pustylnikov's answer.
You can do it without file in the middle, using the following example as encoder/decoder:
The first approach is to create the contiguous memory and put it as an input:
int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + imageSize ;
unsigned char * bmpBuff = new char[size];
int i = 0;
memcpy((void *)( &( bmpBuff[i] )), (void *) &bfheader,sizeof(BITMAPFILEHEADER) );
i+=sizeof(BITMAPFILEHEADER);
memcpy((void *)( &( bmpBuff[i] )), (void *) &infobmp,sizeof(BITMAPINFOHEADER) );
i+=sizeof(BITMAPINFOHEADER);
memcpy((void *)( &( bmpBuff[i] )), (void *) &rq,sizeof(RGBQUAD)*256 );
i+=sizeof(RGBQUAD)*256;
memcpy((void *)( &( bmpBuff[i] )), (void *) firstPixel, imageSize );
std::string encodedImage = base64_encode(bmpBuff, size);
The cons of this approach is that you need to duplicate the memory.
Another approach is to handle the "lost triplet". For this we'll need to define a structure:
struct ScreenShotBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFO infobmp;
RGBQUAD rgb[256];
};
Now here comes the tricky part. Since the encoding handles the triples of bytes, you need to handle the border between two buffers (I haven't tested this so it might contain bugs, just a general approach).
int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256;
unsigned char *info = einterpret_cast<const unsigned char*> &screenshotInfo;
unsigned char *img = einterpret_cast<const unsigned char*> firstPixel;
std::string encodedInfo = base64_encode(info , size);
std::string encodedLostTriplet = "";
int offset = size%3;
unsigned char lostTriplet[3];
if (offset) {
lostTriplet[0]=info[size-offset];
if (offset==2)
lostTriplet[1] = info[size-offset+1];
else
lostTriplet[1] = img[0];
lostTriplet[2] = img[2-offset];
encodedLostTriplet = base64_encode(lostTriplet, 3);
}
else {
offset=3;
}
std::string encodedData = base64_encode(reinterpret_cast<const unsigned char*> &img[3-offset], imageSize - (3 - offset) );
std::string encodedImage = encodedInfo + lostTriplet + encodedData;
A little bit messy but should work on the same memory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With