一、实现思路
通过继承IDWriteTextRenderer定义自己的文本渲染类并重写DrawGlyphRun方法,最终将该类实例作为 IDWriteTextLayout::Draw的参数传入进行文本的绘制。
二、代码实现
1、自定义文本渲染类
class CustomTextRenderer : public IDWriteTextRenderer
{
protected:
ULONG m_cRef;
ID2D1Factory* m_pD2DFactory;
ID2D1RenderTarget *m_pRenderTarget;
ID2D1Brush *m_pTextBodyBrush;
ID2D1SolidColorBrush *m_pTextOutlineBrush;
float mStrokeWidth;
public:
CustomTextRenderer(
ID2D1Factory* pD2DFactory, ID2D1RenderTarget* pRenderTarget,
ID2D1Brush *pTextBodyBrush, ID2D1SolidColorBrush *pTextOutlineBrush,
float strokeWidth = 1.0f);
~CustomTextRenderer();
STDMETHOD(DrawGlyphRun)(
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const *glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription,
IUnknown *clientDrawingEffect
);
STDMETHOD(DrawUnderline)(
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_UNDERLINE const *underline,
IUnknown *clientDrawingEffect
);
STDMETHOD(DrawStrikethrough)(
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const *strikethrough,
IUnknown *clientDrawingEffect
);
STDMETHOD(DrawInlineObject)(
void *clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject *inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown *clientDrawingEffect
)
{
return E_NOTIMPL;
}
STDMETHOD(IsPixelSnappingDisabled)(
void* clientDrawingContext,
BOOL* isDisabled
)
{
*isDisabled = FALSE;
return S_OK;
}
STDMETHOD(GetCurrentTransform)(
void* clientDrawingContext,
DWRITE_MATRIX* transform
)
{
m_pRenderTarget->GetTransform(reinterpret_cast(transform));
return S_OK;
}
STDMETHOD(GetPixelsPerDip)(
void* clientDrawingContext,
FLOAT* pixelsPerDip
)
{
float x, yUnused;
m_pRenderTarget->GetDpi(&x, &yUnused);
*pixelsPerDip = x / 96;
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
if (iid == IID_IUnknown /*|| iid == IID_IDWritePixelSnapping || iid == IID_IDWriteTextRenderer*/)
{
*ppvObj = this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef()
{
return ++m_cRef;
}
ULONG STDMETHODCALLTYPE Release()
{
// Decrement the object's internal counter.
if (0 == --m_cRef)
{
delete this;
}
return m_cRef;
}
};
2、DrawGlyphRun方法实现
HRESULT CustomTextRenderer::DrawGlyphRun(
void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const *glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription, IUnknown *clientDrawingEffect)
{
HRESULT hr = S_OK;
ID2D1PathGeometry* pPathGeometry = nullptr;
hr = m_pD2DFactory->CreatePathGeometry(&pPathGeometry);
ID2D1GeometrySink* pSink = nullptr;
hr = pPathGeometry->Open(&pSink);
hr = glyphRun->fontFace->GetGlyphRunOutline(
glyphRun->fontEmSize,
glyphRun->glyphIndices,
glyphRun->glyphAdvances,
glyphRun->glyphOffsets,
glyphRun->glyphCount,
glyphRun->isSideways,
glyphRun->bidiLevel,
pSink
);
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
);
ID2D1TransformedGeometry *pTransformedGeometry = nullptr;
hr = m_pD2DFactory->CreateTransformedGeometry(pPathGeometry, &matrix, &pTransformedGeometry);
// 绘制文字描边部分
m_pRenderTarget->DrawGeometry(pTransformedGeometry, m_pTextOutlineBrush, mStrokeWidth);
// 绘制文字填充部分
m_pRenderTarget->FillGeometry(pTransformedGeometry, m_pTextBodyBrush);
SafeRelease(&pPathGeometry);
SafeRelease(&pSink);
SafeRelease(&pTransformedGeometry);
return hr;
}
3、使用自定义渲染对象绘制文本
m_pTextRenderer = new CustomTextRenderer(
m_pDirect2dFactory, m_pRenderTarget,
m_pTextBodyBrush, m_pTextOutlineBrush, strokeWidth);
m_pRenderTarget->BeginDraw();
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
m_pTextLayout->Draw(nullptr, m_pTextRenderer, 0.0f, 0.0f);
m_pRenderTarget->EndDraw();
三、绘制结果
1、只有描边部分
2、只有填充部分
3、描边和填充
四、总结
1、自定义文本渲染类的核心是重写IDWriteTextRenderer::DrawGlyphRun这个回调函数。
2、使用自定义文本渲染类绘制文字必须调用IDWriteTextLayout::Draw,而不是ID2D1RenderTarget::DrawText和ID2D1RenderTarget::DrawTextLayout。
PS:
本文代码是基于微软官方文档示例所实现,详情可参考:
how-to-implement-a-custom-text-renderer
direct2d-quickstart