Partilhar via


Visão geral das geometrias

Esta visão geral descreve como criar e usar objetos ID2D1Geometry para definir e manipular figuras 2D. Contém as seguintes secções.

O que é uma geometria Direct2D?

Uma geometria Direct2D é um objeto ID2D1Geometry . Este 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 descrever figuras bidimensionais e oferecem muitos usos, como a definição de regiões de teste de acerto, regiões de clipe e até mesmo caminhos de animação.

As geometrias Direct2D são recursos imutáveis e independentes do dispositivo criados pelo ID2D1Factory. Geralmente, você deve criar geometrias uma vez e mantê-las durante a vida útil do aplicativo, ou até que tenham que ser alteradas. Para obter mais informações sobre recursos independentes e dependentes do dispositivo, consulte a Visão geral de recursos.

As seções a seguir descrevem os diferentes tipos de geometrias.

Geometrias simples

Geometrias simples incluem 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 ID2D1RectangleGemetry ; 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 CreateEllipseGemetry passando em uma estrutura de elipse com o centro definido como (100, 100), x-radius para 100 e y-radius para 50. Em seguida, ele chama DrawGeometry, passando a geometria de elipse retornada, um ponteiro para um ID2D1SolidColorBrush preto e uma largura de traçado de 5. A ilustração a seguir mostra a saída do exemplo de código.

ilustração de uma elipse

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 DrawGemetry . Para pintar seu interior, use o método FillGemetry .

Geometrias de caminho

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.

Ilustração de um rio, montanhas e o sol

Para obter mais informações e exemplos, consulte a Visão geral das 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

Os 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 valores possíveis de D2D1_FILL_MODE_ALTERNATE (alternado) e D2D1_FILL_MODE_WINDING, um array de objetos geométricos a adicionar ao grupo de geometria e o número de elementos nesse array.

O exemplo de código a seguir declara primeiro uma matriz de objetos de geometria. Esses objetos são quatro círculos concêntricos que têm os seguintes raios: 25, 50, 75 e 100. Em seguida, chame o CreateGeometryGroup no objeto ID2D1Factory , passando D2D1_FILL_MODE_ALTERNATE, uma matriz de objetos de geometria para adicionar 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 duas geometrias de grupo do exemplo.

ilustração de dois conjuntos de quatro círculos concêntricos, um com anéis alternados preenchidos e outro com todos os anéis preenchidos

Geometrias transformadas

Existem 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 pode associar uma transformação diretamente a uma geometria usando o método CreateTransformedGeometry para criar uma ID2D1TransformedGeometry.

O método que você deve usar depende do efeito que você deseja. Quando você usa o destino de renderização para transformar e, em seguida, renderizar uma geometria, a transformação afeta tudo sobre a geometria, incluindo a largura de qualquer traçado que você aplicou. 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 dos traçados com D2D1_STROKE_TRANSFORM_TYPE_FIXED ou D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE. Você deve usar esses tipos de transformação para obter linhas independentes de transformação

 

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.

ilustração de um retângulo

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 alvo de renderização para dimensionar a geometria por um fator de 3 e depois desenhá-la. 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, mesmo que a espessura do traço seja 1.

Ilustração de um retângulo menor dentro de um retângulo maior com um traço mais espesso

// 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 curso não aumentou.

ilustração de um retângulo menor dentro de um retângulo maior com a mesma espessura de traçado

 // 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 alvo 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 testes de acertos), calcular as áreas e comprimentos e muito mais. A tabela a seguir descreve as operações geométricas comuns.

Funcionamento Método
Combinar CombineWithGeometry
Limites/ Limites alargados/Recuperar limites, atualização de região suja Ampliar, GetBounds, GetWidenedBounds
Teste de Colisão FillContainsPoint, StrokeContainsPoint
Acidente vascular cerebral StrokeContainsPoint
Comparação CompareWithGeometry
Simplificação (removem arcos e curvas quadráticas de Bezier) Simplifique
Tesselação Tesselato
Contorno (remover interseção) Resumo
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.

 

Combinação de geometrias

Para combinar uma geometria com outra, chame o método ID2D1Geometry::CombineWithGemetry . 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 (interseção), D2D1_COMBINE_MODE_XOR (xor) e D2D1_COMBINE_MODE_EXCLUDE (excluir). O exemplo de código a seguir mostra dois círculos que são combinados usando o modo de combinação de união, onde 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.

Ilustração de dois círculos sobrepostos combinados em uma união

Para obter ilustrações de todos os modos de combinação, consulte a enumeração D2D1_COMBINE_MODE.

Alargar

O método Widen gera uma nova geometria cujo preenchimento é equivalente a acariciar a geometria existente e, em seguida, grava o resultado no objeto ID2D1SimplifiedGeometrySink especificado. O exemplo de código a seguir chama Open no objeto ID2D1PathGemetry . Se Open for bem-sucedido, ele chamará Widen no objeto de geometria.

ID2D1GeometrySink *pGeometrySink = NULL;
hr = pPathGeometry->Open(&pGeometrySink);
if (SUCCEEDED(hr))
{
    hr = pGeometry->Widen(
            strokeWidth,
            pIStrokeStyle,
            pWorldTransform,
            pGeometrySink
            );

Tesselato

O método Tessellate cria um conjunto de triângulos enrolados no sentido horário que cobrem a geometria depois que ela é transformada usando a matriz especificada e achatada usando a tolerância especificada. 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, em seguida, transferidos para um membro da classe, m_pStrokeMesh, para uso posterior durante a renderização.

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 acertos. O exemplo de código a seguir chama FillContainsPoint em um objeto de ID2D1EllipseGeometry, passando um ponto nas coordenadas (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çado da geometria contém o ponto especificado. Você pode usar este método para fazer testes de impacto. 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.
}

Simplifique

O método Simplify remove arcos e curvas quadráticas de Bezier de uma geometria especificada. Assim, a geometria resultante contém apenas linhas e, opcionalmente, curvas cúbicas de Bezier. O exemplo de código a seguir usa Simplify para transformar uma geometria com curvas de 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 foi 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 a interseção incluem D2D1_GEOMETRY_RELATION_DISJOINT (disjuntos), D2D1_GEOMETRY_RELATION_IS_CONTAINED (está contido), D2D1_GEOMETRY_RELATION_CONTAINS (contém) e D2D1_GEOMETRY_RELATION_OVERLAP (sobreposição). "Disjunção" significa que dois preenchimentos de geometria não se cruzam de todo. "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
            );
    }
}

Resumo

O método Outline calcula o contorno 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 auto-interseções. Ele usa a tolerância de nivelamento 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 ela é ampliada pela largura e estilo de traçado especificados e transformada 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çado 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); 

Visão geral das geometrias de caminho

Referência do Direct2D