Visão geral das camadas
Essa visão geral descreve os conceitos básicos do uso de camadas de Direct2D. Ele contém as seguintes seções:
- O que são camadas?
- Camadas em Windows 8 e posteriores
- Criando camadas
- Limites de conteúdo
- Máscaras Geométricas
- Máscaras de Opacidade
- Alternativas a camadas
- Recortar uma forma arbitrária
- Tópicos relacionados
O que são camadas?
Camadas, representadas por objetos ID2D1Layer , permitem que um aplicativo manipule um grupo de operações de desenho. Você usa uma camada "empurrando"-a para um destino de renderização. As operações de desenho subsequentes pelo destino de renderização são direcionadas para a camada. Depois de terminar a camada, você "pop" a camada do destino de renderização, que compõe o conteúdo da camada de volta ao destino de renderização.
Assim como os pincéis, as camadas são recursos dependentes do dispositivo criados por destinos de renderização. As camadas podem ser usadas em qualquer destino de renderização no mesmo domínio de recurso que contém o destino de renderização que o criou. No entanto, um recurso de camada só pode ser usado por um destino de renderização por vez. Para obter mais informações sobre recursos, consulte Visão geral de recursos.
Embora as camadas ofereçam uma técnica de renderização avançada para produzir efeitos interessantes, o número excessivo de camadas em um aplicativo pode afetar negativamente seu desempenho, devido aos vários custos associados ao gerenciamento de camadas e recursos de camada. Por exemplo, há o custo de preencher ou limpar a camada e, em seguida, misturá-la de volta, especialmente em hardware de ponta. Em seguida, há o custo de gerenciar os recursos de camada. Se você realocar esses com frequência, as paradas resultantes em relação à GPU serão o problema mais significativo. Ao projetar seu aplicativo, tente maximizar a reutilização de recursos de camada.
Camadas em Windows 8 e posteriores
Windows 8 introduziu novas APIs relacionadas à camada que simplificam, melhoram o desempenho e adicionam recursos às camadas.
ID2D1DeviceContext e PushLayer
A interface ID2D1DeviceContext é derivada da interface ID2D1RenderTarget e é fundamental para exibir Direct2D conteúdo em Windows 8, para obter mais informações sobre essa interface, consulte Dispositivos e contextos de dispositivo. Com a interface de contexto do dispositivo, você pode ignorar a chamada do método CreateLayer e, em seguida, passar NULL para o método ID2D1DeviceContext::P ushLayer . Direct2D gerencia automaticamente o recurso de camada e pode compartilhar recursos entre camadas e grafos de efeito.
D2D1_LAYER_PARAMETERS1 e D2D1_LAYER_OPTIONS1
A estrutura D2D1_LAYER_PARAMETERS1 é a mesma que D2D1_LAYER_PARAMETERS, exceto que o membro final da estrutura agora é uma enumeração D2D1_LAYER_OPTIONS1 .
D2D1_LAYER_OPTIONS1 não tem nenhuma opção ClearType e tem duas opções diferentes que você pode usar para melhorar o desempenho:
D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND: Direct2D renderiza primitivos para a camada sem limpá-la com preto transparente. Esse não é o padrão, mas, na maioria dos casos, resulta em um melhor desempenho.
D2D1_LAYER_OPTIONS1_IGNORE_ALPHA: se a superfície subjacente estiver definida como D2D1_ALPHA_MODE_IGNORE, essa opção permitirá que Direct2D evite modificar o canal alfa da camada. Não use isso em outros casos.
Modos do Blend
Começando em Windows 8, o contexto do dispositivo tem um modo de combinação primitivo que determina como cada primitivo é combinado com a superfície de destino. Esse modo também se aplica a camadas quando você chama o método PushLayer .
Por exemplo, se você estiver usando uma camada para cortar primitivos com transparência, defina o modo D2D1_PRIMITIVE_BLEND_COPY no contexto do dispositivo para obter resultados adequados. O modo de cópia faz com que o contexto do dispositivo interpole todos os quatro canais de cores, incluindo o canal alfa, de cada pixel com o conteúdo da superfície de destino de acordo com a máscara geométrica da camada.
Interoperação
Começando em Windows 8, Direct2D dá suporte à interoperação com Direct3D e GDI enquanto uma camada ou clipe é enviado por push. Você chama ID2D1GdiInteropRenderTarget::GetDC enquanto uma camada é enviada por push para interoperar com GDI. Você chama ID2D1DeviceContext::Flush e, em seguida, renderiza para a superfície subjacente para interoperar com Direct3D. É sua responsabilidade renderizar dentro da camada ou cortar com Direct3D ou GDI. Se você tentar renderizar fora da camada ou cortar, os resultados serão indefinidos.
Criando camadas
Trabalhar com camadas requer familiaridade com os métodos CreateLayer, PushLayer e PopLayer e a estrutura D2D1_LAYER_PARAMETERS , que contém um conjunto de dados paramétricos que define como a camada pode ser usada. A lista a seguir descreve os métodos e a estrutura.
Chame o método CreateLayer para criar um recurso de camada.
Observação
Começando no Windows 8, você pode ignorar a chamada do método CreateLayer e, em seguida, passar NULL para o método PushLayer na interface ID2D1DeviceContext. Isso é mais simples e permite que Direct2D gerenciem automaticamente o recurso de camada e compartilhem recursos entre camadas e grafos de efeito.
Depois que o destino de renderização começar a desenhar (depois que seu método BeginDraw tiver sido chamado), você poderá usar o método PushLayer . O método PushLayer adiciona a camada especificada ao destino de renderização, de modo que o destino receba todas as operações de desenho subsequentes até que PopLayer seja chamado. Esse método usa um objeto ID2D1Layer retornado chamando CreateLayer e uma layerParameters na estrutura D2D1_LAYER_PARAMETERS . A tabela a seguir descreve os campos da estrutura.
Campo Descrição Contentbounds Os limites de conteúdo da camada. O conteúdo não será renderizado fora desses limites. Esse parâmetro usa como padrão InfiniteRect. Quando o valor padrão é usado, os limites de conteúdo são efetivamente levados para serem os limites do destino de renderização. geometricMask (Opcional) A área, definida por uma ID2D1Geometry, à qual a camada deve ser recortada. Defina como NULL se a camada não deve ser recortada em uma geometria. maskAntialiasMode Um valor que especifica o modo de suavização para a máscara geométrica especificada pelo campo geometricMask . maskTransform Um valor que especifica a transformação que é aplicada à máscara geométrica ao redigir a camada. Isso é relativo à transformação do mundo. opacity O valor de opacidade da camada. A opacidade de cada recurso na camada é multiplicada com esse valor ao compor para o destino. opacityBrush (Opcional) Um pincel usado para modificar a opacidade da camada. O pincel é mapeado para a camada e o canal alfa de cada pixel de pincel mapeado é multiplicado em relação ao pixel de camada correspondente. Defina como NULL se a camada não deve ter uma máscara de opacidade. layerOptions Um valor que especifica se a camada pretende renderizar texto com a suavização ClearType. Esse parâmetro usa como padrão desativado. A ativação permite que o ClearType funcione corretamente, mas resulta em uma velocidade de renderização um pouco mais lenta. Observação
Começando no Windows 8, você não pode renderizar com ClearType em uma camada, portanto, o parâmetro layerOptions sempre deve ser definido como D2D1_LAYER_OPTIONS_NONE
Para conveniência, Direct2D fornece o método D2D1::LayerParameters para ajudá-lo a criar estruturas D2D1_LAYER_PARAMETERS.
Para compor o conteúdo da camada no destino de renderização, chame o método PopLayer . Você deve chamar o método PopLayer antes de chamar o método EndDraw .
O exemplo a seguir mostra como usar CreateLayer, PushLayer e PopLayer. Todos os campos na estrutura D2D1_LAYER_PARAMETERS são definidos como seus padrões, exceto opacityBrush, que é definido como 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);
O código foi omitido neste exemplo.
Observe que, ao chamar PushLayer e PopLayer, verifique se cada PushLayer tem uma chamada PopLayer correspondente. Se houver mais chamadas poplayer do que chamadas do PushLayer , o destino de renderização será colocado em um estado de erro. Se Flush for chamado antes que todas as camadas pendentes sejam exibidas, o destino de renderização será colocado em um estado de erro e retornará um erro. Para limpar o estado de erro, use EndDraw.
Limites de conteúdo
O contentBounds define o limite do que deve ser desenhado para a camada. Somente essas coisas dentro dos limites de conteúdo são compostas de volta para o destino de renderização.
O exemplo a seguir mostra como especificar contentBounds para que a imagem original seja recortada aos limites de conteúdo com o canto superior esquerdo em (10, 108) e o canto inferior direito em (121, 177). A ilustração a seguir mostra a imagem original e o resultado do recorte da imagem nos limites de conteúdo.
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;
}
O código foi omitido neste exemplo.
Observação
A imagem recortada resultante será afetada ainda mais se você especificar um geometricMask. Consulte a seção Máscaras Geométricas para obter mais informações.
Máscaras Geométricas
Uma máscara geométrica é um clipe ou um recorte, definido por um objeto ID2D1Geometry , que mascara uma camada quando é desenhada por um destino de renderização. Você pode usar o campo geometricMask da estrutura D2D1_LAYER_PARAMETERS para mascarar os resultados para uma geometria. Por exemplo, se você quiser exibir uma imagem mascarada por uma letra de bloco "A", você pode primeiro criar uma geometria representando a letra de bloco "A" e usar essa geometria como uma máscara geométrica para uma camada. Em seguida, depois de efetuar push da camada, você pode desenhar a imagem. Estalar a camada faz com que a imagem seja recortada na forma de letra de bloco "A".
O exemplo a seguir mostra como criar um ID2D1PathGeometry contendo uma forma de uma montanha e, em seguida, passar a geometria do caminho para o PushLayer. Em seguida, desenha um bitmap e quadrados. Se houver apenas um bitmap na camada a ser renderizada, use FillGeometry com um pincel de bitmap fixado para obter eficiência. A ilustração a seguir mostra a saída do exemplo.
O primeiro exemplo define a geometria a ser usada como uma máscara.
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);
}
O exemplo a seguir usa a geometria como uma máscara para a camada.
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;
}
O código foi omitido neste exemplo.
Observação
Em geral, se você especificar um geometricMask, poderá usar o valor padrão , InfiniteRect, para o contentBounds.
Se contentBounds for NULL e geometricMask não for NULL, os limites de conteúdo serão efetivamente os limites da máscara geométrica após a transformação da máscara ser aplicada.
Se contentBounds não for NULL e geometricMask não for NULL, a máscara geométrica transformada será efetivamente recortada em limites de conteúdo e os limites de conteúdo serão considerados infinitos.
Máscaras de Opacidade
Uma máscara de opacidade é uma máscara, descrita por um pincel ou bitmap, que é aplicada a outro objeto para tornar esse objeto parcial ou completamente transparente. Ele permite que o uso do canal alfa de um pincel seja usado como uma máscara de conteúdo. Por exemplo, você pode definir um pincel de gradiente radial que varia de opaco para transparente para criar um efeito de vinheta.
O exemplo a seguir usa um ID2D1RadialGradientBrush (m_pRadialGradientBrush) como uma máscara de opacidade. Em seguida, desenha um bitmap e quadrados. Se houver apenas um bitmap na camada a ser renderizada, use FillGeometry com um pincel de bitmap fixado para obter eficiência. A ilustração a seguir mostra a saída deste exemplo.
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;
}
O código foi omitido neste exemplo.
Observação
Este exemplo usa uma camada para aplicar uma máscara de opacidade a um único objeto para manter o exemplo o mais simples possível. Ao aplicar uma máscara de opacidade a um único objeto, é mais eficiente usar os métodos FillOpacityMask ou FillGeometry , em vez de uma camada.
Para obter instruções sobre como aplicar uma máscara de opacidade sem usar uma camada, consulte a Visão geral das Máscaras de Opacidade.
Alternativas a camadas
Conforme mencionado anteriormente, o número excessivo de camadas pode afetar negativamente o desempenho do aplicativo. Para melhorar o desempenho, evite usar camadas sempre que possível; em vez disso, use suas alternativas. O exemplo de código a seguir mostra como usar PushAxisAlignedClip e PopAxisAlignedClip para cortar uma região, como alternativa ao uso de uma camada com limites de conteúdo.
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();
Da mesma forma, use FillGeometry com um pincel bitmap fixado como alternativa ao uso de uma camada com uma máscara de opacidade quando houver apenas um conteúdo na camada a ser renderizado, conforme mostrado no exemplo a seguir.
m_pRenderTarget->FillGeometry(
m_pRectGeo,
m_pLinearFadeFlowersBitmapBrush,
m_pLinearGradientBrush
);
Como alternativa ao uso de uma camada com uma máscara geométrica, considere usar uma máscara de bitmap para cortar uma região, conforme mostrado no exemplo a seguir.
// 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);
Por fim, se você quiser aplicar opacidade a um único primitivo, você deverá multiplicar a opacidade para a cor do pincel e, em seguida, renderizar o primitivo. Você não precisa de uma camada ou um bitmap de máscara de opacidade.
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
);
Recortar uma forma arbitrária
A figura aqui mostra o resultado da aplicação de um clipe a uma imagem.
Você pode obter esse resultado usando camadas com uma máscara de geometria ou o método FillGeometry com um pincel de opacidade.
Aqui está um exemplo que usa uma camada:
// Call PushLayer() and pass in the clipping geometry.
m_d2dContext->PushLayer(
D2D1::LayerParameters(
boundsRect,
geometricMask));
Veja um exemplo que usa o método 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());
Neste exemplo de código, ao chamar o método PushLayer, você não passa uma camada criada pelo aplicativo. Direct2D cria uma camada para você. Direct2D é capaz de gerenciar a alocação e a destruição desse recurso sem qualquer envolvimento do aplicativo. Isso permite que Direct2D reutilizem camadas internamente e apliquem otimizações de gerenciamento de recursos.
Observação
Em Windows 8 muitas otimizações foram feitas para o uso de camadas e recomendamos que você tente usar APIs de camada em vez de FillGeometry sempre que possível.
Clipes alinhados ao eixo
Se a região a ser recortada estiver alinhada ao eixo da superfície de desenho, em vez de arbitrária. Esse caso é adequado para usar um retângulo de clipe em vez de uma camada. O ganho de desempenho é mais para geometria de alias do que geometria suavizada. Para obter mais informações sobre clipes alinhados ao eixo, consulte o tópico PushAxisAlignedClip .
Tópicos relacionados