Omówienie warstw
W tym omówieniu opisano podstawy korzystania z warstw Direct2D. Zawiera on następujące sekcje.
- czym są warstwy?
- warstwy w systemie Windows 8 lub nowszym
- tworzenie warstw
- Granice zawartości
- maski geometryczne
- Maski Nieprzezroczystości
- alternatywy dla warstw
- przycinanie dowolnego kształtu
- Tematy pokrewne
Co to są warstwy?
Warstwy, reprezentowane przez obiekty ID2D1Layer, umożliwiają aplikacji manipulowanie grupą operacji rysowania. Stosujesz warstwę, "wypychając" ją na obiekt docelowy renderowania. Kolejne operacje rysowania wykonywane przez docelowy obiekt renderujący są przesyłane do warstwy. Po zakończeniu pracy z warstwą, „wyskakujesz” tę warstwę z docelowego obiektu renderowania, co powoduje, że zawartość warstwy jest ponownie komponowana z obiektem docelowym renderowania.
Podobnie jak pędzle, warstwy są zasobami zależnymi od urządzenia utworzonymi przez obiekty docelowe renderowania. Warstwy mogą być używane na dowolnym obiekcie docelowym renderowania w tej samej domenie zasobów, która zawiera obiekt docelowy renderowania, który ją utworzył. Jednak zasób warstwy może być używany tylko przez jeden element docelowy renderowania jednocześnie. Aby uzyskać więcej informacji na temat zasobów, zobacz Resources Overview.
Mimo że warstwy oferują zaawansowaną technikę renderowania na potrzeby tworzenia interesujących efektów, nadmierna liczba warstw w aplikacji może niekorzystnie wpłynąć na jej wydajność ze względu na różne koszty związane z zarządzaniem warstwami i zasobami warstw. Na przykład koszt wypełnienia lub wyczyszczenia warstwy, a następnie ponownego połączenia jej, szczególnie w przypadku wysokiej klasy sprzętu. Następnie jest koszt zarządzania zasobami warstwy. Jeśli te zostaną często przydzielone na nowo, wynikowe zawieszenia przeciwko GPU będą najpoważniejszym problemem. Podczas projektowania aplikacji spróbuj zmaksymalizować ponowne wykorzystanie zasobów warstwy.
Warstwy w systemie Windows 8 lub nowszym
System Windows 8 wprowadził nowe interfejsy API powiązane z warstwami, które upraszczają, zwiększają wydajność i dodają funkcje do warstw.
ID2D1DeviceContext i PushLayer
Interfejs ID2D1DeviceContext pochodzi z interfejsu ID2D1RenderTarget i jest kluczowy dla wyświetlania zawartości Direct2D w systemie Windows 8. Aby uzyskać więcej informacji na temat tego interfejsu, zobacz Urządzenia i konteksty urządzeń. Za pomocą interfejsu kontekstu urządzenia można pominąć wywoływanie metody CreateLayer, a następnie przekazać wartość NULL do metody ID2D1DeviceContext::PushLayer. Funkcja Direct2D automatycznie zarządza zasobem warstwy i może udostępniać zasoby między warstwami i wykresami efektowymi.
D2D1_LAYER_PARAMETERS1 i D2D1_LAYER_OPTIONS1
Struktura D2D1_LAYER_PARAMETERS1 jest taka sama jak D2D1_LAYER_PARAMETERS, z wyjątkiem, że końcowy element członkowski struktury jest teraz wyliczeniem D2D1_LAYER_OPTIONS1. Wszystkie inne elementy są identyczne.
D2D1_LAYER_OPTIONS1 nie ma opcji ClearType i ma dwie różne opcje, których można użyć do poprawy wydajności:
D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND: Funkcja Direct2D renderuje prymitywy do warstwy bez wyczyszczenia jej przezroczystą czernią. Nie jest to ustawienie domyślne, ale w większości przypadków skutkuje lepszą wydajnością.
D2D1_LAYER_OPTIONS1_IGNORE_ALPHA: jeśli podstawowa powierzchnia jest ustawiona na D2D1_ALPHA_MODE_IGNORE, ta opcja pozwala usłudze Direct2D uniknąć modyfikowania kanału alfa warstwy. Nie należy tego używać w innych przypadkach.
Tryby mieszania
Począwszy od systemu Windows 8, kontekst urządzenia ma pierwotny tryb mieszania, który określa, jak każdy pierwotny jest mieszany z powierzchnią docelową. Ten tryb dotyczy również warstw podczas wywoływania metody PushLayer.
Jeśli na przykład używasz warstwy do tworzenia wycinków pierwotnych z przezroczystością, ustaw tryb D2D1_PRIMITIVE_BLEND_COPY w kontekście urządzenia w celu uzyskania odpowiednich wyników. Tryb kopiowania powoduje, że kontekst urządzenia liniowo interpoluje wszystkie cztery kanały kolorów, w tym kanał alfa, dla każdego piksela z zawartością powierzchni docelowej, zgodnie z geometryczną maską warstwy.
Współdziałanie
Począwszy od systemu Windows 8, direct2D obsługuje współdziałanie z direct3D i GDI, podczas gdy warstwa lub klip jest wypychany. Wywołanie ID2D1GdiInteropRenderTarget::GetDC podczas wypychania warstwy do współdziałania z interfejsem GDI. Wywołasz metodę ID2D1DeviceContext::Flush, a następnie renderujesz na podstawowej powierzchni w celu współdziałania z Direct3D. Użytkownik ponosi odpowiedzialność za renderowanie wewnątrz warstwy lub klipu za pomocą funkcji Direct3D lub GDI. Jeśli spróbujesz renderować poza warstwą lub wycinek, wyniki są niezdefiniowane.
Tworzenie warstw
Praca z warstwami wymaga znajomości metod CreateLayer, PushLayeri metod PopLayer oraz struktury D2D1_LAYER_PARAMETERS, która zawiera zestaw danych parametrycznych definiujących sposób użycia warstwy. Poniższa lista zawiera opis metod i struktury.
Wywołaj metodę CreateLayer, aby utworzyć zasób warstwy.
Notatka
Począwszy od systemu Windows 8, można pominąć wywoływanie metody CreateLayer, a następnie przekazać wartość NULL do metody PushLayer w interfejsie ID2D1DeviceContext. Jest to prostsze i umożliwia usłudze Direct2D automatyczne zarządzanie zasobem warstwy i udostępnianie zasobów między warstwami i grafami efektów.
Po rozpoczęciu rysowania celu renderowania (po wywołaniu metody BeginDraw), można użyć metody PushLayer. Metoda PushLayer dodaje określoną warstwę do obiektu docelowego renderowania, co pozwala temu obiektowi odbierać wszystkie kolejne operacje rysowania aż do momentu wywołania PopLayer. Ta metoda przyjmuje obiekt ID2D1Layer zwrócony przez wywołanie CreateLayer oraz parametry warstwy w strukturze D2D1_LAYER_PARAMETERS. W poniższej tabeli opisano pola struktury.
Pole Opis contentBounds Granice zawartości warstwy. Zawartość nie będzie renderowana poza tymi granicami. Ten parametr jest domyślnie InfiniteRect. Gdy jest używana wartość domyślna, granice zawartości są skutecznie podejmowane jako granice obiektu docelowego renderowania. geometryczna_maska (Opcjonalnie) Obszar, zdefiniowany przez ID2D1Geometry, do którego warstwa ma być przycięta. Ustaw wartość NULL, jeśli warstwa nie powinna być przycięta do geometrii. maskAntialiasMode Wartość określająca tryb antyaliasingu dla maski geometrycznej określonej przez pole maski geometrycznej. maskaTransform Wartość określająca przekształcenie stosowane do maski geometrycznej podczas komponowania warstwy. Jest to związane z transformacją świata. przezroczystość Wartość nieprzezroczystości warstwy. Nieprzezroczystość każdego zasobu w warstwie jest mnożona tą wartością podczas komponowania do obiektu docelowego. nieprzezroczystośćBrush (Opcjonalnie) Szczotka używana do modyfikowania nieprzezroczystości warstwy. Pędzel jest mapowany na warstwę, a kanał alfa każdego mapowanego piksela pędzla jest mnożony przez odpowiedni piksel warstwy. Ustaw wartość na null, jeśli warstwa nie powinna mieć maski nieprzezroczystości. opcje warstw Wartość określająca, czy warstwa zamierza renderować tekst z antyaliasingiem ClearType. Ten parametr jest domyślnie wyłączony. Włączenie go umożliwia poprawne działanie funkcji ClearType, ale powoduje nieco wolniejsze renderowanie. Notatka
Począwszy od systemu Windows 8, nie można renderować funkcji ClearType w warstwie, więc parametr layerOptions powinien być zawsze ustawiony na D2D1_LAYER_OPTIONS_NONE
Dla wygody funkcja Direct2D udostępnia metodę D2D1::LayerParameters ułatwiając tworzenie struktur D2D1_LAYER_PARAMETERS.
Aby połączyć zawartość warstwy z obiektem docelowym renderowania, wywołaj metodę PopLayer. Przed wywołaniem metody EndDraw należy wywołać metodę PopLayer.
W poniższym przykładzie pokazano, jak używać CreateLayer, PushLayeri PopLayer. Wszystkie pola w strukturze D2D1_LAYER_PARAMETERS są ustawione na wartości domyślne, z wyjątkiem OpacityBrush, który jest ustawiony na 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);
Kod został pominięty w tym przykładzie.
Pamiętaj, że podczas wywoływania PushLayer i PopLayer, upewnij się, że każda PushLayer ma odpowiadające wywołanie PopLayer. Jeśli jest więcej wywołań PopLayer niż wywołań PushLayer, docelowy obiekt renderowania jest umieszczany w stanie błędu. Jeśli Flush jest wywoływane przed zdjęciem wszystkich zaległych warstw, obiekt docelowy renderowania jest umieszczany w stanie błędu i zwraca błąd. Aby wyczyścić stan błędu, użyj EndDraw.
Granice zawartości
contentBounds ustawia limit tego, co ma być przyciągane do warstwy. Tylko te elementy w granicach zawartości są złożone z powrotem do obiektu docelowego renderowania.
W poniższym przykładzie pokazano, jak określić contentBounds, aby oryginalny obraz został skadrowany zgodnie z granicami zawartości, zaczynając od lewego górnego rogu (10, 108) do prawego dolnego rogu (121, 177). Poniższa ilustracja przedstawia oryginalny obraz i efekt kadrowania obrazu zgodnie z granicami zawartości.
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;
}
Kod został pominięty w tym przykładzie.
Notatka
Wynikowy przycięty obraz będzie nadal mieć wpływ, jeśli określisz geometryczną Maskę. Aby uzyskać więcej informacji, zobacz sekcję Geometryczne maski.
Maski geometryczne
Maska geometryczna jest fragmentem lub wycięciem zdefiniowanym przez obiekt ID2D1Geometry, który maskuje warstwę podczas rysowania przez docelowy obiekt renderujący. Aby zamaskować wyniki dla geometrii, można użyć pola geometricMask ze struktury D2D1_LAYER_PARAMETERS. Jeśli na przykład chcesz wyświetlić obraz zamaskowany literą blokową "A", możesz najpierw utworzyć geometrię reprezentującą literę blokową "A" i użyć tej geometrii jako maski geometrycznej dla warstwy. Następnie po wypchnięciu warstwy można narysować obraz. Wyskakujące okienko warstwy powoduje przycięcie obrazu do kształtu litery bloku "A".
W poniższym przykładzie pokazano, jak utworzyć ID2D1PathGeometry zawierający kształt góry, a następnie przekazać geometrię ścieżki do PushLayer. Następnie rysuje mapę bitową i kwadraty. Jeśli w warstwie ma być renderowana tylko mapa bitowa, użyj FillGeometry z zaciśniętym pędzlem mapy bitowej w celu zwiększenia wydajności. Poniższa ilustracja przedstawia dane wyjściowe z przykładu.
W pierwszym przykładzie zdefiniowano geometrię, która ma być używana jako maska.
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);
}
W następnym przykładzie użyto geometrii jako maski dla warstwy.
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;
}
Kod został pominięty w tym przykładzie.
Notatka
Ogólnie rzecz biorąc, jeśli określisz geometrycznyMask, możesz użyć wartości domyślnej , InfiniteRect, dla contentBounds.
Jeśli contentBounds ma wartość NULL, a maska geometryczna nie ma wartości NULL, granice zawartości są skutecznie granicami maski geometrycznej po zastosowaniu przekształcenia maski.
Jeśli contentBounds nie ma wartości NULL, a geometriczna maska nie ma wartości NULL, to przekształcona maska geometryczna jest efektywnie przycinana względem granic zawartości, a granice zawartości są przyjmowane jako nieskończone.
Maski nieprzezroczystości
Maska nieprzezroczystości to maska opisana za pomocą pędzla lub mapy bitowej, która jest stosowana do innego obiektu, aby uczynić ten obiekt częściowo lub całkowicie przezroczystym. Umożliwia użycie kanału alfa pędzla jako maski zawartości. Można na przykład zdefiniować pędzel gradientowy radialny, który przechodzi od nieprzezroczystego do przezroczystego, aby stworzyć efekt winiety.
W poniższym przykładzie użyto ID2D1RadialGradientBrush (m_pRadialGradientBrush) jako maski nieprzezroczystości. Następnie rysuje mapę bitową i kwadraty. Jeśli w warstwie ma być renderowana tylko mapa bitowa, użyj FillGeometry z zaciśniętym pędzlem mapy bitowej w celu zwiększenia wydajności. Poniższa ilustracja przedstawia dane wyjściowe z tego przykładu.
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;
}
Kod został pominięty w tym przykładzie.
Notatka
W tym przykładzie użyto warstwy, aby zastosować maskę nieprzezroczystości do pojedynczego obiektu, aby zachować jak najprostszy przykład. W przypadku stosowania maski nieprzezroczystości do pojedynczego obiektu bardziej wydajne jest użycie metody FillOpacityMask lub FillGeometry, a nie warstwy.
Aby uzyskać instrukcje dotyczące stosowania maski nieprzezroczystości bez używania warstwy, zobacz Maski nieprzezroczystości — omówienie.
Alternatywy dla warstw
Jak wspomniano wcześniej, nadmierna liczba warstw może niekorzystnie wpłynąć na wydajność aplikacji. Aby zwiększyć wydajność, należy unikać używania warstw, gdy jest to możliwe; zamiast tego należy użyć ich alternatyw. W poniższym przykładzie kodu pokazano, jak używać PushAxisAlignedClip i PopAxisAlignedClip w celu wyciąć region, jako alternatywę dla używania warstwy z granicami zawartości.
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();
Podobnie użyj FillGeometry z zaciśniętym pędzlem mapy bitowej jako alternatywą dla używania warstwy z maską nieprzezroczystości, gdy w warstwie ma być renderowana tylko jedna zawartość, jak pokazano w poniższym przykładzie.
m_pRenderTarget->FillGeometry(
m_pRectGeo,
m_pLinearFadeFlowersBitmapBrush,
m_pLinearGradientBrush
);
Alternatywą dla używania warstwy z maską geometryczną jest użycie maski mapy bitowej do przycinania regionu, jak pokazano w poniższym przykładzie.
// 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);
Na koniec, jeśli chcesz zastosować nieprzezroczystość do pojedynczego prymitywu, powinieneś pomnożyć nieprzezroczystość przez kolor pędzla, a następnie wyrenderować prymityw. Nie potrzebujesz warstwy ani mapy bitowej maski przezroczystości.
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
);
Przycinanie dowolnego kształtu
Na rysunku przedstawiono wynik zastosowania klipu do obrazu.
Ten wynik można uzyskać przy użyciu warstw z maską geometryczną lub metodą FillGeometry za pomocą pędzla nieprzezroczystości.
Oto przykład, który używa warstwy:
// Call PushLayer() and pass in the clipping geometry.
m_d2dContext->PushLayer(
D2D1::LayerParameters(
boundsRect,
geometricMask));
Oto przykład, który używa metody 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());
W tym przykładzie kodu podczas wywoływania metody PushLayer nie przekazujesz warstwy utworzonej przez aplikację. Direct2D tworzy warstwę dla Ciebie. Funkcja Direct2D umożliwia zarządzanie alokacją i zniszczeniem tego zasobu bez udziału aplikacji. To umożliwia Direct2D wewnętrzne ponowne użycie warstw oraz stosowanie optymalizacji zarządzania zasobami.
Notatka
W systemie Windows 8 wprowadzono wiele optymalizacji w zakresie użycia warstw i zalecamy wypróbowanie korzystania z API warstw zamiast FillGeometry, gdy tylko to możliwe.
Klipy wyrównane względem osi
Jeśli region, który ma zostać obcięty, jest wyrównany do osi powierzchni rysunku, zamiast dowolnego. Ten przypadek jest odpowiedni do użycia prostokąta przycinania zamiast warstwy. Wzrost wydajności jest większy dla geometrii aliasowanej niż dla geometrii z wygładzaniem krawędzi. Aby uzyskać więcej informacji na temat klipów wyrównanych do osi, znajdziesz w temacie PushAxisAlignedClip.
Tematy pokrewne