如何在C++中从字节流创建并保存Win10可查看的图像?
C++ 从std::vector生成并保存图像方案
你需要将std::vector<unsigned char>格式的图像数据流生成可在Windows 10查看的图像文件,以下提供两种方案:原生Windows API实现和轻量第三方库实现。
一、原生Windows API(GDI+)实现
GDI+是Windows原生图形库,支持PNG、JPEG、BMP等多种格式,无需额外依赖。
步骤与代码示例
- 初始化GDI+
- 将
std::vector<unsigned char>转换为IStream接口 - 加载图像并保存到指定路径
#include <windows.h> #include <gdiplus.h> #include <vector> #include <shlwapi.h> #pragma comment(lib, "gdiplus.lib") #pragma comment(lib, "shlwapi.lib") using namespace Gdiplus; // 获取图像编码格式的CLSID int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT num = 0; UINT size = 0; ImageCodecInfo* pImageCodecInfo = nullptr; GetImageEncodersSize(&num, &size); if (size == 0) return -1; pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); if (pImageCodecInfo == nullptr) return -1; GetImageEncoders(num, size, pImageCodecInfo); for (UINT j = 0; j < num; ++j) { if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) { *pClsid = pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return j; } } free(pImageCodecInfo); return -1; } bool SaveImageFromVector(const std::vector<unsigned char>& imageData, const wchar_t* savePath, const wchar_t* format = L"image/png") { // 初始化GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); bool success = false; // 将vector转换为IStream HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, imageData.size()); if (hGlobal) { void* pData = GlobalLock(hGlobal); if (pData) { memcpy(pData, imageData.data(), imageData.size()); GlobalUnlock(hGlobal); IStream* pStream = nullptr; if (CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) == S_OK) { // 加载图像 Image* pImage = Image::FromStream(pStream); if (pImage && pImage->GetLastStatus() == Ok) { CLSID clsid; if (GetEncoderClsid(format, &clsid) != -1) { // 保存图像 if (pImage->Save(savePath, &clsid) == Ok) { success = true; } } delete pImage; } pStream->Release(); } GlobalFree(hGlobal); } } // 关闭GDI+ GdiplusShutdown(gdiplusToken); return success; } // 使用示例 int main() { std::vector<unsigned char> imageData; // 假设此处已填充图像数据流(如从网络/文件读取) // 保存为PNG到指定路径 if (SaveImageFromVector(imageData, L"C:\\test\\output.png")) { // 保存成功 } else { // 保存失败 } return 0; }
二、轻量第三方库:stb_image_write
stb_image_write是单头文件轻量图像库,无需编译链接,直接包含即可使用,支持跨平台,代码更简洁。
步骤与代码示例
- 将
stb_image_write.h(及解码用的stb_image.h)加入项目 - 定义实现宏后包含头文件
- 分两种场景处理:已编码的图像数据流、原始像素数据流
场景1:数据流是已编码的图像(如PNG/JPEG二进制数据)
需先用stb_image.h解码获取图像参数:
#define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #include <vector> #include <string> bool SaveEncodedImageFromVector(const std::vector<unsigned char>& imageData, const std::string& savePath) { int width, height, channels; // 解码图像数据 unsigned char* pixels = stbi_load_from_memory(imageData.data(), imageData.size(), &width, &height, &channels, 0); if (!pixels) return false; bool success = false; // 根据扩展名判断保存格式 const char* ext = savePath.c_str() + savePath.find_last_of('.'); if (_stricmp(ext, ".png") == 0) { success = stbi_write_png(savePath.c_str(), width, height, channels, pixels, width * channels) != 0; } else if (_stricmp(ext, ".jpg") == 0 || _stricmp(ext, ".jpeg") == 0) { success = stbi_write_jpg(savePath.c_str(), width, height, channels, pixels, 90) != 0; // 90为图像质量 } else if (_stricmp(ext, ".bmp") == 0) { success = stbi_write_bmp(savePath.c_str(), width, height, channels, pixels) != 0; } stbi_image_free(pixels); return success; } // 使用示例 int main() { std::vector<unsigned char> imageData; // 填充图像数据流 if (SaveEncodedImageFromVector(imageData, "C:\\test\\output.png")) { // 保存成功 } return 0; }
场景2:数据流是原始像素数据(如RGB/RGBA像素数组)
直接调用对应格式的保存函数:
#define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #include <vector> bool SaveRawPixels(const std::vector<unsigned char>& rawPixels, int width, int height, int channels, const std::string& savePath) { const char* ext = savePath.c_str() + savePath.find_last_of('.'); bool success = false; if (_stricmp(ext, ".png") == 0) { success = stbi_write_png(savePath.c_str(), width, height, channels, rawPixels.data(), width * channels) != 0; } // 其他格式(JPG/BMP)逻辑同理 return success; }
关于你的C#代码转换
你提供的C#代码核心是从内存流加载图像对象,对应的C++逻辑就是将std::vector<unsigned char>转换为可读取的数据源,加载图像后保存——上面两种方案都实现了这个核心逻辑,只是调用的API不同。
内容的提问来源于stack exchange,提问作者BeanKing
相关产品推荐
相关产品推荐

