Visão geral da interoperabilidade de Direct2D e GDI

Este tópico descreve como usar Direct2D e GDI juntos. Há duas maneiras de combinar Direct2D com a GDI: você pode escrever conteúdo GDI em um destino de renderização compatível com GDI Direct2D ou gravar Direct2D conteúdo em um DC (Contexto de Dispositivo GDI).

Este tópico inclui as seções a seguir.

Pré-requisitos

Essa visão geral pressupõe que você esteja familiarizado com operações básicas de desenho de Direct2D. Para obter um tutorial, consulte Criar um aplicativo de Direct2D simples. Ele também pressupõe que você esteja familiarizado com operações de desenho GDI.

Desenhar Direct2D conteúdo para um contexto de dispositivo GDI

Para desenhar Direct2D conteúdo para um DC GDI, use um ID2D1DCRenderTarget. Para criar um destino de renderização dc, use o método ID2D1Factory::CreateDCRenderTarget . Esse método usa dois parâmetros.

O primeiro parâmetro, uma estrutura de D2D1_RENDER_TARGET_PROPERTIES , especifica informações de renderização, comunicação remota, DPI, formato de pixel e uso. Para habilitar o destino de renderização de DC para trabalhar com GDI, defina o formato DXGI como DXGI_FORMAT_B8G8R8A8_UNORM e o modo alfa como D2D1_ALPHA_MODE_PREMULTIPLIED ou D2D1_ALPHA_MODE_IGNORE.

O segundo parâmetro é o endereço do ponteiro que recebe a referência de destino de renderização dc.

O código a seguir cria um destino de renderização 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);

No código anterior, m_pD2DFactory é um ponteiro para um ID2D1Factory e m_pDCRT é um ponteiro para um ID2D1DCRenderTarget.

Antes de renderizar com o destino de renderização dc, você deve usar seu método BindDC para associá-lo a um DC GDI. Você faz isso sempre que usa um DC diferente ou o tamanho da área que deseja desenhar para as alterações.

O método BindDC usa dois parâmetros, hDC e pSubRect. O parâmetro hDC fornece um identificador para o contexto do dispositivo que recebe a saída do destino de renderização. O parâmetro pSubRect é um retângulo que descreve a parte do contexto do dispositivo para o qual o conteúdo é renderizado. O destino de renderização de DC atualiza seu tamanho para corresponder à área de contexto do dispositivo descrita por pSubRect, caso altere o tamanho.

O código a seguir associa um DC a um destino de renderização de 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);

Depois de associar o destino de renderização de DC a um DC, você poderá usá-lo para desenhar. O código a seguir desenha Direct2D e conteúdo GDI usando um 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;
}

Esse código produz saídas conforme mostrado na ilustração a seguir (textos explicativos foram adicionados para realçar a diferença entre Direct2D e renderização de GDI.)

ilustração de dois gráficos circulares renderizados com direct2d e gdi

ID2D1DCRenderTargets, Transformações de GDI e builds de linguagem da direita para a esquerda do Windows

Quando você usa um ID2D1DCRenderTarget, ele renderiza Direct2D conteúdo para um bitmap interno e renderiza o bitmap para o DC com GDI.

É possível que a GDI aplique uma transformação GDI (por meio do método SetWorldTransform) ou outro efeito ao mesmo DC usado pelo destino de renderização, nesse caso, a GDI transforma o bitmap produzido por Direct2D. O uso de uma transformação GDI para transformar o conteúdo Direct2D tem o potencial de degradar a qualidade visual da saída, pois você está transformando um bitmap para o qual o posicionamento de antialiasing e subpixel já foi calculado.

Por exemplo, suponha que você use o destino de renderização para desenhar uma cena que contenha geometrias e textos suavizados. Se você usar uma transformação GDI para aplicar uma transformação de escala ao DC e dimensionar a cena para que ela seja 10 vezes maior, você verá pixelização e bordas irregulares. (No entanto, se você aplicou uma transformação semelhante usando Direct2D, a qualidade visual da cena não seria degradada.)

Em alguns casos, pode não ser óbvio que o GDI está executando processamento adicional que pode degradar a qualidade do conteúdo Direct2D. Por exemplo, em um build da direita para a esquerda (RTL) do Windows, o conteúdo renderizado por um ID2D1DCRenderTarget pode ser invertido horizontalmente quando a GDI o copia para seu destino. Se o conteúdo é realmente invertido depende das configurações atuais do DC.

Dependendo do tipo de conteúdo que está sendo renderizado, talvez você queira impedir a inversão. Se o conteúdo Direct2D incluir texto ClearType, essa inversão prejudicará a qualidade do texto.

Você pode controlar o comportamento de renderização rtl usando a função SetLayout GDI. Para evitar o espelhamento, chame a função SetLayout GDI e especifique LAYOUT_BITMAPORIENTATIONPRESERVED como o único valor para o segundo parâmetro (não combine-o com LAYOUT_RTL), conforme mostrado no exemplo a seguir:

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Desenhar conteúdo GDI para um destino de renderização de Direct2D GDI-Compatible

A seção anterior descreve como gravar Direct2D conteúdo em um DC GDI. Você também pode gravar conteúdo GDI em um destino de renderização compatível com GDI Direct2D. Essa abordagem é útil para aplicativos que renderizam principalmente com Direct2D mas têm um modelo de extensibilidade ou outro conteúdo herdado que requer a capacidade de renderização com GDI.

Para renderizar o conteúdo GDI para um Direct2D destino de renderização compatível com GDI, use um ID2D1GdiInteropRenderTarget, que fornece acesso a um contexto de dispositivo que pode aceitar chamadas de desenho GDI. Ao contrário de outras interfaces, um objeto ID2D1GdiInteropRenderTarget não é criado diretamente. Em vez disso, use o método QueryInterface de uma instância de destino de renderização existente. O código a seguir mostra como fazer isso:

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

No código anterior, m_pD2DFactory é um ponteiro para um ID2D1Factory e m_pGDIRT é um ponteiro para um ID2D1GdiInteropRenderTarget.

Observe que osinalizador D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE é especificado ao criar o destino de renderização compatível com GDI Hwnd. Se um formato de pixel for necessário, use DXGI_FORMAT_B8G8R8A8_UNORM. Se um modo alfa for necessário, use D2D1_ALPHA_MODE_PREMULTIPLIED ou D2D1_ALPHA_MODE_IGNORE.

Observe que o método QueryInterface sempre é bem-sucedido. Para testar se os métodos da interface ID2D1GdiInteropRenderTarget funcionarão para um determinado destino de renderização, crie um D2D1_RENDER_TARGET_PROPERTIES que especifica a compatibilidade GDI e o formato de pixel apropriado e chame o método IsSupported do destino de renderização para ver se o destino de renderização é compatível com GDI.

O exemplo a seguir mostra como desenhar um gráfico de pizza (conteúdo GDI) para o destino de renderização compatível com 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);
        }

O código gera gráficos conforme mostrado na ilustração a seguir com textos explicativos para realçar a diferença de qualidade de renderização. O gráfico de pizza à direita (conteúdo GDI) tem qualidade de renderização mais baixa do que o gráfico de pizza esquerdo (Direct2D conteúdo). Isso ocorre porque Direct2D é capaz de renderizar com suavização

ilustração de dois gráficos circulares renderizados em um destino de renderização compatível com gdi direct2d

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

Contextos do dispositivo GDI

GDI SDK