[Windows编程]使用 WIC 实现图片的加载与存储

使用 WIC 实现图片的加载与存储

一、概述

WICImage 是一个基于 Windows Imaging Component (WIC) 的图片编解码库,提供了简洁的 C 语言接口用于加载和保存多种格式的图片文件。本文将详细介绍其使用流程、核心数据结构以及完整的代码示例。


二、核心数据结构

1. WICColorFormat - 颜色格式枚举

定义了解码输出和编码输入的像素颜色格式:

typedef enum WICColorFormat
{
    WICColor_24RGB  = 0x00010318,   ///< 24位 RGB 格式
    WICColor_24BGR  = 0x00020318,   ///< 24位 BGR 格式
    WICColor_32RGBA = 0x00030420,   ///< 32位 RGBA 格式
    WICColor_32BGRA = 0x00040420,   ///< 32位 BGRA 格式
} WICColorFormat;

辅助宏

  • WIC_COLOR_BITS(format) - 获取每像素比特数(24 或 32)
  • WIC_COLOR_NUMC(format) - 获取通道数量(3 或 4)
  • WIC_ALIGN4(x) - 4 字节对齐

2. WICFileFormat - 文件格式枚举

定义了支持的输出文件格式:

typedef enum WICFileFormat
{
    WICFile_NONE = 0x00000000,   ///< 未指定格式(根据扩展名自动判断)
    WICFile_BMP  = 0x00010001,   ///< BMP 格式
    WICFile_JPEG = 0x00020001,   ///< JPEG 格式
    WICFile_PNG  = 0x00030001,   ///< PNG 格式
    WICFile_GIF  = 0x00040001,   ///< GIF 格式
    WICFile_TIFF = 0x00050001,   ///< TIFF 格式
} WICFileFormat;

3. WICImageInfo - 图片信息结构体

用于传递图片像素数据和相关参数:

typedef struct WICImageInfo
{
    LPBYTE hBuffer;  ///< 像素数据缓冲区
    UINT   nLength;  ///< 像素数据缓冲区长度
    UINT   nStride;  ///< 行间距(每行字节数)
    UINT   cFormat;  ///< 颜色格式
    UINT   nImageW;  ///< 图片宽度
    UINT   nImageH;  ///< 图片高度
} WICImageInfo, * HWICImageInfo;

行距最小值计算宏

#define WIC_MIN_STRIDE(hImageInfo) \
    WIC_ALIGN4((hImageInfo)->nImageW * WIC_COLOR_NUMC((hImageInfo)->cFormat))

4. WICAllocator - 缓冲区管理器

用于自定义缓冲区的分配和释放:

typedef struct WICAllocator
{
    WIC_ALLOC xFnAlloc;  ///< 缓冲区分配函数
    WIC_FREE  xFnFree;   ///< 缓冲区释放函数
    DWORD_PTR dwContext; ///< 上下文参数
} WICAllocator, * HWICAllocator;

默认实现

  • WICAllocator_DefaultAlloc() - 使用 malloc 分配
  • WICAllocator_DefaultFree() - 使用 free 释放

三、使用流程

1. 初始化 COM 库

在使用 WICImage 之前,必须先初始化 COM 库:

#include <Windows.h>

// 初始化 COM 库(通常在程序启动时调用)
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

2. 创建 WICImage 对象

HWICImage hWICImage = WICImage_Create();
if (NULL == hWICImage)
{
    // 创建失败,处理错误
    return -1;
}

3. 加载图片(解码流程)

步骤说明

  1. 配置 WICImageInfocFormat 为必填项)
  2. 设置 WICAllocator(可使用默认实现)
  3. 调用 WICImage_LoadFromFile()

完整示例

#include "WICImage.h"
#include <stdio.h>

BOOL LoadImageExample(LPCWSTR wzFileName)
{
    HWICImage     hWICImage   = NULL;
    WICImageInfo  stImageInfo = { 0 };
    WICAllocator  stAllocator = { 0 };

    // 1. 创建 WICImage 对象
    hWICImage = WICImage_Create();
    if (NULL == hWICImage)
        return FALSE;

    // 2. 配置颜色格式(必填)
    stImageInfo.cFormat = WICColor_32BGRA;  // 解码为 32 位 BGRA 格式

    // 3. 配置缓冲区管理器(使用默认实现)
    stAllocator.xFnAlloc  = WICAllocator_DefaultAlloc;
    stAllocator.xFnFree   = WICAllocator_DefaultFree;
    stAllocator.dwContext = 0;

    // 4. 加载图片(bFlipV = FALSE 表示不垂直翻转)
    if (!WICImage_LoadFromFile(
            hWICImage,
            wzFileName,
            FALSE,           // 是否垂直翻转
            &stImageInfo,
            &stAllocator))
    {
        printf("Failed to load image!\n");
        WICImage_Destroy(hWICImage);
        return FALSE;
    }

    // 5. 使用图片数据
    printf("Image loaded successfully!\n");
    printf("Width  : %u\n", stImageInfo.nImageW);
    printf("Height : %u\n", stImageInfo.nImageH);
    printf("Stride : %u\n", stImageInfo.nStride);
    printf("Format : 32BGRA\n");

    // 图片像素数据存储在 stImageInfo.hBuffer 中
    // 每行数据长度为 stImageInfo.nStride
    // 总数据长度为 stImageInfo.nLength

    // 6. 释放缓冲区(使用自定义的释放函数)
    if (NULL != stImageInfo.hBuffer)
    {
        stAllocator.xFnFree(
            stImageInfo.hBuffer,
            stImageInfo.nLength,
            stAllocator.dwContext
        );
    }

    // 7. 销毁 WICImage 对象
    WICImage_Destroy(hWICImage);

    return TRUE;
}

垂直翻转选项说明

bFlipV 参数控制是否垂直翻转图片。在某些场景下(如 OpenGL/DirectX 纹理加载),由于坐标系方向差异,需要对图片进行垂直翻转:

// 加载图片并垂直翻转(适用于纹理加载场景)
WICImage_LoadFromFile(hWICImage, wzFileName, TRUE, &stImageInfo, &stAllocator);

4. 保存图片(编码流程)

步骤说明

  1. 填充 WICImageInfo 的所有必填字段
  2. 调用 WICImage_SaveToFile()

完整示例

BOOL SaveImageExample(
    LPCWSTR wzFileName,
    LPBYTE  hPixelBuffer,
    UINT    nWidth,
    UINT    nHeight,
    UINT    nStride)
{
    HWICImage     hWICImage  = NULL;
    WICImageInfo  stImageInfo = {0};

    // 1. 创建 WICImage 对象
    hWICImage = WICImage_Create();
    if (NULL == hWICImage)
        return FALSE;

    // 2. 配置图片信息(所有字段均为必填)
    stImageInfo.hBuffer = hPixelBuffer;   // 像素数据缓冲区
    stImageInfo.nLength = nStride * nHeight;  // 总数据长度
    stImageInfo.nStride = nStride;        // 行间距
    stImageInfo.cFormat = WICColor_32BGRA;  // 颜色格式
    stImageInfo.nImageW = nWidth;         // 图片宽度
    stImageInfo.nImageH = nHeight;        // 图片高度

    // 3. 保存图片

    // 方式一:指定文件格式
    if (!WICImage_SaveToFile(
            hWICImage,
            &stImageInfo,
            FALSE,           // 是否垂直翻转
            wzFileName,
            WICFile_PNG))    // 指定 PNG 格式
    {
        printf("Failed to save image!\n");
        WICImage_Destroy(hWICImage);
        return FALSE;
    }

    // 方式二:根据文件扩展名自动判断格式
    // WICImage_SaveToFile(hWICImage, &stImageInfo, FALSE, wzFileName, WICFile_NONE);

    printf("Image saved successfully!\n");

    // 4. 销毁 WICImage 对象
    WICImage_Destroy(hWICImage);

    return TRUE;
}

四、完整应用示例

以下是一个完整的图片格式转换示例:

#include <Windows.h>
#include <stdio.h>
#include "WICImage.h"

int wmain(int argc, wchar_t* argv[])
{
    if (argc != 3)
    {
        printf("Usage: WICImageDemo <input_image> <output_image>\n");
        return -1;
    }

    // 1. 初始化 COM 库
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    HWICImage     hWICImage   = NULL;
    WICImageInfo  stImageInfo = { 0 };
    WICAllocator  stAllocator = { 0 };

    // 2. 创建 WICImage 对象
    hWICImage = WICImage_Create();
    if (NULL == hWICImage)
    {
        printf("Failed to create WICImage!\n");
        CoUninitialize();
        return -1;
    }

    // 3. 配置加载参数
    stImageInfo.cFormat   = WICColor_32BGRA;
    stAllocator.xFnAlloc  = WICAllocator_DefaultAlloc;
    stAllocator.xFnFree   = WICAllocator_DefaultFree;
    stAllocator.dwContext = 0;

    // 4. 加载源图片
    printf("Loading image: %ls\n", argv[1]);
    if (!WICImage_LoadFromFile(hWICImage, argv[1], FALSE, &stImageInfo, &stAllocator))
    {
        printf("Failed to load image!\n");
        WICImage_Destroy(hWICImage);
        CoUninitialize();
        return -1;
    }

    printf("Image info: %ux%u, %u bytes per row\n",
           stImageInfo.nImageW, stImageInfo.nImageH, stImageInfo.nStride);

    // 5. 保存为新格式(根据扩展名自动判断)
    printf("Saving image: %ls\n", argv[2]);
    if (!WICImage_SaveToFile(hWICImage, &stImageInfo, FALSE, argv[2], WICFile_NONE))
    {
        printf("Failed to save image!\n");
        stAllocator.xFnFree(stImageInfo.hBuffer, stImageInfo.nLength, 0);
        WICImage_Destroy(hWICImage);
        CoUninitialize();
        return -1;
    }

    // 6. 清理资源
    stAllocator.xFnFree(stImageInfo.hBuffer, stImageInfo.nLength, 0);
    WICImage_Destroy(hWICImage);
    CoUninitialize();

    printf("Conversion completed successfully!\n");
    return 0;
}

五、依赖库

  • wincodec.lib - WIC 库

六、注意事项

  1. COM 初始化:使用前必须调用 CoInitializeEx(),使用完后调用 CoUninitialize()
  2. 缓冲区管理:加载图片时,缓冲区由 WICAllocator 管理,使用完毕后需要调用对应的释放函数
  3. 垂直翻转:OpenGL/DirectX 纹理坐标系与图片坐标系不同,加载纹理时通常需要设置 bFlipV = TRUE
  4. 格式支持:支持 BMP、JPEG、PNG、GIF、TIFF 格式的读取和写入
  5. 行对齐:行间距(stride)必须是 4 的倍数;为 0 时,内部通过WIC_MIN_STRIDE(info) 计算最小行间距进行对齐

七、API 速查表

函数 功能
WICImage_Create() 创建 WICImage 对象
WICImage_Destroy() 销毁 WICImage 对象
WICImage_LoadFromFile() 从文件加载图片
WICImage_SaveToFile() 保存图片到文件
辅助宏 功能
WIC_COLOR_BITS(format) 获取每像素比特数
WIC_COLOR_NUMC(format) 获取通道数量
WIC_ALIGN4(x) 4 字节对齐
WIC_MIN_STRIDE(info) 计算最小行间距
默认函数 功能
WICAllocator_DefaultAlloc() 默认缓冲区分配
WICAllocator_DefaultFree() 默认缓冲区释放

八、源码下载地址

gitee: https://gitee.com/Gaaagaa/wicimage

posted @ 2026-07-01 13:26  Gaaagaa  阅读(4)  评论(0)    收藏  举报