Partager via


Vue d’ensemble de l’interopérabilité direct2D et GDI

Cette rubrique explique comment utiliser Direct2D et GDI ensemble. Il existe deux façons de combiner Direct2D avec GDI : vous pouvez écrire du contenu GDI sur une cible de rendu compatible GDI Direct2D, ou vous pouvez écrire du contenu Direct2D dans un contexte d’appareil GDI (DC).

Cette rubrique contient les sections suivantes.

Prérequis

Cette vue d’ensemble suppose que vous êtes familiarisé avec les opérations de dessin Direct2D de base. Pour obtenir un didacticiel, consultez Créer une application Direct2D simple. Il suppose également que vous êtes familiarisé avec les opérations de dessin GDI.

Dessiner du contenu Direct2D dans un contexte d’appareil GDI

Pour dessiner du contenu Direct2D dans un contrôleur de domaine GDI, vous utilisez un ID2D1DCRenderTarget. Pour créer une cible de rendu DC, vous utilisez la méthode ID2D1Factory::CreateDCRenderTarget . Cette méthode prend deux paramètres.

Le premier paramètre, une structure de D2D1_RENDER_TARGET_PROPERTIES , spécifie le rendu, la communication à distance, le ppp, le format de pixel et les informations d’utilisation. Pour permettre à la cible de rendu du contrôleur de domaine de fonctionner avec GDI, définissez le format DXGI sur DXGI_FORMAT_B8G8R8A8_UNORM et le mode alpha sur D2D1_ALPHA_MODE_PREMULTIPLIED ou D2D1_ALPHA_MODE_IGNORE.

Le deuxième paramètre est l’adresse du pointeur qui reçoit la référence de la cible de rendu dc.

Le code suivant crée une cible de rendu 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);

Dans le code précédent, m_pD2DFactory est un pointeur vers un ID2D1Factory, et m_pDCRT est un pointeur vers un ID2D1DCRenderTarget.

Avant de pouvoir effectuer le rendu avec la cible de rendu du contrôleur de domaine, vous devez utiliser sa méthode BindDC pour l’associer à un contrôleur de domaine GDI. Vous le faites chaque fois que vous utilisez un contrôleur de domaine différent, ou la taille de la zone que vous souhaitez dessiner pour les modifications.

La méthode BindDC prend deux paramètres, hDC et pSubRect. Le paramètre hDC fournit un handle au contexte de l’appareil qui reçoit la sortie de la cible de rendu. Le paramètre pSubRect est un rectangle qui décrit la partie du contexte de l’appareil dans laquelle le contenu est rendu. La cible de rendu du contrôleur de domaine met à jour sa taille pour qu’elle corresponde à la zone de contexte de l’appareil décrite par pSubRect, si elle change de taille.

Le code suivant lie un contrôleur de domaine à une cible de rendu 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);

Après avoir associé la cible de rendu dc à un contrôleur de domaine, vous pouvez l’utiliser pour dessiner. Le code suivant dessine le contenu Direct2D et GDI à l’aide d’un contrôleur de domaine.

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

Ce code produit des sorties comme indiqué dans l’illustration suivante (des légendes ont été ajoutées pour mettre en évidence la différence entre le rendu Direct2D et GDI.)

illustration de deux graphiques circulaires rendus avec direct2d et gdi

ID2D1DCRenderTargets, transformations GDI et builds de langage de droite à gauche de Windows

Lorsque vous utilisez un ID2D1DCRenderTarget, il restitue le contenu Direct2D vers une bitmap interne, puis restitue la bitmap au contrôleur de domaine avec GDI.

Il est possible pour GDI d’appliquer une transformation GDI (via la méthode SetWorldTransform ) ou un autre effet au même contrôleur de domaine utilisé par la cible de rendu, auquel cas GDI transforme la bitmap produite par Direct2D. L’utilisation d’une transformation GDI pour transformer le contenu Direct2D a le potentiel de dégrader la qualité visuelle de la sortie, car vous transformez une bitmap pour laquelle le positionnement de l’anticrénelage et du sous-pixel a déjà été calculé.

Par exemple, supposons que vous utilisez la cible de rendu pour dessiner une scène qui contient du texte et des géométries anticrénelées. Si vous utilisez une transformation GDI pour appliquer une transformation d’échelle au contrôleur de domaine et mettre à l’échelle la scène afin qu’elle soit 10 fois plus grande, vous verrez la pixelisation et les bords déchiquetés. (Si, toutefois, vous appliquez une transformation similaire à l’aide de Direct2D, la qualité visuelle de la scène n’est pas dégradée.)

Dans certains cas, il n’est pas évident que GDI effectue un traitement supplémentaire susceptible de dégrader la qualité du contenu Direct2D. Par exemple, sur une build de droite à gauche (RTL) de Windows, le contenu rendu par un ID2D1DCRenderTarget peut être inversé horizontalement lorsque GDI le copie vers sa destination. Le fait que le contenu soit réellement inversé dépend des paramètres actuels du contrôleur de domaine.

Selon le type de contenu affiché, vous souhaiterez peut-être empêcher l’inversion. Si le contenu Direct2D inclut du texte ClearType, cette inversion dégrade la qualité du texte.

Vous pouvez contrôler le comportement de rendu RTL à l’aide de la fonction GDI SetLayout . Pour empêcher la mise en miroir, appelez la fonction GDI SetLayout et spécifiez LAYOUT_BITMAPORIENTATIONPRESERVED comme seule valeur pour le deuxième paramètre (ne la combinez pas avec LAYOUT_RTL), comme illustré dans l’exemple suivant :

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Dessiner du contenu GDI sur une cible de rendu GDI-Compatible Direct2D

La section précédente explique comment écrire du contenu Direct2D dans un contrôleur de domaine GDI. Vous pouvez également écrire du contenu GDI sur une cible de rendu compatible GDI Direct2D. Cette approche est utile pour les applications qui effectuent principalement un rendu avec Direct2D, mais qui ont un modèle d’extensibilité ou d’autres contenus hérités qui nécessitent la possibilité d’effectuer un rendu avec GDI.

Pour afficher le contenu GDI vers une cible de rendu compatible GDI Direct2D, utilisez un ID2D1GdiInteropRenderTarget, qui permet d’accéder à un contexte d’appareil qui peut accepter des appels de dessin GDI. Contrairement à d’autres interfaces, un objet ID2D1GdiInteropRenderTarget n’est pas créé directement. Utilisez plutôt la méthode QueryInterface d’une cible de rendu existante instance. Le code suivant illustre comment procéder :

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

Dans le code précédent, m_pD2DFactory est un pointeur vers un ID2D1Factory et m_pGDIRT est un pointeur vers un ID2D1GdiInteropRenderTarget.

Notez que l’indicateurD2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE est spécifié lors de la création de la cible de rendu compatible GDI Hwnd. Si un format de pixel est requis, utilisez DXGI_FORMAT_B8G8R8A8_UNORM. Si un mode alpha est requis, utilisez D2D1_ALPHA_MODE_PREMULTIPLIED ou D2D1_ALPHA_MODE_IGNORE.

Notez que la méthode QueryInterface réussit toujours. Pour tester si les méthodes de l’interface ID2D1GdiInteropRenderTarget fonctionnent pour une cible de rendu donnée, créez un D2D1_RENDER_TARGET_PROPERTIES qui spécifie la compatibilité GDI et le format de pixel approprié, puis appelez la méthode IsSupported de la cible de rendu pour voir si la cible de rendu est compatible avec GDI.

L’exemple suivant montre comment dessiner un graphique en secteurs (contenu GDI) sur la cible de rendu compatible 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);
        }

Le code génère des graphiques comme illustré dans l’illustration suivante avec des légendes pour mettre en évidence la différence de qualité de rendu. Le graphique en secteurs de droite (contenu GDI) a une qualité de rendu inférieure à celle du graphique à secteurs gauche (contenu Direct2D). Cela est dû au fait que Direct2D est capable de rendre avec l’anticrénelage

illustration de deux graphiques circulaires rendus dans une cible de rendu compatible gdi direct2d

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

Contextes d’appareil GDI

Kit de développement logiciel (SDK) GDI