一、實現思路
通過繼承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<D2D1_MATRIX_3X2_F*>(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