[英]How to convert CImage to Base64 string in MFC C++
我正在将一些代码从 C# 移植到 C++ 和 MFC,有一件事阻止了我。 原始代码生成一个图像,然后将其编码为 base64 字符串,以在生成 HTML 文件时用于嵌入图像。
原代码首先将其转换为字节数组
private byte[] AsBytes(System.Drawing.Image image)
{
using (var ms = new System.IO.MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
}
}
到 Base64 的转换是一个简单的调用,对于 MFC 的Convert.ToBase64String(pictureAsBytes)
有Base64Encode
,但它看起来不太好完成这项工作。 问题是从 CImage 到 CByteArray(或其他有用的东西)。
我的代码让人很头疼,但它看起来像
AsBytes(CImage &image, CByteArray &bytes)
{
int pitch = image.GetPitch();
int size = abs(pitch) * image.GetHeight();
const BYTE *src = (BYTE *)image.GetBits();
if(pitch < 0)
{
src -= size;
}
BYTE *pBitmapData = new BYTE[size];
memcpy(pBitmapData, src, size * sizeof(BYTE));
for(int i = 0; i < size; i++)
{
bytes.Add(pBitmapData[i]);
}
}
经过一些修复后,我的 AsBytes 变成了
bool AsBytes(CImage &image, CByteArray &bytes)
{
IStream *pStream = NULL;
HRESULT hr = CreateStreamOnHGlobal(0, TRUE, &pStream);
if( SUCCEEDED(hr) )
{
hr = image.Save(pStream, Gdiplus::ImageFormatPNG);
if( SUCCEEDED(hr) )
{
// Get size
ULARGE_INTEGER liSize;
IStream_Size(pStream, &liSize);
// Assume no huge files
int size = liSize.QuadPart;
BYTE *result = new BYTE[size];
// Set to start
LARGE_INTEGER offset;
offset.HighPart = 0;
offset.LowPart = 0;
offset.QuadPart = 0;
hr = pStream->Seek(offset, STREAM_SEEK_SET, 0);
ULONG read;
hr = pStream->Read(result, size, &read);
if( SUCCEEDED(hr) )
{
bytes.SetSize(read * sizeof(BYTE));
memcpy(bytes.GetData(), result, read * sizeof(BYTE));
}
delete [] result;
pStream->Release();
return true;
}
}
pStream->Release();
return false;
}
它现在使用 stream 来转换它。
要将CImage
object 序列化为 stream 字节,您可以使用其接受IStream
接口的CImage::Save
重载,并将 memory stream 传递给它。由于我们不知道提前产生的大小,我们必须凑合使用根据需要增长的 stream。 SHCreateMemStream
可以为此目的使用默认值构造。
以下实现将编码为 PNG 数据的CImage
序列化为std::vector<uint8_t>
:
#include <atlimage.h>
#include <comdef.h>
#include <Shlwapi.h>
#include <vector>
std::vector<uint8_t> as_bytes(CImage const& img)
{
// Serialize image to memory stream
CComPtr<IStream> stream {};
stream.Attach(::SHCreateMemStream(nullptr, 0));
_com_util::CheckError(img.Save(stream, Gdiplus::ImageFormatPNG));
// Find size in bytes
ULARGE_INTEGER size {};
_com_util::CheckError(stream->Seek({}, STREAM_SEEK_CUR, &size));
if (size.HighPart != 0)
{
throw std::runtime_error("Images larger than 4GiB not supported");
}
// Read memory stream into vector
std::vector<uint8_t> bytes(size.QuadPart);
_com_util::CheckError(stream->Seek({}, STREAM_SEEK_SET, nullptr));
ULONG bytes_read { 0 };
_com_util::CheckError(stream->Read(bytes.data(),
static_cast<ULONG>(bytes.size()),
&bytes_read));
return bytes;
}
该代码使用 C++ 异常来报告错误,使 function 使用起来更自然。 该实现留下了一些改进空间,特别是额外的bytes
分配和副本。 一种可能的替代方法是使用 class 实现IStream
接口,该接口在内部直接写入vector
。 这将允许在不复制数据的情况下实现。
为了完整起见,这里有一个不依赖于 ATL 实现的 base64 编码器。 它改用CryptBinaryToStringA
:
#include <wincrypt.h>
#include <string>
#include <vector>
#pragma comment(lib, "Crypt32.lib")
std::string to_base64(std::vector<uint8_t> const& bytes)
{
// Return empty string on empty input
if (bytes.empty())
{
return {};
}
// Change as desired
auto const flags { CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF };
// Request required character count (including NUL character)
DWORD chars_required {};
if (!::CryptBinaryToStringA(bytes.data(), static_cast<DWORD>(bytes.size()), flags,
nullptr, &chars_required)
|| chars_required < 1)
{
throw std::runtime_error { "CryptBinaryToStringA() failed" };
}
// Create a sufficiently sized string and have the API write into it
std::string base64(chars_required - 1, 0);
if (!::CryptBinaryToStringA(bytes.data(), static_cast<DWORD>(bytes.size()), flags,
base64.data(), &chars_required))
{
throw std::runtime_error { "CryptBinaryToStringA() failed" };
}
return base64;
}
这需要 C++17 来编译std::string::data()
调用以返回非const
指针。 请注意,用另一个 NUL 终止符覆盖std::string
中尾随的 NUL 终止符也是 C++<something> 的良好定义。
有了它,你就有了一个很好的命令行实用程序,它可以对图像进行 base64 编码:
int wmain(int argc, wchar_t const* argv[])
{
if (argc != 2)
{
return -1;
}
std::wstring const src { argv[1] };
CImage src_img {};
_com_util::CheckError(src_img.Load(src.c_str()));
auto const bytes = as_bytes(src_img);
auto const base64 = to_base64(bytes);
printf("%s", base64.c_str());
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.