ジオメトリの概要
この概要では、 ID2D1Geometry オブジェクトを作成して使用して 2D 図形を定義および操作する方法について説明します。 以下のセクションが含まれます。
Direct2D ジオメトリとは
Direct2D ジオメトリは ID2D1Geometry オブジェクトです。 このオブジェクトには、単純な geometry (ID2D1RectangleGeometry、 ID2D1RoundedRectangleGeometry、または ID2D1EllipseGeometry)、パス ジオメトリ (ID2D1PathGeometry)、複合ジオメトリ (ID2D1GeometryGroup と ID2D1TransformedGeometry) を指定できます。
Direct2D ジオメトリを使用すると、2 次元の図形を記述し、ヒット テスト領域、クリップ領域、さらにはアニメーション パスの定義など、多くの用途を提供できます。
Direct2D ジオメトリは、 ID2D1Factory によって作成された不変およびデバイスに依存しないリソースです。 一般に、ジオメトリは 1 回作成し、アプリケーションの有効期間中、または変更が必要になるまで保持する必要があります。 デバイスに依存しないリソースとデバイスに依存するリソースの詳細については、「 リソースの概要」を参照してください。
以降のセクションでは、さまざまな種類のジオメトリについて説明します。
単純なジオメトリ
単純なジオメトリには 、ID2D1RectangleGeometry、 ID2D1RoundedRectangleGeometry、 ID2D1EllipseGeometry オブジェクトが含まれており、四角形、丸い四角形、円、楕円などの基本的な幾何学的図形を作成するために使用できます。
単純なジオメトリを作成するには、 ID2D1Factory::Create<geometryType>Geometry メソッドのいずれかを使用します。 これらのメソッドは、指定した型のオブジェクトを作成します。 たとえば、四角形を作成するには、 ID2D1Factory::CreateRectangleGeometry を呼び出し、 ID2D1RectangleGeometry オブジェクトを返します。丸められた四角形を作成するには、 ID2D1Factory::CreateRoundedRectangleGeometry を呼び出し、 ID2D1RoundedRectangleGeometry オブジェクトを返します。
次のコード例では、 CreateEllipseGeometry メソッドを呼び出し、 中心 が (100、100)、 x-radius が 100、 y-radius が 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 オブジェクトが 含まれます。
ジオメトリ グループ
ジオメトリ グループは、複数のジオメトリを同時にグループ化する便利な方法であり、複数の異なるジオメトリのすべての図形が 1 つに連結されます。 ID2D1GeometryGroup オブジェクトを作成するには、ID2D1Factory オブジェクトで CreateGeometryGroup メソッドを呼び出し、fillMode をD2D1_FILL_MODE_ALTERNATE (代替) とD2D1_FILL_MODE_WINDING、geometry グループに追加する geometry オブジェクトの配列、およびこの配列内の要素の数を渡します。
次のコード例では、最初に geometry オブジェクトの配列を宣言します。 これらのオブジェクトは、半径が 25、50、75、100 の 4 つの同心円です。 次に、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
);
}
次の図は、この例の 2 つのグループ ジオメトリをレンダリングした結果を示しています。
変換されたジオメトリ
ジオメトリを変換する方法は複数あります。 レンダー ターゲットの SetTransform メソッドを使用してレンダー ターゲットが描画するすべてのものを変換するか、 CreateTransformedGeometry メソッドを使用して ID2D1TransformedGeometry を作成することで、変換をジオメトリに直接関連付けることができます。
使用する必要があるメソッドは、必要な効果によって異なります。 レンダー ターゲットを使用してジオメトリを変換してからレンダリングすると、適用したストロークの幅など、ジオメトリに関するすべてのものが変換に影響します。 一方、 ID2D1TransformedGeometry を使用すると、変換は図形を記述する座標にのみ影響します。 ジオメトリを描画しても、変換はストロークの太さに影響しません。
Note
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 |
Bounds/Widened Bounds/Retrieve Bounds, Dirty Region update | Widen、 GetBounds、 GetWidenedBounds |
ヒット テスト | FillContainsPoint、 StrokeContainsPoint |
ひと筆 | StrokeContainsPoint |
比較 | CompareWithGeometry |
簡略化 (円弧と 2 次ベジエ曲線を削除) | 簡素化 |
テセレーション | テセレート |
アウトライン (交差部分を削除) | 枠線 |
ジオメトリの面積または長さを計算する | ComputeArea、 ComputeLength、 ComputePointAtLength |
Note
Windows 8以降、ID2D1PathGeometry1 の ComputePointAndSegmentAtLength メソッドを使用して、ジオメトリの面積または長さを計算できます。
ジオメトリの組み合わせ
あるジオメトリを別のジオメトリと結合するには、 ID2D1Geometry::CombineWithGeometry メソッドを 呼び出します。 ジオメトリを結合する場合、結合操作を実行する 4 つの方法のいずれかを指定 します。D2D1_COMBINE_MODE_UNION (共用体)、 D2D1_COMBINE_MODE_INTERSECT (交差)、 D2D1_COMBINE_MODE_XOR (xor)、 D2D1_COMBINE_MODE_EXCLUDE (除外) です。 次のコード例は、結合モードを使用して結合される 2 つの円を示しています。最初の円の中心点は (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 つの円を示しています。
すべての結合モードの図については、「 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 を表す三角形のリストを作成します。 三角形は 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) のポイントと 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 メソッドは、各セグメントが 1 行にアンロールされた場合に、指定したジオメトリの長さを計算します。 これには、ジオメトリが閉じている場合の暗黙的な終了セグメントが含まれます。 次のコード例では 、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 (重なり) があります。 "不整合" とは、2 つのジオメトリの塗りつぶしがまったく交差しないことを意味します。 "is contained" とは、ジオメトリが指定されたジオメトリに完全に含まれていることを意味します。 "contains" は、ジオメトリに指定されたジオメトリが完全に含まれていることを意味し、"重なり" は 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);
関連トピック