Добавление клиентских эффектов рисования в текстовый макет
Краткое руководство по добавлению клиентских эффектов рисования в приложение DirectWrite, которое отображает текст с помощью интерфейса IDWriteTextLayout и пользовательского отрисовщика текста.
Конечным продуктом этого учебника является приложение, которое отображает текст с диапазонами текста с разными цветными эффектами рисования, как показано на следующем снимке экрана.
Примечание
Это руководство предназначено для упрощения создания пользовательских клиентских эффектов рисования, а не простого метода рисования цветного текста. Дополнительные сведения см. на странице справочника IDWriteTextLayout::SetDrawingEffect .
Это руководство содержит следующие части:
- Шаг 1. Создание макета текста
- Шаг 2. Реализация класса пользовательского эффекта рисования
- Шаг 3. Реализация класса пользовательского отрисовщика текста
- Шаг 4. Создание отрисовщика текста
- Шаг 5. Создание экземпляра объектов цветного эффекта рисования
- Шаг 6. Настройка эффекта рисования для определенных текстовых диапазонов
- Шаг 7. Рисование макета текста с помощью пользовательского отрисовщика
- Шаг 8. Очистка
Шаг 1. Создание макета текста
Для начала потребуется приложение с объектом IDWriteTextLayout . Если у вас уже есть приложение, отображающее текст с текстовым макетом, или вы используете пример пользовательского кода DrawingEffect, перейдите к шагу 2.
Чтобы добавить текстовый макет, выполните следующие действия.
Объявите указатель на интерфейс IDWriteTextLayout в качестве члена класса .
IDWriteTextLayout* pTextLayout_;
В конце метода CreateDeviceIndependentResources создайте объект интерфейса IDWriteTextLayout , вызвав метод CreateTextLayout .
// Create a text layout using the text format. if (SUCCEEDED(hr)) { RECT rect; GetClientRect(hwnd_, &rect); float width = rect.right / dpiScaleX_; float height = rect.bottom / dpiScaleY_; hr = pDWriteFactory_->CreateTextLayout( wszText_, // The string to be laid out and formatted. cTextLength_, // The length of the string. pTextFormat_, // The text format to apply to the string (contains font information, etc). width, // The width of the layout box. height, // The height of the layout box. &pTextLayout_ // The IDWriteTextLayout interface pointer. ); }
Наконец, не забудьте освободить макет текста в деструкторе.
SafeRelease(&pTextLayout_);
Шаг 2. Реализация класса пользовательского эффекта рисования
Помимо методов, унаследованных от IUnknown, настраиваемый интерфейс клиентского эффекта рисования не имеет требований к тому, что он должен реализовать. В этом случае класс ColorDrawingEffect просто содержит значение D2D1_COLOR_F и объявляет методы для получения и задания этого значения, а также конструктор, который может задать цвет изначально.
После применения эффекта рисования клиента к текстовому диапазону в объекте IDWriteTextLayout эффект рисования передается в метод IDWriteTextRenderer::D rawGlyphRun любого отрисовки глифа. Методы эффекта рисования становятся доступными для отрисовщика текста.
Клиентский эффект рисования может быть настолько сложным, насколько вы хотите сделать его, и содержать больше информации, чем в этом примере, а также предоставлять методы для изменения глифов, создания объектов, которые будут использоваться для рисования, и т. д.
Шаг 3. Реализация класса пользовательского отрисовщика текста
Чтобы воспользоваться преимуществами клиентского эффекта рисования, необходимо реализовать пользовательский отрисовщик текста. Этот отрисовщик текста будет применять эффект рисования, переданный ему методом IDWriteTextLayout::D raw , к отрисовываемого выполнения глифа.
Конструктор
Конструктор пользовательского отрисовщика текста хранит объект ID2D1Factory , который будет использоваться для создания объектов Direct2D , и целевой объект отрисовки Direct2D, на котором будет нарисован текст.
CustomTextRenderer::CustomTextRenderer(
ID2D1Factory* pD2DFactory,
ID2D1HwndRenderTarget* pRT
)
:
cRefCount_(0),
pD2DFactory_(pD2DFactory),
pRT_(pRT)
{
pD2DFactory_->AddRef();
pRT_->AddRef();
}
Метод DrawGlyphRun
Выполнение глифа — это набор глифов, которые имеют одинаковый формат, включая эффект рисования клиента. Метод DrawGlyphRun отвечает за отрисовку текста для указанного выполнения глифа.
Сначала создайте ID2D1PathGeometry и ID2D1GeometrySink, а затем получите структуру запуска глифа с помощью IDWriteFontFace::GetGlyphRunOutline. Затем преобразуйте источник геометрии с помощью метода Direct2DID2D1Factory::CreateTransformedGeometry , как показано в следующем коде.
HRESULT hr = S_OK;
// 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
);
}
// 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
);
}
// 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
);
// Create the transformed geometry
ID2D1TransformedGeometry* pTransformedGeometry = NULL;
if (SUCCEEDED(hr))
{
hr = pD2DFactory_->CreateTransformedGeometry(
pPathGeometry,
&matrix,
&pTransformedGeometry
);
}
Затем объявите объект сплошной кисти Direct2D .
ID2D1SolidColorBrush* pBrush = NULL;
Если параметр clientDrawingEffect не имеет значения NULL, запросите к объекту интерфейс ColorDrawingEffect . Это будет работать, так как этот класс будет задан в качестве клиентского эффекта рисования для текстовых диапазонов объекта макета текста.
Получив указатель на интерфейс ColorDrawingEffect , можно получить значение D2D1_COLOR_F , которое он хранит, с помощью метода GetColor . Затем используйте D2D1_COLOR_F , чтобы создать ID2D1SolidColorBrush в этом цвете.
Если параметр clientDrawingEffect имеет значение NULL, просто создайте черный ID2D1SolidColorBrush.
// If there is a drawing effect create a color brush using it, otherwise create a black brush.
if (clientDrawingEffect != NULL)
{
// Go from IUnknown to ColorDrawingEffect.
ColorDrawingEffect *colorDrawingEffect;
clientDrawingEffect->QueryInterface(__uuidof(ColorDrawingEffect), reinterpret_cast<void**>(&colorDrawingEffect));
// Get the color from the ColorDrawingEffect object.
D2D1_COLOR_F color;
colorDrawingEffect->GetColor(&color);
// Create the brush using the color specified by our ColorDrawingEffect object.
if (SUCCEEDED(hr))
{
hr = pRT_->CreateSolidColorBrush(
color,
&pBrush);
}
SafeRelease(&colorDrawingEffect);
}
else
{
// Create a black brush.
if (SUCCEEDED(hr))
{
hr = pRT_->CreateSolidColorBrush(
D2D1::ColorF(
D2D1::ColorF::Black
),
&pBrush);
}
}
Наконец, нарисуйте геометрию контура и заполните ее с помощью только что созданной кисти сплошного цвета.
if (SUCCEEDED(hr))
{
// Draw the outline of the glyph run
pRT_->DrawGeometry(
pTransformedGeometry,
pBrush
);
// Fill in the glyph run
pRT_->FillGeometry(
pTransformedGeometry,
pBrush
);
}
Деструктор
Не забудьте освободить фабрику Direct2D и отобразить целевой объект в деструкторе.
CustomTextRenderer::~CustomTextRenderer()
{
SafeRelease(&pD2DFactory_);
SafeRelease(&pRT_);
}
Шаг 4. Создание отрисовщика текста
В ресурсах CreateDeviceDependent создайте пользовательский объект отрисовщика текста. Он зависит от устройства, так как использует ID2D1RenderTarget, который сам по себе зависит от устройства.
// Create the text renderer
pTextRenderer_ = new CustomTextRenderer(
pD2DFactory_,
pRT_
);
Шаг 5. Создание экземпляра объектов цветного эффекта рисования
Создайте экземпляры объектов ColorDrawingEffect красного, зеленого и синего цветов.
// Instantiate some custom color drawing effects.
redDrawingEffect_ = new ColorDrawingEffect(
D2D1::ColorF(
D2D1::ColorF::Red
)
);
blueDrawingEffect_ = new ColorDrawingEffect(
D2D1::ColorF(
D2D1::ColorF::Blue
)
);
greenDrawingEffect_ = new ColorDrawingEffect(
D2D1::ColorF(
D2D1::ColorF::Green
)
);
Шаг 6. Настройка эффекта рисования для определенных текстовых диапазонов
Задайте эффект рисования для определенных диапазонов текста с помощью метода IDWriteTextLayou::SetDrawingEffect и структуры DWRITE_TEXT_RANGE .
// Set the drawing effects.
// Red.
if (SUCCEEDED(hr))
{
// Set the drawing effect for the specified range.
DWRITE_TEXT_RANGE textRange = {0,
14};
if (SUCCEEDED(hr))
{
hr = pTextLayout_->SetDrawingEffect(redDrawingEffect_, textRange);
}
}
// Blue.
if (SUCCEEDED(hr))
{
// Set the drawing effect for the specified range.
DWRITE_TEXT_RANGE textRange = {14,
7};
if (SUCCEEDED(hr))
{
hr = pTextLayout_->SetDrawingEffect(blueDrawingEffect_, textRange);
}
}
// Green.
if (SUCCEEDED(hr))
{
// Set the drawing effect for the specified range.
DWRITE_TEXT_RANGE textRange = {21,
8};
if (SUCCEEDED(hr))
{
hr = pTextLayout_->SetDrawingEffect(greenDrawingEffect_, textRange);
}
}
Шаг 7. Рисование макета текста с помощью пользовательского отрисовщика
Необходимо вызвать метод IDWriteTextLayout::D raw , а не метод ID2D1RenderTarget::D rawText или ID2D1RenderTarget::D rawTextLayout .
// Draw the text layout using DirectWrite and the CustomTextRenderer class.
hr = pTextLayout_->Draw(
NULL,
pTextRenderer_, // Custom text renderer.
origin.x,
origin.y
);
Шаг 8. Очистка
В деструкторе DemoApp отпустите пользовательский отрисовщик текста.
SafeRelease(&pTextRenderer_);
После этого добавьте код, чтобы освободить классы клиентских эффектов рисования.
SafeRelease(&redDrawingEffect_);
SafeRelease(&blueDrawingEffect_);
SafeRelease(&greenDrawingEffect_);