圖層概觀
本概觀描述使用 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 選項,而且有兩個不同的選項可用來改善效能:
D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND:Direct2D 會將基本類型轉譯至圖層,而不需以透明黑色清除。 這不是預設值,但在大多數情況下,會產生更好的效能。
D2D1_LAYER_OPTIONS1_IGNORE_ALPHA:如果基礎資料表面設定為 D2D1_ALPHA_MODE_IGNORE,此選項可讓 Direct2D 避免修改圖層的 Alpha 色板。 在其他情況下請勿使用此專案。
混合模式
從Windows 8開始,裝置內容具有基本混合模式,決定每個基本類型如何與目標表面混合。 當您呼叫 PushLayer 方法時,此模式也適用于圖層。
例如,如果您使用圖層來裁剪具有透明度的基本類型,請在裝置內容上設定 D2D1_PRIMITIVE_BLEND_COPY 模式,以取得適當的結果。 複製模式會根據圖層的幾何遮罩,讓裝置內容線性插補所有 4 個色彩色板,包括 Alpha 色板,以及每個圖元的內容。
互通
從Windows 8開始,Direct2D 支援在推送圖層或剪輯時與 Direct3D 和 GDI 互通。 當您將圖層推送至與 GDI 交互操作時,您會呼叫 ID2D1GdiInteropRenderTarget::GetDC 。 您呼叫 ID2D1DeviceCoNtext::Flush ,然後轉譯至基礎介面以與 Direct3D 交互操作。 您必須負責使用 Direct3D 或 GDI 在圖層內轉譯或裁剪。 如果您嘗試在圖層外部轉譯,或裁剪結果未定義。
建立圖層
使用圖層需要熟悉CreateLayer、PushLayer和PopLayer方法,以及D2D1_LAYER_PARAMETERS結構,其中包含一組參數資料,可定義圖層的使用方式。 下列清單描述方法和結構。
呼叫 CreateLayer 方法來建立圖層資源。
注意
從 Windows 8 開始,您可以略過呼叫CreateLayer方法,然後將 Null 傳遞至ID2D1DeviceCoNtext介面上的PushLayer方法。 這是更簡單的,可讓 Direct2D 自動管理圖層資源,並在圖層和效果圖形之間共用資源。
在呼叫 BeginDraw 方法) 之後,轉譯目標開始繪製 (之後,您可以使用 PushLayer 方法。 PushLayer方法會將指定的圖層新增至轉譯目標,以便目標接收所有後續的繪圖作業,直到呼叫PopLayer為止。 此方法會採用由呼叫D2D1_LAYER_PARAMETERS結構中的CreateLayer和layerParameters所傳回的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 方法。
下列範例示範如何使用CreateLayer、PushLayer 和 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);
此範例已省略程式碼。
請注意,當您呼叫 PushLayer 和 PopLayer時,請確定每個 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;
}
此範例已省略程式碼。
注意
此範例會使用圖層將不透明度遮罩套用至單一物件,讓範例盡可能簡單。 將不透明度遮罩套用至單一物件時,使用 FillOpacityMask 或 FillGeometry 方法會更有效率,而不是圖層。
如需如何在不使用圖層的情況下套用不透明度遮罩的指示,請參閱 不透明度遮罩概觀。
圖層的替代專案
如先前所述,過多層可能會對應用程式的效能造成負面影響。 若要改善效能,請盡可能避免使用圖層;請改用其替代方案。 下列程式碼範例示範如何使用 PushAxisAlignedClip 和 PopAxisAlignedClip 來裁剪區域,以替代使用具有內容界限的圖層。
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 主題。
相關主題