次の方法で共有


ジオメトリの概要

この概要では、 ID2D1Geometry オブジェクトを作成して使用して 2D 図形を定義および操作する方法について説明します。 次のセクションが含まれています。

Direct2D ジオメトリとは

Direct2D ジオメトリは ID2D1Geometry オブジェクトです。 このオブジェクトには、単純なジオメトリ (ID2D1RectangleGeometryID2D1RoundedRectangleGeometryID2D1EllipseGeometry)、パス ジオメトリ (ID2D1PathGeometry)、複合ジオメトリ (ID2D1GeometryGroup および ID2D1TransformedGeometry) を指定できます。

Direct2D ジオメトリを使用すると、2 次元の図形を記述し、ヒット テスト領域、クリップ領域、さらにはアニメーション パスの定義など、多くの用途を提供できます。

Direct2D ジオメトリは、 ID2D1Factory によって作成された不変でデバイスに依存しないリソースです。 一般に、ジオメトリは 1 回作成し、アプリケーションの有効期間中、または変更が必要になるまで保持する必要があります。 デバイスに依存しないリソースとデバイスに依存するリソースの詳細については、「 リソースの概要」を参照してください。

以降のセクションでは、さまざまな種類のジオメトリについて説明します。

単純なジオメトリ

単純なジオメトリには 、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 インターフェイスによって表されます。 これらのオブジェクトを使用して、円弧、曲線、線などのセグメントで構成される複雑なジオメトリ 図形を記述できます。 次の図は、パス ジオメトリを使用して作成された図面を示しています。

川、山、太陽のイラスト

詳細と例については、「 パス ジオメトリの概要」を参照してください。

複合ジオメトリ

複合ジオメトリは、別のジオメトリ オブジェクトまたは変換によってグループ化または結合されたジオメトリです。 複合ジオメトリには 、ID2D1TransformedGeometry オブジェクトと ID2D1GeometryGroup オブジェクトが 含まれます。

ジオメトリ グループ

ジオメトリ グループは、複数のジオメトリを同時にグループ化する便利な方法であり、複数の異なるジオメトリのすべての図形が 1 つに連結されます。 ID2D1GeometryGroup オブジェクトを作成するには、ID2D1Factory オブジェクトに対して CreateGeometryGroup メソッドを呼び出し、fillModeD2D1_FILL_MODE_ALTERNATE (代替) とD2D1_FILL_MODE_WINDINGの使用可能な値、ジオメトリ グループに追加するジオメトリ オブジェクトの配列、およびこの配列内の要素の数を渡します。

次のコード例では、最初に geometry オブジェクトの配列を宣言します。 これらのオブジェクトは、半径が 25、50、75、100 の 4 つの同心円です。 次に、ID2D1Factory オブジェクトで CreateGeometryGroup を呼び出し、D2D1_FILL_MODE_ALTERNATE、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
        );
}

次の図は、この例の 2 つのグループ ジオメトリをレンダリングした結果を示しています。

4 つの同心円の 2 つのセットの図、1 つは塗りつぶされた交互のリングで、もう 1 つは塗りつぶされたすべてのリング

変換されたジオメトリ

ジオメトリを変換する方法は複数あります。 レンダー ターゲットの 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 インターフェイスには、幾何学図形の操作と測定に使用できる複数のジオメトリ操作が用意されています。 たとえば、それらを使用して境界を計算して返したり、あるジオメトリが別のジオメトリにどのように空間的に関連しているかを比較したり (ヒット テストに役立つ)、領域や長さを計算したりできます。 次の表では、一般的なジオメトリック演算について説明します。

オペレーション メソッド
組み合わせる CombineWithGeometry
境界/拡大境界/境界の取得、ダーティ リージョンの更新 WidenGetBoundsGetWidenedBounds
ヒット テスト FillContainsPointStrokeContainsPoint
卒中 StrokeContainsPoint
比較 CompareWithGeometry
簡略化 (円弧と 2 次ベジエ曲線を削除) 簡素 化
テッセレーション テッセレーション
アウトライン (交差部分を削除) 概要
ジオメトリの面積または長さを計算する ComputeAreaComputeLengthComputePointAtLength

 

Windows 8 以降では、ID2D1PathGeometry1ComputePointAndSegmentAtLength メソッドを使用して、ジオメトリの面積または長さを計算できます。

 

ジオメトリの組み合わせ

あるジオメトリを別のジオメトリと結合するには、 ID2D1Geometry::CombineWithGeometry メソッドを 呼び出します。 ジオメトリを結合するときは、結合操作を実行する方法として、D2D1_COMBINE_MODE_UNION (和集合)、D2D1_COMBINE_MODE_INTERSECT (交差)、D2D1_COMBINE_MODE_XOR (xor)、D2D1_COMBINE_MODE_EXCLUDE (除外) のいずれかを指定します。 次のコード例は、和集合結合モードを使用して結合される 2 つの円を示しています。1 つ目の円の中心点は (75, 75)、半径は 50、2 番目の円の中心点は (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);
    }
}

次の図は、集合体の結合モードで組み合わされた 2 つの円を表しています。

2 つの重なり合う円が集合体に結合した図

すべての結合モードの図については、 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

テッセレート メソッドは、指定したマトリックスを使用して変換し、指定した許容値を使用してフラット化した後、ジオメトリをカバーする時計回りの巻き付き三角形のセットを作成します。 次のコード例では 、テッセレーション を使用して 、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) と ID マトリックスを渡します。

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 メソッドは、指定したジオメトリから円弧と 2 次ベジエ曲線を削除します。 したがって、結果のジオメトリには、直線と、必要に応じて 3 次ベジエ曲線のみが含まれます。 次のコード例では 、[簡略化] を使用して、ベジエ曲線を持つジオメトリを、線分のみを含むジオメトリに変換します。

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 メソッドは、このメソッドを呼び出すジオメトリと指定したジオメトリの交差部分を記述します。 交差に使用できる値として、D2D1_GEOMETRY_RELATION_DISJOINT (disjoint)、 D2D1_GEOMETRY_RELATION_IS_CONTAINED (is contained)、D2D1_GEOMETRY_RELATION_CONTAINS (contains), and D2D1_GEOMETRY_RELATION_OVERLAP (overlap) が挙げられます。 "不整合" とは、2 つのジオメトリ フィルがまったく交差しないことを意味します。 "含まれている" とは、ジオメトリが指定されたジオメトリに完全に含まれていることを意味します。 "contains" はジオメトリが指定されたジオメトリを完全に含むことを意味し、"overlap" とは 2 つのジオメトリが重なっているが、どちらも完全に他方を含んでいないことを意味します。

次のコード例は、半径が 50 で、オフセットが 50 の 2 つの円を比較する方法を示しています。

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 リファレンス