如何使用ATL::CImage类将带有alpha通道的32位 BMP图像转换为PNG格式

后端开发 2026-07-10

我们使用32位 RGB位图,信息编码在第四字节中,通常用作“Alpha”通道,但在我们的场景中用来编码其他元数据。多年来我们一直无障碍地保存/加载32位 BMP格式的图像,全部使用(旧的)ATL::CImage 类。

我们想切换到PNG格式以节省磁盘空间,但只能实现以下两种结果之一:

  • m_hasAlphaChannel = FALSE:PNG图像在所有位置的alpha通道都设为0xFF。
  • m_hasAlphaChannel = TRUE:所有像素值在保存前会乘以alpha值,因此每当alpha值为零时,图像中的信息都会丢失(这是典型情况)。

我们可以使用哪种API调用组合来确保在保存元数据的同时,未修改的像素值也被保存到图像中?

解决方案

也许你应该考虑使用其他库来保存/加载图像。

以下代码演示了如何使用 Windows成像组件(WIC) 来将数组保存为PNG,或从PNG加载数组。
为简化起见,省略了资源释放和错误处理。你应使用你偏好的COM智能指针来管理资源。

你可以把它改写为一对辅助函数,用于保存/加载 CImage,并添加适当的错误处理。

#include<Windows.h>
#include<wincodec.h>
#include<combaseapi.h>
#include<cassert>
#include<vector>
int main() {
    CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
    const int width = 200;
    const int height = 200;
    // Fill some random data. 
    // You should replace this with the pixel data you obtained from your CImage.
    std::vector<BYTE> data(width * height * 4);
    for (int i = 0; i < data.size(); ++i) {
        data[i] = static_cast<BYTE>(i);
    }

    IWICImagingFactory* factory = nullptr; // can be reused
    HRESULT hr = CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&factory)
    );
    if (1) {
        IWICBitmapEncoder* encoder = nullptr;
        factory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &encoder);

        IWICStream* stream = nullptr;
        factory->CreateStream(&stream);
        stream->InitializeFromFilename(L"output.png", GENERIC_WRITE);

        encoder->Initialize(stream, WICBitmapEncoderNoCache);

        IWICBitmapFrameEncode* frame = nullptr;
        encoder->CreateNewFrame(&frame, nullptr);
        frame->Initialize(nullptr);

        frame->SetSize(width, height);

        WICPixelFormatGUID format = GUID_WICPixelFormat32bppBGRA;
        frame->SetPixelFormat(&format);

        frame->WritePixels(
            height,
            width * 4,
            width * height * 4,
            data.data()
        );

        frame->Commit();
        encoder->Commit();
    }
    else {
        // Confirm data is stored correctly
        IWICBitmapDecoder* decoder = nullptr;
        factory->CreateDecoderFromFilename(
            L"output.png",
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &decoder
        );

        IWICBitmapFrameDecode* frame = nullptr;
        decoder->GetFrame(0, &frame);

        IWICFormatConverter* converter = nullptr;
        factory->CreateFormatConverter(&converter);

        converter->Initialize(
            frame,
            GUID_WICPixelFormat32bppBGRA,
            WICBitmapDitherTypeNone,
            nullptr,
            0.0,
            WICBitmapPaletteTypeCustom
        );

        UINT width2, height2;
        converter->GetSize(&width2, &height2);
        assert(width2 == width && height2 == height);

        std::vector<BYTE> pixels(width2 * height2 * 4);

        converter->CopyPixels(
            nullptr,
            width2 * 4,
            pixels.size(),
            pixels.data()
        );

        assert(data == pixels);
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章