共用方式為


圖層概觀

本概觀描述使用 Direct2D 層的基本概念。 包含以下幾節。

什麼是圖層?

ID2D1Layer物件所代表的圖層,可讓應用程式操作繪圖作業的群組。 您可以使用圖層,將它「推送」到轉譯目標。 轉譯目標的後續繪圖作業會導向至圖層。 完成圖層之後,您會從轉譯目標「快顯」圖層,以將圖層的內容複合回轉譯目標。

如同筆刷,圖層是轉譯目標所建立的裝置相依資源。 圖層可以用於相同資源網域中任何轉譯目標,其中包含建立它的轉譯目標。 不過,圖層資源一次只能由一個轉譯目標使用。 如需資源的詳細資訊,請參閱 資源概觀

雖然圖層提供功能強大的轉譯技術來產生有趣的效果,但應用程式中過多的圖層可能會對效能造成負面影響,因為與管理圖層和圖層資源相關聯的各種成本。 例如,填滿或清除圖層的成本,然後混合回層,特別是在較高階的硬體上。 然後,管理圖層資源的成本是一項。 如果您經常重新配置這些,則針對 GPU 產生的停止將會是最重要的問題。 當您設計應用程式時,請嘗試最大化重複使用圖層資源。

Windows 8和更新版本中的圖層

Windows 8引進了新的層相關 API,可簡化、改善的效能,並將功能新增至圖層。

ID2D1DeviceCoNtext 和 PushLayer

ID2D1DeviceCoNtext介面衍生自ID2D1RenderTarget介面,而且是在 Windows 8 中顯示 Direct2D 內容的關鍵,如需此介面的詳細資訊,請參閱裝置和裝置內容。 透過裝置內容介面,您可以略過呼叫 CreateLayer 方法,然後將 Null 傳遞至 ID2D1DeviceCoNtext::P ushLayer 方法。 Direct2D 會自動管理圖層資源,並可共用圖層與效果圖形之間的資源。

D2D1_LAYER_PARAMETERS1和D2D1_LAYER_OPTIONS1

D2D1_LAYER_PARAMETERS1結構與D2D1_LAYER_PARAMETERS相同,但結構的最終成員現在是D2D1_LAYER_OPTIONS1列舉。

D2D1_LAYER_OPTIONS1 沒有 ClearType 選項,而且有兩個不同的選項可用來改善效能:

混合模式

從Windows 8開始,裝置內容具有基本混合模式,決定每個基本類型如何與目標表面混合。 當您呼叫 PushLayer 方法時,此模式也適用于圖層。

例如,如果您使用圖層來裁剪具有透明度的基本類型,請在裝置內容上設定 D2D1_PRIMITIVE_BLEND_COPY 模式,以取得適當的結果。 複製模式會根據圖層的幾何遮罩,讓裝置內容線性插補所有 4 個色彩色板,包括 Alpha 色板,以及每個圖元的內容。

互通

從Windows 8開始,Direct2D 支援在推送圖層或剪輯時與 Direct3D 和 GDI 互通。 當您將圖層推送至與 GDI 交互操作時,您會呼叫 ID2D1GdiInteropRenderTarget::GetDC 。 您呼叫 ID2D1DeviceCoNtext::Flush ,然後轉譯至基礎介面以與 Direct3D 交互操作。 您必須負責使用 Direct3D 或 GDI 在圖層內轉譯或裁剪。 如果您嘗試在圖層外部轉譯,或裁剪結果未定義。

建立圖層

使用圖層需要熟悉CreateLayer、PushLayerPopLayer方法,以及D2D1_LAYER_PARAMETERS結構,其中包含一組參數資料,可定義圖層的使用方式。 下列清單描述方法和結構。

  • 呼叫 CreateLayer 方法來建立圖層資源。

    注意

    從 Windows 8 開始,您可以略過呼叫CreateLayer方法,然後將 Null 傳遞至ID2D1DeviceCoNtext介面上的PushLayer方法。 這是更簡單的,可讓 Direct2D 自動管理圖層資源,並在圖層和效果圖形之間共用資源。

     

  • 在呼叫 BeginDraw 方法) 之後,轉譯目標開始繪製 (之後,您可以使用 PushLayer 方法。 PushLayer方法會將指定的圖層新增至轉譯目標,以便目標接收所有後續的繪圖作業,直到呼叫PopLayer為止。 此方法會採用由呼叫D2D1_LAYER_PARAMETERS結構中的CreateLayerlayerParameters所傳回的ID2D1Layer物件。 下表描述 結構的欄位。

    欄位 Description
    contentBounds 圖層的內容界限。 內容不會在這些界限之外轉譯。 此參數預設為 InfiniteRect。 使用預設值時,會有效地將內容界限視為轉譯目標的界限。
    幾何遮罩 (選擇性) 由 ID2D1Geometry定義的區域,圖層應該裁剪到其中。 如果圖層不應裁剪為幾何,請將 設定為 Null
    maskAntialiasMode 值,指定幾何遮罩欄位所指定 之幾何 遮罩的反鋸齒模式。
    maskTransform 值,指定撰寫圖層時套用至幾何遮罩的轉換。 這與世界轉換相對。
    透明度 圖層的不透明度值。 當組成目標時,圖層中每個資源的不透明度會乘以這個值。
    opacityBrush (選擇性) 用來修改圖層不透明度的筆刷。 筆刷會對應至圖層,而每個對應筆刷圖元的 Alpha 色板會與對應的圖層圖元相乘。 如果圖層不應該有不透明度遮罩,請將 設定為 Null
    layerOptions 值,指定圖層是否想要使用 ClearType 反鋸齒轉譯文字。 此參數預設為 off。 開啟它可讓 ClearType 正常運作,但它會產生稍微慢的轉譯速度。

     

    注意

    從 Windows 8 開始,您無法在圖層中使用 ClearType 轉譯,因此layerOptions參數應該一律設定為D2D1_LAYER_OPTIONS_NONE

     

    為了方便起見,Direct2D 提供 D2D1::LayerParameters 方法來協助您建立 D2D1_LAYER_PARAMETERS 結構。

  • 若要將圖層的內容複合到轉譯目標中,請呼叫 PopLayer 方法。 您必須先呼叫 PopLayer 方法,才能呼叫 EndDraw 方法。

下列範例示範如何使用CreateLayerPushLayer 和 PopLayer D2D1_LAYER_PARAMETERS結構中的所有欄位都會設定為預設值,但opacityBrush除外,這會設定為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);

此範例已省略程式碼。

請注意,當您呼叫 PushLayerPopLayer時,請確定每個 PushLayer 都有相符的 PopLayer 呼叫。 如果 PopLayer 呼叫比 PushLayer 呼叫還多,轉譯目標就會進入錯誤狀態。 如果在快顯所有未完成的圖層之前呼叫 Flush ,轉譯目標就會進入錯誤狀態並傳回錯誤。 若要清除錯誤狀態,請使用 EndDraw

內容界限

contentBounds會設定要繪製到圖層之專案的限制。 只有內容界限內的那些專案會複合回轉譯目標。

下列範例示範如何指定 contentBounds ,讓原始影像裁剪至左上角 (10、108) 和右下角 (121、177) 的內容界限。 下圖顯示將影像裁剪至內容界限的原始影像和結果。

原始圖片和所產生裁剪圖片上內容界限的圖例

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

此範例已省略程式碼。

注意

如果您指定 幾何遮罩,產生的裁剪影像會進一步受到影響。 如需詳細資訊 ,請參閱幾何遮罩 一節。

 

幾何遮罩

幾何遮罩是由 ID2D1Geometry 物件所定義的裁剪或剪下,會在轉譯目標繪製圖層時遮罩圖層。 您可以使用D2D1_LAYER_PARAMETERS結構的geometrMask欄位,將結果遮罩至幾何。 例如,如果您想要顯示以區塊字母 「A」 遮罩的影像,您可以先建立代表區塊字母 「A」 的幾何,然後使用該幾何做為圖層的幾何遮罩。 然後,在推送圖層之後,您可以繪製影像。 快顯圖層會導致影像裁剪成區塊字母 「A」 圖形。

下列範例示範如何建立 ID2D1PathGeometry ,其中包含 Mountain 的形狀,然後將路徑幾何傳遞至 PushLayer。 然後,它會繪製點陣圖和正方形。 如果圖層中只有要轉譯的點陣圖,請使用 FillGeometry 搭配固定點陣圖筆刷以提高效率。 下圖顯示範例的輸出。

套用山地幾何遮罩之後,分葉圖片和結果圖片的圖例

第一個範例會定義要當做遮罩使用的幾何。

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

下一個範例會使用幾何作為圖層的遮罩。

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

此範例已省略程式碼。

注意

一般而言,如果您指定幾何遮罩,您可以使用contentBounds的預設值InfiniteRect

如果 contentBounds 為 Null,而 geometrMask 為非 Null,則套用遮罩轉換之後,內容界限實際上是幾何遮罩的界限。

如果 contentBounds 為非 Null,而 geometrMask 為非 Null,則轉換的幾何遮罩會有效地針對內容界限進行裁剪,且內容界限會假設為無限。

 

不透明度遮罩

不透明度遮罩是由筆刷或點陣圖所描述的遮罩,會套用至另一個物件,讓該物件部分或完全透明。 它允許使用筆刷的 Alpha 色板做為內容遮罩。 例如,您可以定義星形漸層筆刷,從不透明到透明,以建立 vignette 效果。

下列範例會使用 ID2D1RadialGradientBrush (m_pRadialGradientBrush) 作為不透明度遮罩。 然後,它會繪製點陣圖和正方形。 如果圖層中只有要轉譯的點陣圖,請使用 FillGeometry 搭配固定點陣圖筆刷以提高效率。 下圖顯示此範例的輸出。

套用不透明度遮罩之後的樹狀圖和結果圖片的圖例

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

此範例已省略程式碼。

注意

此範例會使用圖層將不透明度遮罩套用至單一物件,讓範例盡可能簡單。 將不透明度遮罩套用至單一物件時,使用 FillOpacityMaskFillGeometry 方法會更有效率,而不是圖層。

 

如需如何在不使用圖層的情況下套用不透明度遮罩的指示,請參閱 不透明度遮罩概觀

圖層的替代專案

如先前所述,過多層可能會對應用程式的效能造成負面影響。 若要改善效能,請盡可能避免使用圖層;請改用其替代方案。 下列程式碼範例示範如何使用 PushAxisAlignedClipPopAxisAlignedClip 來裁剪區域,以替代使用具有內容界限的圖層。

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

同樣地,當圖層中只有一個要呈現的內容時,請使用 FillGeometry 搭配固定點陣圖筆刷作為使用圖層不透明度遮罩的替代方式,如下列範例所示。

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

除了搭配幾何遮罩使用圖層,請考慮使用點陣圖遮罩來裁剪區域,如下列範例所示。

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

最後,如果您想要將不透明度套用至單一基本類型,您應該將不透明度乘以筆刷色彩,然後轉譯基本類型。 您不需要圖層或不透明度遮罩點陣圖。

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

裁剪任意圖形

下圖顯示將剪輯套用至影像的結果。

顯示剪輯前後影像範例的影像。

您可以使用具有幾何遮罩的圖層或具有不透明度筆刷的 FillGeometry 方法來取得此結果。

以下是使用圖層的範例:

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

以下是使用 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()); 

在此程式碼範例中,當您呼叫 PushLayer 方法時,不會傳入應用程式建立的層。 Direct2D 會為您建立圖層。 Direct2D 能夠管理此資源的配置和解構,而不需要來自應用程式的任何介入。 這可讓 Direct2D 在內部重複使用圖層,並套用資源管理優化。

注意

在Windows 8許多優化已使用圖層,建議您盡可能嘗試使用圖層 API,而不是FillGeometry

 

軸對齊剪輯

如果要裁剪的區域會對齊繪圖介面的軸,而不是任意的。 此案例適用于使用裁剪矩形,而不是圖層。 效能提升比反鋸齒幾何更適合別名幾何。 如需軸對齊剪輯的詳細資訊,請參閱 PushAxisAlignedClip 主題。

Direct2D 參考