使用自定义文本呈现器进行呈现
派生自 IDWriteTextRenderer 的自定义文本呈现器可以绘制DirectWrite文本布局。 需要自定义呈现器才能利用DirectWrite的某些高级功能,例如呈现到位图或 GDI 图面、内联对象和客户端绘图效果。 本教程介绍 IDWriteTextRenderer 的方法,并提供使用 Direct2D 呈现具有位图填充的文本的示例实现。
本教程包含以下部分:
- 构造函数
- DrawGlyphRun ()
- DrawUnderline () 和 DrawStrikethrough ()
- 像素对齐、每个 DIP 的像素数和转换
- DrawInlineObject ()
- 析构函数
- 使用自定义文本呈现器
除了 IDWriteTextRenderer 引用页及下方列出的方法外,自定义文本呈现器还必须实现从 IUnknown 继承的方法。
有关自定义文本呈现器的完整源代码,请参阅DirectWrite Hello World示例的 CustomTextRenderer.cpp 和 CustomTextRenderer.h 文件。
构造函数
自定义文本呈现器需要构造函数。 此示例使用实心画笔和位图 Direct2D 画笔来呈现文本。
因此,构造函数采用下表中的参数以及说明。
参数 | 说明 |
---|---|
pD2DFactory | 指向 ID2D1Factory 对象的指针,该对象将用于创建所需的任何 Direct2D 资源。 |
Prt | 指向文本将呈现到的 ID2D1HwndRenderTarget 对象的指针。 |
pOutlineBrush | 指向 ID2D1SolidColorBrush 的指针,该 ID2D1SolidColorBrush 将用于绘制文本的轮廓 |
pFillBrush | 指向 ID2D1BitmapBrush 的指针,该 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 对象,然后使用 ID2D1PathGeometry::Open 方法检索 ID2D1GeometrySink 对象。
// 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 ); }
传递给 DrawGlyphRun 的DWRITE_GLYPH_RUN包含一个名为 fontFace 的 IDWriteFontFace 对象,该对象代表整个字形运行的字体。 使用 IDWriteFontFace:: GetGlyphRunOutline 方法将字形的轮廓放入 geometry 接收器中,如以下代码所示。
// 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 );
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 结构。 传递给 DrawUnderline 回调方法的DWRITE_UNDERLINE结构提供下划线的偏移量、宽度和粗细。
D2D1_RECT_F rect = D2D1::RectF( 0, underline->offset, underline->width, underline->offset + underline->thickness );
接下来,使用 ID2D1Factory::CreateRectangleGeometry 方法和初始化的 D2D1_RECT_F 结构创建 ID2D1RectangleGeometry 对象。
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 方法执行绘图函数。