Vue d’ensemble des couches

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

Que sont 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 la « poussant » vers une cible de rendu. Les opérations de dessin suivantes par la cible de rendu sont dirigées vers la couche. Une fois que vous avez terminé avec la couche, vous « pop » le calque à partir de la cible de rendu, ce qui combine le contenu de la couche à 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 domaine de ressources qui contient la cible de rendu qui les a créées. Toutefois, une ressource de couche ne peut être utilisée que par une seule cible de rendu à la fois. Pour plus d’informations sur les ressources, consultez 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 du remplissage ou de l’effacement de la couche, puis de sa fusion, en particulier sur le matériel haut de gamme. Ensuite, il y a le coût de gestion des ressources de couche. Si vous les réalloue fréquemment, les blocages résultants sur le GPU seront 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 aux couches qui simplifient, améliorent les performances des couches et ajoutent des fonctionnalités aux couches.

ID2D1DeviceContext et PushLayer

L’interface ID2D1DeviceContext est dérivée de l’interface ID2D1RenderTarget et est essentielle à l’affichage du contenu Direct2D dans Windows 8. Pour plus d’informations sur cette interface, consultez Appareils et contextes d’appareil. Avec l’interface de contexte de l’appareil, vous pouvez ignorer l’appel de la méthode CreateLayer , puis passer la valeur NULL à la méthode ID2D1DeviceContext::P ushLayer . Direct2D gère automatiquement la ressource de couche et peut partager des ressources entre les couches et les 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 désormais une énumération D2D1_LAYER_OPTIONS1 .

D2D1_LAYER_OPTIONS1 n’a pas d’option ClearType et dispose de 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 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 rend le contexte de l’appareil linéaire interpoler les 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 du calque.

Interopérabilité

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

Création de couches

L’utilisation des couches nécessite une connaissance des méthodes CreateLayer, PushLayer et PopLayer , ainsi que de 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

    À partir 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 les couches et les graphiques d’effet.

     

  • Une fois que la cible de rendu a commencé à dessiner (une fois que sa méthode BeginDraw a été appelée), 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 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 clippée. Définissez la valeur NULL si la couche ne doit pas être rogné sur une géométrie.
    maskAntialiasMode Valeur qui spécifie le mode anti-attirail pour le masque géométrique spécifié par le champ geometricMask .
    maskTransform Valeur qui spécifie la transformation appliquée au masque géométrique lors de la composition du calque. C’est relatif à la transformation du monde.
    Opacité Valeur d’opacité du calque. L’opacité de chaque ressource de la couche est multipliée par cette valeur lors de la composition vers la cible.
    opacityBrush (Facultatif) Pinceau utilisé pour modifier l’opacité du calque. Le pinceau est mappé au calque 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’anti-ataliasing ClearType. Ce paramètre est désactivé par défaut. Son activation permet à ClearType de fonctionner correctement, mais elle entraîne une vitesse de rendu légèrement plus lente.

     

    Notes

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

     

    Par souci de commodité, 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 opacityBrush, 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 dans cet exemple.

Notez que lorsque vous appelez PushLayer et PopLayer, vérifiez que chaque PushLayer a un appel PopLayer correspondant. S’il y a plus d’appels PopLayer que d’appels PushLayer , la cible de rendu est placée dans un état d’erreur. Si Flush est appelé avant que toutes les couches en attente ne soient dépilé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. Seuls les éléments contenus dans les limites de contenu sont regroupés dans la cible de rendu.

L’exemple qui suit montre comment spécifier contentBounds afin que l’image d’origine soit coupée aux limites de contenu avec l’angle supérieur gauche à (10, 108) et l’angle inférieur droit à (121, 177). L’illustration suivante montre l’image d’origine et le résultat de la capture de l’image dans les limites de contenu.

illustration des limites de contenu sur une image d’origine et l’image découpée résultante

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 dans cet exemple.

Notes

L’image découpée résultante 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 un calque lorsqu’il est dessiné par une cible de rendu. Vous pouvez utiliser le champ geometryMask de la structure D2D1_LAYER_PARAMETERS pour masquer les résultats en une géométrie. Par exemple, si vous souhaitez afficher une image masquée par une lettre bloc « A », vous pouvez d’abord créer une géométrie représentant la lettre bloc « A » et utiliser cette géométrie comme masque géométrique pour une couche. Ensuite, après avoir poussé la couche, vous pouvez dessiner l’image. Le fait de faire éclater la couche entraîne le découpage de l’image en forme de lettre bloc « A ».

L’exemple qui suit montre comment créer un 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 plus d’efficacité. L’illustration suivante montre la sortie de l’exemple.

illustration d’une image d’une feuille et l’image résultante après l’application d’un masque géométrique d’une montagne

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 dans cet exemple.

Notes

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 a la valeur NULL et que geometricMask n’est pas NULL, les limites de contenu sont en fait les limites du masque géométrique après l’application de la transformation du masque.

Si contentBounds n’a pas la valeur NULL et que geometricMask n’est pas NULL, le masque géométrique transformé est effectivement découpé 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, qui est appliqué à un autre objet pour rendre cet objet partiellement ou complètement transparent. Il permet d’utiliser le canal alpha d’un pinceau comme masque de contenu. Par exemple, vous pouvez définir un pinceau dégradé radial qui varie d’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 plus d’efficacité. L’illustration suivante montre la sortie de cet exemple.

illustration d’une image d’arborescences et de l’image résultante après l’application d’un masque d’opacité

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 dans cet exemple.

Notes

Cet exemple utilise une couche pour appliquer un masque d’opacité à un seul objet afin que l’exemple reste aussi simple que possible. Lors de l’application d’un masque d’opacité à un seul objet, 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 calque, consultez vue d’ensemble des masques d’opacité.

Alternatives aux couches

Comme mentionné précédemment, un nombre excessif de couches peut nuire aux performances de votre application. Pour améliorer les performances, évitez d’utiliser des couches dans la mesure du possible . à la place, utilisez leurs alternatives. L’exemple de code suivant montre comment utiliser PushAxisAlignedClip et PopAxisAlignedClip pour découper une région, au lieu d’utiliser 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
            );

Comme alternative à l’utilisation d’une couche avec un masque géométrique, envisagez d’utiliser un masque bitmap pour dé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 restituer la primitive. Vous n’avez pas besoin d’une image bitmap de calque ou 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 montre ici le résultat de l’application d’un clip à une image.

Image qui montre un exemple d’image avant et après un clip.

Vous pouvez obtenir ce résultat à l’aide de couches avec un masque de géométrie ou de 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 une couche créée par l’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 intervention de l’application. Cela permet à Direct2D de réutiliser les couches en interne et d’appliquer des optimisations de gestion des ressources.

Notes

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 dans la mesure du possible.

 

Clips alignés sur l’axe

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

Référence Direct2D