Panoramica delle geometrie

Questa panoramica descrive come creare e usare oggetti ID2D1Geometry per definire e modificare le figure 2D. Include le sezioni seguenti:

Che cos'è una geometria Direct2D?

Una geometria Direct2D è un oggetto ID2D1Geometry . Questo oggetto può essere una geometria semplice (ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry o ID2D1EllipseGeometry), una geometria del percorso (ID2D1PathGeometry) o una geometria composita (ID2D1GeometryGroup e ID2D1TransformedGeometry).

Le geometrie Direct2D consentono di descrivere figure bidimensionali e offrire molti usi, ad esempio la definizione di aree di hit test, aree clip e percorsi di animazione.

Le geometrie Direct2D sono non modificabili e le risorse indipendenti dal dispositivo create da ID2D1Factory. In genere, è necessario creare geometrie una volta e mantenerle per la vita dell'applicazione o fino a quando non devono essere modificate. Per altre informazioni sulle risorse dipendenti dal dispositivo e indipendenti dal dispositivo, vedere Panoramica delle risorse.

Le sezioni seguenti descrivono i diversi tipi di geometrie.

Geometrie semplici

Le geometrie semplici includono OGGETTI ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry e ID2D1EllipseGeometry e possono essere usati per creare figure geometriche di base, ad esempio rettangoli, rettangoli arrotondati, cerchi e puntini di sospensione.

Per creare una geometria semplice, usare uno dei metodi ID2D1Factory::CreategeometryTypeGeometry><. Questi metodi creano un oggetto del tipo specificato. Ad esempio, per creare un rettangolo, chiamare ID2D1Factory::CreateRectangleGeometry, che restituisce un oggetto ID2D1RectangleGeometry ; per creare un rettangolo arrotondato, chiamare ID2D1Factory::CreateRoundedRectangleGeometry, che restituisce un oggetto ID2D1RoundedRectangleGeometry e così via.

Nell'esempio di codice seguente viene chiamato il metodo CreateEllipseGeometry , passando una struttura di sospensione con il centro impostato su (100, 100), raggio x a 100 e raggio y a 50. Chiama quindi DrawGeometry, passando la geometria dei puntini di sospensione restituita, un puntatore a un ID2D1SolidColorBrush nero e una larghezza del tratto pari a 5. Nella figura seguente viene illustrato l'output dell'esempio di codice.

illustration of an ellipse

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);

Per disegnare la struttura di qualsiasi geometria, usare il metodo DrawGeometry . Per disegnare l'interno, usare il metodo FillGeometry .

Geometrie del percorso

Le geometrie del percorso sono rappresentate dall'interfaccia ID2D1PathGeometry . Questi oggetti possono essere usati per descrivere figure geometriche complesse composte da segmenti quali archi, curve e linee. La figura seguente mostra un disegno creato usando la geometria del percorso.

illustration of a river, mountains, and the sun

Per altre informazioni ed esempi, vedere Panoramica delle geometrie del percorso.

Geometrie composte

Una geometria composita è una geometria raggruppata o combinata con un altro oggetto geometry o con una trasformazione. Le geometrie composte includono oggetti ID2D1TransformedGeometry e ID2D1GeometryGroup .

Gruppi geometry

I gruppi geometry sono un modo pratico per raggruppare più geometrie contemporaneamente, in modo che tutte le figure di diverse geometrie distinte siano concatenate in una. Per creare un oggetto ID2D1GeometryGroup, chiamare il metodo CreateGeometryGroup nell'oggetto ID2D1Factory, passando i valori possibili di D2D1_FILL_MODE_ALTERNATE (alternativa) e D2D1_FILL_MODE_WINDING, una matrice di oggetti geometry da aggiungere al gruppo geometry e il numero di elementi in questa matrice.

Nell'esempio di codice seguente viene innanzitutto dichiarata una matrice di oggetti geometry. Questi oggetti sono quattro cerchi concentrici che hanno lo radii seguente: 25, 50, 75 e 100. Chiamare quindi CreateGeometryGroup nell'oggetto ID2D1Factory , passando in D2D1_FILL_MODE_ALTERNATE, una matrice di oggetti geometry da aggiungere al gruppo geometry e il numero di elementi in questa matrice.

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
        );
}

Nella figura seguente vengono illustrati i risultati del rendering delle due geometrie del gruppo dall'esempio.

illustration of two sets of four concentric circles, one with alternating rings filled and one with all rings filled

Geometrie trasformate

Esistono più modi per trasformare una geometria. È possibile usare il metodo SetTransform di una destinazione di rendering per trasformare tutto ciò che la destinazione di rendering disegna oppure è possibile associare una trasformazione direttamente a una geometria usando il metodo CreateTransformedGeometry per creare un ID2D1TransformedGeometry.

Il metodo da usare dipende dall'effetto desiderato. Quando si usa la destinazione di rendering per trasformare e quindi eseguire il rendering di una geometria, la trasformazione influisce su tutto ciò che riguarda la geometria, inclusa la larghezza di qualsiasi tratto applicato. D'altra parte, quando si usa un ID2D1TransformedGeometry, la trasformazione influisce solo sulle coordinate che descrivono la forma. La trasformazione non influisce sullo spessore del tratto quando viene disegnata la geometria.

Nota

A partire da Windows 8 la trasformazione mondiale non influisce sullo spessore del tratto di tratti con D2D1_STROKE_TRANSFORM_TYPE_FIXED or D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE. È consigliabile usare questi tipi di trasformazione per ottenere tratti indipendenti di trasformazione

 

L'esempio seguente crea un ID2D1RectangleGeometry, quindi lo disegna senza trasformarlo. Produce l'output illustrato nella figura seguente.

illustration of a rectangle

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);

Nell'esempio successivo viene usata la destinazione di rendering per ridimensionare la geometria in base a un fattore 3, quindi la disegna. La figura seguente mostra il risultato del disegno del rettangolo senza la trasformazione e con la trasformazione. Si noti che il tratto è più spesso dopo la trasformazione, anche se lo spessore del tratto è 1.

illustration of a smaller rectangle inside a larger rectangle with a thicker stroke

// 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);

Nell'esempio successivo viene usato il metodo CreateTransformedGeometry per ridimensionare la geometria in base a un fattore 3, quindi lo disegna. Produce l'output illustrato nella figura seguente. Si noti che, anche se il rettangolo è più grande, il suo tratto non è aumentato.

illustration of a smaller rectangle inside a larger rectangle with the same stroke thickness

 // 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);

Geometrie come maschere

È possibile usare un oggetto ID2D1Geometry come maschera geometrica quando si chiama il metodo PushLayer . La maschera geometrica specifica l'area del livello composita nella destinazione di rendering. Per altre informazioni, vedere la sezione Maschere geometriche della panoramica dei livelli.

Operazioni geometriche

L'interfaccia ID2D1Geometry offre diverse operazioni geometriche che è possibile usare per modificare e misurare le figure geometriche. Ad esempio, è possibile usarli per calcolare e restituire i relativi limiti, confrontare il modo in cui una geometria è correlata in modo spaziale a un'altra (utile per il hit test), calcolare le aree e le lunghezze e altro ancora. Nella tabella seguente vengono descritte le operazioni geometriche comuni.

Operazione Metodo
Combina CombineWithGeometry
Limiti/ Limiti ampliati/Recupero di limiti, aggiornamento dell'area dirty Estensione, GetBounds,GetWidenedBounds
Hit Testing FillContainsPoint, StrokeContainsPoint
Stroke StrokeContainsPoint
Confronto CompareWithGeometry
Semplificazione (rimuove gli archi e le curve quadratiche di Bezier) Semplificare
Suddivisione a mosaico Tessellate
Struttura (rimozione dell'intersezione) Riquadro
Calcolare l'area o la lunghezza di una geometria ComputeArea, ComputeLength, ComputePointAtLength

 

Nota

A partire da Windows 8, è possibile usare il metodo ComputePointAndSegmentAtLengthnell'ID2D1PathGeometry1 per calcolare l'area o la lunghezza di una geometria.

 

Combinazione di geometrie

Per combinare una geometria con un'altra, chiamare il metodo ID2D1Geometry::CombineWithGeometry . Quando si combinano le geometrie, si specifica uno dei quattro modi per eseguire l'operazione di combinazione: D2D1_COMBINE_MODE_UNION (unione), D2D1_COMBINE_MODE_INTERSECT (intersect), D2D1_COMBINE_MODE_XOR (xor) e D2D1_COMBINE_MODE_EXCLUDE (escludi). L'esempio di codice seguente mostra due cerchi combinati usando la modalità di combinazione unione, dove il primo cerchio ha il punto centrale (75, 75) e il raggio di 50 e il secondo cerchio ha il punto centrale (125, 75) e il raggio di 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);
    }
}

La figura seguente mostra due cerchi combinati con una modalità di unione.

illustration of two overlapping circles combined into a union

Per le illustrazioni di tutte le modalità di combinazione, vedere l'enumerazione D2D1_COMBINE_MODE.

Ampliare

Il metodo Widen genera una nuova geometria il cui riempimento equivale a strozzare la geometria esistente e quindi scrive il risultato nell'oggetto ID2D1SimplifiedGeometrySink specificato. Nell'esempio di codice seguente viene chiamato Open nell'oggetto ID2D1PathGeometry . Se Open ha esito positivo, chiama Widen nell'oggetto geometry.

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

Tessellate

Il metodo Tessellate crea un set di triangoli ferita in senso orario che coprono la geometria dopo la trasformazione usando la matrice specificata e appiattita usando la tolleranza specificata. L'esempio di codice seguente usa Tessellate per creare un elenco di triangoli che rappresentano pPathGeometry. I triangoli vengono archiviati in un ID2D1Mesh, pMesh, quindi trasferiti a un membro della classe, m_pStrokeMesh, per usarli successivamente durante il rendering.

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 e StrokeContainsPoint

Il metodo FillContainsPoint indica se l'area riempita dalla geometria contiene il punto specificato. È possibile usare questo metodo per eseguire hit testing. L'esempio di codice seguente chiama FillContainsPoint in un oggetto ID2D1EllipseGeometry , passando un punto in corrispondenza di (0,0) e una matrice Identity .

BOOL containsPoint1;
hr = m_pCircleGeometry1->FillContainsPoint(
    D2D1::Point2F(0,0),
    D2D1::Matrix3x2F::Identity(),
    &containsPoint1
    );

if (SUCCEEDED(hr))
{
    // Process containsPoint.
}

Il metodo StrokeContainsPoint determina se il tratto della geometria contiene il punto specificato. È possibile usare questo metodo per eseguire hit testing. Nell'esempio di codice seguente viene utilizzato 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.
}

Semplificare

Il metodo Simplify rimuove gli archi e le curve quadratiche di Bezier da una geometria specificata. Pertanto, la geometria risultante contiene solo linee e, facoltativamente, curve di Bézier cubiche. Nell'esempio di codice riportato di seguito viene usato l'istruzione Simplify per trasformare una geometria con curve di Bezier in una geometria che contiene solo segmenti di linea.

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 e ComputeArea

Il metodo ComputeLength calcola la lunghezza della geometria specificata se ogni segmento è stato eseguito il rollback in una linea. Ciò include il segmento di chiusura implicito se la geometria è chiusa. Nell'esempio di codice seguente viene usato ComputeLength per calcolare la lunghezza di un cerchio specificato (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.
}

Il metodo ComputeArea calcola l'area della geometria specificata. Nell'esempio di codice seguente viene usato ComputeArea per calcolare l'area di un cerchio specificato (m_pCircleGeometry1).

float area;

// Compute the area of circle1
hr = m_pCircleGeometry1->ComputeArea(
    D2D1::IdentityMatrix(),
    &area
    );

CompareWithGeometry

Il metodo CompareWithGeometry descrive l'intersezione tra la geometria che chiama questo metodo e la geometria specificata. I valori possibili per l'intersezione includono D2D1_GEOMETRY_RELATION_DISJOINT (disgiunto), D2D1_GEOMETRY_RELATION_IS_CONTAINED (è contenuto), D2D1_GEOMETRY_RELATION_CONTAINS (contiene) e D2D1_GEOMETRY_RELATION_OVERLAP (sovrapposizione). "disgiunto" significa che due riempimenti geometrici non si intersecano affatto. "è contenuto" significa che la geometria è completamente contenuta nella geometria specificata. "contains" indica che la geometria contiene completamente la geometria specificata e la "sovrapposizione" indica che le due geometrie si sovrappongono, ma nessuna delle due geometrie contiene completamente l'altra.

Nell'esempio di codice seguente viene illustrato come confrontare due cerchi con lo stesso raggio di 50 ma con offset di 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
            );
    }
}

Riquadro

Il metodo Outline calcola la struttura della geometria (una versione della geometria in cui nessuna figura si incrocia o nessun'altra figura) e scrive il risultato in un oggetto ID2D1SimplifiedGeometrySink. Nell'esempio di codice seguente viene usato Outline per costruire una geometria equivalente senza intersezioni autosezioni. Usa la tolleranza flat predefinita.

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 e GetWidenedBounds

Il metodo GetBounds recupera i limiti della geometria. Nell'esempio di codice seguente viene usato GetBounds per recuperare i limiti di un cerchio specificato (m_pCircleGeometry1).

D2D1_RECT_F bounds;

hr = m_pCircleGeometry1->GetBounds(
      D2D1::IdentityMatrix(),
      &bounds
     );

if (SUCCEEDED(hr))
{
    // Retrieve the bounds.
}

Il metodo GetWidenedBounds recupera i limiti della geometria dopo che viene ampliata in base alla larghezza e allo stile del tratto specificati e trasformati dalla matrice specificata. Nell'esempio di codice seguente viene utilizzato GetWidenedBounds per recuperare i limiti di un cerchio specificato (m_pCircleGeometry1) dopo che viene ampliato in base alla larghezza del tratto specificata.

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

Il metodo ComputePointAtLength calcola il punto e il vettore tangente alla distanza specificata lungo la geometria. Nell'esempio di codice seguente viene usato ComputePointAtLength.

D2D1_POINT_2F point;
D2D1_POINT_2F tangent;

hr = m_pCircleGeometry1->ComputePointAtLength(
    10, 
    NULL, 
    &point, 
    &tangent); 

Cenni preliminari sulle geometrie di percorso

Informazioni di riferimento su Direct2D