Direct2D 和 GDI 互操作性概述

本主题介绍如何同时使用 Direct2D 和 GDI 。 可通过两种方法将 Direct2D 与 GDI 结合使用:可以将 GDI 内容写入与 Direct2D GDI 兼容的呈现目标,也可以将 Direct2D 内容写入 GDI 设备上下文 (DC)

本主题包含以下各节:

先决条件

本概述假定你熟悉基本的 Direct2D 绘图操作。 有关教程,请参阅 创建简单的 Direct2D 应用程序。 它还假定你熟悉 GDI 绘图操作。

将 Direct2D 内容绘制到 GDI 设备上下文

若要将 Direct2D 内容绘制到 GDI DC,请使用 ID2D1DCRenderTarget。 若要创建 DC 呈现器目标,请使用 ID2D1Factory::CreateDCRenderTarget 方法。 此方法采用两个参数。

第一个参数 (D2D1_RENDER_TARGET_PROPERTIES 结构)指定呈现、远程处理、DPI、像素格式和使用情况信息。 若要使 DC 呈现目标能够使用 GDI,请将 DXGI 格式设置为 DXGI_FORMAT_B8G8R8A8_UNORM ,将 alpha 模式设置为 D2D1_ALPHA_MODE_PREMULTIPLIEDD2D1_ALPHA_MODE_IGNORE

第二个参数是接收 DC 呈现目标引用的指针的地址。

以下代码创建 DC 呈现目标。

// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
    D2D1_RENDER_TARGET_TYPE_DEFAULT,
    D2D1::PixelFormat(
        DXGI_FORMAT_B8G8R8A8_UNORM,
        D2D1_ALPHA_MODE_IGNORE),
    0,
    0,
    D2D1_RENDER_TARGET_USAGE_NONE,
    D2D1_FEATURE_LEVEL_DEFAULT
    );

hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);

在前面的代码中, m_pD2DFactory 是指向 ID2D1Factory 的指针, m_pDCRT 是指向 ID2D1DCRenderTarget 的指针。

在使用 DC 呈现器目标进行呈现之前,必须使用其 BindDC 方法将其与 GDI DC 相关联。 每次使用不同的 DC 或要绘制的区域的大小都会发生变化时执行此操作。

BindDC 方法采用两个参数:hDCpSubRecthDC 参数为接收呈现器目标输出的设备上下文提供句柄。 pSubRect 参数是描述内容呈现到的设备上下文部分的矩形。 如果 DC 呈现目标更改大小,则 DC 呈现目标会更新其大小,以匹配 pSubRect 描述的设备上下文区域。

以下代码将 DC 绑定到 DC 呈现目标。

HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{


// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
C++
// Bind the DC to the DC render target.
hr = m_pDCRT->BindDC(ps.hdc, &rc);

将 DC 呈现目标与 DC 关联后,可以使用它进行绘制。 以下代码使用 DC 绘制 Direct2D 和 GDI 内容。

HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{

    HRESULT hr;
    RECT rc;

    // Get the dimensions of the client drawing area.
    GetClientRect(m_hwnd, &rc);

    //
    // Draw the pie chart with Direct2D.
    //

    // Create the DC render target.
    hr = CreateDeviceResources();

    if (SUCCEEDED(hr))
    {
        // Bind the DC to the DC render target.
        hr = m_pDCRT->BindDC(ps.hdc, &rc);

        m_pDCRT->BeginDraw();

        m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());

        m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));

        m_pDCRT->DrawEllipse(
            D2D1::Ellipse(
                D2D1::Point2F(150.0f, 150.0f),
                100.0f,
                100.0f),
            m_pBlackBrush,
            3.0
            );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F(
                (150.0f + 100.0f * 0.15425f),
                (150.0f - 100.0f * 0.988f)),
            m_pBlackBrush,
            3.0
            );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F(
                (150.0f + 100.0f * 0.525f),
                (150.0f + 100.0f * 0.8509f)),
            m_pBlackBrush,
            3.0
            );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F(
                (150.0f - 100.0f * 0.988f),
                (150.0f - 100.0f * 0.15425f)),
            m_pBlackBrush,
            3.0
            );

        hr = m_pDCRT->EndDraw();
        if (SUCCEEDED(hr))
        {
            //
            // Draw the pie chart with GDI.
            //

            // Save the original object.
            HGDIOBJ original = NULL;
            original = SelectObject(
                ps.hdc,
                GetStockObject(DC_PEN)
                );

            HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
            SelectObject(ps.hdc, blackPen);

            Ellipse(ps.hdc, 300, 50, 500, 250);

            POINT pntArray1[2];
            pntArray1[0].x = 400;
            pntArray1[0].y = 150;
            pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
            pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

            POINT pntArray2[2];
            pntArray2[0].x = 400;
            pntArray2[0].y = 150;
            pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
            pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);


            POINT pntArray3[2];
            pntArray3[0].x = 400;
            pntArray3[0].y = 150;
            pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
            pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

            Polyline(ps.hdc, pntArray1, 2);
            Polyline(ps.hdc, pntArray2, 2);
            Polyline(ps.hdc, pntArray3, 2);

            DeleteObject(blackPen);

            // Restore the original object.
            SelectObject(ps.hdc, original);
        }
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }

    return hr;
}

此代码生成输出,如下图所示, (标注已添加,以突出显示 Direct2D 和 GDI 呈现之间的差异。)

使用 direct2d 和 gdi 呈现的两个圆形图表的插图

Windows 的 ID2D1DCRenderTargets、GDI 转换和从右到左语言版本

使用 ID2D1DCRenderTarget 时,它会将 Direct2D 内容呈现到内部位图,然后使用 GDI 将位图呈现到 DC。

GDI 可以通过 SetWorldTransform 方法将 GDI 转换 () 或其他效果应用于呈现器目标使用的同一 DC,在这种情况下,GDI 将转换 Direct2D 生成的位图。 使用 GDI 转换转换 Direct2D 内容可能会降低输出的视觉质量,因为要转换的位图已经计算了抗锯齿和子像素定位。

例如,假设使用呈现器目标绘制包含抗锯齿几何图形和文本的场景。 如果使用 GDI 转换将缩放转换应用到 DC 并缩放场景,使其大 10 倍,则会看到像素化和锯齿状边缘。 (但是,如果使用 Direct2D 应用了类似的转换,则场景的视觉质量不会降低。)

在某些情况下,GDI 正在执行可能降低 Direct2D 内容质量的其他处理可能并不明显。 例如,在从右到左 (RTL) Windows 内部版本上,当 GDI 将其复制到目标时, ID2D1DCRenderTarget 呈现的内容可能会被水平反转。 内容是否实际反转取决于 DC 的当前设置。

根据要呈现的内容的类型,可能需要防止反转。 如果 Direct2D 内容包含 ClearType 文本,这种反转将降低文本的质量。

可以使用 SetLayout GDI 函数控制 RTL 呈现行为。 若要防止镜像,请调用 SetLayout GDI 函数,并将 LAYOUT_BITMAPORIENTATIONPRESERVED 指定为第二个参数的唯一值, (不将其与 LAYOUT_RTL) 合并,如以下示例所示:

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

将 GDI 内容绘制到 Direct2D GDI-Compatible呈现器目标

上一部分介绍如何将 Direct2D 内容写入 GDI DC。 还可以将 GDI 内容写入与 Direct2D GDI 兼容的呈现目标。 此方法适用于主要使用 Direct2D 呈现但具有扩展性模型或其他需要 GDI 呈现能力的旧内容的应用程序。

若要将 GDI 内容呈现到与 Direct2D GDI 兼容的呈现器目标,请使用 ID2D1GdiInteropRenderTarget,该 ID2D1GdiInteropRenderTarget 提供对可接受 GDI 绘制调用的设备上下文的访问。 与其他接口不同,不会直接创建 ID2D1GdiInteropRenderTarget 对象。 请改用现有呈现器目标实例的 QueryInterface 方法。 以下代码演示了如何执行此操作:

        D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
        rtProps.usage =  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;

        // Create a GDI compatible Hwnd render target.
        hr = m_pD2DFactory->CreateHwndRenderTarget(
            rtProps,
            D2D1::HwndRenderTargetProperties(m_hwnd, size),
            &m_pRenderTarget
            );


        if (SUCCEEDED(hr))
        {
            hr = m_pRenderTarget->QueryInterface(__uuidof(ID2D1GdiInteropRenderTarget), (void**)&m_pGDIRT); 
        }

在前面的代码中, m_pD2DFactory 是指向 ID2D1Factory 的指针, m_pGDIRT 是指向 ID2D1GdiInteropRenderTarget 的指针。

请注意,创建 Hwnd GDI 兼容的呈现目标时指定了D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE 标志。 如果需要像素格式,请使用 DXGI_FORMAT_B8G8R8A8_UNORM。 如果需要 alpha 模式,请使用 D2D1_ALPHA_MODE_PREMULTIPLIEDD2D1_ALPHA_MODE_IGNORE

请注意, QueryInterface 方法始终成功。 若要测试 ID2D1GdiInteropRenderTarget 接口的方法是否适用于给定的呈现目标,请创建一个指定 GDI 兼容性和相应像素格式 的D2D1_RENDER_TARGET_PROPERTIES ,然后调用呈现目标的 IsSupported 方法以查看呈现目标是否与 GDI 兼容。

以下示例演示如何绘制与 Hwnd GDI 兼容的呈现目标) (GDI 内容的饼图。

        HDC hDC = NULL;
        hr = m_pGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &hDC);

        if (SUCCEEDED(hr))
        {
            // Draw the pie chart to the GDI render target associated with the Hwnd render target.
            HGDIOBJ original = NULL;
            original = SelectObject(
                hDC,
                GetStockObject(DC_PEN)
                );

            HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
            SelectObject(hDC, blackPen);

            Ellipse(hDC, 300, 50, 500, 250);

            POINT pntArray1[2];
            pntArray1[0].x = 400;
            pntArray1[0].y = 150;
            pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
            pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

            POINT pntArray2[2];
            pntArray2[0].x = 400;
            pntArray2[0].y = 150;
            pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
            pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);

            POINT pntArray3[2];
            pntArray3[0].x = 400;
            pntArray3[0].y = 150;
            pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
            pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

            Polyline(hDC, pntArray1, 2);
            Polyline(hDC, pntArray2, 2);
            Polyline(hDC, pntArray3, 2);

            DeleteObject(blackPen);

            // Restore the original object.
            SelectObject(hDC, original);

            m_pGDIRT->ReleaseDC(NULL);
        }

代码输出图表,如下图所示,标注突出显示呈现质量差异。 右饼图 (GDI 内容) 的呈现质量低于左饼图 (Direct2D 内容) 。 这是因为 Direct2D 能够使用抗锯齿进行渲染

direct2d gdi 兼容呈现目标中呈现的两个圆形图表的插图

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

GDI 设备上下文

GDI SDK