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

DirectX绘制PNG图像颜色变淡问题求助

问题描述

使用DirectX(D3D11+D2D1+DComp)绘制PNG图像时,所有图片均出现颜色变淡的情况(左侧为程序窗口显示效果,右侧为原图)。通过LoadPngAsBitmap创建的Bitmap保存后颜色正常,怀疑是Alpha预乘相关问题,但修改CreateBitmapFromDxgiSurface中的Alpha模式为D2D1_ALPHA_MODE_IGNORE后无改善,寻求解决方案。

代码示例
#include <Windows.h>
#include <d3d11.h>
#include <d3d11_4.h>
#include <d2d1.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <dxgi.h>
#include <wincodec.h>
#include <dcomp.h>
#include <tchar.h>
#include <atlbase.h>

#pragma comment (lib, "d3dcompiler")
#pragma comment (lib, "D2d1")
#pragma comment (lib, "dxguid")
#pragma comment (lib, "d3d11")
#pragma comment (lib, "dxgi")
#pragma comment (lib, "windowscodecs")
#pragma comment (lib, "Dwrite")
#pragma comment (lib, "uuid")
#pragma comment (lib, "dcomp")
#pragma comment (lib, "ole32")

#define WINDOW_CLASS_NAME _T("mainWindow")
#define HR(result) if(SUCCEEDED(hr)){hr = result;}

class Tool
{
    Tool()
    {
        HRESULT hr = OleInitialize(NULL);
        int const d3dDeviceOptions = { D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG };
        D2D1_FACTORY_OPTIONS const d2dDeviceoptions = { D2D1_DEBUG_LEVEL_INFORMATION };
        D3D_FEATURE_LEVEL featureLevelSupported;
        HR(D3D11CreateDevice(
            nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, d3dDeviceOptions, NULL, 0, D3D11_SDK_VERSION, &D3dDevice, &featureLevelSupported, &D3dDeviceContext
        ));
        HR(D3dDevice->QueryInterface(__uuidof(ID3D11Device5), reinterpret_cast<void**>(&D3dDevice5)));
        HR(D3dDevice5->QueryInterface(&D3d_DXGI_IDC_));
        HR(DCompositionCreateDevice3(D3d_DXGI_IDC_, __uuidof(IDCompositionDevice), reinterpret_cast<void**>(&IdcDevice)));


        HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dDeviceoptions, &D2dFactory));
        HR(D2dFactory->QueryInterface(__uuidof(ID2D1Factory6), reinterpret_cast<void**>(&D2dFactory6)));
        HR(D3dDevice5->QueryInterface(&D3d_DXGI_D2d_));
        HR(D2dFactory6->CreateDevice(D3d_DXGI_D2d_, &D2dDevice5));
        HR(D2dDevice5->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &D2dDeviceContext5));

        HR(CoCreateInstance(CLSID_WICImagingFactory2, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&IWICFactory2)));
    }
    static Tool tool;

public:
    static CComPtr<ID3D11Device>            D3dDevice;
    static CComPtr<ID3D11Device5>           D3dDevice5;
    static CComPtr<ID3D11DeviceContext>     D3dDeviceContext;
    static CComPtr<ID2D1Device5>            D2dDevice5;
    static CComPtr<ID2D1Factory>            D2dFactory;
    static CComPtr<ID2D1Factory6>           D2dFactory6;
    static CComPtr<ID2D1DeviceContext5>     D2dDeviceContext5;
    static CComPtr<IDCompositionDevice>     IdcDevice;
    static CComPtr<IWICImagingFactory2>     IWICFactory2;
    static CComPtr<IDXGIDevice4>            D3d_DXGI_IDC_;
    static CComPtr<IDXGIDevice4>            D3d_DXGI_D2d_;
};
CComPtr<ID3D11Device>           Tool::D3dDevice;
CComPtr<ID3D11Device5>          Tool::D3dDevice5;
CComPtr<ID3D11DeviceContext>    Tool::D3dDeviceContext;
CComPtr<ID2D1Device5>           Tool::D2dDevice5;
CComPtr<ID2D1Factory>           Tool::D2dFactory;
CComPtr<ID2D1Factory6>          Tool::D2dFactory6;
CComPtr<ID2D1DeviceContext5>    Tool::D2dDeviceContext5;
CComPtr<IDCompositionDevice>    Tool::IdcDevice;
CComPtr<IWICImagingFactory2>    Tool::IWICFactory2;
CComPtr<IDXGIDevice4>           Tool::D3d_DXGI_IDC_;
CComPtr<IDXGIDevice4>           Tool::D3d_DXGI_D2d_;
Tool Tool::tool = Tool::Tool();

HRESULT LoadPngAsBitmap(_In_ PCWSTR uri, _Outptr_ ID2D1Bitmap** bitmap)
{
    HRESULT hr = S_OK;

    CComPtr<IWICStream> stream;
    HR(Tool::IWICFactory2->CreateStream(&stream));
    HR(stream->InitializeFromFilename(uri, GENERIC_READ));

    CComPtr<IWICBitmapDecoder> decoder;
    HR(Tool::IWICFactory2->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnLoad, &decoder));

    CComPtr<IWICBitmapFrameDecode> frameDecode;
    HR(decoder->GetFrame(0, &frameDecode));

    CComPtr<IWICFormatConverter> converter;
    HR(Tool::IWICFactory2->CreateFormatConverter(&converter));

    HR(converter->Initialize(frameDecode, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom));
    HR(Tool::D2dDeviceContext5->CreateBitmapFromWicBitmap(converter, NULL, bitmap));
    return hr;
}

LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LRESULT hr = S_OK;
    switch (msg)
    {
    default:
        hr = DefWindowProc(hwnd, msg, wParam, lParam);
        break;
    }
    return hr;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR pszCmdLine, _In_ int iCmdShow)
{
    HRESULT hr = S_OK;
    HWND mainWindow;
    CComPtr<IDCompositionTarget>  IdcTarget;
    CComPtr<IDCompositionVisual>  IdcVisual;
    CComPtr<IDCompositionSurface> IdcSurface;
    CComPtr<ID2D1Bitmap1>         ExchangeChainsBitmap;
    WNDCLASSEX wndClassEx = { };
    wndClassEx.cbSize = sizeof(WNDCLASSEX);
    wndClassEx.lpfnWndProc = wndProc;
    wndClassEx.hInstance = hInstance;
    wndClassEx.cbClsExtra = DLGWINDOWEXTRA;
    wndClassEx.style = CS_DBLCLKS;
    wndClassEx.lpszClassName = WINDOW_CLASS_NAME;
    if (!RegisterClassEx(&wndClassEx)) {
        return 0;
    }
    mainWindow = CreateWindowEx(
        WS_EX_ACCEPTFILES,
        WINDOW_CLASS_NAME, _T("text"),
        WS_VISIBLE | WS_POPUP,
        100, 100, 1000, 1000,
        NULL, NULL, hInstance, NULL
    );
    ShowWindow(mainWindow, SW_SHOWDEFAULT);


    HR(Tool::IdcDevice->CreateTargetForHwnd(mainWindow, TRUE, &IdcTarget));
    HR(Tool::IdcDevice->CreateVisual(&IdcVisual));

    RECT windowRect;
    GetClientRect(mainWindow, &windowRect);
    HR(Tool::IdcDevice->CreateSurface(
        windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
        DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_ALPHA_MODE_PREMULTIPLIED,
        &IdcSurface
    ));
    HR(IdcVisual->SetContent(IdcSurface));

    HR(IdcTarget->SetRoot(IdcVisual));
    HR(Tool::IdcDevice->Commit());

    POINT offset;
    CComPtr<IDXGISurface> _Surface_DXGI_Draw;
    HR(IdcSurface->BeginDraw(
        NULL, __uuidof(IDXGISurface), reinterpret_cast<void**>(&_Surface_DXGI_Draw), &offset
    ));

    D2D1_BITMAP_PROPERTIES1 properties = { };
    properties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_R16G16B16A16_FLOAT, D2D1_ALPHA_MODE_PREMULTIPLIED);
    properties.dpiX = properties.dpiY = 96;
    properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
    properties.colorContext = NULL;
    HR(Tool::D2dDeviceContext5->CreateBitmapFromDxgiSurface(
        _Surface_DXGI_Draw, properties, &ExchangeChainsBitmap
    ));

    Tool::D2dDeviceContext5->SetTarget(ExchangeChainsBitmap);
    Tool::D2dDeviceContext5->BeginDraw();
    Tool::D2dDeviceContext5->Clear(NULL);
    /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    CComPtr<ID2D1Bitmap> img;
    HR(LoadPngAsBitmap(_T("C:\\image2.png"), &img));
    Tool::D2dDeviceContext5->DrawBitmap(img);
    /*-------------------------------------------------------------------------------------------------------*/
    HR(Tool::D2dDeviceContext5->EndDraw());
    HR(IdcSurface->EndDraw());
    HR(Tool::IdcDevice->Commit());


    int i = 0;
    MSG msg = { };
    while ((i = GetMessage(&msg, NULL, 0, 0))) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return i;
}
解决方案

颜色变淡的核心原因是颜色空间不匹配:DComposition Surface使用了DXGI_FORMAT_R16G16B16A16_FLOAT线性颜色空间格式,但通过WIC加载的PNG图片属于sRGB颜色空间,直接绘制会导致颜色被错误转换,出现变淡现象。

解决步骤

  1. 为D2D目标Bitmap添加sRGB颜色上下文
    创建并关联sRGB颜色上下文,让D2D明确目标表面的颜色空间:

    // 创建sRGB颜色上下文
    CComPtr<ID2D1ColorContext> srgbColorContext;
    HR(Tool::D2dFactory6->CreateColorContextFromDxgiColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, &srgbColorContext));
    
    D2D1_BITMAP_PROPERTIES1 properties = { };
    properties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_R16G16B16A16_FLOAT, D2D1_ALPHA_MODE_PREMULTIPLIED);
    properties.dpiX = properties.dpiY = 96;
    properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
    properties.colorContext = srgbColorContext; // 绑定sRGB颜色上下文
    HR(Tool::D2dDeviceContext5->CreateBitmapFromDxgiSurface(
        _Surface_DXGI_Draw, properties, &ExchangeChainsBitmap
    ));
    
  2. 同步D2D设备上下文的颜色空间
    在绘制前,将设备上下文的颜色空间设置为sRGB,确保与目标Bitmap一致:

    Tool::D2dDeviceContext5->SetTarget(ExchangeChainsBitmap);
    Tool::D2dDeviceContext5->SetColorContext(D2D1_COLOR_CONTEXT_TYPE_SRGB, srgbColorContext);
    Tool::D2dDeviceContext5->BeginDraw();
    
  3. 验证Alpha预乘一致性

    • 加载PNG时,WIC转换为GUID_WICPixelFormat32bppPBGRA,D2D创建Bitmap默认会处理为预乘Alpha,这部分逻辑正确。
    • DComposition Surface的DXGI_ALPHA_MODE_PREMULTIPLIED与D2D目标Bitmap的Alpha模式保持一致,无需修改。

替代方案

如果不需要使用浮点格式,可直接将DComposition Surface的格式改为sRGB兼容的8位格式,避免颜色转换问题:

HR(Tool::IdcDevice->CreateSurface(
    windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
    DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_PREMULTIPLIED,
    &IdcSurface
));

内容的提问来源于stack exchange,提问作者feng lei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:54:54