将Win32 UI代码从GDI/GDI+迁移至Direct2D的问题咨询
问题1:图像条邻接图像溢出(伪影)
这个问题大概率是Direct2D默认的线性插值采样导致的——当绘制时源矩形边缘未严格对齐像素,或DPI缩放带来采样偏移,会让Direct2D采样到相邻图像的像素。解决方法如下:
- 强制使用最近邻插值:绘制位图时指定
D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,避免跨像素采样:
pRenderTarget->DrawBitmap( pBitmap, destRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, sourceRect // 必须精确到单个图像的像素范围,比如index*48, 0, 48, 48 );
检查源矩形计算精度:确保
sourceRect的坐标和尺寸为整数(图像条内单个图像是48x48像素),避免浮点误差导致采样范围偏移,尤其在DPI缩放场景下。锁定位图采样范围:创建ID2D1Bitmap时指定与RenderTarget一致的DPI,绘制前通过
SetSourceRectangle锁定单个图像的采样边界:
D2D1_BITMAP_PROPERTIES bitmapProps = D2D1::BitmapProperties( D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), 96.0f, // 和RenderTarget一致的DPI 96.0f ); pRenderTarget->CreateBitmapFromWicBitmap(pWicBitmap, &bitmapProps, &pBitmap); // 绘制前锁定采样范围 pBitmap->SetSourceRectangle(sourceRect);
问题2:禁用元素的位图灰度绘制
不用切换到ID2D1DeviceContext也能实现,推荐两种实用方法:
方法1:颜色矩阵实时转换(D2D1.1及以上支持)
利用PushLayer的颜色变换参数,给绘制的位图叠加灰度转换矩阵:
// 灰度转换矩阵(遵循ITU-R BT.601标准) D2D1_MATRIX_5X_F grayMatrix = D2D1::Matrix5x5F( 0.299f, 0.299f, 0.299f, 0.0f, 0.0f, 0.587f, 0.587f, 0.587f, 0.0f, 0.0f, 0.114f, 0.114f, 0.114f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); D2D1_LAYER_PARAMETERS1 layerParams = D2D1::LayerParameters1( destRect, // 目标绘制区域 nullptr, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, D2D1::IdentityMatrix(), 1.0f, nullptr, D2D1_LAYER_OPTIONS_NONE, &grayMatrix // 应用灰度转换 ); ID2D1Layer* pLayer = nullptr; pRenderTarget->CreateLayer(nullptr, &pLayer); pRenderTarget->PushLayer(&layerParams, pLayer); // 绘制位图 pRenderTarget->DrawBitmap(pBitmap, destRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, sourceRect); pRenderTarget->PopLayer(); SafeRelease(&pLayer);
方法2:预转换灰度位图(适合静态按钮图像)
如果按钮图像不频繁变化,可在加载资源时提前生成灰度版本,禁用时直接绘制:
// 用WIC转换为灰度格式 IWICFormatConverter* pConverter = nullptr; pWicFactory->CreateFormatConverter(&pConverter); pConverter->Initialize( pWicBitmapSource, GUID_WICPixelFormat8bppGray, WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeCustom ); // 从转换后的WIC源创建灰度Direct2D位图 pRenderTarget->CreateBitmapFromWicBitmap(pConverter, nullptr, &pGrayBitmap);
问题3:ID2D1Bitmap保存为PNG
通过WIC(Windows Imaging Component)实现,核心是把Direct2D位图像素复制到WIC位图,再编码为PNG:
HRESULT SaveBitmapToPNG(ID2D1Bitmap* pD2DBitmap, IWICImagingFactory* pWicFactory, LPCWSTR filePath) { HRESULT hr = S_OK; ID2D1RenderTarget* pRenderTarget = nullptr; pD2DBitmap->GetRenderTarget(&pRenderTarget); D2D1_SIZE_U bitmapSize = pD2DBitmap->GetPixelSize(); WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppPBGRA; // 匹配D2D的B8G8R8A8格式 // 创建WIC位图 IWICBitmap* pWicBitmap = nullptr; hr = pWicFactory->CreateBitmap( bitmapSize.width, bitmapSize.height, pixelFormat, WICBitmapCacheOnLoad, &pWicBitmap ); if (FAILED(hr)) goto cleanup; // 锁定WIC位图内存并复制D2D像素 IWICBitmapLock* pLock = nullptr; hr = pWicBitmap->Lock(&D2D1::RectU(0,0,bitmapSize.width,bitmapSize.height), WICBitmapLockWrite, &pLock); if (FAILED(hr)) goto cleanup; UINT stride = 0; BYTE* pBuffer = nullptr; hr = pLock->GetStride(&stride); hr = pLock->GetDataPointer(&bitmapSize.width, reinterpret_cast<BYTE**>(&pBuffer)); if (FAILED(hr)) goto cleanup; pD2DBitmap->CopyFromMemory(nullptr, pBuffer, stride); // 创建PNG编码器并写入文件 IWICBitmapEncoder* pEncoder = nullptr; hr = pWicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &pEncoder); if (FAILED(hr)) goto cleanup; IWICStream* pStream = nullptr; hr = pWicFactory->CreateStream(&pStream); hr = pStream->InitializeFromFilename(filePath, GENERIC_WRITE); if (FAILED(hr)) goto cleanup; hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache); IWICBitmapFrameEncode* pFrameEncode = nullptr; hr = pEncoder->CreateNewFrame(&pFrameEncode, nullptr); hr = pFrameEncode->Initialize(nullptr); // 设置位图DPI FLOAT dpiX, dpiY; pD2DBitmap->GetDpi(&dpiX, &dpiY); hr = pFrameEncode->SetResolution(dpiX, dpiY); hr = pFrameEncode->WriteSource(pWicBitmap, nullptr); hr = pFrameEncode->Commit(); hr = pEncoder->Commit(); cleanup: SafeRelease(&pLock); SafeRelease(&pWicBitmap); SafeRelease(&pStream); SafeRelease(&pFrameEncode); SafeRelease(&pEncoder); SafeRelease(&pRenderTarget); return hr; }
注意:需提前初始化WIC工厂(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pWicFactory))),确保Direct2D位图与WIC的像素格式匹配。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

