几何概述

本概述介绍如何创建和使用 ID2D1Geometry 对象来定义和操作 2D 图形。 其中包含以下各节。

什么是 Direct2D 几何图形?

Direct2D 几何图形是 ID2D1Geometry 对象。 此对象可以是 ID2D1RectangleGeometry (简单几何图形, ID2D1RoundedRectangleGeometryID2D1EllipseGeometry) 、 (ID2D1PathGeometry) 的路径几何图形,或 (ID2D1GeometryGroupID2D1TransformedGeometry) 复合几何图形。

使用 Direct2D 几何图形可以描述二维图形并提供许多用途,例如定义命中测试区域、剪辑区域甚至动画路径。

Direct2D 几何图形是由 ID2D1Factory 创建的不可变且与设备无关的资源。 通常,应创建一次几何图形,并在应用程序的生命周期内保留它们,或者直到必须更改它们为止。 有关与设备无关和依赖于设备的资源的详细信息,请参阅 资源概述

以下部分介绍不同类型的几何图形。

简单几何图形

简单几何图形包括 ID2D1RectangleGeometryID2D1RoundedRectangleGeometryID2D1EllipseGeometry 对象,可用于创建基本几何图形,如矩形、圆角矩形、圆和椭圆。

若要创建简单的几何图形,请使用 ID2D1Factory::Create<geometryType>Geometry 方法之一。 这些方法创建指定类型的对象。 例如,若要创建矩形,请调用 ID2D1Factory::CreateRectangleGeometry,这将返回 ID2D1RectangleGeometry 对象;若要创建圆角矩形,请调用 ID2D1Factory::CreateRoundedRectangleGeometry,这将返回 ID2D1RoundedRectangleGeometry 对象,依此类移。

下面的代码示例调用 CreateEllipseGeometry 方法,传入一个椭圆形结构, 中心设置为 ( 100,100) , x 半径 设置为 100, y 半径 设置为 50。 然后,它调用 DrawGeometry,传入返回的椭圆几何图形、指向黑色 ID2D1SolidColorBrush 的指针和 5 的笔划宽度。 下图显示了代码示例的输出。

椭圆形图示

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

若要绘制任何几何图形的轮廓,请使用 DrawGeometry 方法。 若要绘制其内部,请使用 FillGeometry 方法。

路径几何图形

路径几何图形由 ID2D1PathGeometry 接口表示。 这些对象可用于描述由弧线、曲线和线条等线段组成的复杂几何图形。 下图显示了使用路径几何图形创建的绘图。

河流、山脉和太阳的插图

有关详细信息和示例,请参阅 路径几何图形概述

复合几何图形

复合几何图形是分组或与另一个几何对象组合的几何图形,或与转换组合的几何图形。 复合几何图形包括 ID2D1TransformedGeometryID2D1GeometryGroup 对象。

几何组

几何组是同时组合多个几何图形的便捷方法,因此多个不同几何图形的所有图形都连接成一个。 若要创建 ID2D1GeometryGroup 对象,请对 ID2D1Factory 对象调用 CreateGeometryGroup 方法,传入 fillMode,其可能的值为 D2D1_FILL_MODE_ALTERNATE (备用) 和D2D1_FILL_MODE_WINDING、要添加到几何图形组的几何对象数组以及此数组中的元素数。

下面的代码示例首先声明 geometry 对象的数组。 这些对象是四个同心圆,其半径如下:25、50、75 和 100。 然后对 ID2D1Factory 对象调用 CreateGeometryGroup,传入D2D1_FILL_MODE_ALTERNATE、要添加到 geometry 组的 geometry 对象数组以及此数组中的元素数。

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
        );
}

下图显示了呈现示例中的两个组几何图形的结果。

两组四个同心圆的插图,一组填充了交替环,一组填充了所有圆环

转换后的几何图形

有多种方法可以转换几何图形。 可以使用呈现目标的 SetTransform 方法转换呈现器目标绘制的所有内容,也可以通过使用 CreateTransformedGeometry 方法创建 ID2D1TransformedGeometry 将转换直接与几何图形相关联。

应使用的方法取决于所需的效果。 使用呈现器目标转换然后呈现几何图形时,转换会影响有关几何图形的所有内容,包括已应用的任何笔划的宽度。 另一方面,使用 ID2D1TransformedGeometry 时,转换仅影响描述形状的坐标。 绘制几何图形时,转换不会影响笔划粗细。

注意

从Windows 8开始,世界转换不会影响具有D2D1_STROKE_TRANSFORM_TYPE_FIXEDD2D1_STROKE_TRANSFORM_TYPE_HAIRLINE的笔划粗细。 应使用这些转换类型来实现独立于转换的笔划

 

以下示例创建 ID2D1RectangleGeometry,然后绘制它而不转换它。 它生成下图所示的输出。

矩形图示

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

下一个示例使用呈现器目标将几何图形缩放为 3,然后绘制它。 下图显示了不使用转换和转换绘制矩形的结果。 请注意,转换后笔划较粗,即使笔划粗细为 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);

下一个示例使用 CreateTransformedGeometry 方法将几何图形缩放为 3,然后绘制它。 它生成下图所示的输出。 请注意,尽管矩形较大,但其笔划并没有增加。

图示:具有相同笔划粗细的较大矩形内的较小矩形

 // 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);

作为掩码的几何图形

调用 PushLayer 方法时,可以使用 ID2D1Geometry 对象作为几何掩码。 几何蒙板指定复合到呈现目标的图层的区域。 有关详细信息,请参阅 层概述的几何掩码部分。

几何运算

ID2D1Geometry 接口提供了多个可用于操作和测量几何图形的几何运算。 例如,可以使用它们来计算和返回其边界,比较以查看一个几何图形与另一个几何图形在空间上如何相关, (可用于命中测试) 、计算面积和长度等。 下表描述了常见的几何运算。

操作 方法
Combine CombineWithGeometry
边界/扩大边界/检索边界,脏区域更新 WidenGetBoundsGetWidenedBounds
命中测试 FillContainsPointStrokeContainsPoint
笔划 StrokeContainsPoint
比较 CompareWithGeometry
简化 (消除了弧线和二次贝塞尔曲线) 简化
分割 Tessellate
大纲 (删除交集) 大纲
计算几何图形的面积或长度 ComputeAreaComputeLengthComputePointAtLength

 

注意

从 Windows 8 开始,可以使用 ID2D1PathGeometry1 上的 ComputePointAndSegmentAtLength 方法计算几何图形的面积或长度。

 

组合几何图形

若要将一个几何图形与另一个几何图形合并,请调用 ID2D1Geometry::CombineWithGeometry 方法。 组合几何图形时,可以指定执行合并操作的四种方法之一: D2D1_COMBINE_MODE_UNION (联合) 、 D2D1_COMBINE_MODE_INTERSECT (与) 相交、 D2D1_COMBINE_MODE_XOR (xor) ,以及 D2D1_COMBINE_MODE_EXCLUDE (排除) 。 下面的代码示例演示使用联合组合模式组合的两个圆,其中第一个圆的中心点为 (75,75) ,半径为 50,第二个圆的中心点为 (125,75) ,半径为 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);
    }
}

下图显示了结合联合模式的两个圆圈。

合并成联合的两个重叠圆的插图

有关所有组合模式的插图,请参阅 D2D1_COMBINE_MODE 枚举

扩大

Wide 方法生成一个新的几何图形,其填充等同于抚摸现有几何图形,然后将结果写入指定的 ID2D1SimplifiedGeometrySink 对象。 下面的代码示例在 ID2D1PathGeometry 对象上调用 Open。 如果 Open 成功,它会在 geometry 对象上调用 Wide

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

Tessellate

Tessellate 方法创建一组顺时针三角形,这些三角形在使用指定的矩阵进行转换后覆盖几何图形,并使用指定的容差平展几何图形。 下面的代码示例使用 Tessellate 创建表示 pPathGeometry 的三角形列表。 三角形存储在 ID2D1Mesh、pMesh 中,然后传输到类成员m_pStrokeMesh,供以后在呈现时使用。

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 和 StrokeContainsPoint

FillContainsPoint 方法指示由几何图形填充的区域是否包含指定的点。 可以使用此方法执行命中测试。 下面的代码示例在 ID2D1EllipseGeometry 对象上调用 FillContainsPoint,传入 (0,0) 和 Identity 矩阵的点。

BOOL containsPoint1;
hr = m_pCircleGeometry1->FillContainsPoint(
    D2D1::Point2F(0,0),
    D2D1::Matrix3x2F::Identity(),
    &containsPoint1
    );

if (SUCCEEDED(hr))
{
    // Process containsPoint.
}

StrokeContainsPoint 方法确定几何图形的笔划是否包含指定的点。 可以使用此方法执行命中测试。 下面的代码示例使用 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.
}

简化

Simplify 方法从指定的几何图形中删除弧线和二次贝塞尔曲线。 因此,生成的几何图形仅包含线条和(可选)三次方贝塞尔曲线。 下面的代码示例使用 Simplify 将具有贝塞尔曲线的几何图形转换为仅包含线段的几何图形。

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 和 ComputeArea

如果每个段都展开为一条线, ComputeLength 方法将计算指定几何图形的长度。 如果几何图形已关闭,则包括隐式结束段。 下面的代码示例使用 ComputeLength 计算指定圆的长度 (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.
}

ComputeArea 方法计算指定几何图形的面积。 下面的代码示例使用 ComputeArea 计算指定圆 (m_pCircleGeometry1) 的区域。

float area;

// Compute the area of circle1
hr = m_pCircleGeometry1->ComputeArea(
    D2D1::IdentityMatrix(),
    &area
    );

CompareWithGeometry

CompareWithGeometry 方法描述调用此方法的几何图形与指定几何图形之间的交集。 交集的可能值包括 D2D1_GEOMETRY_RELATION_DISJOINT (不连续) 、 D2D1_GEOMETRY_RELATION_IS_CONTAINED (包含在) 、 D2D1_GEOMETRY_RELATION_CONTAINS (包含) ,以及 D2D1_GEOMETRY_RELATION_OVERLAP (重叠) 。 “不相交”表示两个几何填充根本不相交。 “包含”表示该几何图形完全包含在指定的几何图形中。 “contains”表示几何图形完全包含指定的几何图形,“重叠”表示两个几何图形重叠,但两个几何图形都不完全包含另一个几何图形。

下面的代码示例演示如何比较具有相同半径为 50 但偏移量为 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
            );
    }
}

轮廓

Outline 方法计算几何图形的轮廓 (一个版本的几何图形,其中没有图形交叉自身或任何其他图形) ,并将结果写入 ID2D1SimplifiedGeometrySink。 下面的代码示例使用 Outline 构造一个没有任何自交的等效几何图形。 它使用默认的平展容差。

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 和 GetWidenedBounds

GetBounds 方法检索几何图形的边界。 下面的代码示例使用 GetBounds 检索指定圆 (m_pCircleGeometry1) 的边界。

D2D1_RECT_F bounds;

hr = m_pCircleGeometry1->GetBounds(
      D2D1::IdentityMatrix(),
      &bounds
     );

if (SUCCEEDED(hr))
{
    // Retrieve the bounds.
}

GetWidenedBounds 方法检索几何图形的边界后,该几何图形按指定的笔划宽度和样式加宽并按指定矩阵进行转换。 下面的代码示例使用 GetWidenedBounds 检索指定圆 (m_pCircleGeometry1) 的边界后,指定笔划宽度扩大。

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

ComputePointAtLength 方法计算沿几何图形的指定距离处的点和切向量。 以下代码示例使用 ComputePointAtLength

D2D1_POINT_2F point;
D2D1_POINT_2F tangent;

hr = m_pCircleGeometry1->ComputePointAtLength(
    10, 
    NULL, 
    &point, 
    &tangent); 

路径几何图形概述

Direct2D 参考