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

将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 17:31:15