Gambaran Umum Interoperabilitas Direct2D dan GDI

Topik ini menjelaskan cara menggunakan Direct2D dan GDI bersama-sama. Ada dua cara untuk menggabungkan Direct2D dengan GDI: Anda dapat menulis konten GDI ke target render yang kompatibel dengan GDI Direct2D, atau Anda dapat menulis konten Direct2D ke Konteks Perangkat GDI (DC).

Topik ini berisi bagian berikut.

Prasyarat

Gambaran umum ini mengasumsikan bahwa Anda terbiasa dengan operasi gambar Direct2D dasar. Untuk tutorial, lihat Membuat aplikasi Direct2D sederhana. Ini juga mengasumsikan bahwa Anda terbiasa dengan operasi gambar GDI.

Gambar Konten Direct2D ke Konteks Perangkat GDI

Untuk menggambar konten Direct2D ke GDI DC, Anda menggunakan ID2D1DCRenderTarget. Untuk membuat target render DC, Anda menggunakan metode ID2D1Factory::CreateDCRenderTarget . Metode ini mengambil dua parameter.

Parameter pertama, struktur D2D1_RENDER_TARGET_PROPERTIES , menentukan informasi rendering, remoting, DPI, format piksel, dan penggunaan. Untuk mengaktifkan target render DC agar berfungsi dengan GDI, atur format DXGI ke DXGI_FORMAT_B8G8R8A8_UNORM dan mode alfa ke D2D1_ALPHA_MODE_PREMULTIPLIED atau D2D1_ALPHA_MODE_IGNORE.

Parameter kedua adalah alamat penunjuk yang menerima referensi target render DC.

Kode berikut membuat target render 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);

Dalam kode sebelumnya, m_pD2DFactory adalah penunjuk ke ID2D1Factory, dan m_pDCRT adalah penunjuk ke ID2D1DCRenderTarget.

Sebelum Anda dapat merender dengan target render DC, Anda harus menggunakan metode BindDC-nya untuk mengaitkannya dengan GDI DC. Anda melakukan ini setiap kali Anda menggunakan DC yang berbeda, atau ukuran area yang ingin Anda gambar untuk perubahan.

Metode BindDC mengambil dua parameter, hDC dan pSubRect. Parameter hDC menyediakan handel ke konteks perangkat yang menerima output dari target render. Parameter pSubRect adalah persegi panjang yang menjelaskan bagian konteks perangkat tempat konten dirender. Target render DC memperbarui ukurannya agar sesuai dengan area konteks perangkat yang dijelaskan oleh pSubRect, jika ukurannya berubah.

Kode berikut mengikat DC ke target render 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);

Setelah mengaitkan target render DC dengan DC, Anda dapat menggunakannya untuk menggambar. Kode berikut menggambar konten Direct2D dan GDI menggunakan DC.

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;
}

Kode ini menghasilkan output seperti yang ditunjukkan dalam ilustrasi berikut (callout telah ditambahkan untuk menyoroti perbedaan antara penyajian Direct2D dan GDI.)

ilustrasi dua bagan melingkar yang dirender dengan direct2d dan gdi

ID2D1DCRenderTargets, Transformasi GDI, dan Build Bahasa Kanan-ke-Kiri Windows

Saat Anda menggunakan ID2D1DCRenderTarget, id2D1DCRenderTarget merender konten Direct2D ke bitmap internal, lalu merender bitmap ke DC dengan GDI.

GDI dapat menerapkan transformasi GDI (melalui metode SetWorldTransform ) atau efek lain ke DC yang sama yang digunakan oleh target render, dalam hal ini GDI mengubah bitmap yang dihasilkan oleh Direct2D. Menggunakan transformasi GDI untuk mengubah konten Direct2D berpotensi menurunkan kualitas visual output, karena Anda mengubah bitmap yang posisi antialias dan subpikselnya telah dihitung.

Misalnya, Anda menggunakan target render untuk menggambar adegan yang berisi geometri dan teks yang diantisipasi. Jika Anda menggunakan transformasi GDI untuk menerapkan transformasi skala ke DC dan menskalakan adegan sehingga 10 kali lebih besar, Anda akan melihat pikselisasi dan tepi berjatuhan. (Namun, jika Anda menerapkan transformasi serupa menggunakan Direct2D, kualitas visual adegan tidak akan terdegradasi.)

Dalam beberapa kasus, mungkin tidak jelas bahwa GDI melakukan pemrosesan tambahan yang mungkin menurunkan kualitas konten Direct2D. Misalnya, pada build Windows kanan-ke-kiri (RTL), konten yang dirender oleh ID2D1DCRenderTarget mungkin terbalik secara horizontal ketika GDI menyalinnya ke tujuannya. Apakah konten benar-benar terbalik tergantung pada pengaturan DC saat ini.

Bergantung pada jenis konten yang dirender, Anda mungkin ingin mencegah inversi. Jika konten Direct2D menyertakan teks ClearType, inversi ini akan menurunkan kualitas teks.

Anda dapat mengontrol perilaku penyajian RTL dengan menggunakan fungsi SetLayout GDI. Untuk mencegah pencerminan, panggil fungsi SetLayout GDI dan tentukan LAYOUT_BITMAPORIENTATIONPRESERVED sebagai satu-satunya nilai untuk parameter kedua (jangan gabungkan dengan LAYOUT_RTL), seperti yang ditunjukkan dalam contoh berikut:

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Gambar Konten GDI ke Target Render GDI-Compatible Direct2D

Bagian sebelumnya menjelaskan cara menulis konten Direct2D ke GDI DC. Anda juga dapat menulis konten GDI ke target render yang kompatibel dengan GDI Direct2D. Pendekatan ini berguna untuk aplikasi yang terutama dirender dengan Direct2D tetapi memiliki model ekstensibilitas atau konten warisan lainnya yang memerlukan kemampuan untuk merender dengan GDI.

Untuk merender konten GDI ke target render yang kompatibel dengan GDI Direct2D, gunakan ID2D1GdiInteropRenderTarget, yang menyediakan akses ke konteks perangkat yang dapat menerima panggilan gambar GDI. Tidak seperti antarmuka lain, objek ID2D1GdiInteropRenderTarget tidak dibuat secara langsung. Sebagai gantinya, gunakan metode QueryInterface dari instans target render yang ada. Kode berikut menunjukkan cara melakukan ini:

        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); 
        }

Dalam kode sebelumnya, m_pD2DFactory adalah penunjuk ke ID2D1Factory, dan m_pGDIRT adalah penunjuk ke ID2D1GdiInteropRenderTarget.

Perhatikan bahwa benderaD2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE ditentukan saat membuat target render yang kompatibel dengan GDI Hwnd. Jika format piksel diperlukan, gunakan DXGI_FORMAT_B8G8R8A8_UNORM. Jika mode alfa diperlukan, gunakan D2D1_ALPHA_MODE_PREMULTIPLIED atau D2D1_ALPHA_MODE_IGNORE.

Perhatikan bahwa metode QueryInterface selalu berhasil. Untuk menguji apakah metode antarmuka ID2D1GdiInteropRenderTarget akan berfungsi untuk target render tertentu, buat D2D1_RENDER_TARGET_PROPERTIES yang menentukan kompatibilitas GDI dan format piksel yang sesuai, lalu panggil metode IsSupported target render untuk melihat apakah target render kompatibel dengan GDI.

Contoh berikut menunjukkan cara menggambar bagan pai (konten GDI) ke target render yang kompatibel dengan GDI Hwnd.

        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);
        }

Kode menghasilkan bagan seperti yang ditunjukkan dalam ilustrasi berikut dengan callout untuk menyoroti perbedaan kualitas penyajian. Bagan pai kanan (konten GDI) memiliki kualitas penyajian yang lebih rendah daripada bagan pai kiri (konten Direct2D). Ini karena Direct2D mampu merender dengan antialias

ilustrasi dua bagan melingkar yang dirender dalam target render yang kompatibel dengan gdi direct2d

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

Konteks Perangkat GDI

GDI SDK