[英]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.