几何概述
本概述介绍如何创建和使用 ID2D1Geometry 对象来定义和操作 2D 图形。 其中包含以下各节。
什么是 Direct2D 几何图形?
Direct2D 几何图形是 ID2D1Geometry 对象。 此对象可以是 ID2D1RectangleGeometry (简单几何图形, ID2D1RoundedRectangleGeometry 或 ID2D1EllipseGeometry) 、 (ID2D1PathGeometry) 的路径几何图形,或 (ID2D1GeometryGroup 和 ID2D1TransformedGeometry) 复合几何图形。
使用 Direct2D 几何图形可以描述二维图形并提供许多用途,例如定义命中测试区域、剪辑区域甚至动画路径。
Direct2D 几何图形是由 ID2D1Factory 创建的不可变且与设备无关的资源。 通常,应创建一次几何图形,并在应用程序的生命周期内保留它们,或者直到必须更改它们为止。 有关与设备无关和依赖于设备的资源的详细信息,请参阅 资源概述。
以下部分介绍不同类型的几何图形。
简单几何图形
简单几何图形包括 ID2D1RectangleGeometry、 ID2D1RoundedRectangleGeometry 和 ID2D1EllipseGeometry 对象,可用于创建基本几何图形,如矩形、圆角矩形、圆和椭圆。
若要创建简单的几何图形,请使用 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 接口表示。 这些对象可用于描述由弧线、曲线和线条等线段组成的复杂几何图形。 下图显示了使用路径几何图形创建的绘图。
有关详细信息和示例,请参阅 路径几何图形概述。
复合几何图形
复合几何图形是分组或与另一个几何对象组合的几何图形,或与转换组合的几何图形。 复合几何图形包括 ID2D1TransformedGeometry 和 ID2D1GeometryGroup 对象。
几何组
几何组是同时组合多个几何图形的便捷方法,因此多个不同几何图形的所有图形都连接成一个。 若要创建 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_FIXED或D2D1_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 |
边界/扩大边界/检索边界,脏区域更新 | Widen、 GetBounds、 GetWidenedBounds |
命中测试 | FillContainsPoint、 StrokeContainsPoint |
笔划 | StrokeContainsPoint |
比较 | CompareWithGeometry |
简化 (消除了弧线和二次贝塞尔曲线) | 简化 |
分割 | Tessellate |
大纲 (删除交集) | 大纲 |
计算几何图形的面积或长度 | ComputeArea、 ComputeLength、 ComputePointAtLength |
注意
从 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);
相关主题