Condividi tramite


Panoramica dei livelli

Questa panoramica descrive le nozioni di base sull'uso dei livelli Direct2D. Contiene 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 utilizza un livello "spingendolo" su un target di rendering. Le successive operazioni di disegno del target di rendering vengono indirizzate al livello. Dopo aver completato il livello, si rimuove il livello dal target di rendering, che lo ricompone nel target 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 l'ha creata. Tuttavia, una risorsa di 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 potente tecnica di rendering 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 riempire o cancellare il livello e poi di integrarlo nuovamente, soprattutto sull'hardware di alta gamma. Quindi, c'è il costo della gestione delle risorse del livello. Se si riallocano spesso questi elementi, i conflitti con la 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 ha introdotto nuove API correlate al livello che semplificano, migliorano le prestazioni e aggiungono funzionalità ai livelli.

ID2D1DeviceContext e PushLayer

L'interfacciaID2D1DeviceContextè derivata dall'interfacciaID2D1RenderTargete rappresenta la chiave 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::PushLayer. 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 include 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 come ogni primitiva viene combinata 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à D2D1_PRIMITIVE_BLEND_COPY nel contesto del dispositivo per ottenere risultati appropriati. La modalità di copia rende il contesto del dispositivo interpolare linearmente tutti i quattro canali di colore, incluso il canale alfa, di ogni pixel nei contenuti della superficie di destinazione secondo la maschera geometrica del livello.

Interoperabilità

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. È tua responsabilità 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, PushLayere i metodi 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 di livello.

    Nota

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

     

  • Dopo che il target di rendering ha iniziato a disegnare (dopo che è stato chiamato il suo metodo BeginDraw), è possibile usare il metodo PushLayer. Il metodo PushLayer aggiunge il livello specificato al target di rendering, affinché quest'ultimo riceva tutte le operazioni di disegno successive fino a quando non viene chiamato PopLayer. 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 i limiti della destinazione di rendering.
    maschera geometrica (Facoltativo) L'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à antialiasing per la maschera geometrica specificata dal campo geometricMask.
    maskTransform Valore che specifica la trasformazione applicata alla maschera geometrica durante la composizione del livello. Questo è relativo alla trasformazione globale.
    opacità Il valore di opacità del livello. L'opacità di ogni risorsa nel livello viene moltiplicata con questo valore durante la composizione alla destinazione.
    opacityBrush (Facoltativo) Un pennello utilizzato per modificare l'opacità del livello. Il pennello è associato al livello, e il canale alfa di ciascun pixel del pennello associato viene moltiplicato per il pixel del livello corrispondente. Impostare su NULL se il livello non dovrebbe avere una maschera di opacità.
    layer Opzioni Valore che specifica se il livello intende eseguire il rendering del testo con l'antialiasing ClearType. Questo parametro è disattivato per impostazione predefinita. 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.

L'esempio seguente illustra come usare CreateLayer, PushLayere PopLayer. Tutti i campi nella struttura D2D1_LAYER_PARAMETERS vengono 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.

Nota che quando chiami PushLayer e PopLayer, assicurati che ogni chiamata PushLayer abbia una corrispondente chiamata PopLayer. Se ci sono più chiamate PopLayer rispetto a PushLayer, la destinazione di rendering viene messa in uno stato di errore. Se viene chiamato Flush prima che tutti i livelli in sospeso siano rimossi, la destinazione di rendering viene messa in uno stato di errore e restituisce un errore. Per cancellare lo stato di errore, usare EndDraw.

Limiti di contenuto

Il contentBounds imposta il limite di ciò che deve essere disegnato sul livello. Solo gli elementi all'interno dei limiti del contenuto vengono ridistribuiti alla destinazione di rendering.

L'esempio seguente illustra come specificare contentBounds in modo che l'immagine originale venga ritagliata ai limiti del contenuto con l'angolo superiore sinistro in (10, 108) e l'angolo inferiore destro in (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 di un'immagine originale e dell'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 viene ulteriormente influenzata se si specifica un geometricMask. Per ulteriori 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 utilizzare il campo geometrico della struttura D2D1_LAYER_PARAMETERS per mascherare i risultati in una geometria. Ad esempio, se si desidera visualizzare un'immagine mascherata da una lettera di blocco "A", è prima possibile creare una geometria che rappresenta la lettera di blocco "A" e usare tale geometria come maschera geometrica per un livello. Quindi, dopo aver aggiunto il livello, puoi disegnare l'immagine. Se si estrae il livello, l'immagine viene ritagliata alla forma "A" della lettera di blocco.

L'esempio seguente illustra come creare un ID2D1PathGeometry contenente una forma di montagna e quindi passare la geometria del percorso all'PushLayer. Disegna quindi una bitmap e i quadrati. Se è presente solo una bitmap nel livello di cui eseguire il rendering, usare FillGeometry con un pennello bitmap bloccato per maggiore efficienza. La figura seguente mostra 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 utilizzare 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 seguente 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 un geometricMask, è possibile usare il valore predefinito, InfiniteRect, per contentBounds.

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

Se contentBounds è diverso da NULL e geometricMask è diverso da NULL, la maschera geometrica trasformata viene ritagliata rispetto ai limiti di contenuto e si presuppone che questi siano infiniti.

 

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 come maschera di contenuto. È possibile, ad esempio, definire un pennello sfumato radiale che varia da opaco a trasparente per creare un effetto di vignettatura.

L'esempio seguente usa un ID2D1RadialGradientBrush (m_pRadialGradientBrush) come maschera di opacità. Poi disegna una bitmap e dei quadrati. Se è presente solo una bitmap nel livello da renderizzare, usare FillGeometry con un pennello bitmap clampato per efficienza. La figura seguente mostra l'output di questo esempio.

illustrazione di un'immagine di 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 utilizzato 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, consultare la panoramica delle maschere di opacità .

Alternative ai livelli

Come accennato in precedenza, un numero eccessivo di livelli può influire negativamente sulle prestazioni dell'applicazione. Per migliorare le prestazioni, evitare di usare livelli quando possibile; usare invece le alternative. L'esempio di codice seguente illustra come usare PushAxisAlignedClip e PopAxisAlignedClip per ritagliare un'area, in 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 di cui 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 desidera 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 mostra il risultato dell'applicazione di una clip a un'immagine.

un'immagine che mostra un esempio di un'immagine prima e dopo un ritaglio.

È possibile ottenere questo risultato usando i livelli con una maschera geometrica 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));

Di seguito è riportato 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 chiami il metodo PushLayer, non passi un livello creato dall'app. Direct2D crea automaticamente un livello. Direct2D è in grado di gestire l'allocazione e la distruzione di questa risorsa senza alcun coinvolgimento dell'app. In questo modo Direct2D può 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 ti consigliamo di provare a usare le API di livello invece di FillGeometry quando possibile.

 

Clip allineate all'asse

Se l'area da ritagliare è allineata all'asse della superficie di disegno, anziché arbitraria. Questo caso è adatto per utilizzare un rettangolo di ritaglio anziché un livello. Il miglioramento delle prestazioni è maggiore per la geometria con alias che per la geometria antialiased. Per maggiori informazioni sulle clip allineate sugli assi, vedi l'argomento PushAxisAlignedClip.

Direct2D riferimento