Direct2D と GDI の相互運用性の概要

このトピックでは、Direct2D と GDI を一緒に使用する方法について説明します。 Direct2D と GDI を組み合わせる方法は 2 つあります。GDI コンテンツを Direct2D GDI 互換のレンダー ターゲットに書き込むか、GDI デバイス コンテキスト (DC) に Direct2D コンテンツを書き込むことができます。

このトピックは、次のセクションで構成されています。

必須コンポーネント

この概要では、基本的な Direct2D 描画操作について理解していることを前提としています。 チュートリアルについては、「 単純な Direct2D アプリケーションを作成する」を参照してください。 また、GDI 描画操作に精通していることも前提としています。

GDI デバイス コンテキストに Direct2D コンテンツを描画する

DIRECT2D コンテンツを GDI DC に描画するには、 ID2D1DCRenderTarget を使用します。 DC レンダー ターゲットを作成するには、 ID2D1Factory::CreateDCRenderTarget メソッドを使用します。 このメソッドは、2 つのパラメーターを受け取ります。

最初のパラメーター (D2D1_RENDER_TARGET_PROPERTIES 構造体) は、レンダリング、リモート処理、DPI、ピクセル形式、および使用状況情報を指定します。 DC レンダー ターゲットが GDI で動作できるようにするには、DXGI 形式を DXGI_FORMAT_B8G8R8A8_UNORM に設定し、アルファ モードを D2D1_ALPHA_MODE_PREMULTIPLIED または D2D1_ALPHA_MODE_IGNORE に設定します。

2 番目のパラメーターは、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_pD2DFactoryID2D1Factory へのポインターであり、 m_pDCRTID2D1DCRenderTarget へのポインターです。

DC レンダー ターゲットでレンダリングするには、その BindDC メソッドを使用して GDI DC に関連付ける必要があります。 これを行うには、別の DC を使用するか、変更に描画する領域のサイズを使用するたびに行います。

BindDC メソッドは、hDCpSubRect の 2 つのパラメーターを受け取ります。 hDC パラメーターは、レンダー ターゲットの出力を受け取るデバイス コンテキストへのハンドルを提供します。 pSubRect パラメーターは、コンテンツがレンダリングされるデバイス コンテキストの部分を表す四角形です。 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 でレンダリングされた 2 つの円グラフの図

WINDOWS の ID2D1DCRenderTargets、GDI 変換、および右から左への言語ビルド

ID2D1DCRenderTarget を使用すると、Direct2D コンテンツが内部ビットマップにレンダリングされ、GDI を使用して DC にビットマップがレンダリングされます。

GDI では、( SetWorldTransform メソッドを使用して) GDI 変換またはその他の効果をレンダー ターゲットで使用されるのと同じ DC に適用できます。この場合、GDI は Direct2D によって生成されたビットマップを変換します。 GDI 変換を使用して Direct2D コンテンツを変換すると、アンチエイリアシングとサブピクセル配置が既に計算されているビットマップを変換するため、出力のビジュアル品質が低下する可能性があります。

たとえば、レンダリング ターゲットを使用して、アンチエイリアシングされたジオメトリとテキストを含むシーンを描画するとします。 GDI 変換を使用して DC にスケール変換を適用し、シーンを 10 倍大きくするようにスケーリングすると、ピクセル化とジャグエッジが表示されます。 (ただし、Direct2D を使用して同様の変換を適用した場合、シーンのビジュアル品質は低下しません)。

場合によっては、GDI が Direct2D コンテンツの品質を低下させる可能性のある追加処理を実行していることは明らかでない場合があります。 たとえば、Windows の右から左 (RTL) ビルドでは、GDI がコピー先にコピーすると、 ID2D1DCRenderTarget によってレンダリングされるコンテンツが水平方向に反転される可能性があります。 コンテンツが実際に反転されるかどうかは、DC の現在の設定によって異なります。

レンダリングされるコンテンツの種類によっては、反転を防ぐことができます。 Direct2D コンテンツに ClearType テキストが含まれている場合、この反転によってテキストの品質が低下します。

SETLayout GDI 関数を使用して、RTL レンダリング動作を制御できます。 ミラーリングを防ぐには、 SetLayout GDI 関数を呼び出し、次の例に示すように 2 番目のパラメーターの唯一の値として LAYOUT_BITMAPORIENTATIONPRESERVED を指定します ( LAYOUT_RTLと組み合わせないでください)。

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Direct2D GDI-Compatible レンダー ターゲットに GDI コンテンツを描画する

前のセクションでは、GDI DC に Direct2D コンテンツを書き込む方法について説明します。 また、GDI コンテンツを Direct2D GDI 互換のレンダー ターゲットに書き込むこともできます。 この方法は、主に Direct2D でレンダリングされるが、GDI でレンダリングする機能を必要とする拡張性モデルまたはその他のレガシ コンテンツを持つアプリケーションに役立ちます。

GDI コンテンツを Direct2D GDI 互換のレンダー ターゲットにレンダリングするには、 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_pD2DFactoryID2D1Factory へのポインターであり、 m_pGDIRTID2D1GdiInteropRenderTarget へのポインターです。

Hwnd GDI 互換レンダー ターゲットを作成するときに、D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE フラグが指定されていることに注意してください。 ピクセル形式が必要な場合は、 DXGI_FORMAT_B8G8R8A8_UNORMを使用します。 アルファ モードが必要な場合は、 D2D1_ALPHA_MODE_PREMULTIPLIED または D2D1_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 互換レンダー ターゲットでレンダリングされた 2 つの円グラフの図

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

GDI デバイス コンテキスト

GDI SDK