Отрисовка с использованием пользовательского средства отрисовки текста
Макет DirectWriteтекста может быть нарисован пользовательским отрисовщиком текста, производным от IDWriteTextRenderer. Пользовательский отрисовщик необходим для использования некоторых расширенных функций DirectWrite, таких как отрисовка в растровом рисунке или поверхности GDI, встроенные объекты и клиентские эффекты рисования. В этом руководстве описаны методы IDWriteTextRenderer и приведен пример реализации, в котором Direct2D используется для отрисовки текста с растровой заливкой.
Это руководство содержит следующие части:
- Конструктор
- DrawGlyphRun()
- DrawUnderline() и DrawStrikethrough()
- Привязка пикселей, пикселей на DIP и преобразование
- DrawInlineObject()
- Деструктор
- Использование пользовательского отрисовщика текста
Пользовательский отрисовщик текста должен реализовывать методы, унаследованные от IUnknown, в дополнение к методам, перечисленным на странице ссылки IDWriteTextRenderer и ниже.
Полный исходный код пользовательского отрисовщика текста см. в файлах CustomTextRenderer.cpp и CustomTextRenderer.h примера DirectWrite Hello World.
Конструктор
Для пользовательского отрисовщика текста потребуется конструктор. В этом примере для отрисовки текста используются как кисти Direct2D сплошной, так и точечный рисунок.
По этой причине конструктор принимает параметры, приведенные в таблице ниже, с описаниями.
Параметр | Описание |
---|---|
pD2DFactory | Указатель на объект ID2D1Factory , который будет использоваться для создания необходимых ресурсов Direct2D. |
Prt | Указатель на объект ID2D1HwndRenderTarget , в который будет отображаться текст. |
pOutlineBrush | Указатель на ID2D1SolidColorBrush , который будет использоваться для рисования контура текста |
pFillBrush | Указатель на ID2D1BitmapBrush , который будет использоваться для заполнения текста. |
Они будут храниться конструктором, как показано в следующем коде.
CustomTextRenderer::CustomTextRenderer(
ID2D1Factory* pD2DFactory,
ID2D1HwndRenderTarget* pRT,
ID2D1SolidColorBrush* pOutlineBrush,
ID2D1BitmapBrush* pFillBrush
)
:
cRefCount_(0),
pD2DFactory_(pD2DFactory),
pRT_(pRT),
pOutlineBrush_(pOutlineBrush),
pFillBrush_(pFillBrush)
{
pD2DFactory_->AddRef();
pRT_->AddRef();
pOutlineBrush_->AddRef();
pFillBrush_->AddRef();
}
DrawGlyphRun()
Метод DrawGlyphRun является main методом обратного вызова отрисовщика текста. В дополнение к таким сведениям, как источник базового плана и режим измерения, передается выполнение глифов, которые будут отображаться. Он также передает объект клиентского эффекта рисования для применения к выполнению глифа. Дополнительные сведения см. в разделе Добавление клиентских эффектов рисования в текстовый макет .
Эта реализация отрисовщика текста отрисовывает глифы, преобразуя их в геометрические объекты Direct2D , а затем рисуя и заполняя геометрические объекты. Это состоит из следующих шагов.
Создайте объект ID2D1PathGeometry , а затем получите объект ID2D1GeometrySink с помощью метода ID2D1PathGeometry::Open .
// Create the path geometry. ID2D1PathGeometry* pPathGeometry = NULL; hr = pD2DFactory_->CreatePathGeometry( &pPathGeometry ); // Write to the path geometry using the geometry sink. ID2D1GeometrySink* pSink = NULL; if (SUCCEEDED(hr)) { hr = pPathGeometry->Open( &pSink ); }
DWRITE_GLYPH_RUN, передаваемый в DrawGlyphRun, содержит объект IDWriteFontFace с именем fontFace, который представляет лицо шрифта для всего выполнения глифа. Поместите контур глифа в приемник geometry с помощью метода IDWriteFontFace:: GetGlyphRunOutline , как показано в следующем коде.
// Get the glyph run outline geometries back from DirectWrite and place them within the // geometry sink. if (SUCCEEDED(hr)) { hr = glyphRun->fontFace->GetGlyphRunOutline( glyphRun->fontEmSize, glyphRun->glyphIndices, glyphRun->glyphAdvances, glyphRun->glyphOffsets, glyphRun->glyphCount, glyphRun->isSideways, glyphRun->bidiLevel%2, pSink ); }
После заполнения приемника geometry закройте его.
// Close the geometry sink if (SUCCEEDED(hr)) { hr = pSink->Close(); }
Источник выполнения глифа должен быть преобразован таким образом, чтобы он отображался из правильного базового источника, как показано в следующем коде.
// Initialize a matrix to translate the origin of the glyph run. D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F( 1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY );
BaselineOriginX и baselineOriginY передаются в качестве параметров методу обратного вызова DrawGlyphRun.
Создайте преобразованную геометрию с помощью метода ID2D1Factory::CreateTransformedGeometry и передав геометрию пути и матрицу перевода.
// Create the transformed geometry ID2D1TransformedGeometry* pTransformedGeometry = NULL; if (SUCCEEDED(hr)) { hr = pD2DFactory_->CreateTransformedGeometry( pPathGeometry, &matrix, &pTransformedGeometry ); }
Наконец, нарисуйте контур преобразованной геометрии и заполните ее с помощью методов ID2D1RenderTarget::D rawGeometry и ID2D1RenderTarget::FillGeometry и кистей Direct2D , хранящихся в виде переменных-членов.
// Draw the outline of the glyph run pRT_->DrawGeometry( pTransformedGeometry, pOutlineBrush_ ); // Fill in the glyph run pRT_->FillGeometry( pTransformedGeometry, pFillBrush_ );
Теперь, когда рисование завершено, не забудьте очистить объекты, созданные в этом методе.
SafeRelease(&pPathGeometry); SafeRelease(&pSink); SafeRelease(&pTransformedGeometry);
DrawUnderline() и DrawStrikethrough()
IDWriteTextRenderer также имеет обратные вызовы для рисования подчеркивания и зачеркиния. В этом примере рисуется простой прямоугольник для подчеркивания или зачеркиния, но могут быть нарисованы и другие фигуры.
Рисование подчеркивания с помощью Direct2D состоит из следующих шагов.
Сначала создайте D2D1_RECT_F структуру размера и формы подчеркивания. Структура DWRITE_UNDERLINE , передаваемая методу обратного вызова DrawUnderline , обеспечивает смещение, ширину и толщину подчеркивания.
D2D1_RECT_F rect = D2D1::RectF( 0, underline->offset, underline->width, underline->offset + underline->thickness );
Затем создайте объект ID2D1RectangleGeometry с помощью метода ID2D1Factory::CreateRectangleGeometry и инициализированнойструктуры D2D1_RECT_F .
ID2D1RectangleGeometry* pRectangleGeometry = NULL; hr = pD2DFactory_->CreateRectangleGeometry( &rect, &pRectangleGeometry );
Как и при выполнении глифа, источник геометрии подчеркивания должен быть преобразован на основе базовых исходных значений с помощью метода CreateTransformedGeometry .
// Initialize a matrix to translate the origin of the underline D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F( 1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY ); ID2D1TransformedGeometry* pTransformedGeometry = NULL; if (SUCCEEDED(hr)) { hr = pD2DFactory_->CreateTransformedGeometry( pRectangleGeometry, &matrix, &pTransformedGeometry ); }
Наконец, нарисуйте контур преобразованной геометрии и заполните ее с помощью методов ID2D1RenderTarget::D rawGeometry и ID2D1RenderTarget::FillGeometry и кистей Direct2D , хранящихся в виде переменных-членов.
// Draw the outline of the glyph run pRT_->DrawGeometry( pTransformedGeometry, pOutlineBrush_ ); // Fill in the glyph run pRT_->FillGeometry( pTransformedGeometry, pFillBrush_ );
Теперь, когда рисование завершено, не забудьте очистить объекты, созданные в этом методе.
SafeRelease(&pRectangleGeometry); SafeRelease(&pTransformedGeometry);
Процесс рисования зачеркивки такой же. Однако зачеркивная будет иметь другое смещение и, скорее всего, другую ширину и толщину.
Привязка пикселей, пикселей на DIP и преобразование
IsPixelSnappingDisabled()
Этот метод вызывается для определения того, отключена ли привязка пикселей. Рекомендуемое значение по умолчанию — FALSE, и это выходные данные этого примера.
*isDisabled = FALSE;
GetCurrentTransform()
В этом примере выполняется отрисовка в целевой объект отрисовки Direct2D, поэтому пересылайте преобразование из целевого объекта отрисовки с помощью ID2D1RenderTarget::GetTransform.
//forward the render target's transform
pRT_->GetTransform(reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
GetPixelsPerDip()
Этот метод вызывается для получения количества пикселей на устройство независимого пикселя (DIP).
float x, yUnused;
pRT_->GetDpi(&x, &yUnused);
*pixelsPerDip = x / 96;
DrawInlineObject()
Пользовательский отрисовщик текста также имеет обратный вызов для рисования встроенных объектов. В этом примере DrawInlineObject возвращает E_NOTIMPL. Объяснение того, как рисовать встроенные объекты, выходит за область этого руководства. Дополнительные сведения см. в разделе Добавление встроенных объектов в текстовый макет .
Деструктор
Важно освободить все указатели, которые использовались классом пользовательского отрисовщика текста.
CustomTextRenderer::~CustomTextRenderer()
{
SafeRelease(&pD2DFactory_);
SafeRelease(&pRT_);
SafeRelease(&pOutlineBrush_);
SafeRelease(&pFillBrush_);
}
Использование пользовательского отрисовщика текста
Отрисовка выполняется с помощью пользовательского отрисовщика с помощью метода IDWriteTextLayout::D raw , который принимает интерфейс обратного вызова, производный от IDWriteTextRenderer , в качестве аргумента, как показано в следующем коде.
// Draw the text layout using DirectWrite and the CustomTextRenderer class.
hr = pTextLayout_->Draw(
NULL,
pTextRenderer_, // Custom text renderer.
origin.x,
origin.y
);
Метод IDWriteTextLayout::D raw вызывает методы пользовательского обратного вызова отрисовщика, который вы предоставляете. Описанные выше методы DrawGlyphRun, DrawUnderline, DrawInlineObject и DrawStrikethrough выполняют функции рисования.