Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Esta visão geral descreve como criar e usar objetos ID2D1Geometry para definir e manipular figuras 2D. Ele contém as seções a seguir.
O que é uma geometria Direct2D?
Uma geometria Direct2D é um objeto ID2D1Geometry . Esse objeto pode ser uma geometria simples (ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry ou ID2D1EllipseGeometry), uma geometria de caminho (ID2D1PathGeometry) ou uma geometria composta (ID2D1GeometryGroup e ID2D1TransformedGeometry).
As geometrias Direct2D permitem que você descreva figuras bidimensionais e ofereça muitos usos, como definir regiões de teste de ocorrência, regiões de clipe e até mesmo caminhos de animação.
As geometrias Direct2D são recursos imutáveis e independentes do dispositivo criados pela ID2D1Factory. Em geral, você deve criar geometrias uma vez e mantê-las durante a vida útil do aplicativo ou até que elas precisem ser alteradas. Para obter mais informações sobre recursos independentes do dispositivo e dependentes do dispositivo, consulte a Visão geral de recursos.
As seções a seguir descrevem os diferentes tipos de geometrias.
Geometrias simples
As geometrias simples incluem objetos ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry e ID2D1EllipseGeometry e podem ser usadas para criar figuras geométricas básicas, como retângulos, retângulos arredondados, círculos e elipses.
Para criar uma geometria simples, use um dos métodos ID2D1Factory::Create<geometryType>Geometry . Esses métodos criam um objeto do tipo especificado. Por exemplo, para criar um retângulo, chame ID2D1Factory::CreateRectangleGeometry, que retorna um objeto ID2D1RectangleGeometry ; para criar um retângulo arredondado, chame ID2D1Factory::CreateRoundedRectangleGeometry, que retorna um objeto ID2D1RoundedRectangleGeometry e assim por diante.
O exemplo de código a seguir chama o método CreateEllipseGeometry , passando uma estrutura de elipse com o centro definido como (100, 100), raio x para 100 e raio y para 50. Em seguida, ele chama DrawGeometry, passando a geometria de elipse retornada, um ponteiro para um ID2D1SolidColorBrush preto e uma largura de traço de 5. A ilustração a seguir mostra a saída do exemplo de código.
ID2D1EllipseGeometry *m_pEllipseGeometry;
if (SUCCEEDED(hr))
{
hr = m_pD2DFactory->CreateEllipseGeometry(
D2D1::Ellipse(D2D1::Point2F(100.f, 60.f), 100.f, 50.f),
&m_pEllipseGeometry
);
}
m_pRenderTarget->DrawGeometry(m_pEllipseGeometry, m_pBlackBrush, 5);
Para desenhar o contorno de qualquer geometria, use o método DrawGeometry. Para pintar seu interior, use o método FillGeometry .
Geometrias de trajetórias
As geometrias de caminho são representadas pela interface ID2D1PathGeometry . Esses objetos podem ser usados para descrever figuras geométricas complexas compostas por segmentos como arcos, curvas e linhas. A ilustração a seguir mostra um desenho criado usando a geometria do caminho.
Para obter mais informações e exemplos, consulte a visão geral de geometrias de caminho.
Geometrias compostas
Uma geometria composta é uma geometria agrupada ou combinada com outro objeto de geometria ou com uma transformação. As geometrias compostas incluem objetos ID2D1TransformedGeometry e ID2D1GeometryGroup .
Grupos de geometria
Grupos de geometria são uma maneira conveniente de agrupar várias geometrias ao mesmo tempo para que todas as figuras de várias geometrias distintas sejam concatenadas em uma. Para criar um objeto ID2D1GeometryGroup, chame o método CreateGeometryGroup no objeto ID2D1Factory, passando o fillMode com possíveis valores de D2D1_FILL_MODE_ALTERNATE (alternado) e D2D1_FILL_MODE_WINDING, uma matriz de objetos de geometria a serem adicionados ao grupo de geometria e o número de elementos nessa matriz.
O exemplo de código a seguir primeiro declara uma matriz de objetos de geometria. Esses objetos são quatro círculos concêntricos que têm o seguinte raio: 25, 50, 75 e 100. Em seguida, chame o CreateGeometryGroup no objeto ID2D1Factory , passando D2D1_FILL_MODE_ALTERNATE, uma matriz de objetos de geometria a serem adicionados ao grupo de geometria e o número de elementos nessa matriz.
ID2D1Geometry *ppGeometries[] =
{
m_pEllipseGeometry1,
m_pEllipseGeometry2,
m_pEllipseGeometry3,
m_pEllipseGeometry4
};
hr = m_pD2DFactory->CreateGeometryGroup(
D2D1_FILL_MODE_ALTERNATE,
ppGeometries,
ARRAYSIZE(ppGeometries),
&m_pGeoGroup_AlternateFill
);
if (SUCCEEDED(hr))
{
hr = m_pD2DFactory->CreateGeometryGroup(
D2D1_FILL_MODE_WINDING,
ppGeometries,
ARRAYSIZE(ppGeometries),
&m_pGeoGroup_WindingFill
);
}
A ilustração a seguir mostra os resultados da renderização das geometrias de dois grupos do exemplo.
Geometrias transformadas
Há várias maneiras de transformar uma geometria. Você pode usar o método SetTransform de um destino de renderização para transformar tudo o que o destino de renderização desenha ou associar uma transformação diretamente a uma geometria usando o método CreateTransformedGeometry para criar um ID2D1TransformedGeometry.
O método que você deve usar depende do efeito desejado. Quando você usa o alvo de renderização para transformar e renderizar uma geometria, a transformação afeta tudo relacionado à geometria, incluindo a largura de qualquer traço que tenha sido aplicado. Por outro lado, quando você usa um ID2D1TransformedGeometry, a transformação afeta apenas as coordenadas que descrevem a forma. A transformação não afetará a espessura do traço quando a geometria for desenhada.
Observação
A partir do Windows 8, a transformação global não afetará a espessura do traço com D2D1_STROKE_TRANSFORM_TYPE_FIXED ou D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE. Você deve usar esses tipos de transformação para obter traços cuja aparência seja independente das transformações.
O exemplo a seguir cria um ID2D1RectangleGeometry e, em seguida, desenha-o sem transformá-lo. Ele produz a saída mostrada na ilustração a seguir.
hr = m_pD2DFactory->CreateRectangleGeometry(
D2D1::RectF(150.f, 150.f, 200.f, 200.f),
&m_pRectangleGeometry
);
// Draw the untransformed rectangle geometry.
m_pRenderTarget->DrawGeometry(m_pRectangleGeometry, m_pBlackBrush, 1);
O próximo exemplo usa o destino de renderização para dimensionar a geometria por um fator de 3 e, em seguida, desenha-a. A ilustração a seguir mostra o resultado do desenho do retângulo sem a transformação e com a transformação. Observe que o traço é mais espesso após a transformação, embora a espessura do traço seja 1.
// Transform the render target, then draw the rectangle geometry again.
m_pRenderTarget->SetTransform(
D2D1::Matrix3x2F::Scale(
D2D1::SizeF(3.f, 3.f),
D2D1::Point2F(175.f, 175.f))
);
m_pRenderTarget->DrawGeometry(m_pRectangleGeometry, m_pBlackBrush, 1);
O próximo exemplo usa o método CreateTransformedGeometry para dimensionar a geometria por um fator de 3 e, em seguida, desenha-a. Ele produz a saída mostrada na ilustração a seguir. Observe que, embora o retângulo seja maior, seu traço não aumentou.
// Create a geometry that is a scaled version
// of m_pRectangleGeometry.
// The new geometry is scaled by a factory of 3
// from the center of the geometry, (35, 35).
hr = m_pD2DFactory->CreateTransformedGeometry(
m_pRectangleGeometry,
D2D1::Matrix3x2F::Scale(
D2D1::SizeF(3.f, 3.f),
D2D1::Point2F(175.f, 175.f)),
&m_pTransformedGeometry
);
// Replace the previous render target transform.
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
// Draw the transformed geometry.
m_pRenderTarget->DrawGeometry(m_pTransformedGeometry, m_pBlackBrush, 1);
Geometrias como máscaras
Você pode usar um objeto ID2D1Geometry como uma máscara geométrica ao chamar o método PushLayer . A máscara geométrica especifica a área da camada que é composta no destino de renderização. Para obter mais informações, consulte a seção Máscaras Geométricas da Visão Geral de Camadas.
Operações geométricas
A interface ID2D1Geometry fornece várias operações geométricas que você pode usar para manipular e medir figuras geométricas. Por exemplo, você pode usá-los para calcular e retornar seus limites, comparar para ver como uma geometria está espacialmente relacionada a outra (útil para teste de ocorrência), calcular as áreas e comprimentos e muito mais. A tabela a seguir descreve as operações geométricas comuns.
Operação | Método |
---|---|
Combinar | CombineWithGeometry |
Limites/Limites Ampliados/Recuperar Limites, Atualização de Região Suja | Widen, GetBounds, GetWidenedBounds |
Testes de clique | FillContainsPoint, StrokeContainsPoint |
Acidente vascular cerebral | StrokeContainsPoint |
Comparação | CompareWithGeometry |
Simplificação (remove arcos e curvas quadráticas de Bézier) | Simplificar |
Mosaico | Tessellate |
Estrutura de tópicos (remover interseção) | Esquema |
Calcular a área ou o comprimento de uma geometria | ComputeArea, ComputeLength, ComputePointAtLength |
Observação
A partir do Windows 8, você pode usar o método ComputePointAndSegmentAtLength no ID2D1PathGeometry1 para calcular a área ou o comprimento de uma geometria.
Combinando geometrias
Para combinar uma geometria com outra, chame o método ID2D1Geometry::CombineWithGeometry . Ao combinar as geometrias, você especifica uma das quatro maneiras de executar a operação de combinação: D2D1_COMBINE_MODE_UNION (união), D2D1_COMBINE_MODE_INTERSECT (intersecção), D2D1_COMBINE_MODE_XOR (xor) e D2D1_COMBINE_MODE_EXCLUDE (excluir). O exemplo de código a seguir mostra dois círculos combinados usando o modo de combinação de união, em que o primeiro círculo tem o ponto central de (75, 75) e o raio de 50, e o segundo círculo tem o ponto central de (125, 75) e o raio de 50.
HRESULT hr = S_OK;
ID2D1GeometrySink *pGeometrySink = NULL;
// Create the first ellipse geometry to merge.
const D2D1_ELLIPSE circle1 = D2D1::Ellipse(
D2D1::Point2F(75.0f, 75.0f),
50.0f,
50.0f
);
hr = m_pD2DFactory->CreateEllipseGeometry(
circle1,
&m_pCircleGeometry1
);
if (SUCCEEDED(hr))
{
// Create the second ellipse geometry to merge.
const D2D1_ELLIPSE circle2 = D2D1::Ellipse(
D2D1::Point2F(125.0f, 75.0f),
50.0f,
50.0f
);
hr = m_pD2DFactory->CreateEllipseGeometry(circle2, &m_pCircleGeometry2);
}
if (SUCCEEDED(hr))
{
//
// Use D2D1_COMBINE_MODE_UNION to combine the geometries.
//
hr = m_pD2DFactory->CreatePathGeometry(&m_pPathGeometryUnion);
if (SUCCEEDED(hr))
{
hr = m_pPathGeometryUnion->Open(&pGeometrySink);
if (SUCCEEDED(hr))
{
hr = m_pCircleGeometry1->CombineWithGeometry(
m_pCircleGeometry2,
D2D1_COMBINE_MODE_UNION,
NULL,
NULL,
pGeometrySink
);
}
if (SUCCEEDED(hr))
{
hr = pGeometrySink->Close();
}
SafeRelease(&pGeometrySink);
}
}
A ilustração a seguir mostra dois círculos combinados com um modo combinado de união.
Para obter ilustrações de todos os modos de combinação, consulte a enumeração D2D1_COMBINE_MODE.
Widen
O método Widen gera uma nova geometria cujo preenchimento é equivalente a acariciá-lo e grava o resultado no objeto ID2D1SimplifiedGeometrySink especificado. O exemplo de código a seguir chama Open no objeto ID2D1PathGeometry . Se Open tiver sucesso, isso chamará Widen no objeto de geometria.
ID2D1GeometrySink *pGeometrySink = NULL;
hr = pPathGeometry->Open(&pGeometrySink);
if (SUCCEEDED(hr))
{
hr = pGeometry->Widen(
strokeWidth,
pIStrokeStyle,
pWorldTransform,
pGeometrySink
);
Tessellate
O método Tessellate cria um conjunto de triângulos orientados no sentido horário que cobrem a geometria depois que ela é transformada usando a matriz e a tolerância especificadas. O exemplo de código a seguir usa Tessellate para criar uma lista de triângulos que representam pPathGeometry. Os triângulos são armazenados em um ID2D1Mesh, pMesh e transferidos para um membro de classe, m_pStrokeMesh, para uso posterior ao renderizar.
ID2D1Mesh *pMesh = NULL;
hr = m_pRT->CreateMesh(&pMesh);
if (SUCCEEDED(hr))
{
ID2D1TessellationSink *pSink = NULL;
hr = pMesh->Open(&pSink);
if (SUCCEEDED(hr))
{
hr = pPathGeometry->Tessellate(
NULL, // world transform (already handled in Widen)
pSink
);
if (SUCCEEDED(hr))
{
hr = pSink->Close();
if (SUCCEEDED(hr))
{
SafeReplace(&m_pStrokeMesh, pMesh);
}
}
pSink->Release();
}
pMesh->Release();
}
FillContainsPoint e StrokeContainsPoint
O método FillContainsPoint indica se a área preenchida pela geometria contém o ponto especificado. Você pode usar esse método para fazer testes de clique. O exemplo de código a seguir chama FillContainsPoint em um objeto ID2D1EllipseGeometry, passando um ponto em (0,0) e uma matriz Identity.
BOOL containsPoint1;
hr = m_pCircleGeometry1->FillContainsPoint(
D2D1::Point2F(0,0),
D2D1::Matrix3x2F::Identity(),
&containsPoint1
);
if (SUCCEEDED(hr))
{
// Process containsPoint.
}
O método StrokeContainsPoint determina se o traço da geometria contém o ponto especificado. Você pode usar esse método para fazer testes de clique. O exemplo de código a seguir usa StrokeContainsPoint.
BOOL containsPoint;
hr = m_pCircleGeometry1->StrokeContainsPoint(
D2D1::Point2F(0,0),
10, // stroke width
NULL, // stroke style
NULL, // world transform
&containsPoint
);
if (SUCCEEDED(hr))
{
// Process containsPoint.
}
Simplificar
O método Simplify remove arcos e curvas quadráticas de Bezier de uma geometria especificada. Portanto, a geometria resultante contém apenas linhas e, opcionalmente, curvas bezier cúbicas. O exemplo de código a seguir usa Simplificar para transformar uma geometria com curvas bezier em uma geometria que contém apenas segmentos de linha.
HRESULT D2DFlatten(
ID2D1Geometry *pGeometry,
float flatteningTolerance,
ID2D1Geometry **ppGeometry
)
{
HRESULT hr;
ID2D1Factory *pFactory = NULL;
pGeometry->GetFactory(&pFactory);
ID2D1PathGeometry *pPathGeometry = NULL;
hr = pFactory->CreatePathGeometry(&pPathGeometry);
if (SUCCEEDED(hr))
{
ID2D1GeometrySink *pSink = NULL;
hr = pPathGeometry->Open(&pSink);
if (SUCCEEDED(hr))
{
hr = pGeometry->Simplify(
D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
NULL, // world transform
flatteningTolerance,
pSink
);
if (SUCCEEDED(hr))
{
hr = pSink->Close();
if (SUCCEEDED(hr))
{
*ppGeometry = pPathGeometry;
(*ppGeometry)->AddRef();
}
}
pSink->Release();
}
pPathGeometry->Release();
}
pFactory->Release();
return hr;
}
CalcularComprimento e CalcularÁrea
O método ComputeLength calcula o comprimento da geometria especificada se cada segmento fosse desenrolado em uma linha. Isso inclui o segmento de fechamento implícito se a geometria estiver fechada. O exemplo de código a seguir usa ComputeLength para calcular o comprimento de um círculo especificado (m_pCircleGeometry1).
float length;
// Compute the area of circle1
hr = m_pCircleGeometry1->ComputeLength(
D2D1::IdentityMatrix(),
&length
);
if (SUCCEEDED(hr))
{
// Process the length of the geometry.
}
O método ComputeArea calcula a área da geometria especificada. O exemplo de código a seguir usa ComputeArea para calcular a área de um círculo especificado (m_pCircleGeometry1).
float area;
// Compute the area of circle1
hr = m_pCircleGeometry1->ComputeArea(
D2D1::IdentityMatrix(),
&area
);
CompareWithGeometry
O método CompareWithGeometry descreve a interseção entre a geometria que chama esse método e a geometria especificada. Os valores possíveis para interseção incluem D2D1_GEOMETRY_RELATION_DISJOINT (disjunto), D2D1_GEOMETRY_RELATION_IS_CONTAINED (está contido), D2D1_GEOMETRY_RELATION_CONTAINS (contém) e D2D1_GEOMETRY_RELATION_OVERLAP (sobrepõe). "disjunto" significa que dois preenchimentos de geometria não se cruzam. "está contido" significa que a geometria está completamente contida pela geometria especificada. "contém" significa que a geometria contém completamente a geometria especificada e "sobreposição" significa que as duas geometrias se sobrepõem, mas nenhuma contém completamente a outra.
O exemplo de código a seguir mostra como comparar dois círculos que têm o mesmo raio de 50, mas são deslocados por 50.
HRESULT hr = S_OK;
ID2D1GeometrySink *pGeometrySink = NULL;
// Create the first ellipse geometry to merge.
const D2D1_ELLIPSE circle1 = D2D1::Ellipse(
D2D1::Point2F(75.0f, 75.0f),
50.0f,
50.0f
);
hr = m_pD2DFactory->CreateEllipseGeometry(
circle1,
&m_pCircleGeometry1
);
if (SUCCEEDED(hr))
{
// Create the second ellipse geometry to merge.
const D2D1_ELLIPSE circle2 = D2D1::Ellipse(
D2D1::Point2F(125.0f, 75.0f),
50.0f,
50.0f
);
hr = m_pD2DFactory->CreateEllipseGeometry(circle2, &m_pCircleGeometry2);
}
D2D1_GEOMETRY_RELATION result = D2D1_GEOMETRY_RELATION_UNKNOWN;
// Compare circle1 with circle2
hr = m_pCircleGeometry1->CompareWithGeometry(
m_pCircleGeometry2,
D2D1::IdentityMatrix(),
0.1f,
&result
);
if (SUCCEEDED(hr))
{
static const WCHAR szGeometryRelation[] = L"Two circles overlap.";
m_pRenderTarget->SetTransform(D2D1::IdentityMatrix());
if (result == D2D1_GEOMETRY_RELATION_OVERLAP)
{
m_pRenderTarget->DrawText(
szGeometryRelation,
ARRAYSIZE(szGeometryRelation) - 1,
m_pTextFormat,
D2D1::RectF(25.0f, 160.0f, 200.0f, 300.0f),
m_pTextBrush
);
}
}
Esboço
O método Outline calcula a estrutura de tópicos da geometria (uma versão da geometria na qual nenhuma figura se cruza ou qualquer outra figura) e grava o resultado em um ID2D1SimplifiedGeometrySink. O exemplo de código a seguir usa Outline para construir uma geometria equivalente sem nenhuma auto-interseção. Ele usa a tolerância de achatamento padrão.
HRESULT D2DOutline(
ID2D1Geometry *pGeometry,
ID2D1Geometry **ppGeometry
)
{
HRESULT hr;
ID2D1Factory *pFactory = NULL;
pGeometry->GetFactory(&pFactory);
ID2D1PathGeometry *pPathGeometry = NULL;
hr = pFactory->CreatePathGeometry(&pPathGeometry);
if (SUCCEEDED(hr))
{
ID2D1GeometrySink *pSink = NULL;
hr = pPathGeometry->Open(&pSink);
if (SUCCEEDED(hr))
{
hr = pGeometry->Outline(NULL, pSink);
if (SUCCEEDED(hr))
{
hr = pSink->Close();
if (SUCCEEDED(hr))
{
*ppGeometry = pPathGeometry;
(*ppGeometry)->AddRef();
}
}
pSink->Release();
}
pPathGeometry->Release();
}
pFactory->Release();
return hr;
}
GetBounds e GetWidenedBounds
O método GetBounds recupera os limites da geometria. O exemplo de código a seguir usa GetBounds para recuperar os limites de um círculo especificado (m_pCircleGeometry1).
D2D1_RECT_F bounds;
hr = m_pCircleGeometry1->GetBounds(
D2D1::IdentityMatrix(),
&bounds
);
if (SUCCEEDED(hr))
{
// Retrieve the bounds.
}
O método GetWidenedBounds recupera os limites da geometria depois que ele é ampliado pela largura e estilo de traço especificados e transformado pela matriz especificada. O exemplo de código a seguir usa GetWidenedBounds para recuperar os limites de um círculo especificado (m_pCircleGeometry1) depois que ele é ampliado pela largura de traço especificada.
float dashes[] = {1.f, 1.f, 2.f, 3.f, 5.f};
m_pD2DFactory->CreateStrokeStyle(
D2D1::StrokeStyleProperties(
D2D1_CAP_STYLE_FLAT,
D2D1_CAP_STYLE_FLAT,
D2D1_CAP_STYLE_ROUND,
D2D1_LINE_JOIN_ROUND, // lineJoin
10.f, //miterLimit
D2D1_DASH_STYLE_CUSTOM,
0.f //dashOffset
),
dashes,
ARRAYSIZE(dashes)-1,
&m_pStrokeStyle
);
D2D1_RECT_F bounds1;
hr = m_pCircleGeometry1->GetWidenedBounds(
5.0,
m_pStrokeStyle,
D2D1::IdentityMatrix(),
&bounds1
);
if (SUCCEEDED(hr))
{
// Retrieve the widened bounds.
}
ComputePointAtLength
O método ComputePointAtLength calcula o ponto e o vetor tangente na distância especificada ao longo da geometria. O exemplo de código a seguir usa ComputePointAtLength.
D2D1_POINT_2F point;
D2D1_POINT_2F tangent;
hr = m_pCircleGeometry1->ComputePointAtLength(
10,
NULL,
&point,
&tangent);
Tópicos relacionados