Поделиться через


Обзор геометрий

В этом обзоре описывается, как создавать и использовать объекты ID2D1Geometry для определения и обработки двухd-фигур. В него входят следующие разделы.

Что такое геометрия 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-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 . Эти объекты можно использовать для описания сложных геометрических фигур, состоящих из сегментов, таких как дуги, кривые и линии. На следующем рисунке показан рисунок, созданный с помощью геометрии пути.

изображение реки, гор и солнца

Дополнительные сведения и примеры см. в статье Общие сведения о геометриях путей.

Составные геометрические объекты

Составная геометрия — это геометрия, сгруппированная или объединенная с другим геометрическим объектом или с преобразованием. Составные геометрические объекты включают объекты ID2D1TransformedGeometry и ID2D1GeometryGroup .

Группы геометрии

Геометрические группы — это удобный способ группирования нескольких геометрических объектов одновременно, поэтому все фигуры нескольких различных геометрических объектов объединяются в одну. Чтобы создать объект ID2D1GeometryGroup , вызовите метод CreateGeometryGroup в объекте ID2D1Factory , передав в fillMode возможные значения D2D1_FILL_MODE_ALTERNATE и D2D1_FILL_MODE_WINDING, массив объектов geometry для добавления в группу geometry и количество элементов в этом массиве.

В следующем примере кода сначала объявляется массив объектов geometry. Это четыре концентрических круга со следующими радиусами: 25, 50, 75 и 100. Затем вызовите CreateGeometryGroup для объекта ID2D1Factory , передав 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
        );
}

На следующем рисунке показаны результаты отрисовки двух геометрических объектов групп из примера.

Иллюстрация двух наборов из четырех концентрических кругов, один с чередующимися кольцами и один со всеми заполненными кольцами

Преобразованные геометрические объекты

Существует несколько способов преобразования геометрии. Вы можете использовать метод 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);

Геометрические объекты как маски

Объект ID2D1Geometry можно использовать в качестве геометрической маски при вызове метода PushLayer . Геометрическая маска задает область слоя, который состоит в целевом объекте отрисовки. Дополнительные сведения см. в разделе Геометрические маски статьи Общие сведения о слоях.

Геометрические операции

Интерфейс ID2D1Geometry предоставляет несколько геометрических операций, которые можно использовать для управления геометрическими фигурами и их измерения. Например, их можно использовать для вычисления и возврата границ, сравнения, чтобы увидеть, как одна геометрия пространственно связана с другой (полезно для проверки попадания), вычислять области и длины и многое другое. В следующей таблице описаны распространенные геометрические операции.

Операция Метод
Combine CombineWithGeometry
Bounds/ Widened Bounds/Retrieve Bounds, Dirty Region update Widen, GetBounds, GetWidenedBounds
Проверка нажатия FillContainsPoint, StrokeContainsPoint
Stroke StrokeContainsPoint
Сравнение CompareWithGeometry
Упрощение (удаляет дуги и квадратические кривые Безье) Упростить
Тесселяция Тесселлате
Структура (удалить пересечение) Контур
Вычисление области или длины геометрии ComputeArea, ComputeLength, ComputePointAtLength

 

Примечание

Начиная с Windows 8, вы можете использовать метод ComputePointAndSegmentAtLength в ID2D1PathGeometry1 для вычисления области или длины геометрии.

 

Объединение геометрических объектов

Чтобы объединить одну геометрию с другой, вызовите метод 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 . В следующем примере кода вызывается метод Open для объекта ID2D1PathGeometry . Если открыть успешно, он вызывает Widen для объекта geometry.

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

Тесселлате

Метод 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 указывает, содержит ли область, заполненная геометрическим объектом, указанную точку. Этот метод можно использовать для проверки попадания. В следующем примере кода вызывается метод FillContainsPoint для объекта ID2D1EllipseGeometry , передавая точку в (0,0) и матрицу удостоверений .

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 удаляет дуги и кривые Безье из указанной геометрии. Таким образом, результирующая геометрия содержит только линии и, при необходимости, кубические кривые Безье. В следующем примере кода используется упрощение для преобразования геометрии с кривыми Безье в геометрию, содержащую только сегменты линий.

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" означает, что геометрия полностью содержит указанную геометрию, а "перекрытие" означает, что две геометрии перекрываются, но ни один из этих элементов не полностью содержит другую.

В следующем примере кода показано, как сравнить два круга с одинаковым радиусом 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. В следующем примере кода структура используется для создания эквивалентной геометрии без каких-либо самосвязок. Он использует допустимое значение по умолчанию для плоских значений.

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