幾何概觀

本概觀描述如何建立和使用 ID2D1Geometry 物件來定義及操作 2D 圖表。 包含以下幾節。

什麼是 Direct2D 幾何?

Direct2D 幾何是 ID2D1Geometry 物件。 此物件可以是簡單的幾何 (ID2D1RectangleGeometryID2D1RoundedRectangleGeometryID2D1EllipseGeometry) 、路徑幾何 (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 介面表示。 這些物件可用來描述由弧線、曲線和線條等線段組成的複雜幾何圖形。 下圖顯示使用路徑幾何建立的繪圖。

水流、太陽和太陽的圖例

如需詳細資訊和範例,請參閱 路徑幾何概觀

複合幾何

複合幾何是一個幾何群組或結合另一個 geometry 物件或轉換。 複合幾何包括 ID2D1TransformedGeometryID2D1GeometryGroup 物件。

幾何群組

幾何群組是一種方便的方式,可同時分組數個幾何,因此數個不同幾何的所有圖形都會串連成一個。 若要建立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
界限/擴展界限/擷取界限、中途區域更新 WidenGetBoundsGetWidenedBounds
點擊測試 FillContainsPointStrokeContainsPoint
筆勢 StrokeContainsPoint
比較 CompareWithGeometry
簡化 (會移除弧形和二次方 Bezier 曲線) 簡化
鑲嵌 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 成功,它會在 geometry 物件上呼叫 Widen

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

Tessellate

Tessellate方法會建立一組順時針式三角形,該三角形會在使用指定的矩陣進行轉換後涵蓋幾何,並使用指定的容錯壓平。 下列程式碼範例會使用 Tessellate 來建立代表 pPathGeometry的三角形清單。 三角形會儲存在 ID2D1MeshpMesh中,然後傳送至類別成員 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); 

路徑幾何概觀

Direct2D 參考