幾何概觀
本概觀描述如何建立和使用 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 介面表示。 這些物件可用來描述由弧線、曲線和線條等線段組成的複雜幾何圖形。 下圖顯示使用路徑幾何建立的繪圖。
如需詳細資訊和範例,請參閱 路徑幾何概觀。
複合幾何
複合幾何是一個幾何群組或結合另一個 geometry 物件或轉換。 複合幾何包括 ID2D1TransformedGeometry 和 ID2D1GeometryGroup 物件。
幾何群組
幾何群組是一種方便的方式,可同時分組數個幾何,因此數個不同幾何的所有圖形都會串連成一個。 若要建立ID2D1GeometryGroup物件,請在ID2D1Factory物件上呼叫CreateGeometryGroup方法,並傳入fillMode,其中可能值為 D2D1_FILL_MODE_ALTERNATE (替代) 和D2D1_FILL_MODE_WINDING、要新增至 geometry 群組的 geometry 物件陣列,以及此陣列中的元素數目。
下列程式碼範例會先宣告 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 |
簡化 (會移除弧形和二次方 Bezier 曲線) | 簡化 |
鑲嵌 | 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列舉。
擴大
Widen方法會產生新的幾何,其填滿相當於製作現有幾何,然後將結果寫入指定的ID2D1SimplifiedGeometrySink物件。 下列程式碼範例會在ID2D1PathGeometry物件上呼叫Open。 如果 Open 成功,它會在 geometry 物件上呼叫 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.
}
簡化
Simplify方法會從指定的幾何中移除弧形和二次方 Bezier 曲線。 因此,產生的幾何只包含線條,並選擇性地包含三次方 Bezier 曲線。 下列程式碼範例使用 「簡化 」,將具有 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 完全包含指定的幾何,而 「重迭」表示兩個幾何重迭,但兩者都未完全包含另一個幾何。
下列程式碼範例示範如何比較兩個具有相同半徑 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);
相關主題