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


Отрисовка текста с помощью Direct2D и DirectWrite

В отличие от других API, таких как GDI, GDI+ или WPF, Direct2D взаимодействует с другим API , DirectWrite, для управления и отрисовки текста. В этом разделе описываются преимущества и взаимодействие этих отдельных компонентов.

Эта тема описана в следующих разделах.

Direct2D обеспечивает добавочное внедрение

Перемещение приложения из одного графического API в другой может быть сложным или не тем, что вы хотите по различным причинам. Это может быть связано с необходимостью поддерживать подключаемые модули, которые по-прежнему принимают старые интерфейсы, поскольку само приложение слишком велико, чтобы перенести его на новый API за один выпуск, или потому что некоторые части нового API являются желательными, но старый API достаточно хорошо работает для других частей приложения.

Так как Direct2D и DirectWrite реализованы в виде отдельных компонентов, вы можете обновить всю 2D-графическую систему или только текстовую часть. Например, можно обновить приложение для использования DirectWrite для текста, но по-прежнему использовать GDI или GDI + для отрисовки.

Службы текста и отрисовка текста

По мере развития приложений требования к обработке текста становятся все более сложными. Изначально текст обычно был ограничен статично оформленным пользовательским интерфейсом и отображался в четко определённой области, например в виде кнопки. Поскольку приложения стали доступны в растущем количестве языков, этот подход стал более сложным, поскольку ширина и высота переведенного текста могут значительно отличаться между языками. Чтобы адаптироваться, приложения начали динамически приспосабливать свой пользовательский интерфейс так, чтобы он зависел от фактического визуализированного размера текста, а не наоборот.

Чтобы помочь приложениям выполнить эту задачу, DirectWrite предоставляет интерфейс IDWriteTextLayout. Этот API позволяет приложению указать фрагмент текста со сложными характеристиками, такими как различные шрифты и размеры шрифтов, подчеркивания, зачеркиваемые фрагменты, двунаправленный текст, эффекты, многоточие и даже внедренные символы без глифа (например, растровое изображение или значок). Затем приложение может изменить различные характеристики текста, так как он итеративно определяет его макет пользовательского интерфейса. Пример DirectWrite Hello World, показанный на следующем рисунке и в руководстве по началу работы с DirectWrite , показывает многие из этих эффектов.

Снимок экрана примера

Макет может размещать глифы в идеале на основе их ширины (как это делает WPF), или он может привязать глифы к ближайшим позициям пикселей (как делает GDI ).

Помимо получения текстовых измерений, приложение может проводить проверку взаимодействия с различными частями текста. Например, может потребоваться знать, что гиперссылка в тексте щелкается. (Дополнительные сведения о тестировании попаданий см. в разделе Как выполнить тестирование на макете текста.)

Интерфейс макета текста отделен от API отрисовки, который использует приложение, как показано на следующей схеме:

схема текстового макета и графического API.

Это разделение возможно, так как DirectWrite предоставляет интерфейс отрисовки (IDWriteTextRenderer), который приложения могут реализовать для отрисовки текста с помощью любого нужного графического API. Приложение использует метод обратного вызова IDWriteTextRenderer::DrawGlyphRun, вызываемый DirectWrite при отрисовке текстового макета. Этот метод отвечает за выполнение операций рисования или их передачу дальше.

Для рисования глифов Direct2D предоставляет ID2D1RenderTarget::DrawGlyphRun для рисования на поверхность Direct2D, а DirectWrite предоставляет IDWriteBitmapRenderTarget::DrawGlyphRun для рисования в область GDI, которую затем можно передать в окно с помощью GDI. К счастью, DrawGlyphRun в Direct2D и DirectWrite имеют полностью совместимые параметры с методом DrawGlyphRun, который реализуется приложением в IDWriteTextRenderer.

После аналогичного разделения функции, относящиеся к тексту (например, перечисление шрифтов и управление, анализ глифов и т. д.), обрабатываются DirectWrite вместо Direct2D. Объекты DirectWrite принимаются Direct2D напрямую. Чтобы помочь существующим приложениям GDI воспользоваться преимуществами DirectWrite, он предоставляет интерфейс метода IDWriteGdiInterop с методами для выполнения следующих действий:

Глифы и текст

Текст представляет собой набор кодовых точек Юникода (символов), с различными стилистическими модификаторами (шрифтами, весами, подчеркиваниями, начерками и т. д.), которые размещаются в прямоугольнике. Глиф, напротив, представляет собой определенный индекс в определенный файл шрифта. Глиф определяет набор кривых, которые могут быть отрисованы, но не имеет никакого текстового значения. Возможно множественное сопоставление между глифами и символами. Последовательность глифов, поступающих из того же начертания шрифта и последовательно расположенных по базовой линии, называется GlyphRun. Как DirectWrite, так и Direct2D вызывают свой самый точный API для отрисовки глифов DrawGlyphRun, и они имеют очень похожие сигнатуры. Следующий текст взят из ID2D1RenderTarget в Direct2D:

STDMETHOD_(void, DrawGlyphRun)(
        D2D1_POINT_2F baselineOrigin,
        __in CONST DWRITE_GLYPH_RUN *glyphRun,
        __in ID2D1Brush *foregroundBrush,
        DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL 
        ) PURE;

И этот метод получен из IDWriteBitmapRenderTarget в DirectWrite:

STDMETHOD(DrawGlyphRun)(
        FLOAT baselineOriginX,
        FLOAT baselineOriginY,
        DWRITE_MEASURING_MODE measuringMode,
        __in DWRITE_GLYPH_RUN const* glyphRun,
        IDWriteRenderingParams* renderingParams,
        COLORREF textColor,
        __out_opt RECT* blackBoxRect = NULL
        ) PURE;

Версия DirectWrite сохраняет базовый источник, режим измерения и параметры выполнения глифа и включает дополнительные параметры.

DirectWrite также позволяет использовать пользовательский отрисовщик для глифов, реализуя интерфейс IDWriteTextRenderer. Этот интерфейс также имеет метод DrawGlyphRun , как показано в следующем примере кода.

STDMETHOD(DrawGlyphRun)(
        __maybenull void* clientDrawingContext,
        FLOAT baselineOriginX,
        FLOAT baselineOriginY,
        DWRITE_MEASURING_MODE measuringMode,
        __in DWRITE_GLYPH_RUN const* glyphRun,
        __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
        __maybenull IUnknown* clientDrawingEffect
        ) PURE;

Эта версия включает дополнительные параметры, которые полезны при реализации пользовательского отрисовщика текста. Окончательный параметр используется для пользовательских эффектов рисования, реализованных приложением. (Дополнительные сведения о эффектах рисования клиента см. в разделе Добавление эффектов рисования клиента в текстовый макет.

Каждый глиф запускается в источнике и помещается в строку, начиная с этого источника. Глифы изменяются текущим преобразованием мира и параметрами отрисовки выделенного текста в связанном целевом объекте отрисовки. Этот API обычно вызывается непосредственно приложениями, выполняющими собственный макет (например, обработчик Word) или приложением, реализующим интерфейс IDWriteTextRenderer .

DirectWrite и Direct2D

Direct2D предоставляет службы отрисовки уровня глифа с помощью DrawGlyphRun. Однако для этого требуется, чтобы приложение реализовало детали отрисовки, которое в основном воспроизводит функциональные возможности API DrawText из GDI самостоятельно.

Поэтому Direct2D предоставляет API, которые принимают текст вместо глифов: ID2D1RenderTarget::DrawTextLayout и ID2D1RenderTarget::DrawText. Оба метода отображаются в поверхности Direct2D. Для отрисовки на поверхности GDI используется метод IDWriteBitmapRenderTarget::DrawGlyphRun. Но этот метод требует, чтобы настраиваемый отрисовщик текста был реализован приложением. (Дополнительные сведения см. в разделе Отрисовка на поверхность GDI.)

Использование текста приложением обычно начинается просто: поместите ОК или Отмена на кнопку фиксированного макета, например. Однако с течением времени она становится более сложной, так как добавляются интернационализация и другие функции. В конечном итоге многим приложениям придется использовать объекты текстового макета DirectWrite и реализовать отрисовщик текста.

Таким образом, Direct2D предоставляет многоуровневые API, которые позволяют приложению начинать с простого и развивать более сложные возможности, не требуя возвращения к предыдущим шагам или отказа от существующего кода. Упрощенное представление показано на следующей схеме:

схема приложений directwrite и direct2d.

Отобразить текст

DrawText — это самый простой интерфейс API для использования. Он принимает строку Юникода, кисть переднего плана, один объект формата и прямоугольник назначения. Он выложит и отрисовывает всю строку в прямоугольнике макета и при необходимости закрепит ее. Это полезно, если вы помещаете простой фрагмент текста в часть пользовательского интерфейса фиксированного макета.

Рисование макета текста

Создав объект IDWriteTextLayout , приложение может начать измерять и упорядочивать текст и другие элементы пользовательского интерфейса, а также поддерживать несколько шрифтов, стилей, подчеркивания и зачеркивание. Direct2D предоставляет API DrawTextLayout , который напрямую принимает этот объект и отрисовывает текст в заданной точке. (Ширина и высота предоставляются объектом макета. Помимо реализации всех ожидаемых функций макета текста, Direct2D интерпретирует любой объект эффекта как кисть и применяет ее к выбранному диапазону глифов. Он также автоматически вызывает встроенные объекты. После этого приложение может вставить символы без глифов (значки) в текст, если это нужно. Еще одним преимуществом использования объекта текстового макета является то, что позиции глифа кэшируются в нем. Таким образом, большое повышение производительности возможно путем повторного использования одного объекта макета для нескольких вызовов рисования и предотвращения пересчета позиций глифов для каждого вызова. Эта возможность отсутствует для объекта DrawText GDI.

DrawGlyphRun

Наконец, приложение может реализовать сам интерфейс IDWriteTextRenderer и вызвать DrawGlyphRun и FillRectangle или любой другой API отрисовки. Все существующее взаимодействие с объектом "Макет текста" останется неизменным.

Пример реализации пользовательского отрисовщика текста см. в разделе "Отрисовка с помощью пользовательского отрисовщика текста".

Отрисовка глифа

Добавление DirectWrite в существующее приложение GDI позволяет приложению использовать API IDWriteBitmapRenderTarget для отрисовки глифов. Метод IDWriteBitmapRenderTarget::D rawGlyphRun, предоставляемый DirectWrite, будет отображаться в сплошном цвете в контроллер памяти, не требуя дополнительных API, таких как Direct2D.

Это позволяет приложению получать расширенные функции отрисовки текста, такие как:

  • Технология субпиксельного сглаживания ClearType позволяет приложению размещать глифы на субпиксельных позициях, чтобы обеспечить резкое отображение и корректную компоновку глифов.
  • Сглаживание по оси Y обеспечивает более плавную отрисовку кривых на крупных глифах.

Приложение, перемещающееся в Direct2D , также получит следующие функции:

  • Аппаратное ускорение.
  • Возможность заливки текста произвольной кистью Direct2D, такой как радиальные градиенты, линейные градиенты и растровые изображения.
  • Дополнительная поддержка слоёв и вырезки через PushAxisAlignedClip, PushLayer и CreateCompatibleRenderTarget API.
  • Возможность поддержки отрисовки текста в сером масштабе. Корректно заполняет целевой альфа-канал в соответствии с непрозрачностью кисти текста и сглаживанием текста.

Для эффективной поддержки аппаратного ускорения Direct2D использует немного другое приближение к гамма-коррекции, называемой альфа-коррекцией. Для этого не требуется Direct2D для проверки целевого пикселя цвета отрисовки при отрисовке текста.

Заключение

В этом разделе объясняется различия и сходство между Direct2D и DirectWrite и архитектурными мотивациями для предоставления их в качестве отдельных совместных API.