Поддержка шрифта цвета (предварительная версия)
Примечание
Некоторые сведения относятся к предварительной версии продукта, в которую перед коммерческим выпуском могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В этом разделе описываются цветовые шрифты, их поддержка в DirectWrite и Direct2D и их использование в приложении. Дополнительные сведения см . в статье о поддержке шрифтов Color.
Самый простой способ включить цвет — вызвать метод DrawGlyphRunWithColorSupport вместо DrawGlyphRun. Direct2D предоставляет этот метод через интерфейс ID2D1DeviceContext7 . DirectWrite предоставляет его через интерфейс IDWriteBitmapRenderTarget3 .
На более низком уровне включение цвета включает преобразование монохромного базового глифа в последовательность выполнения глифа цвета. Затем каждое выполнение глифа цвета должно быть отрисовывается с помощью соответствующего метода в зависимости от формата изображения глифа. Перевод выполняется методом TranslateColorGlyphRun объекта фабрики. Он и связанные API описаны в следующих разделах.
Метод фабрики TranslateColorGlyphRun преобразует монохромный базовый глиф в последовательность выполнения глифа цвета. Существует несколько способов представления глифов цвета, известных как форматы изображения глифа и определяемые перечислением DWRITE_GLYPH_IMAGE_FORMATS . При вызове TranslateColorGlyphRun укажите сочетание поддерживаемых форматов изображений глифа. TranslateColorGlyphRun возвращает объект перечислителя glyph run (интерфейс IDWriteColorGlyphRunEnumerator1), который используется для итерации по последовательности выполнения глифа цвета, представленной структурой DWRITE_COLOR_GLYPH_RUN1. Каждый запуск глифа цвета имеет формат изображения глифа. Ветвь в формате глифа цветового запуска, чтобы определить, как отрисовка цвета выполняется.
Если в базовом глифе нет глифов цвета в любом из запрошенных форматов изображения глифа, метод TranslateColorGlyphRun возвращает DWRITE_E_NOCOLOR, в этом случае необходимо отобразить базовый глиф в виде монохромного глифа.
В противном случае выполнение базового глифа может содержать сочетание цветных глифов в разных форматах изображений глифов, а также монохромных глифов. Для каждого базового глифа метод TranslateColorGlyphRun определяет доступный формат изображения глифа (при наличии), который лучше всего соответствует запрошенным форматам. Выполнение базового глифа разделено на подранги на основе соответствующего формата изображения глифа. Глифы, которые не поддерживают любой из запрошенных форматов, рассматриваются как монохромные глифы.
Форматы изображения глифа делятся на четыре общие категории:
- Монохромные глифы (DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE или DWRITE_GLYPH_IMAGE_FORMATS_CFF)
- Слои сплошных глифов (DWRITE_GLYPH_IMAGE_FORMATS_COLR)
- Растровые изображения цвета, внедренные в шрифт (DWRITE_GLYPH_IMAGE_FORMATS_PNG, DWRITE_GLYPH_IMAGE_FORMATS_JPEG, DWRITE_GLYPH_IMAGE_FORMATS_TIFF и DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8)
- Векторная графика, закодированная в шрифте как SVG (DWRITE_GLYPH_IMAGE_FORMATS_SVG)
- Векторная графика как дерево элементов краски (DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE)
В следующих подразделах описывается каждый из этих подходов.
Форматы изображений глифов DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE и DWRITE_GLYPH_IMAGE_FORMATS_CFF представляют монохромные глифы, которые можно отобразить с помощью метода DrawGlyphRun. Два формата соответствуют различным способам представления контуров глифа в шрифте. Перечисленная DWRITE_COLOR_GLYPH_RUN1 структура может указать эти форматы по одной из двух причин:
- Один или несколько базовых глифов не поддерживают ни один из запрошенных форматов изображения глифа, поэтому их следует отображать как монохромные глифы.
- Лучший формат изображения глифа для глифа был DWRITE_GLYPH_IMAGE_FORMATS_COLR. Глифы в этом формате изображения отображаются путем перечисления монохромного запуска в указанном цвете для каждого слоя, как описано в следующем разделе.
В первом случае необходимо отобразить перечисленную глифу, выполняемую с помощью кисти переднего плана, определяемой приложением. Это указывает, задав элемент палитрыIndex структуры DWRITE_COLOR_GLYPH_RUN DWRITE_NO_PALETTE_INDEX (0xFFFF). Если элемент палитрыIndex имеет любое другое значение, необходимо отобразить текст в указанном цвете.
Значение DWRITE_GLYPH_IMAGE_FORMATS_COLR указывает цветной формат, поддерживающий версию 0 таблицы OpenType COLR. В этом формате глиф цвета отрисовывается путем рисования нескольких монохромных глифов на вершине друг друга, каждый из которых имеет указанный цвет.
Шаблон вызова, используемый с этим форматом изображения, отличается от всех других форматов изображений глифа. Это связано с тем, что то, что получает отрисовку, наконец, просто ряд обычных, монохромных глифов работает — только с различными идентификаторами глифов и разными цветами, чем были указаны первоначально. Так как перечисленные цветовые глифы отрисовываются как обычные монохромные глифы, их форматы изображения глифа являются либо DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE, либо DWRITE_GLYPH_IMAGE_FORMATS_CFF.
Например, предположим, что запуск базового глифа указывает глиф смайлик лица. Если бы мы отрисовывали базовый глиф с помощью DrawGlyphRun, то визуальный результат будет монохромным представлением смайлика (идентификатор базового глифа). Таблица COLR в шрифте сопоставляет этот базовый идентификатор глифа с массивом записей слоев, каждый из которых задает идентификатор глифа замены и индекс в определяемую шрифтом цветовую палитру. Объект IDWriteColorGlyphRunEnumerator, возвращаемый translateColorGlyphRun, перечисляет один глиф цвета для каждой из этих записей слоя. Каждый запуск глифа цвета имеет позицию, а также один идентификатор глифа (идентификатор замены глифа для слоя) и цвет (производный от индекса записи палитры). Вы нарисуете перечисляемый глиф цвета в указанных цветах по указанным координатам, чтобы получить нужный визуальный результат. В примере смайлик лица, который может состоять из заполненного круг глифа, отображаемого желтым цветом, за которым следует глиф глаза и рот, отрисованный черным.
Перечисление DWRITE_GLYPH_IMAGE_FORMATS содержит значения для нескольких форматов растровых изображений: PNG, JPEG, TIFF и предварительно заданный формат BGRA32. Шрифт может представлять глифы цвета, внедряя растровые изображения в любой из этих форматов в шрифте. Шрифт может включать растровые изображения или несколько размеров для поддержки различных размеров шрифтов и масштабирования DPI; хотя это происходит с очевидной стоимостью с точки зрения размера файла.
Если приложение запрашивает форматы изображений растрового изображения, то TranslateColorGlyphRun проверяет, имеет ли шрифт цветовые растровые изображения для указанных входных глифов в указанных форматах. Если это так, то перечисленные цветовые глифы указывают идентификаторы базовых глифов и соответствующий формат изображения. В этом случае идентификаторы глифов замены отсутствуют, как и с DWRITE_GLYPH_IMAGE_FORMATS_COLR. Однако выполнение базового глифа по-прежнему может быть разделено на несколько цветов, если в разных входных глифах имеются цветовые растровые изображения в разных форматах изображений; или если некоторые глифы имеют цветовые представления, а другие — только монохромные.
Приложение с помощью Direct2D может отрисовывать перечисленные цветовые глифы в этих форматах путем вызова метода ID2DeviceContext3::D rawColorBitmapGlyphRun .
Приложение нижнего уровня может вызывать IDWriteFontFace3::GetGlyphImageData , чтобы получить необработанные данные растрового изображения для каждого глифа в возвращаемом формате изображения. Затем приложение отвечает за декодирование и отрисовку растровых изображений. Внутренняя реализация Direct2D в DrawColorBitmapGlyphRun вызывает GetGlyphImageData , чтобы получить растровые изображения для каждого глифа.
Значение DWRITE_GLYPH_IMAGE_FORMATS_SVG указывает формат изображения глифа, в котором цветные глифы представлены как векторные рисунки, внедренные в шрифт в формате масштабируемой векторной графики (SVG). Внедренный SVG может храниться с сжатием gzip.
Шаблон вызова, используемый с SVG, аналогичен формату растрового изображения и в отличие от шаблона вызова для DWRITE_GLYPH_IMAGE_FORMATS_COLR. Если приложение запрашивает формат SVG и указанные идентификаторы глифов имеют представления SVG, то перечисленные глифы цвета указывают идентификаторы базовых глифов и формат изображения SVG.
Приложение, использующее Direct2D, может отображать перечисленные цветовые глифы в этом формате, вызвав метод ID2DeviceContext3::D rawSvgGlyphRun .
Приложение нижнего уровня может вызывать IDWriteFontFace3::GetGlyphImageData , чтобы получить необработанные данные SVG для каждого глифа. Затем приложение отвечает за синтаксический анализ и отрисовку самого SVG. Внутренняя реализация Direct2D в DrawSvgGlyphRun вызывает GetGlyphImageData , чтобы получить данные SVG для каждого глифа.
В таблице OpenType COLR версии 1 цветовые глифы представлены деревом элементов краски. Существуют различные типы элементов краски для геометрий (представленных как глифы), преобразования, твердые и градиентные заливки и композиция. Формат DWRITE_GLYPH_IMAGE_FORMATS_COLR поддерживает слои только глифы сплошного цвета поверх друг друга. Поэтому для поддержки COLR версии 1 и больше требуется другой формат изображения глифа, и это DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE. При использовании этого формата изображения глифа приложение также должно указать максимальный уровень возможностей краски (DWRITE_PAINT_FEATURE_LEVEL), который он поддерживает. Это позволяет поддерживать будущие версии таблицы COLR без необходимости определять дополнительные форматы изображений глифов.
Шаблон вызова, используемый с этим форматом изображения глифа, аналогичен тому, что используется с SVG. Если приложение запрашивает формат COLR-paint, а указанные идентификаторы глифов имеют представления COLR, то перечисленные цветные глифы указывают базовые идентификаторы глифов и формат изображения COLR-paint.
Приложение, использующее Direct2D, может отображать перечисленные цветовые глифы в этом формате, вызвав метод ID2DeviceContext7::D rawPaintGlyphRun . Приложение с помощью целевого API отображения растрового изображения DirectWrite может вызывать IDWriteBitmapRenderTarget3::D rawPaintGlyphRun.
Приложение нижнего уровня может использовать API цветовой краски, описанные в следующем разделе, чтобы получить дерево элементов краски для каждого глифа и отрисовывать его. Реализации Direct2D и DirectWrite в DrawPaintGlyphRun используют эти API нижнего уровня.
Глиф в формате изображения DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE представлен как дерево элементов краски. Спецификация таблицы OpenType COLR описывает направленный ациклический граф записей краски, но подграф для определенного глифа всегда является деревом. Приложение может использовать объект чтения краски (интерфейс IDWritePaintReader ) для чтения дерева элементов краски для глифа.
Каждый элемент краски имеет тип краски (перечисление DWRITE_PAINT_TYPE). Существуют различные типы краски для графических элементов, таких как геометрии (представленные как глифы), преобразования и твердые и градиентные заливки. Методы чтения краски возвращают сведения о элементе краски путем заполнения DWRITE_PAINT_ELEMENT структуры, которая состоит из типа краски и объединения.
Дополнительные типы краски могут быть добавлены в будущих версиях спецификации COLR. Чтобы разрешить это, приложение должно указать максимальный уровень возможностей краски (DWRITE_PAINT_FEATURE_LEVEL), который он поддерживает при создании объекта чтения краски или при вызове TranslateColorGlyphRun. Возвращаются только элементы краски типов, поддерживаемых указанным уровнем признаков, методами чтения краски. Кроме того, методы, которые принимают выходной параметр типа DWRITE_PAINT_ELEMENT также принимают параметр размера, так как размер структуры может измениться при определении дополнительных типов краски.
Алгоритм отрисовки цветового глифа — рекурсивно отрисовывать элементы краски, каждый из которых соответствует его типу. Определены следующие типы краски:
Тип краски | Действие отрисовки |
---|---|
DWRITE_PAINT_TYPE_NONE | Никаких действий |
DWRITE_PAINT_TYPE_LAYERS | Отрисовка дочерних элементов краски в нижнем порядке. |
DWRITE_PAINT_TYPE_SOLID_GLYPH | Заполните указанную фигуру глифа указанным цветом. |
DWRITE_PAINT_TYPE_SOLID | Заполните текущий клип указанным цветом. |
DWRITE_PAINT_TYPE_LINEAR_GRADIENT | Заполните текущий клип указанным градиентом. |
DWRITE_PAINT_TYPE_RADIAL_GRADIENT | Заполните текущий клип указанным градиентом. |
DWRITE_PAINT_TYPE_SWEEP_GRADIENT | Заполните текущий клип указанным градиентом. |
DWRITE_PAINT_TYPE_GLYPH | Заполните указанную фигуру глифа дочерним элементом краски. |
DWRITE_PAINT_TYPE_COLOR_GLYPH | Отрисовка дочернего элемента краски. |
DWRITE_PAINT_TYPE_TRANSFORM | Отрисовка дочернего элемента краски с указанным преобразованием. |
DWRITE_PAINT_TYPE_COMPOSITE | Отрисовка двух дочерних элементов краски и их создание с помощью указанного составного режима. |
Дополнительные сведения о типах краски см. в спецификации таблицы OpenType COLR. Некоторые типы краски в API DirectWrite соответствуют нескольким форматам краски в таблице COLR. Это связано с тем, что API скрывает некоторые сложности, такие как различия в том, как некоторые элементы краски кодируются в переменных и не переменных шрифтов.
Некоторые из приведенных ниже примеров кода используют типы (например, ComPtr) из библиотеки шаблонов C++ среда выполнения Windows C++ (WRL). C++/WinRT является рекомендуемой заменой WRL корпорации Майкрософт.
Другие примеры кода используют библиотеки реализации Windows (WIL). Удобный способ установки WIL — перейти в Visual Studio, щелкните "Управление пакетами NuGet"...>>Просмотрите, введите или вставьте Microsoft.Windows.ImplementationLibrary в поле поиска, выберите элемент в результатах поиска и нажмите кнопку "Установить", чтобы установить пакет для этого проекта.
В этом разделе показан пример метода TextRenderer::D rawGlyphRun, реализующего метод DrawGlyphRun интерфейса IDWriteTextRenderer. Интерфейс обратного вызова IDWriteTextRenderer используется idWriteTextLayout::D raw для отображения глифов, подчеркивания и т. д. Использование интерфейса обратного вызова позволяет API макета текста отделить от конкретного графического модуля. Например, можно реализовать IDWriteTextRenderer с точки зрения Direct2D или с точки зрения IDWriteBitmapRenderTarget или другим способом. В этом примере используется Direct2D.
Пример включает только метод DrawGlyphRun . Остальная часть класса TextRenderer опущена для краткости, но в примере предполагается, что класс реализует интерфейс IDWriteTextRenderer и имеет следующие переменные-члены:
wil::com_ptr<IDWriteFactory4> m_dwriteFactory;
wil::com_ptr<ID2D1DeviceContext4> m_deviceContext;
wil::com_ptr<ID2D1Brush> m_foregroundBrush;
Пример метода отрисовывает глифы цвета одним из двух способов. Простой способ — вызвать ID2D1DeviceContext7::D rawGlyphRunWithColorSupport. Однако в примере также содержится резервный путь кода, если доступная версия Direct2D не реализует интерфейс ID2D1DeviceContext7 . Резервный путь кода использует TranslateColorGlyphRun.
// Implementation of IDWriteTextRenderer::DrawGlyphRun.
HRESULT STDMETHODCALLTYPE TextRenderer::DrawGlyphRun(
_In_opt_ void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
_In_ DWRITE_GLYPH_RUN const* glyphRun,
_In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
_In_opt_ IUnknown* clientDrawingEffect
) noexcept
{
D2D_POINT_2F baselineOrigin{ baselineOriginX, baselineOriginY };
// If a brush is specified via the clientDrawingEffect parameter, then use it as
// the foreground brush for this glyph run. Otherwise, use the brush specified
// by the m_foregroundBrush member.
wil::com_ptr<ID2D1Brush> foregroundBrush;
if (clientDrawingEffect != nullptr)
{
foregroundBrush = wil::try_com_query<ID2D1Brush>(clientDrawingEffect);
}
if (foregroundBrush == nullptr)
{
foregroundBrush = m_foregroundBrush;
}
// If available, use the ID2D1DeviceContext7 interface to draw
// the glyph run with color the "easy" way.
if (auto deviceContext7 = m_deviceContext.try_query<ID2D1DeviceContext7>())
{
// Note: Direct2D drawing methods return void. If a drawing
// operation fails, then an error is returned by EndDraw.
deviceContext7->DrawGlyphRunWithColorSupport(
baselineOrigin,
glyphRun,
foregroundBrush.get(),
measuringMode,
/*colorPaletteIndex*/ 0
);
return S_OK;
}
// If we fall through to here, then we're using an older version of
// Direct2D that doesn't implement the ID2D1DeviceContext7 interface.
// We'll need to call TranslateColorGlyphRun. First determine the
// world to device transform.
D2D_MATRIX_3X2_F transform;
m_deviceContext->GetTransform(&transform);
float dpiX, dpiY;
m_deviceContext->GetDpi(&dpiX, &dpiY);
transform = transform * D2D1::Matrix3x2F::Scale(dpiX, dpiY);
// Combination of image formats we support. Do *not* include
// DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE, because this code
// path is for an older version of Direct2D which doesn't support
// the ID2D1DeviceContext7 interface.
constexpr DWRITE_GLYPH_IMAGE_FORMATS desiredGlyphImageFormats =
DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE |
DWRITE_GLYPH_IMAGE_FORMATS_CFF |
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
DWRITE_GLYPH_IMAGE_FORMATS_SVG |
DWRITE_GLYPH_IMAGE_FORMATS_PNG |
DWRITE_GLYPH_IMAGE_FORMATS_JPEG |
DWRITE_GLYPH_IMAGE_FORMATS_TIFF |
DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
// Perform color translation.
wil::com_ptr<IDWriteColorGlyphRunEnumerator1> colorRunEnumerator;
HRESULT hr = m_dwriteFactory->TranslateColorGlyphRun(
baselineOrigin,
glyphRun,
glyphRunDescription,
desiredGlyphImageFormats,
measuringMode,
reinterpret_cast<DWRITE_MATRIX const*>(&transform),
/*colorPaletteIndex*/ 0,
/*out*/ & colorRunEnumerator
);
// Handle DWRITE_E_NOCOLOR, which is returned if the base glyph run doesn't
// have any color glyphs in the desired glyph image formats. In that case,
// just render the base glyph run as a monochrome glyph run.
if (hr == DWRITE_E_NOCOLOR)
{
// Note: Direct2D drawing methods return void. If a drawing
// operation fails, then an error is returned by EndDraw.
m_deviceContext->DrawGlyphRun(
baselineOrigin,
glyphRun,
foregroundBrush.get(),
measuringMode
);
return S_OK;
}
// Any other failure HRESULT from TranslateColorGlyphRun is an error.
RETURN_IF_FAILED(hr);
// Solid color brush to be lazily created if needed.
wil::com_ptr<ID2D1SolidColorBrush> solidBrush;
// Use the returned enumerator object to iterate over the color glyph runs.
for (;;)
{
// Advanced to the first or next run.
BOOL haveRun;
RETURN_IF_FAILED(colorRunEnumerator->MoveNext(&haveRun));
if (!haveRun)
break;
// Retrieve a pointer to the color glyph run structure.
DWRITE_COLOR_GLYPH_RUN1 const* colorGlyphRun;
RETURN_IF_FAILED(colorRunEnumerator->GetCurrentRun(&colorGlyphRun));
// Determine the brush to use for this color glyph run.
wil::com_ptr<ID2D1Brush> runBrush;
if (colorGlyphRun->paletteIndex == DWRITE_NO_PALETTE_INDEX)
{
// Special palette index meaning use the text foreground brush.
runBrush = foregroundBrush;
}
else
{
// Use the specified color from the font's color palette.
// Lazily create the solid color brush, or set its color if already created.
if (solidBrush == nullptr)
{
RETURN_IF_FAILED(m_deviceContext->CreateSolidColorBrush(
colorGlyphRun->runColor,
&solidBrush
));
}
else
{
solidBrush->SetColor(colorGlyphRun->runColor);
}
// Use the solid color brush as the current run's brush.
runBrush = solidBrush;
}
// Branch depending on the run's glyph image format.
switch (colorGlyphRun->glyphImageFormat)
{
case DWRITE_GLYPH_IMAGE_FORMATS_NONE:
// do nothing
break;
case DWRITE_GLYPH_IMAGE_FORMATS_PNG:
case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8:
// Note: Direct2D drawing methods return void. If a drawing
// operation fails, then an error is returned by EndDraw.
m_deviceContext->DrawColorBitmapGlyphRun(
colorGlyphRun->glyphImageFormat,
baselineOrigin,
&colorGlyphRun->glyphRun,
colorGlyphRun->measuringMode,
D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT
);
break;
case DWRITE_GLYPH_IMAGE_FORMATS_SVG:
// Note: Direct2D drawing methods return void. If a drawing
// operation fails, then an error is returned by EndDraw.
m_deviceContext->DrawSvgGlyphRun(
baselineOrigin,
&colorGlyphRun->glyphRun,
runBrush.get(),
/*SVG style*/ nullptr,
/*colorPaletteIndex*/ 0,
colorGlyphRun->measuringMode
);
break;
default:
// Treat any other format as a monochrome glyph run.
// This is used for monochrome glyphs, or for each layer
// of a glyph in DWRITE_GLYPH_IMAGE_FORMATS_COLR.
m_deviceContext->DrawGlyphRun(
baselineOrigin,
&colorGlyphRun->glyphRun,
colorGlyphRun->glyphRunDescription,
runBrush.get(),
colorGlyphRun->measuringMode
);
break;
}
}
return S_OK;
}
Глиф цвета в формате DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE представлен в виде визуального дерева графических элементов, называемых элементами краски. В этом разделе показано, как использовать интерфейс IDWritePaintReader для обхода дерева элементов краски для глифа.
Пример функции DumpPaintTree в этом разделе не отображает глиф цвета, но выводит текстовое представление дерева краски. Ниже приведен пример выходных данных:
- glyphIndex: 35
- clipBox: { 0, -1, 0.5, -0.5 }
- attributes: DWRITE_PAINT_ATTRIBUTES_USES_PALETTE
- paintTree:
DWRITE_PAINT_TYPE_COMPOSITE:
- mode: DWRITE_COLOR_COMPOSITE_SRC_OVER
- children (source, destination):
DWRITE_PAINT_TYPE_SOLID_GLYPH:
- glyphIndex: 84
- color: { 0.501961, 0.501961, 0.501961, 0.400024 }
DWRITE_PAINT_TYPE_COLOR_GLYPH:
- glyphIndex: 88
- clipBox: { 0.1, -0.9, 0.9, -0.1 }
- child:
DWRITE_PAINT_TYPE_COLOR_GLYPH:
- glyphIndex: 20
- clipBox: { 0, -1, 1, -0 }
- child:
DWRITE_PAINT_TYPE_GLYPH:
- glyphIndex: 82
- child:
DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
- center0: (0.166, -0.768)
- radius0: 0
- center1: (0.166, -0.768)
- radius1: 0.256
- extendMode: D2D1_EXTEND_MODE_MIRROR (2)
- gradientStops:
D2D1_GRADIENT_STOP:
- position: 0
- color: { 0, 0.501961, 0, 1 }
D2D1_GRADIENT_STOP:
- position: 0.5
- color: { 1, 1, 1, 1 }
D2D1_GRADIENT_STOP:
- position: 1
- color: { 1, 0, 0, 1 }
Для удобства в примере определяются вспомогательные типы отступа и PropName с соответствующими операторами вывода потока. Он также определяет операторы вывода потока для различных типов API. Эти вспомогательные типы и операторы отображаются в конце этого раздела.
Функция DumpPaintTree создает объект IDWritePaintReader , задает текущий глиф, выводит свойства глифа, а затем вызывает рекурсивную функцию DumpPaintElement для вывода дерева элементов краски.
void DumpPaintTree(std::ostream& out, IDWriteFontFace7* fontFace, uint32_t glyphIndex)
{
wil::com_ptr<IDWritePaintReader> paintReader;
THROW_IF_FAILED(fontFace->CreatePaintReader(
DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
DWRITE_PAINT_FEATURE_LEVEL_COLR_V1,
&paintReader
));
DWRITE_PAINT_ELEMENT paintElement;
D2D_RECT_F clipBox;
DWRITE_PAINT_ATTRIBUTES attributes;
THROW_IF_FAILED(paintReader->SetCurrentGlyph(glyphIndex, &paintElement, &clipBox, &attributes));
out << PropName{ "glyphIndex" } << glyphIndex << '\n'
<< PropName{ "clipBox" } << clipBox << '\n'
<< PropName{ "attributes" } << attributes << '\n'
<< PropName{ "paintTree" } << '\n';
DumpPaintElement(out, Indent{ 1 }, paintReader.get(), paintElement);
}
Рекурсивная функция DumpPaintElement выводит описание указанного элемента краски и его дочерних элементов. Структура DWRITE_PAINT_ELEMENT — это объединение с тегами. Ветвь DumpPaintElement на элементе paintType , чтобы определить, что нужно написать.
void DumpPaintElement(std::ostream& out, Indent indent, IDWritePaintReader* reader, DWRITE_PAINT_ELEMENT& element)
{
// Helper to write gradient information, for gradient paint types.
auto WriteGradient = [&](D2D1_EXTEND_MODE extendMode, uint32_t gradientStopCount)
{
out << indent << PropName{ "extendMode" } << extendMode << '\n';
out << indent << PropName{ "gradientStops" } << '\n';
DumpGradientStops(out, indent + 1, reader, gradientStopCount);
};
// Helper to recursively call DumpPaintElement for a child paint element.
auto Recurse = [&]()
{
DumpPaintElement(out, indent + 1, reader, element);
};
// Helper to write the specified number of children.
// The number of children is specified by the caller, because it depends on
// the paint type. See the documentation for the DWRITE_PAINT_ELEMENT structure
// for more information.
auto WriteChildren = [&](uint32_t childCount)
{
if (childCount != 0)
{
HR(reader->MoveToFirstChild(&element));
Recurse();
for (uint32_t i = 1; i < childCount; i++)
{
HR(reader->MoveToNextSibling(&element));
Recurse();
}
HR(reader->MoveToParent());
}
};
// Write information about the paint element, depending on its type.
switch (element.paintType)
{
case DWRITE_PAINT_TYPE_NONE:
out << indent << "DWRITE_PAINT_TYPE_NONE\n";
break;
case DWRITE_PAINT_TYPE_LAYERS:
{
out << indent << "DWRITE_PAINT_TYPE_LAYERS:\n";
auto const& paint = element.paint.layers;
// Write the children.
// A layers paint element has a variable number of children.
WriteChildren(paint.childCount);
break;
}
case DWRITE_PAINT_TYPE_SOLID_GLYPH:
{
auto const& paint = element.paint.solidGlyph;
// Write the properties.
// A solid glyph paint element has no children.
out << indent << "DWRITE_PAINT_TYPE_SOLID_GLYPH:\n"
<< indent << PropName{ "glyphIndex" } << paint.glyphIndex << '\n'
<< indent << PropName{ "color" } << paint.color.value << '\n';
break;
}
case DWRITE_PAINT_TYPE_SOLID:
{
auto const& paint = element.paint.solid;
// Write the properties.
// A solid paint element has no children.
out << indent << "DWRITE_PAINT_TYPE_SOLID:\n"
<< indent << PropName{ "color" } << paint.value << '\n';
break;
}
case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
{
auto const& paint = element.paint.linearGradient;
// Write the properties, including gradient stops.
// A linear gradient paint element has no children.
out << indent << "DWRITE_PAINT_TYPE_LINEAR_GRADIENT:\n"
<< indent << PropName{ "p0" } << D2D_POINT_2F{ paint.x0, paint.y0 } << '\n'
<< indent << PropName{ "p1" } << D2D_POINT_2F{ paint.x1, paint.y1 } << '\n'
<< indent << PropName{ "p2" } << D2D_POINT_2F{ paint.x2, paint.y2 } << '\n';
WriteGradient(static_cast<D2D1_EXTEND_MODE>(paint.extendMode), paint.gradientStopCount);
break;
}
case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
{
auto const& paint = element.paint.radialGradient;
// Write the properties, including gradient stops.
// A radial gradient paint element has no children.
out << indent << "DWRITE_PAINT_TYPE_RADIAL_GRADIENT:\n"
<< indent << PropName{ "center0" } << D2D_POINT_2F{ paint.x0, paint.y0 } << '\n'
<< indent << PropName{ "radius0" } << paint.radius0 << '\n'
<< indent << PropName{ "center1" } << D2D_POINT_2F{ paint.x1, paint.y1 } << '\n'
<< indent << PropName{ "radius1" } << paint.radius1 << '\n';
WriteGradient(static_cast<D2D1_EXTEND_MODE>(paint.extendMode), paint.gradientStopCount);
break;
}
case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
{
auto const& paint = element.paint.sweepGradient;
// Write the properties, including gradient stops.
// A sweep gradient paint element has no children.
out << indent << "DWRITE_PAINT_TYPE_SWEEP_GRADIENT:\n"
<< indent << PropName{ "center" } << D2D_POINT_2F{ paint.centerX, paint.centerY } << '\n'
<< indent << PropName{ "startAngle" } << paint.startAngle << '\n'
<< indent << PropName{ "endAngle" } << paint.endAngle << '\n';
WriteGradient(static_cast<D2D1_EXTEND_MODE>(paint.extendMode), paint.gradientStopCount);
break;
}
case DWRITE_PAINT_TYPE_GLYPH:
{
auto const& paint = element.paint.glyph;
// Write the properties and the child element.
// A glyph paint element always has one child, which represents the fill
// for the glyph shape.
out << indent << "DWRITE_PAINT_TYPE_GLYPH:\n"
<< indent << PropName{ "glyphIndex" } << paint.glyphIndex << '\n'
<< indent << PropName{ "child" } << '\n';
WriteChildren(1);
break;
}
case DWRITE_PAINT_TYPE_COLOR_GLYPH:
{
auto const& paint = element.paint.colorGlyph;
// Write the properties and the child element.
// A color glyph paint element always has one child, which is the root
// of the paint tree for the glyph specified by glyphIndex.
out << indent << "DWRITE_PAINT_TYPE_COLOR_GLYPH:\n"
<< indent << PropName{ "glyphIndex" } << paint.glyphIndex << '\n'
<< indent << PropName{ "clipBox" } << paint.clipBox << '\n'
<< indent << PropName{ "child" } << '\n';
WriteChildren(1);
break;
}
case DWRITE_PAINT_TYPE_TRANSFORM:
{
DWRITE_MATRIX const& paint = element.paint.transform;
// Write the properties and the child element.
// A transform paint element always has one child, which represents the
// transformed content.
out << indent << "DWRITE_PAINT_TYPE_TRANSFORM:\n"
<< indent << PropName{ "transform" } << paint << '\n'
<< indent << PropName{ "child" } << '\n';
WriteChildren(1);
break;
}
case DWRITE_PAINT_TYPE_COMPOSITE:
{
auto const& paint = element.paint.composite;
// Write the properties and the child elements.
// A composite paint element always has two children, which represent
// the source and destination of the composite operation.
out << indent << "DWRITE_PAINT_TYPE_COMPOSITE:\n"
<< indent << PropName{ "mode" } << paint.mode << '\n'
<< indent << PropName{ "children (source, destination)" } << '\n';
WriteChildren(2);
break;
}
default:
out << indent << "Unknown paint type: " << element.paintType << '\n';
break;
}
}
Функция DumpPaintElement вызывает следующую функцию DumpGradientStops для вывода массива остановок градиента для элемента paint:
void DumpGradientStops(std::ostream& out, Indent indent, IDWritePaintReader* reader, uint32_t gradientStopCount)
{
std::vector<D2D1_GRADIENT_STOP> stops;
stops.resize(gradientStopCount);
HR(reader->GetGradientStops(0, gradientStopCount, /*out*/ stops.data()));
for (auto& stop : stops)
{
out << indent << "D2D1_GRADIENT_STOP:\n"
<< indent << PropName{ "position" } << stop.position << '\n'
<< indent << PropName{ "color" } << stop.color << '\n';
}
}
Вспомогательный тип отступа представляет уровень отступа . Его связанный оператор потока выводит четыре пробела для каждого уровня отступа:
struct Indent { int value; };
std::ostream& operator<<(std::ostream& out, Indent const& indent)
{
for (int i = 0; i < indent.value; i++)
{
out << " ";
}
return out;
}
Indent operator+(Indent lhs, int rhs)
{
return Indent{ lhs.value + rhs };
}
Вспомогательный тип PropName представляет имя свойства:
struct PropName { char const* name; };
std::ostream& operator<<(std::ostream& out, PropName const& value)
{
return out << " - " << value.name << ": ";
}
Для удобства в примере используются следующие операторы вывода потока:
std::ostream& operator<<(std::ostream& out, DWRITE_COLOR_F const& value)
{
return out << "{ " << value.r << ", " << value.g << ", " << value.b << ", " << value.a << " }";
}
std::ostream& operator<<(std::ostream& out, D2D_POINT_2F const& value)
{
return out << '(' << value.x << ", " << value.y << ')';
}
std::ostream& operator<<(std::ostream& out, D2D_RECT_F const& value)
{
return out << "{ "
<< value.left << ", "
<< value.top << ", "
<< value.right << ", "
<< value.bottom
<< " }";
}
std::ostream& operator<<(std::ostream& out, DWRITE_MATRIX const& value)
{
return out << "{ "
<< value.m11 << ", "
<< value.m12 << ", "
<< value.m21 << ", "
<< value.m22 << ", "
<< value.dx << ", "
<< value.dy << " }";
}
std::ostream& operator<<(std::ostream& out, D2D1_EXTEND_MODE value)
{
switch (value)
{
case D2D1_EXTEND_MODE_CLAMP: return out << "D2D1_EXTEND_MODE_CLAMP (0)";
case D2D1_EXTEND_MODE_WRAP: return out << "D2D1_EXTEND_MODE_WRAP (1)";
case D2D1_EXTEND_MODE_MIRROR: return out << "D2D1_EXTEND_MODE_MIRROR (2)";
default: return out << (int)value;
}
}
std::ostream& operator<<(std::ostream& out, DWRITE_PAINT_ATTRIBUTES value)
{
switch (value)
{
case DWRITE_PAINT_ATTRIBUTES_USES_PALETTE:
return out << "DWRITE_PAINT_ATTRIBUTES_USES_PALETTE";
case DWRITE_PAINT_ATTRIBUTES_USES_TEXT_COLOR:
return out << "DWRITE_PAINT_ATTRIBUTES_USES_TEXT_COLOR";
case DWRITE_PAINT_ATTRIBUTES_USES_PALETTE | DWRITE_PAINT_ATTRIBUTES_USES_TEXT_COLOR:
return out << "DWRITE_PAINT_ATTRIBUTES_USES_PALETTE | DWRITE_PAINT_ATTRIBUTES_USES_TEXT_COLOR";
default:
return out << (int)value;
}
}
std::ostream& operator<<(std::ostream& out, DWRITE_COLOR_COMPOSITE_MODE value)
{
switch (value)
{
case DWRITE_COLOR_COMPOSITE_CLEAR: return out << "DWRITE_COLOR_COMPOSITE_CLEAR";
case DWRITE_COLOR_COMPOSITE_SRC: return out << "DWRITE_COLOR_COMPOSITE_SRC";
case DWRITE_COLOR_COMPOSITE_DEST: return out << "DWRITE_COLOR_COMPOSITE_DEST";
case DWRITE_COLOR_COMPOSITE_SRC_OVER: return out << "DWRITE_COLOR_COMPOSITE_SRC_OVER";
case DWRITE_COLOR_COMPOSITE_DEST_OVER: return out << "DWRITE_COLOR_COMPOSITE_DEST_OVER";
case DWRITE_COLOR_COMPOSITE_SRC_IN: return out << "DWRITE_COLOR_COMPOSITE_SRC_IN";
case DWRITE_COLOR_COMPOSITE_DEST_IN: return out << "DWRITE_COLOR_COMPOSITE_DEST_IN";
case DWRITE_COLOR_COMPOSITE_SRC_OUT: return out << "DWRITE_COLOR_COMPOSITE_SRC_OUT";
case DWRITE_COLOR_COMPOSITE_DEST_OUT: return out << "DWRITE_COLOR_COMPOSITE_DEST_OUT";
case DWRITE_COLOR_COMPOSITE_SRC_ATOP: return out << "DWRITE_COLOR_COMPOSITE_SRC_ATOP";
case DWRITE_COLOR_COMPOSITE_DEST_ATOP: return out << "DWRITE_COLOR_COMPOSITE_DEST_ATOP";
case DWRITE_COLOR_COMPOSITE_XOR: return out << "DWRITE_COLOR_COMPOSITE_XOR";
case DWRITE_COLOR_COMPOSITE_PLUS: return out << "DWRITE_COLOR_COMPOSITE_PLUS";
case DWRITE_COLOR_COMPOSITE_SCREEN: return out << "DWRITE_COLOR_COMPOSITE_SCREEN";
case DWRITE_COLOR_COMPOSITE_OVERLAY: return out << "DWRITE_COLOR_COMPOSITE_OVERLAY";
case DWRITE_COLOR_COMPOSITE_DARKEN: return out << "DWRITE_COLOR_COMPOSITE_DARKEN";
case DWRITE_COLOR_COMPOSITE_LIGHTEN: return out << "DWRITE_COLOR_COMPOSITE_LIGHTEN";
case DWRITE_COLOR_COMPOSITE_COLOR_DODGE: return out << "DWRITE_COLOR_COMPOSITE_COLOR_DODGE";
case DWRITE_COLOR_COMPOSITE_COLOR_BURN: return out << "DWRITE_COLOR_COMPOSITE_COLOR_BURN";
case DWRITE_COLOR_COMPOSITE_HARD_LIGHT: return out << "DWRITE_COLOR_COMPOSITE_HARD_LIGHT";
case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT: return out << "DWRITE_COLOR_COMPOSITE_SOFT_LIGHT";
case DWRITE_COLOR_COMPOSITE_DIFFERENCE: return out << "DWRITE_COLOR_COMPOSITE_DIFFERENCE";
case DWRITE_COLOR_COMPOSITE_EXCLUSION: return out << "DWRITE_COLOR_COMPOSITE_EXCLUSION";
case DWRITE_COLOR_COMPOSITE_MULTIPLY: return out << "DWRITE_COLOR_COMPOSITE_MULTIPLY";
case DWRITE_COLOR_COMPOSITE_HSL_HUE: return out << "DWRITE_COLOR_COMPOSITE_HSL_HUE";
case DWRITE_COLOR_COMPOSITE_HSL_SATURATION: return out << "DWRITE_COLOR_COMPOSITE_HSL_SATURATION";
case DWRITE_COLOR_COMPOSITE_HSL_COLOR: return out << "DWRITE_COLOR_COMPOSITE_HSL_COLOR";
case DWRITE_COLOR_COMPOSITE_HSL_LUMINOSITY: return out << "DWRITE_COLOR_COMPOSITE_HSL_LUMINOSITY";
default: return out << (int)value;
}
}