Panoramica dei livelli

Questa panoramica descrive le nozioni di base dell'uso dei livelli Direct2D. Include le sezioni seguenti:

Che cosa sono i livelli?

I livelli, rappresentati da oggetti ID2D1Layer , consentono a un'applicazione di modificare un gruppo di operazioni di disegno. Si usa un livello "push" in una destinazione di rendering. Le operazioni di disegno successive dalla destinazione di rendering vengono indirizzate al livello. Dopo aver completato il livello, si "pop" il livello dalla destinazione di rendering, che composita il contenuto del livello alla destinazione di rendering.

Come i pennelli, i livelli sono risorse dipendenti dal dispositivo create dalle destinazioni di rendering. I livelli possono essere usati su qualsiasi destinazione di rendering nello stesso dominio di risorse che contiene la destinazione di rendering che lo ha creato. Tuttavia, una risorsa livello può essere usata solo da una destinazione di rendering alla volta. Per altre informazioni sulle risorse, vedere Panoramica delle risorse.

Anche se i livelli offrono una tecnica di rendering potente per produrre effetti interessanti, un numero eccessivo di livelli in un'applicazione può influire negativamente sulle prestazioni, a causa dei vari costi associati alla gestione dei livelli e delle risorse del livello. Ad esempio, c'è il costo di riempimento o cancellazione del livello e quindi di fonderlo nuovamente, soprattutto su hardware di fascia superiore. È quindi previsto il costo della gestione delle risorse del livello. Se si riallocano spesso questi elementi, i blocchi risultanti rispetto alla GPU saranno il problema più significativo. Quando si progetta l'applicazione, provare a ottimizzare il riutilizzo delle risorse del livello.

Livelli in Windows 8 e versioni successive

Windows 8 sono state introdotte nuove API correlate al livello che semplificano, migliorano le prestazioni e aggiungono funzionalità ai livelli.

ID2D1DeviceContext e PushLayer

L'interfaccia ID2D1DeviceContext deriva dall'interfaccia ID2D1RenderTarget ed è fondamentale per visualizzare il contenuto Direct2D in Windows 8, per altre informazioni su questa interfaccia, vedere Dispositivi e contesti di dispositivo. Con l'interfaccia del contesto di dispositivo, è possibile ignorare la chiamata al metodo CreateLayer e quindi passare NULL al metodo ID2D1DeviceContext::P ushLayer . Direct2D gestisce automaticamente la risorsa livello e può condividere le risorse tra i livelli e i grafici degli effetti.

D2D1_LAYER_PARAMETERS1 e D2D1_LAYER_OPTIONS1

La struttura D2D1_LAYER_PARAMETERS1 è uguale a D2D1_LAYER_PARAMETERS, ad eccezione del membro finale della struttura è ora un'enumerazione D2D1_LAYER_OPTIONS1 .

D2D1_LAYER_OPTIONS1 non dispone di un'opzione ClearType e dispone di due opzioni diverse che è possibile usare per migliorare le prestazioni:

Modalità di fusione

A partire da Windows 8, il contesto del dispositivo ha una modalità di fusione primitiva che determina la modalità di fusione di ogni primitiva con la superficie di destinazione. Questa modalità si applica anche ai livelli quando si chiama il metodo PushLayer .

Ad esempio, se si usa un livello per ritagliare primitive con trasparenza, impostare la modalità di D2D1_PRIMITIVE_BLEND_COPY nel contesto del dispositivo per ottenere risultati appropriati. La modalità di copia rende il contesto di dispositivo lineare interpolare tutti e 4 i canali di colore, incluso il canale alfa, di ogni pixel con il contenuto della superficie di destinazione in base alla maschera geometrica del livello.

Interazione

A partire da Windows 8, Direct2D supporta l'interoperabilità con Direct3D e GDI mentre viene eseguito il push di un livello o di un clip. Si chiama ID2D1GdiInteropRenderTarget::GetDC mentre viene eseguito il push di un livello per interagire con GDI. Chiami ID2D1DeviceContext::Flush e quindi esegui il rendering sulla superficie sottostante per interagire con Direct3D. È responsabilità dell'utente eseguire il rendering all'interno del livello o clip con Direct3D o GDI. Se si tenta di eseguire il rendering all'esterno del livello o ritagliare i risultati non sono definiti.

Creazione di livelli

L'uso dei livelli richiede familiarità con i metodi CreateLayer, PushLayer e PopLayer e la struttura D2D1_LAYER_PARAMETERS , che contiene un set di dati parametrici che definisce come usare il livello. Nell'elenco seguente vengono descritti i metodi e la struttura.

  • Chiamare il metodo CreateLayer per creare una risorsa livello.

    Nota

    A partire da Windows 8, è possibile ignorare la chiamata al metodo CreateLayer e quindi passare NULL al metodo PushLayer nell'interfaccia ID2D1DeviceContext. Questo è più semplice e consente a Direct2D di gestire automaticamente la risorsa del livello e condividere le risorse tra livelli e grafici degli effetti.

     

  • Dopo che la destinazione di rendering ha iniziato a disegnare (dopo la chiamata al metodo BeginDraw ), è possibile usare il metodo PushLayer . Il metodo PushLayer aggiunge il livello specificato alla destinazione di rendering, in modo che la destinazione riceva tutte le operazioni di disegno successive finché PopLayer non viene chiamato. Questo metodo accetta un oggetto ID2D1Layer restituito chiamando CreateLayer e un layerParameters nella struttura D2D1_LAYER_PARAMETERS . Nella tabella seguente vengono descritti i campi della struttura.

    Campo Descrizione
    contentBounds Limiti del contenuto del livello. Il rendering del contenuto non verrà eseguito all'esterno di questi limiti. Per impostazione predefinita, questo parametro è InfiniteRect. Quando si usa il valore predefinito, i limiti del contenuto vengono effettivamente considerati come limiti della destinazione di rendering.
    geometricMask (Facoltativo) Area, definita da un ID2D1Geometry, a cui deve essere ritagliato il livello. Impostare su NULL se il livello non deve essere ritagliato su una geometria.
    maskAntialiasMode Valore che specifica la modalità di anti-aliasing per la maschera geometrica specificata dal campo geometricoMask .
    maskTransform Valore che specifica la trasformazione applicata alla maschera geometrica durante la composizione del livello. Questo è relativo alla trasformazione del mondo.
    Opacità Valore di opacità del livello. L'opacità di ogni risorsa nel livello viene moltiplicata con questo valore durante la composizione alla destinazione.
    opacityBrush (Facoltativo) Pennello utilizzato per modificare l'opacità del livello. Il pennello viene mappato al livello e il canale alfa di ogni pixel del pennello mappato viene moltiplicato rispetto al pixel del livello corrispondente. Impostare su NULL se il livello non deve avere una maschera di opacità.
    layerOptions Valore che specifica se il livello intende eseguire il rendering del testo con l'antialiasing ClearType. Per impostazione predefinita, questo parametro è disattivato. L'attivazione consente a ClearType di funzionare correttamente, ma comporta una velocità di rendering leggermente più lenta.

     

    Nota

    A partire da Windows 8, non è possibile eseguire il rendering con ClearType in un livello, quindi il parametro layerOptions deve essere sempre impostato su D2D1_LAYER_OPTIONS_NONE

     

    Per praticità, Direct2D fornisce il metodo D2D1::LayerParameters per creare strutture D2D1_LAYER_PARAMETERS .

  • Per comporre il contenuto del livello nella destinazione di rendering, chiamare il metodo PopLayer . È necessario chiamare il metodo PopLayer prima di chiamare il metodo EndDraw .

Nell'esempio seguente viene illustrato come usare CreateLayer, PushLayer e PopLayer. Tutti i campi della struttura D2D1_LAYER_PARAMETERS sono impostati sui valori predefiniti, ad eccezione di opacityBrush, che è impostato su 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);

Il codice è stato omesso da questo esempio.

Si noti che quando si chiama PushLayer e PopLayer, assicurarsi che ogni PushLayer abbia una chiamata PopLayer corrispondente. Se sono presenti più chiamate PopLayer rispetto alle chiamate PushLayer , la destinazione di rendering viene inserita in uno stato di errore. Se Flush viene chiamato prima che vengano inseriti tutti i livelli in sospeso, la destinazione di rendering viene inserita in uno stato di errore e restituisce un errore. Per cancellare lo stato di errore, usare EndDraw.

Limiti di contenuto

Il contenutoBounds imposta il limite di ciò che deve essere disegnato al livello. Solo tali elementi all'interno dei limiti del contenuto vengono compositi alla destinazione di rendering.

Nell'esempio seguente viene illustrato come specificare contentBounds in modo che l'immagine originale venga ritagliata ai limiti del contenuto con l'angolo superiore sinistro in corrispondenza (10, 108) e l'angolo inferiore destro in corrispondenza (121, 177). La figura seguente mostra l'immagine originale e il risultato del ritaglio dell'immagine nei limiti del contenuto.

illustrazione dei limiti di contenuto su un'immagine originale e l'immagine ritagliata risultante

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

Il codice è stato omesso da questo esempio.

Nota

L'immagine ritagliata risultante è ulteriormente interessata se si specifica una maschera geometrica. Per altre informazioni, vedere la sezione Maschere geometriche .

 

Maschere geometriche

Una maschera geometrica è una clip o un ritaglio, definito da un oggetto ID2D1Geometry , che maschera un livello quando viene disegnato da una destinazione di rendering. È possibile usare il campo geometricoMask della struttura D2D1_LAYER_PARAMETERS per mascherare i risultati in una geometria. Ad esempio, se si vuole visualizzare un'immagine mascherata da una lettera a blocchi "A", è prima possibile creare una geometria che rappresenta la lettera a blocchi "A" e usare tale geometria come maschera geometrica per un livello. Quindi, dopo aver eseguito il push del livello, è possibile disegnare l'immagine. La comparsa del livello comporta l'inserimento dell'immagine nella forma "A" della lettera a blocchi.

Nell'esempio seguente viene illustrato come creare un ID2D1PathGeometry contenente una forma di una montagna e quindi passare la geometria del percorso a PushLayer. Disegna quindi una bitmap e quadrati. Se nel livello è presente solo una bitmap per il rendering, usare FillGeometry con un pennello bitmap bloccato per l'efficienza. L'immagine seguente illustra l'output dell'esempio.

illustrazione di un'immagine di una foglia e dell'immagine risultante dopo l'applicazione di una maschera geometrica di una montagna

Il primo esempio definisce la geometria da usare come maschera.

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

Nell'esempio successivo viene usata la geometria come maschera per il livello.

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

Il codice è stato omesso da questo esempio.

Nota

In generale, se si specifica una maschera geometrica, è possibile usare il valore predefinito InfiniteRect per i contenutiBounds.

Se contentBounds è NULL e geometricMask non è NULL, i limiti del contenuto sono effettivamente i limiti della maschera geometrica dopo l'applicazione della trasformazione maschera maschera.

Se contentBounds non è NULL e geometricMask non è NULL, la maschera geometrica trasformata viene effettivamente ritagliata rispetto ai limiti di contenuto e i limiti di contenuto vengono considerati infinito.

 

Maschere di Opacità

Una maschera di opacità è una maschera, descritta da un pennello o una bitmap, applicata a un altro oggetto per rendere l'oggetto parzialmente o completamente trasparente. Consente l'uso del canale alfa di un pennello da usare come maschera di contenuto. Ad esempio, è possibile definire un pennello sfumatura radiale che varia da opaco a trasparente per creare un effetto vignetto.

L'esempio seguente usa una maschera di opacità ID2D1RadialGradientBrush (m_pRadialGradientBrush). Disegna quindi una bitmap e quadrati. Se nel livello è presente solo una bitmap per il rendering, usare FillGeometry con un pennello bitmap bloccato per l'efficienza. Nella figura seguente viene illustrato l'output di questo esempio.

illustrazione di un'immagine degli alberi e dell'immagine risultante dopo l'applicazione di una maschera di 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;
    
}

Il codice è stato omesso da questo esempio.

Nota

In questo esempio viene usato un livello per applicare una maschera di opacità a un singolo oggetto per mantenere l'esempio il più semplice possibile. Quando si applica una maschera di opacità a un singolo oggetto, è più efficiente usare i metodi FillOpacityMask o FillGeometry anziché un livello.

 

Per istruzioni su come applicare una maschera di opacità senza usare un livello, vedere Panoramica delle maschere Opacity.

Alternative ai livelli

Come accennato in precedenza, il numero eccessivo di livelli può influire negativamente sulle prestazioni dell'applicazione. Per migliorare le prestazioni, evitare di usare livelli ogni volta che è possibile; invece usano le loro alternative. Nell'esempio di codice seguente viene illustrato come usare PushAxisAlignedClip e PopAxisAlignedClip per ritagliare un'area, come alternativa all'uso di un livello con limiti di contenuto.

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

Analogamente, usare FillGeometry con un pennello bitmap bloccato come alternativa all'uso di un livello con una maschera di opacità quando è presente un solo contenuto nel livello per eseguire il rendering, come illustrato nell'esempio seguente.

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

In alternativa all'uso di un livello con una maschera geometrica, è consigliabile usare una maschera bitmap per ritagliare un'area, come illustrato nell'esempio seguente.

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

Infine, se si vuole applicare l'opacità a una singola primitiva, è necessario moltiplicare l'opacità nel colore del pennello e quindi eseguire il rendering della primitiva. Non è necessario un livello o una bitmap di maschera di 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
    ); 

Ritaglio di una forma arbitraria

La figura qui mostra il risultato dell'applicazione di un clip a un'immagine.

immagine che mostra un esempio di immagine prima e dopo un clip.

È possibile ottenere questo risultato usando livelli con una maschera geometry o il metodo FillGeometry con un pennello di opacità.

Ecco un esempio che usa un livello:

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

Ecco un esempio che usa il metodo 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()); 

In questo esempio di codice, quando si chiama il metodo PushLayer, non si passa un livello creato dall'app. Direct2D crea un livello per l'utente. Direct2D è in grado di gestire l'allocazione e la distruzione di questa risorsa senza alcun coinvolgimento dell'app. Ciò consente a Direct2D di riutilizzare i livelli internamente e applicare ottimizzazioni di gestione delle risorse.

Nota

In Windows 8 sono state apportate molte ottimizzazioni all'utilizzo dei livelli e si consiglia di provare a usare le API di livello anziché FillGeometry ogni volta che possibile.

 

Clip allineate all'asse

Se l'area da ritagliare è allineata all'asse della superficie di disegno anziché arbitraria. Questo caso è adatto per l'uso di un rettangolo di clip anziché di un livello. Il miglioramento delle prestazioni è maggiore per la geometria aliasata rispetto alla geometria antialiased. Per altre informazioni sulle clip allineate sull'asse, vedere l'argomento PushAxisAlignedClip .

Riferimento a Direct2D