Panoramica dell'interoperabilità direct2D e GDI

Questo argomento descrive come usare Direct2D e GDI insieme. Esistono due modi per combinare Direct2D con GDI: è possibile scrivere contenuto GDI in una destinazione di rendering compatibile con Direct2D GDI oppure scrivere contenuto Direct2D in un contesto di dispositivo GDI (DC).

In questo argomento sono contenute le sezioni seguenti.

Prerequisiti

Questa panoramica presuppone che si abbia familiarità con le operazioni di disegno Direct2D di base. Per un'esercitazione, vedere Creare un'applicazione Direct2D semplice. Si presuppone anche che si abbia familiarità con le operazioni di disegno GDI.

Disegnare contenuto Direct2D in un contesto di dispositivo GDI

Per disegnare contenuto Direct2D in un controller di dominio GDI, usare un ID2D1DCRenderTarget. Per creare una destinazione di rendering dc, usare il metodo ID2D1Factory::CreateDCRenderTarget . Questo metodo accetta due parametri.

Il primo parametro, una struttura D2D1_RENDER_TARGET_PROPERTIES , specifica il rendering, la comunicazione remota, DPI, il formato pixel e le informazioni sull'utilizzo. Per abilitare la destinazione di rendering del controller di dominio in modo che funzioni con GDI, impostare il formato DXGI su DXGI_FORMAT_B8G8R8A8_UNORM e la modalità alfa su D2D1_ALPHA_MODE_PREMULTIPLIED o D2D1_ALPHA_MODE_IGNORE.

Il secondo parametro è l'indirizzo del puntatore che riceve il riferimento di destinazione del rendering del controller di dominio.

Il codice seguente crea una destinazione di rendering del controller di 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);

Nel codice precedente , m_pD2DFactory è un puntatore a un ID2D1Factory e m_pDCRT è un puntatore a un ID2D1DCRenderTarget.

Prima di poter eseguire il rendering con la destinazione di rendering del controller di dominio, è necessario usare il metodo BindDC per associarlo a un controller di dominio GDI. Questa operazione viene eseguita ogni volta che si usa un controller di dominio diverso o le dimensioni dell'area da disegnare alle modifiche.

Il metodo BindDC accetta due parametri, hDC e pSubRect. Il parametro hDC fornisce un handle al contesto del dispositivo che riceve l'output della destinazione di rendering. Il parametro pSubRect è un rettangolo che descrive la parte del contesto del dispositivo a cui viene eseguito il rendering del contenuto. La destinazione di rendering del controller di dominio aggiorna le dimensioni per corrispondere all'area del contesto del dispositivo descritta da pSubRect, se modifica le dimensioni.

Il codice seguente associa un controller di dominio a una destinazione di rendering del controller di 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);

Dopo aver associato la destinazione di rendering del controller di dominio a un controller di dominio, è possibile usarla per disegnare. Il codice seguente disegna contenuto Direct2D e GDI usando un controller di 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;
}

Questo codice produce output come illustrato nella figura seguente (i callout sono stati aggiunti per evidenziare la differenza tra il rendering Direct2D e GDI).

illustrazione di due grafici circolari sottoposti a rendering con direct2d e gdi

ID2D1DCRenderTargets, trasformazioni GDI e build del linguaggio da destra a sinistra di Windows

Quando si usa un ID2D1DCRenderTarget, esegue il rendering del contenuto Direct2D in una bitmap interna e quindi esegue il rendering della bitmap nel controller di dominio con GDI.

È possibile che GDI applichi una trasformazione GDI (tramite il metodo SetWorldTransform ) o un altro effetto alla stessa controller di dominio utilizzata dalla destinazione di rendering, in cui GDI trasforma la bitmap prodotta da Direct2D. L'uso di una trasformazione GDI per trasformare il contenuto Direct2D ha il potenziale di degradare la qualità visiva dell'output, perché si sta trasformando una bitmap per la quale sono già stati calcolati la posizione antialiasing e subpixel.

Si supponga, ad esempio, di usare la destinazione di rendering per disegnare una scena contenente geometrie antialiased e testo. Se si usa una trasformazione GDI per applicare una trasformazione di scala al controller di dominio e ridimensionare la scena in modo che sia maggiore di 10 volte, si noterà la pixelizzazione e i bordi ingranditi. Se tuttavia si applica una trasformazione simile usando Direct2D, la qualità visiva della scena non sarà danneggiata.

In alcuni casi, potrebbe non essere evidente che GDI esegue un'elaborazione aggiuntiva che potrebbe degradare la qualità del contenuto Direct2D. Ad esempio, in una compilazione RTL (RTL) di Windows, il contenuto eseguito da un ID2D1DCRenderTarget potrebbe essere invertito orizzontalmente quando GDI lo copia nella destinazione. Se il contenuto è effettivamente invertito dipende dalle impostazioni correnti del controller di dominio.

A seconda del tipo di contenuto sottoposto a rendering, è possibile impedire l'inversione. Se il contenuto Direct2D include il testo ClearType, questa inversione degraderà la qualità del testo.

È possibile controllare il comportamento di rendering RTL usando la funzione SetLayout GDI. Per evitare il mirroring, chiamare la funzione SetLayout GDI e specificare LAYOUT_BITMAPORIENTATIONPRESERVED come unico valore per il secondo parametro (non combinarlo con LAYOUT_RTL), come illustrato nell'esempio seguente:

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Disegnare contenuto GDI in una destinazione di rendering direct2D GDI-Compatible

La sezione precedente descrive come scrivere contenuto Direct2D in un controller di dominio GDI. È anche possibile scrivere contenuto GDI in una destinazione di rendering compatibile con Direct2D GDI. Questo approccio è utile per le applicazioni che eseguono principalmente il rendering con Direct2D, ma hanno un modello di estendibilità o altri contenuti legacy che richiedono la possibilità di eseguire il rendering con GDI.

Per eseguire il rendering del contenuto GDI in una destinazione di rendering compatibile con Direct2D GDI, usare un ID2D1GdiInteropRenderTarget, che fornisce l'accesso a un contesto di dispositivo che può accettare chiamate di disegno GDI. A differenza di altre interfacce, non viene creato direttamente un oggetto ID2D1GdiInteropRenderTarget . Usare invece il metodo QueryInterface di un'istanza di destinazione di rendering esistente. Nel codice seguente viene illustrato come eseguire questa operazione:

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

Nel codice precedente , m_pD2DFactory è un puntatore a un ID2D1Factory e m_pGDIRT è un puntatore a un ID2D1GdiInteropRenderTarget.

Si noti che il flagD2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE viene specificato durante la creazione della destinazione di rendering compatibile con GDI Hwnd. Se è necessario un formato pixel, usare DXGI_FORMAT_B8G8R8A8_UNORM. Se è necessaria una modalità alfa, usare D2D1_ALPHA_MODE_PREMULTIPLIED o D2D1_ALPHA_MODE_IGNORE.

Si noti che il metodo QueryInterface ha sempre esito positivo. Per verificare se i metodi dell'interfaccia ID2D1GdiInteropRenderTarget funzioneranno per una determinata destinazione di rendering, creare un D2D1_RENDER_TARGET_PROPERTIES che specifica la compatibilità GDI e il formato pixel appropriato e quindi chiamare il metodo IsSupported della destinazione di rendering per verificare se la destinazione di rendering è compatibile con GDI.

Nell'esempio seguente viene illustrato come disegnare un grafico a torta (contenuto GDI) alla destinazione di rendering compatibile con 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);
        }

I grafici di codice vengono visualizzati come illustrato nella figura seguente con callout per evidenziare la differenza di qualità del rendering. Il grafico a torta a destra (contenuto GDI) ha una qualità di rendering inferiore rispetto al grafico a torta a sinistra (contenuto Direct2D). Questo perché Direct2D è in grado di eseguire il rendering con antialiasing

illustrazione di due grafici circolari sottoposti a rendering in una destinazione di rendering compatibile con gdi direct2d

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

Contesti di dispositivo GDI

GDI SDK