Поделиться через


Общие сведения о взаимодействии Direct2D и GDI

В этом разделе описывается совместное использование Direct2D и GDI . Существует два способа объединения Direct2D с GDI: вы можете записать содержимое GDI в целевой объект отрисовки, совместимый с Direct2D GDI, или записать содержимое Direct2D в контекст устройства GDI (DC).

В этом разделе содержатся следующие подразделы.

Предварительные требования

В этом обзоре предполагается, что вы знакомы с основными операциями рисования Direct2D. Руководство см. в статье Создание простого приложения Direct2D. Также предполагается, что вы знакомы с операциями рисования GDI.

Рисование содержимого Direct2D в контексте устройства GDI

Чтобы рисовать содержимое Direct2D в контроллере домена GDI, используйте ID2D1DCRenderTarget. Чтобы создать целевой объект отрисовки контроллера домена, используйте метод ID2D1Factory::CreateDCRenderTarget . Этот метод принимает два параметра.

Первый параметр, D2D1_RENDER_TARGET_PROPERTIES структура, указывает отрисовку, удаленное взаимодействие, DPI, формат пикселей и сведения об использовании. Чтобы обеспечить работу целевого объекта отрисовки контроллера домена с GDI, задайте для формата DXGI значение DXGI_FORMAT_B8G8R8A8_UNORM , а для альфа-режима — D2D1_ALPHA_MODE_PREMULTIPLIED или D2D1_ALPHA_MODE_IGNORE.

Второй параметр — это адрес указателя, который получает ссылку на целевой объект отрисовки контроллера домена.

В следующем коде создается целевой объект отрисовки контроллера домена.

// 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.

Перед отрисовкой с целевым объектом отрисовки контроллера домена необходимо использовать его метод BindDC , чтобы связать его с контроллером домена GDI. Это можно сделать при каждом использовании другого контроллера домена или при изменении размера области, на которую вы хотите нарисовать.

Метод BindDC принимает два параметра: hDC и pSubRect. Параметр hDC предоставляет дескриптор контекста устройства, который получает выходные данные целевого объекта отрисовки. Параметр pSubRect — это прямоугольник, описывающий часть контекста устройства, в которую отображается содержимое. Целевой объект отрисовки контроллера домена обновляет свой размер в соответствии с областью контекста устройства, описанной в pSubRect, в случае изменения размера.

Следующий код привязывает контроллер домена к целевому объекту отрисовки контроллера домена.

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

После связывания целевого объекта отрисовки контроллера домена с контроллером домена его можно использовать для рисования. Следующий код рисует содержимое 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

ID2D1DCRenderTargets, преобразования GDI и языковые сборки Windows справа налево

При использовании ID2D1DCRenderTarget он отрисовывает содержимое Direct2D во внутреннее растровое изображение, а затем отображает его в контроллере домена с помощью GDI.

GDI может применить преобразование GDI (с помощью метода SetWorldTransform ) или другой эффект к тому же контроллеру домена, который используется целевым объектом отрисовки. В этом случае GDI преобразует растровое изображение, созданное Direct2D. Использование преобразования GDI для преобразования содержимого Direct2D может привести к снижению качества визуального отображения выходных данных, так как вы преобразуете растровое изображение, для которого уже рассчитаны сглаживание и положение подпиксели.

Например, предположим, что вы используете целевой объект отрисовки для рисования сцены, содержащей сглаживание геометрических объектов и текста. Если вы используете преобразование GDI для применения преобразования масштабирования к контроллеру домена и масштабирования сцены таким образом, чтобы она была в 10 раз больше, вы увидите пикселизацию и зубчатые края. (Однако если вы применили аналогичное преобразование с помощью Direct2D, качество визуализации сцены не будет снижено.)

В некоторых случаях может быть не очевидно, что GDI выполняет дополнительную обработку, которая может ухудшить качество содержимого Direct2D. Например, в сборке Windows справа налево (RTL) содержимое, отображаемое id2D1DCRenderTarget , может быть горизонтально инвертировано, когда GDI копирует его в место назначения. Фактически ли инвертировано содержимое, зависит от текущих параметров контроллера домена.

В зависимости от типа отображаемого содержимого может потребоваться предотвратить инверсию. Если содержимое Direct2D содержит текст ClearType, это приведет к снижению качества текста.

Поведением отрисовки RTL можно управлять с помощью функции SetLayout GDI. Чтобы предотвратить зеркальное отображение, вызовите функцию SetLayout GDI и укажите LAYOUT_BITMAPORIENTATIONPRESERVED в качестве единственного значения для второго параметра (не объединяйте его с LAYOUT_RTL), как показано в следующем примере:

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Рисование содержимого GDI в целевой объект отрисовки GDI-Compatible Direct2D

В предыдущем разделе описывается запись содержимого Direct2D в контроллер домена GDI. Вы также можете записать содержимое 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_pD2DFactory является указателем на ID2D1Factory, а m_pGDIRT — указатель на ID2D1GdiInteropRenderTarget.

Обратите внимание, что флагD2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE указан при создании целевого объекта отрисовки, совместимого с Hwnd GDI. Если требуется формат пикселей, используйте DXGI_FORMAT_B8G8R8A8_UNORM. Если требуется альфа-режим, используйте D2D1_ALPHA_MODE_PREMULTIPLIED или D2D1_ALPHA_MODE_IGNORE.

Обратите внимание, что метод QueryInterface всегда выполняется успешно. Чтобы проверить, будут ли методы интерфейса ID2D1GdiInteropRenderTarget работать для заданного целевого объекта отрисовки, создайте D2D1_RENDER_TARGET_PROPERTIES , указывающую совместимость GDI и соответствующий формат пикселей, а затем вызовите метод IsSupported целевого объекта отрисовки, чтобы узнать, совместим ли целевой объект отрисовки с GDI.

В следующем примере показано, как нарисовать круговую диаграмму (содержимое GDI) для целевого объекта отрисовки, совместимого с Hwnd 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