Compartir a través de


Introducción a la interoperabilidad de Direct2D y GDI

En este tema se describe cómo usar Direct2D y GDI juntos. Hay dos maneras de combinar Direct2D con GDI: puedes escribir contenido GDI en un destino de representación compatible con GDI de Direct2D, o puedes escribir contenido de Direct2D en un contexto de dispositivo GDI (DC).

En este tema se incluyen las siguientes secciones.

Prerrequisitos

En esta introducción se da por supuesto que está familiarizado con las operaciones básicas de dibujo de Direct2D. Para ver un tutorial, consulte Creación de una aplicación simple de Direct2D. También supone que está familiarizado con las operaciones de dibujo de GDI.

Dibujar contenido de Direct2D en un contexto de dispositivo GDI

Para dibujar contenido de Direct2D en un controlador de dominio GDI, usa un ID2D1DCRenderTarget. Para crear un destino de representación de controlador de dominio, use el método ID2D1Factory::CreateDCRenderTarget . Este método toma dos parámetros.

El primer parámetro, una estructura de D2D1_RENDER_TARGET_PROPERTIES , especifica la representación, comunicación remota, PPP, formato de píxeles e información de uso. Para permitir que el destino de representación del controlador de dominio funcione con GDI, establezca el formato DXGI en DXGI_FORMAT_B8G8R8A8_UNORM y el modo alfa en D2D1_ALPHA_MODE_PREMULTIPLIED o D2D1_ALPHA_MODE_IGNORE.

El segundo parámetro es la dirección del puntero que recibe la referencia de destino de representación del controlador de dominio.

El código siguiente crea un destino de representación de controlador de dominio.

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

En el código anterior, m_pD2DFactory es un puntero a id2D1Factory y m_pDCRT es un puntero a un id2D1DCRenderTarget.

Para poder representar con el destino de representación del controlador de dominio, debe usar su método BindDC para asociarlo a un controlador de dominio GDI. Esto se hace cada vez que se usa un controlador de dominio diferente o el tamaño del área que desea dibujar para los cambios.

El método BindDC toma dos parámetros, hDC y pSubRect. El parámetro hDC proporciona un identificador para el contexto del dispositivo que recibe la salida del destino de representación. El parámetro pSubRect es un rectángulo que describe la parte del contexto del dispositivo al que se representa el contenido. El destino de representación del controlador de dominio actualiza su tamaño para que coincida con el área de contexto del dispositivo descrita por pSubRect, en caso de que cambie el tamaño.

El código siguiente enlaza un controlador de dominio a un destino de representación de controlador de dominio.

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

Después de asociar el destino de representación del controlador de dominio a un controlador de dominio, puede usarlo para dibujar. El código siguiente dibuja contenido de Direct2D y GDI mediante un controlador de dominio.

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

Este código genera salidas como se muestra en la ilustración siguiente (se han agregado llamadas para resaltar la diferencia entre la representación de Direct2D y GDI).

Ilustración de dos gráficos circulares representados con direct2d y gdi

Id2D1DCRenderTargets, transformaciones GDI y compilaciones de lenguaje de derecha a izquierda de Windows

Cuando usa un ID2D1DCRenderTarget, representa el contenido de Direct2D en un mapa de bits interno y, a continuación, representa el mapa de bits en el controlador de dominio con GDI.

Es posible que GDI aplique una transformación GDI (mediante el método SetWorldTransform ) u otro efecto al mismo controlador de dominio usado por el destino de representación, en cuyo caso GDI transforma el mapa de bits generado por Direct2D. El uso de una transformación GDI para transformar el contenido de Direct2D tiene la posibilidad de degradar la calidad visual de la salida, ya que está transformando un mapa de bits para el que ya se han calculado el posicionamiento de contornos y subpíxeles.

Por ejemplo, supongamos que usa el destino de representación para dibujar una escena que contenga geometrías con contorno y texto. Si usa una transformación GDI para aplicar una transformación de escala al controlador de dominio y escalar la escena para que sea 10 veces mayor, verá bordes pixelados y irregulares. (Sin embargo, si aplicaste una transformación similar mediante Direct2D, la calidad visual de la escena no se degradaría).

En algunos casos, es posible que no sea obvio que GDI está realizando un procesamiento adicional que podría degradar la calidad del contenido de Direct2D. Por ejemplo, en una compilación de derecha a izquierda (RTL) de Windows, el contenido representado por un ID2D1DCRenderTarget podría invertirse horizontalmente cuando GDI lo copia en su destino. Si el contenido se invierte realmente depende de la configuración actual del controlador de dominio.

En función del tipo de contenido que se represente, es posible que desee evitar la inversión. Si el contenido de Direct2D incluye texto ClearType, esta inversión degradará la calidad del texto.

Puede controlar el comportamiento de representación de RTL mediante la función SetLayout GDI. Para evitar la creación de reflejo, llame a la función SetLayout GDI y especifique LAYOUT_BITMAPORIENTATIONPRESERVED como el único valor del segundo parámetro (no lo combine con LAYOUT_RTL), como se muestra en el ejemplo siguiente:

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Dibujar contenido GDI en un destino de representación de Direct2D GDI-Compatible

En la sección anterior se describe cómo escribir contenido de Direct2D en un controlador de dominio GDI. También puede escribir contenido GDI en un destino de representación compatible con GDI de Direct2D. Este enfoque es útil para las aplicaciones que se representan principalmente con Direct2D, pero tienen un modelo de extensibilidad u otro contenido heredado que requiere la capacidad de representarse con GDI.

Para representar contenido GDI en un destino de representación compatible con GDI de Direct2D, use un ID2D1GdiInteropRenderTarget, que proporciona acceso a un contexto de dispositivo que puede aceptar llamadas de dibujo GDI. A diferencia de otras interfaces, no se crea directamente un objeto ID2D1GdiInteropRenderTarget . En su lugar, use el método QueryInterface de una instancia de destino de representación existente. El código siguiente muestra cómo hacerlo:

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

En el código anterior, m_pD2DFactory es un puntero a id2D1Factory y m_pGDIRT es un puntero a un id2D1GdiInteropRenderTarget.

Observe que se especifica lamarca D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE al crear el destino de representación compatible con Hwnd GDI. Si se requiere un formato de píxel, use DXGI_FORMAT_B8G8R8A8_UNORM. Si se requiere un modo alfa, use D2D1_ALPHA_MODE_PREMULTIPLIED o D2D1_ALPHA_MODE_IGNORE.

Tenga en cuenta que el método QueryInterface siempre se realiza correctamente. Para probar si los métodos de la interfaz ID2D1GdiInteropRenderTarget funcionarán para un destino de representación determinado, cree un D2D1_RENDER_TARGET_PROPERTIES que especifique la compatibilidad de GDI y el formato de píxel adecuado y, a continuación, llame al método IsSupported del destino de representación para ver si el destino de representación es compatible con GDI.

En el ejemplo siguiente se muestra cómo dibujar un gráfico circular (contenido GDI) en el destino de representación compatible con GDI de 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);
        }

El código genera gráficos como se muestra en la ilustración siguiente con llamadas para resaltar la diferencia de calidad de representación. El gráfico circular derecho (contenido GDI) tiene una calidad de representación inferior a la del gráfico circular izquierdo (contenido de Direct2D). Esto se debe a que Direct2D es capaz de representar con suavizado de contornos

Ilustración de dos gráficos circulares representados en un destino de representación compatible con gdi de direct2d

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

Contextos de dispositivo GDI

GDI SDK