幾何概觀
本概觀說明如何建立和使用 ID2D1Geometry 對象來定義及作 2D 圖形。 其中包含下列各節。
什麼是 Direct2D 幾何?
Direct2D geometry 是 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 (alternate) 和 D2D1_FILL_MODE_WINDING,這是要新增至幾何群組的幾何物件陣列, 和這個陣列中的項目數目。
下列程式碼範例會先宣告幾何對象的陣列。 這些物件是具有下列弧度的四個同心圓:25、50、75 和 100。 然後在 ID2D1Factory 物件上呼叫 CreateGeometryGroup,傳入 D2D1_FILL_MODE_ALTERNATE、一個要加入到幾何群組的幾何物件陣列,以及此陣列中的元素數量。
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 方法將幾何物件縮放三倍,然後繪製出來。 其會產生下圖所示的輸出。 請注意,雖然矩形較大,但其筆劃並未增加。
// 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 介面提供幾種幾何操作,可用來操作和測量幾何圖形。 例如,您可以使用它們來計算和傳回其邊界、比較查看某個幾何與另一個幾何在空間上的關係(適用於碰撞檢測)、計算面積和周長等等。 下表描述常見的幾何作業。
操作 | 方法 |
---|---|
合併 | 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 列舉。
擴大
Widen 方法會產生一個新的幾何體,其填充效果等同於對現有幾何體進行描邊,然後將結果寫入指定的 ID2D1SimplifiedGeometrySink 物件。 下列程式代碼範例會在 ID2D1PathGeometry 物件上呼叫 Open。 如果 Open 成功,它就會在幾何物件上呼叫 Widen。
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.
}
簡化
簡化 方法會從指定的幾何中移除弧線和二次方貝塞爾曲線。 因此,產生的幾何只包含線條,並選擇性地包含立方貝塞爾曲線。 下列程式代碼範例會使用 簡化,將具有 Bezier 曲線的幾何轉換成只包含線條線段的幾何。
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(重疊)。 「不相交」表示兩個幾何填滿不會交集。 “is contained” 表示幾何完全由指定的幾何所包含。 “contains” 表示 geometry 完全包含指定的幾何,而 “overlap” 表示兩個幾何重疊,但兩者都沒有完全包含另一個幾何。
下列程式碼範例示範如何比較兩個具有相同半徑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);
相關主題