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


Общие сведения о геометриях

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

Что такое геометрия 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 равным 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.

Геометрические группы

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

В следующем примере кода сначала объявляется массив геометрических объектов. Эти объекты являются четырьмя концентрическими кругами, имеющими следующие радии: 25, 50, 75 и 100. Затем вызовите CreateGeometryGroup на объекте ID2D1Factory, передавая D2D1_FILL_MODE_ALTERNATE, массив геометрических объектов, добавляемых в группу геометрии, и количество элементов в этом массиве.

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

Операция Метод
Сочетать СкомбинироватьСГеометрией
Границы/Расширенные границы/Получение границ, обновление грязной области Widen, GetBounds, GetWidenedBounds
Тестирование нажатий FillContainsPoint, StrokeContainsPoint
Удар StrokeContainsPoint
Сравнение СравнитьСГеометрией
Упрощение (удаляет дуги и квадратные кривые Bezier) упростить
Тесселяция Тесселлате
Контур (удаление пересечения) структуры
Вычисление области или длины геометрии ComputeArea, ComputeLength, ComputePointAtLength

 

Заметка

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

 

Объединение геометрий

Чтобы объединить одну геометрию с другой, вызовите метод ID2D1Geometry::CombineWithGeometry. При объединении геометрий можно указать один из четырех способов выполнения операции объединения: D2D1_COMBINE_MODE_UNION (объединение), D2D1_COMBINE_MODE_INTERSECT (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. Если Open выполнен успешно, он вызывает Widen на геометрическом объекте.

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) и матрицу 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.
}

Упрощать

Метод Упростить удаляет дуги и квадратичные кривые Безье из указанной геометрии. Таким образом, результирующая геометрия содержит только линии и, при необходимости, кубическую кривую 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 (перекрытие). «Непересекающиеся» означает, что две геометрические фигуры совсем не пересекаются. "содержится" означает, что геометрия полностью находится внутри указанной геометрии. "содержит" означает, что геометрия полностью содержит указанную геометрию, а "перекрывается" означает, что две геометрии перекрываются, но ни одна полностью не содержит другую.

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

D2D1_POINT_2F point;
D2D1_POINT_2F tangent;

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

Обзор геометрических форм пути

Справочник по Direct2D