C++位图转为base64

6

我有以下代码来创建位图:

//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

我将其保存如下:
    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);

我认为以上是保存位图图像的标准方法。然而,我不想保存图像,而是想将其作为BASE64可用并传递到HTTP Post中。因此,这个问题涉及到这个问题,但我在将bmp结构转换为BASE64时遇到了很多困难。我已经从这里获取了BASE64编码器,但我不知道如何将BMPFILEHEADER、BMPINFOHEADER、RGBQUAD和原始数据结构作为参数传递给BASE64编码器。
有什么想法或指针可以告诉我如何将我收集的信息结合起来吗?
更新
感谢Roman Pustylnikov,我已经取得了一些进展: 我正在创建一个类似于以下的结构:
    struct ImageBuffer
{
    BITMAPFILEHEADER bfheader;
    BITMAPINFOHEADER infobmp;
    RGBQUAD rgb[256];
    PBYTE bitmap;
};

请按以下方式填写:
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;

将其转换为以下内容:
int totalSize = sizeof(capture.bfheader) + sizeof(capture.infobmp) + sizeof(capture.rgb) + imageSize;
std::string encodedImage = base64_encode(reinterpret_cast<const unsigned char*>(&capture), totalSize);

然而,它给了我一个无效的位图。另外,当我从磁盘加载位图(用writefile生成的那个)时,我得到了一个不同的Base64字符串。我使用C#代码比较这两个Base64字符串:

            // 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);

更新二:解决方案

解决方案可以在Roman Pustylnikov最新的回答中找到。

1个回答

3
你可以不使用中间文件,而是使用以下示例作为编码器/解码器来完成它:
第一种方法是创建连续的内存并将其放置在输入中:
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);

这种方法的缺点是需要复制内存。
另一种方法是处理“丢失的三元组”。为此,我们需要定义一个结构:
struct ScreenShotBuffer
{
    BITMAPFILEHEADER bfheader;
    BITMAPINFO infobmp;
    RGBQUAD rgb[256];      
};

现在来到棘手的部分。由于编码处理三个字节,您需要处理两个缓冲区之间的边界(我还没有测试过,所以可能包含错误,这只是一种一般方法)。
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;

有点凌乱,但应该在同一内存上运行。

我相信您可以使用结构体将头文件与图像拼接在一起作为结果。由于头文件应该始终是固定长度的(作为头文件的长度组合和编码),其余部分将是图像数据。应该很容易解码。我已经更新了答案。 - Roman Pustylnikov
我认为你很接近了,但它仍然无法正常工作。请查看我的问题更新以获取详细信息。 - Michael
我可能错了,但是sizeof(PBYTE)是sizeof(char),而不是imageSize。这就是为什么我要分开编码的原因。你必须将图像与头部分开编码。位图不一定存储在结构体旁边,所以totalSize从未定义的位置读取而不是数据。 - Roman Pustylnikov
长度看起来没问题,如果我用C#反转保存的样本并将其与C++ Base64字符串的长度进行比较,它们的长度相等。我似乎只是在'encodedImage'变量中放入了错误的值.. - Michael
让我们在聊天中继续这个讨论 - Michael
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接