You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中从字节流创建并保存Win10可查看的图像?

C++ 从std::vector生成并保存图像方案

你需要将std::vector<unsigned char>格式的图像数据流生成可在Windows 10查看的图像文件,以下提供两种方案:原生Windows API实现和轻量第三方库实现。

一、原生Windows API(GDI+)实现

GDI+是Windows原生图形库,支持PNG、JPEG、BMP等多种格式,无需额外依赖。

步骤与代码示例

  1. 初始化GDI+
  2. 将std::vector<unsigned char>转换为IStream接口
  3. 加载图像并保存到指定路径
#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是单头文件轻量图像库,无需编译链接,直接包含即可使用,支持跨平台,代码更简洁。

步骤与代码示例

  1. 将stb_image_write.h(及解码用的stb_image.h)加入项目
  2. 定义实现宏后包含头文件
  3. 分两种场景处理:已编码的图像数据流、原始像素数据流

场景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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 04:45:34