Vue d’ensemble des couches

Cette vue d’ensemble décrit les principes fondamentaux de l’utilisation des couches Direct2D. Elle contient les sections suivantes.

Qu’est-ce que les couches ?

Les couches, représentées par des objets ID2D1Layer , permettent à une application de manipuler un groupe d’opérations de dessin. Vous utilisez une couche en l’appuyant sur une cible de rendu. Les opérations de dessin suivantes effectuées par la cible de rendu sont dirigées vers la couche. Une fois la couche terminée, vous « faites apparaître » la couche à partir de la cible de rendu, qui combine le contenu de la couche vers la cible de rendu.

Comme les pinceaux, les couches sont des ressources dépendantes de l’appareil créées par des cibles de rendu. Les couches peuvent être utilisées sur n’importe quelle cible de rendu dans le même domaine de ressource qui contient la cible de rendu qui l’a créée. Toutefois, une ressource de couche ne peut être utilisée que par une cible de rendu à la fois. Pour plus d’informations sur les ressources, consultez la vue d’ensemble des ressources.

Bien que les couches offrent une technique de rendu puissante pour produire des effets intéressants, un nombre excessif de couches dans une application peut nuire à ses performances, en raison des différents coûts associés à la gestion des couches et des ressources de couche. Par exemple, il y a le coût de remplissage ou d’effacement de la couche, puis de la fusionner, en particulier sur le matériel haut de gamme. Ensuite, il y a le coût de la gestion des ressources de couche. Si vous réallouer ces données fréquemment, les blocages résultants sur le GPU sont le problème le plus important. Lorsque vous concevez votre application, essayez d’optimiser la réutilisation des ressources de couche.

Couches dans Windows 8 et versions ultérieures

Windows 8 introduit de nouvelles API liées à la couche qui simplifient, améliorent les performances et ajoutent des fonctionnalités aux couches.

ID2D1DeviceContext et PushLayer

L’interface ID2D1DeviceContext est dérivée de l’interface ID2D1RenderTarget et est essentielle pour afficher du contenu Direct2D dans Windows 8, pour plus d’informations sur cette interface, voir Périphériques et contextes d’appareil. Avec l’interface de contexte de l’appareil, vous pouvez ignorer l’appel de la méthode CreateLayer , puis passer NULL à la méthode ID2D1DeviceContext::P ushLayer . Direct2D gère automatiquement la ressource de couche et peut partager des ressources entre des couches et des graphiques d’effet.

D2D1_LAYER_PARAMETERS1 et D2D1_LAYER_OPTIONS1

La structure D2D1_LAYER_PARAMETERS1 est identique à D2D1_LAYER_PARAMETERS, sauf que le membre final de la structure est maintenant une énumération D2D1_LAYER_OPTIONS1 .

D2D1_LAYER_OPTIONS1 n’a pas d’option ClearType et propose deux options différentes que vous pouvez utiliser pour améliorer les performances :

Modes de fusion

À compter de Windows 8, le contexte de l’appareil a un mode de fusion primitif qui détermine la façon dont chaque primitive est fusionnée avec la surface cible. Ce mode s’applique également aux couches lorsque vous appelez la méthode PushLayer .

Par exemple, si vous utilisez une couche pour découper des primitives avec transparence, définissez le mode D2D1_PRIMITIVE_BLEND_COPY sur le contexte de l’appareil pour obtenir des résultats appropriés. Le mode de copie fait en sorte que le contexte de l’appareil interpole l’ensemble des 4 canaux de couleur, y compris le canal alpha, de chaque pixel avec le contenu de la surface cible en fonction du masque géométrique de la couche.

Interopérabilité

À compter de Windows 8, Direct2D prend en charge l’interopérabilité avec Direct3D et GDI pendant qu’une couche ou un clip est envoyé (push). Vous appelez ID2D1GdiInteropRenderTarget::GetDC pendant qu’une couche est envoyée pour interagir avec GDI. Vous appelez ID2D1DeviceContext::Flush , puis effectuez un rendu sur la surface sous-jacente pour interagir avec Direct3D. Il vous incombe d’effectuer un rendu à l’intérieur de la couche ou du clip avec Direct3D ou GDI. Si vous essayez d’effectuer un rendu en dehors de la couche ou du découpage, les résultats ne sont pas définis.

Création de couches

L’utilisation de couches nécessite une connaissance des méthodes CreateLayer, PushLayer et PopLayer , ainsi que la structure D2D1_LAYER_PARAMETERS , qui contient un ensemble de données paramétriques qui définit la façon dont la couche peut être utilisée. La liste suivante décrit les méthodes et la structure.

  • Appelez la méthode CreateLayer pour créer une ressource de couche.

    Notes

    À compter de Windows 8, vous pouvez ignorer l’appel de la méthode CreateLayer, puis passer NULL à la méthode PushLayer sur l’interface ID2D1DeviceContext. Cela est plus simple et permet à Direct2D de gérer automatiquement la ressource de couche et de partager des ressources entre des couches et des graphiques d’effet.

     

  • Une fois que la cible de rendu a commencé à dessiner (après son appel à la méthode BeginDraw ), vous pouvez utiliser la méthode PushLayer . La méthode PushLayer ajoute la couche spécifiée à la cible de rendu, afin que la cible reçoive toutes les opérations de dessin suivantes jusqu’à ce que PopLayer soit appelé. Cette méthode prend un objet ID2D1Layer retourné en appelant CreateLayer et un layerParameters dans la structure D2D1_LAYER_PARAMETERS . Le tableau suivant décrit les champs de la structure.

    Champ Description
    contentBounds Limites de contenu de la couche. Le contenu ne s’affiche pas en dehors de ces limites. Ce paramètre est défini par défaut sur InfiniteRect. Lorsque la valeur par défaut est utilisée, les limites de contenu sont effectivement considérées comme les limites de la cible de rendu.
    géométriqueMask (Facultatif) Zone, définie par un ID2D1Geometry, dans laquelle la couche doit être découpée. Définissez la valeur NULL si la couche ne doit pas être découpée sur une géométrie.
    maskAntialiasMode Valeur qui spécifie le mode anticrénelage pour le masque géométrique spécifié par le champ géométriqueMask .
    maskTransform Valeur qui spécifie la transformation appliquée au masque géométrique lors de la composition du calque. Ceci est relatif à la transformation du monde.
    Opacité Valeur d’opacité de la couche. L’opacité de chaque ressource de la couche est multipliée par cette valeur lors de la composition à la cible.
    opacityBrush (Facultatif) Pinceau utilisé pour modifier l’opacité du calque. Le pinceau est mappé à la couche et le canal alpha de chaque pixel de pinceau mappé est multiplié par rapport au pixel de couche correspondant. Définissez la valeur NULL si le calque ne doit pas avoir de masque d’opacité.
    layerOptions Valeur qui spécifie si la couche a l’intention de restituer du texte avec l’anticrénelage ClearType. Ce paramètre est désactivé par défaut. L’activation permet à ClearType de fonctionner correctement, mais cela entraîne une vitesse de rendu légèrement plus lente.

     

    Remarque

    À compter de Windows 8, vous ne pouvez pas effectuer le rendu avec ClearType dans une couche. Le paramètre layerOptions doit donc toujours être défini sur D2D1_LAYER_OPTIONS_NONE

     

    Pour des raisons pratiques, Direct2D fournit la méthode D2D1::LayerParameters pour vous aider à créer des structures D2D1_LAYER_PARAMETERS .

  • Pour compositer le contenu de la couche dans la cible de rendu, appelez la méthode PopLayer . Vous devez appeler la méthode PopLayer avant d’appeler la méthode EndDraw .

L’exemple suivant montre comment utiliser CreateLayer, PushLayer et PopLayer. Tous les champs de la structure D2D1_LAYER_PARAMETERS sont définis sur leurs valeurs par défaut, à l’exception de l’opacitéBrush, qui est défini sur un ID2D1RadialGradientBrush.

// Create a layer.
ID2D1Layer *pLayer = NULL;
hr = pRT->CreateLayer(NULL, &pLayer);

if (SUCCEEDED(hr))
{
    pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 250));

    // Push the layer with the content bounds.
    pRT->PushLayer(
        D2D1::LayerParameters(
            D2D1::InfiniteRect(),
            NULL,
            D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
            D2D1::IdentityMatrix(),
            1.0,
            m_pRadialGradientBrush,
            D2D1_LAYER_OPTIONS_NONE),
        pLayer
        );

    pRT->DrawBitmap(m_pBambooBitmap, D2D1::RectF(0, 0, 190, 127));

    pRT->FillRectangle(
        D2D1::RectF(25.f, 25.f, 50.f, 50.f), 
        m_pSolidColorBrush
        );
    pRT->FillRectangle(
        D2D1::RectF(50.f, 50.f, 75.f, 75.f),
        m_pSolidColorBrush
        ); 
    pRT->FillRectangle(
        D2D1::RectF(75.f, 75.f, 100.f, 100.f),
        m_pSolidColorBrush
        );    
 
    pRT->PopLayer();
}
SafeRelease(&pLayer);

Le code a été omis de cet exemple.

Notez que lorsque vous appelez PushLayer et PopLayer, vérifiez que chaque PushLayer a un appel PopLayer correspondant. S’il existe plus d’appels PopLayer que d’appels PushLayer , la cible de rendu est placée dans un état d’erreur. Si le vide est appelé avant que toutes les couches en attente soient déclenchées, la cible de rendu est placée dans un état d’erreur et retourne une erreur. Pour effacer l’état d’erreur, utilisez EndDraw.

Limites de contenu

ContentBounds définit la limite de ce qui doit être dessiné dans la couche. Seules ces choses dans les limites de contenu sont composites à la cible de rendu.

L’exemple suivant montre comment spécifier contentBounds afin que l’image d’origine soit clippée aux limites de contenu avec le coin supérieur gauche à (10, 108) et le coin inférieur droit à (121, 177). L’illustration suivante montre l’image d’origine et le résultat du découpage de l’image vers les limites de contenu.

illustration of content bounds on an original picture and the resulting clipped picture

HRESULT DemoApp::RenderWithLayerWithContentBounds(ID2D1RenderTarget *pRT)
{
    
    HRESULT hr = S_OK;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 0));

        // Push the layer with the content bounds.
        pRT->PushLayer(
            D2D1::LayerParameters(D2D1::RectF(10, 108, 121, 177)),
            pLayer
            );

        pRT->DrawBitmap(m_pWaterBitmap, D2D1::RectF(0, 0, 128, 192));
        pRT->PopLayer();
    }

    SafeRelease(&pLayer);

    return hr;
    
}

Le code a été omis de cet exemple.

Notes

L’image clippée obtenue est davantage affectée si vous spécifiez un masque géométrique. Pour plus d’informations, consultez la section Masques géométriques .

 

Masques géométriques

Un masque géométrique est un clip ou un découpage, défini par un objet ID2D1Geometry , qui masque une couche lorsqu’elle est dessinée par une cible de rendu. Vous pouvez utiliser le champ geometryMask de la structure D2D1_LAYER_PARAMETERS pour masquer les résultats à une géométrie. Par exemple, si vous souhaitez afficher une image masquée par une lettre de bloc « A », vous pouvez d’abord créer une géométrie représentant la lettre de bloc « A » et utiliser cette géométrie comme masque géométrique pour une couche. Ensuite, après avoir envoyé la couche, vous pouvez dessiner l’image. Le fait de faire apparaître la couche entraîne le découpage de l’image dans la forme « A » de la lettre de bloc.

L’exemple suivant montre comment créer une valeur ID2D1PathGeometry contenant une forme de montagne, puis passer la géométrie de chemin à PushLayer. Il dessine ensuite une bitmap et des carrés. S’il n’y a qu’une bitmap dans la couche à afficher, utilisez FillGeometry avec un pinceau bitmap serré pour l’efficacité. L’illustration suivante montre la sortie de l’exemple.

illustration of a picture of a leaf and the resulting picture after a geometric mask of a mountain is applied

Le premier exemple définit la géométrie à utiliser comme masque.

hr = m_pD2DFactory->CreatePathGeometry(&m_pPathGeometry);
    
if(SUCCEEDED(hr))
{
    ID2D1GeometrySink *pSink = NULL;
    // Write to the path geometry using the geometry sink.
    hr = m_pPathGeometry->Open(&pSink);

    if (SUCCEEDED(hr))
    {
        pSink->SetFillMode(D2D1_FILL_MODE_WINDING);
        pSink->BeginFigure(
            D2D1::Point2F(0, 90),
            D2D1_FIGURE_BEGIN_FILLED
            );

        D2D1_POINT_2F points[7] = {
           D2D1::Point2F(35, 30),
           D2D1::Point2F(50, 50),
           D2D1::Point2F(70, 45),
           D2D1::Point2F(105, 90),
           D2D1::Point2F(130, 90),
           D2D1::Point2F(150, 60),
           D2D1::Point2F(170, 90)
           };

        pSink->AddLines(points, 7);
        pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
        hr = pSink->Close();
    }
    SafeRelease(&pSink);
       }

L’exemple suivant utilise la géométrie comme masque pour la couche.

HRESULT DemoApp::RenderWithLayerWithGeometricMask(ID2D1RenderTarget *pRT)
{
    
    HRESULT hr;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 450));

        pRT->PushLayer(
            D2D1::LayerParameters(D2D1::InfiniteRect(), m_pPathGeometry),
            pLayer
            );

        pRT->DrawBitmap(m_pLeafBitmap, D2D1::RectF(0, 0, 198, 132));

        pRT->FillRectangle(
            D2D1::RectF(50.f, 50.f, 75.f, 75.f), 
            m_pSolidColorBrush
            ); 
        pRT->FillRectangle(
            D2D1::RectF(75.f, 75.f, 100.f, 100.f),
            m_pSolidColorBrush
            );        

        pRT->PopLayer();
    }

    SafeRelease(&pLayer);

    return hr;
    
}

Le code a été omis de cet exemple.

Remarque

En général, si vous spécifiez un masque géométrique, vous pouvez utiliser la valeur par défaut, InfiniteRect, pour contentBounds.

Si contentBounds est NULL et géométriqueMask n’est pas NULL, les limites de contenu sont effectivement les limites du masque géométrique après l’application de la transformation du masque de masque.

Si contentBounds n’est pas NULL, et géométriqueMask n’est pas NULL, le masque géométrique transformé est effectivement clippé par rapport aux limites de contenu et les limites de contenu sont supposées être infinies.

 

Masques d’opacité

Un masque d’opacité est un masque décrit par un pinceau ou une bitmap, appliqué à un autre objet pour rendre cet objet partiellement ou complètement transparent. Il permet l’utilisation du canal alpha d’un pinceau à utiliser comme masque de contenu. Par exemple, vous pouvez définir un pinceau de dégradé radial qui varie de opaque à transparent pour créer un effet de vignette.

L’exemple suivant utilise un ID2D1RadialGradientBrush (m_pRadialGradientBrush) comme masque d’opacité. Il dessine ensuite une bitmap et des carrés. S’il n’y a qu’une bitmap dans la couche à afficher, utilisez FillGeometry avec un pinceau bitmap serré pour l’efficacité. L’illustration suivante montre la sortie de cet exemple.

illustration of a picture of trees and the resulting picture after an opacity mask is applied

HRESULT DemoApp::RenderWithLayerWithOpacityMask(ID2D1RenderTarget *pRT)
{   

    HRESULT hr = S_OK;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 250));

        // Push the layer with the content bounds.
        pRT->PushLayer(
            D2D1::LayerParameters(
                D2D1::InfiniteRect(),
                NULL,
                D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
                D2D1::IdentityMatrix(),
                1.0,
                m_pRadialGradientBrush,
                D2D1_LAYER_OPTIONS_NONE),
            pLayer
            );

        pRT->DrawBitmap(m_pBambooBitmap, D2D1::RectF(0, 0, 190, 127));

        pRT->FillRectangle(
            D2D1::RectF(25.f, 25.f, 50.f, 50.f), 
            m_pSolidColorBrush
            );
        pRT->FillRectangle(
            D2D1::RectF(50.f, 50.f, 75.f, 75.f),
            m_pSolidColorBrush
            ); 
        pRT->FillRectangle(
            D2D1::RectF(75.f, 75.f, 100.f, 100.f),
            m_pSolidColorBrush
            );    
 
        pRT->PopLayer();
    }
    SafeRelease(&pLayer);
   
    return hr;
    
}

Le code a été omis de cet exemple.

Remarque

Cet exemple utilise une couche pour appliquer un masque d’opacité à un objet unique pour conserver l’exemple aussi simple que possible. Lors de l’application d’un masque d’opacité à un objet unique, il est plus efficace d’utiliser les méthodes FillOpacityMask ou FillGeometry , plutôt qu’une couche.

 

Pour obtenir des instructions sur l’application d’un masque d’opacité sans utiliser de couche, consultez la vue d’ensemble des masques d’opacité.

Alternatives aux couches

Comme mentionné précédemment, un nombre excessif de couches peut affecter négativement les performances de votre application. Pour améliorer les performances, évitez d’utiliser des couches chaque fois que possible ; utilisez plutôt leurs alternatives. L’exemple de code suivant montre comment utiliser PushAxisA alignClip et PopAxisA alignClip pour cliper une région, comme alternative à l’utilisation d’une couche avec des limites de contenu.

pRT->PushAxisAlignedClip(
    D2D1::RectF(20, 20, 100, 100),
    D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
    );

pRT->FillRectangle(D2D1::RectF(0, 0, 200, 133), m_pOriginalBitmapBrush);
pRT->PopAxisAlignedClip();

De même, utilisez FillGeometry avec un pinceau bitmap serré comme alternative à l’utilisation d’une couche avec un masque d’opacité lorsqu’il n’y a qu’un seul contenu dans la couche à afficher, comme illustré dans l’exemple suivant.

        m_pRenderTarget->FillGeometry(
            m_pRectGeo, 
            m_pLinearFadeFlowersBitmapBrush, 
            m_pLinearGradientBrush
            );

En guise d’alternative à l’utilisation d’une couche avec un masque géométrique, envisagez d’utiliser un masque bitmap pour couper une région, comme illustré dans l’exemple suivant.

// D2D1_ANTIALIAS_MODE_ALIASED must be set for FillOpacityMask
// to function properly.
m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

m_pRenderTarget->FillOpacityMask(
    m_pBitmapMask,
    m_pOriginalBitmapBrush,
    D2D1_OPACITY_MASK_CONTENT_GRAPHICS,
    &rcBrushRect,
    NULL
    );

m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);

Enfin, si vous souhaitez appliquer l’opacité à une seule primitive, vous devez multiplier l’opacité dans la couleur de pinceau, puis afficher la primitive. Vous n’avez pas besoin d’une couche ou d’une bitmap de masque d’opacité.

float opacity = 0.9f;

ID2D1SolidColorBrush *pBrush = NULL;
hr = pCompatibleRenderTarget->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f * opacity)),
    &pBrush
    );

m_pRenderTarget->FillRectangle(
    D2D1::RectF(50.0f, 50.0f, 75.0f, 75.0f), 
    pBrush
    ); 

Découpage d’une forme arbitraire

La figure ci-dessous montre le résultat de l’application d’un clip à une image.

an image that shows an example of an image before and after a clip.

Vous pouvez obtenir ce résultat en utilisant des couches avec un masque géométrique ou la méthode FillGeometry avec un pinceau d’opacité.

Voici un exemple qui utilise une couche :

// Call PushLayer() and pass in the clipping geometry.
m_d2dContext->PushLayer(
    D2D1::LayerParameters(
        boundsRect,
        geometricMask));

Voici un exemple qui utilise la méthode FillGeometry :

// Create an opacity bitmap and render content.
m_d2dContext->CreateBitmap(size, nullptr, 0,
    D2D1::BitmapProperties(
        D2D1_BITMAP_OPTIONS_TARGET,
        D2D1::PixelFormat(
            DXGI_FORMAT_A8_UNORM,
            D2D1_ALPHA_MODE_PREMULTIPLIED),
        dpiX, dpiY),
    &opacityBitmap);

m_d2dContext->SetTarget(opacityBitmap.Get());
m_d2dContext->BeginDraw();
…
m_d2dContext->EndDraw();

// Create an opacity brush from the opacity bitmap.
m_d2dContext->CreateBitmapBrush(opacityBitmap.Get(),
    D2D1::BitmapBrushProperties(),
    D2D1::BrushProperties(),
    &bitmapBrush);

// Call the FillGeometry method and pass in the clip geometry and the opacity brush
m_d2dContext->FillGeometry( 
    clipGeometry.Get(),
    brush.Get(),
    opacityBrush.Get()); 

Dans cet exemple de code, lorsque vous appelez la méthode PushLayer, vous ne passez pas dans une couche créée par une application. Direct2D crée une couche pour vous. Direct2D est en mesure de gérer l’allocation et la destruction de cette ressource sans aucune implication de l’application. Cela permet à Direct2D de réutiliser des couches en interne et d’appliquer des optimisations de gestion des ressources.

Remarque

Dans Windows 8 de nombreuses optimisations ont été apportées à l’utilisation des couches et nous vous recommandons d’essayer d’utiliser des API de couche au lieu de FillGeometry chaque fois que possible.

 

Clips alignés sur l’axe

Si la région à couper est alignée sur l’axe de la surface de dessin, au lieu d’arbitraire. Ce cas est adapté à l’utilisation d’un rectangle clip au lieu d’une couche. Le gain de performances est plus important pour la géométrie alias que la géométrie antialiased. Pour plus d’informations sur les clips alignés sur l’axe, consultez la rubrique PushAxisA alignClip .

Référence Direct2D