Visão geral de geometrias

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

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).

Direct2D geometrias permitem que você descreva figuras bidimensionais e ofereça muitos usos, como definir regiões de teste de clique, regiões de recorte e até mesmo caminhos de animação.

Direct2D geometrias são recursos imutáveis e independentes do dispositivo criados por 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

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 reticências.

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 como 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.

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 a estrutura de tópicos de qualquer geometria, use o método DrawGeometry . Para pintar seu interior, use o método FillGeometry .

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 de geometrias de caminho.

Geometrias compostas

Uma geometria composta é uma geometria agrupada ou combinada com outro objeto geometry 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 valores possíveis de D2D1_FILL_MODE_ALTERNATE (alternativo) e D2D1_FILL_MODE_WINDING, uma matriz de objetos geometry a serem adicionados ao grupo geometry e o número de elementos nessa matriz.

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

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 pode 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 destino de renderização para transformar e renderizar uma geometria, a transformação afeta tudo sobre a geometria, incluindo a largura de qualquer traço 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 mundial não afetará a espessura do traço de traços com D2D1_STROKE_TRANSFORM_TYPE_FIXEDou D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE. Você deve usar esses tipos de transformação para obter traços 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 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 grosso 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 grosso

// 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.

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

 // 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 composta no destino de renderização. Para obter mais informações, consulte a seção Máscaras Geométricas da Visão geral das 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 clique), calcular as áreas e comprimentos e muito mais. A tabela a seguir descreve as operações geométricas comuns.

Operação Método
Combine CombineWithGeometry
Limites/Limites Ampliados/Recuperar Limites, Atualização de Região Suja Widen, GetBounds, GetWidenedBounds
Testes de clique FillContainsPoint, StrokeContainsPoint
Traço StrokeContainsPoint
Comparação CompareWithGeometry
Simplificação (remove arcos e curvas quadráticas de Bézier) Simplificar
Mosaico Mosaico
Estrutura de tópicos (remover interseção) Contorno
Calcular a área ou o comprimento de uma geometria ComputeArea, ComputeLength, ComputePointAtLength

 

Observação

Começando no 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 (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 de combinação 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.

Ampliar

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

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

Mosaico

O método Tessellate cria um conjunto de triângulos de feridas 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 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 Bézier de uma geometria especificada. Portanto, a geometria resultante contém apenas linhas e, opcionalmente, curvas de Bézier cúbicas. O exemplo de código a seguir usa Simplificar para transformar uma geometria com curvas de Bézier 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;
}

ComputeLength e ComputeArea

O método ComputeLength calculará o comprimento da geometria especificada se cada segmento tiver sido desrollado em uma linha. Isso incluirá 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 (não contíguo), D2D1_GEOMETRY_RELATION_IS_CONTAINED (está contido), D2D1_GEOMETRY_RELATION_CONTAINS (contém) e D2D1_GEOMETRY_RELATION_OVERLAP (sobreposição). "desarticulado" significa que dois preenchimentos de geometria não se cruzam. "está contido" significa que a geometria está completamente contida pela geometria especificada. "contains" 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 em 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
            );
    }
}

Contorno

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 interseção. 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 pelo estilo de traço 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ç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 vetor de ponto e 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 de Direct2D