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颜色空间,直接绘制会导致颜色被错误转换,出现变淡现象。
解决步骤
为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 ));同步D2D设备上下文的颜色空间
在绘制前,将设备上下文的颜色空间设置为sRGB,确保与目标Bitmap一致:Tool::D2dDeviceContext5->SetTarget(ExchangeChainsBitmap); Tool::D2dDeviceContext5->SetColorContext(D2D1_COLOR_CONTEXT_TYPE_SRGB, srgbColorContext); Tool::D2dDeviceContext5->BeginDraw();验证Alpha预乘一致性
- 加载PNG时,WIC转换为
GUID_WICPixelFormat32bppPBGRA,D2D创建Bitmap默认会处理为预乘Alpha,这部分逻辑正确。 - DComposition Surface的
DXGI_ALPHA_MODE_PREMULTIPLIED与D2D目标Bitmap的Alpha模式保持一致,无需修改。
- 加载PNG时,WIC转换为
替代方案
如果不需要使用浮点格式,可直接将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
相关产品推荐
相关产品推荐

