The previous articles from this series show how to format the text layout using built-in DirectWrite methods. However, as said earlier, we can do more custom formatting (e.g. draw double/triple underline/strikethrough, highlight text and so on). How can be done? First, let’s note that ID2D1RenderTarget::DrawTextLayout internally calls IDWriteTextLayout::Draw which has the following prototype:
HRESULT Draw( void* clientDrawingContext, IDWriteTextRenderer* renderer, FLOAT originX, FLOAT originY)
We can write our own implementation of IDWriteTextRenderer interface providing custom text rendering then directly call IDWriteTextLayout::Draw instead of CRenderTarget::DrawTextLayout.
Code samples
class CCustomTextRenderer : public CCmdTarget
{
DECLARE_DYNAMIC(CCustomTextRenderer)
public:
CCustomTextRenderer() = default;
virtual ~CCustomTextRenderer() = default;
IDWriteTextRenderer* Get();
public:
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(CustomTextRenderer, IDWriteTextRenderer)
// override IDWriteTextRenderer methods
STDMETHOD(DrawGlyphRun)(void*, FLOAT, FLOAT, DWRITE_MEASURING_MODE, const DWRITE_GLYPH_RUN*,
const DWRITE_GLYPH_RUN_DESCRIPTION*, IUnknown*);
STDMETHOD(DrawInlineObject)(void*, FLOAT, FLOAT, IDWriteInlineObject*, BOOL, BOOL, IUnknown*);
STDMETHOD(DrawStrikethrough)(void*, FLOAT, FLOAT, const DWRITE_STRIKETHROUGH*, IUnknown*);
STDMETHOD(DrawUnderline)(void*, FLOAT, FLOAT, const DWRITE_UNDERLINE*, IUnknown*);
// override IDWritePixelSnapping methods
STDMETHOD(GetCurrentTransform)(void*, DWRITE_MATRIX*);
STDMETHOD(GetPixelsPerDip)(void*, FLOAT*);
STDMETHOD(IsPixelSnappingDisabled)(void*, BOOL*);
// implementation helpers
void _FillRectangle(void*, IUnknown*, FLOAT, FLOAT, FLOAT, FLOAT,
DWRITE_READING_DIRECTION, DWRITE_FLOW_DIRECTION);
END_INTERFACE_PART(CustomTextRenderer)
};BEGIN_INTERFACE_MAP(CCustomTextRenderer, CCmdTarget)
INTERFACE_PART(CCustomTextRenderer, __uuidof(IDWriteTextRenderer), CustomTextRenderer)
END_INTERFACE_MAP()
STDMETHODIMP CCustomTextRenderer::XCustomTextRenderer::DrawGlyphRun(void* pClientDrawingContext,
FLOAT fBaselineOriginX, FLOAT fBaselineOriginY, DWRITE_MEASURING_MODE measuringMode,
const DWRITE_GLYPH_RUN* pGlyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION* pGlyphRunDescription,
IUnknown* pClientDrawingEffect)
{
// NOTE: This does the same as the default implementation.
// In a future version will be modified in order to perform some custom rendering.
CDrawingContext* pDrawingContext = static_cast<CDrawingContext*>(pClientDrawingContext);
ASSERT_VALID(pDrawingContext);
ID2D1Brush* pBrush = pDrawingContext->GetDefaultBrush()->Get();
if(NULL != pClientDrawingEffect)
{
pBrush = static_cast<ID2D1Brush*>(pClientDrawingEffect);
}
CRenderTarget* pRenderTarget = pDrawingContext->GetRenderTarget();
ASSERT_VALID(pRenderTarget);
pRenderTarget->GetRenderTarget()->DrawGlyphRun(CD2DPointF(fBaselineOriginX, fBaselineOriginY),
pGlyphRun, pBrush, measuringMode);
return S_OK;
}
// ...
// Please, find the other methods implementation in the attached demo project!
// ...void CDirectWriteStaticCtrl::_DrawTextLayout(CHwndRenderTarget* pRenderTarget)
{
CD2DTextFormat textFormat(pRenderTarget,
m_strFontFamilyName, m_fFontSize, m_eFontWeight, m_eFontStyle, m_eFontStretch);
textFormat.Get()->SetTextAlignment(m_eTextAlignment);
textFormat.Get()->SetWordWrapping(m_eWordWrapping);
textFormat.Get()->SetParagraphAlignment(m_eParagraphAlignment);
CD2DSizeF sizeTarget = pRenderTarget->GetSize();
CD2DSizeF sizeDraw(sizeTarget.width - 2 * m_fMargin, sizeTarget.height - 2 * m_fMargin);
CD2DTextLayout textLayout(pRenderTarget, m_strText, textFormat, sizeDraw);
POSITION pos = m_listTextRangeFormat.GetHeadPosition();
while (NULL != pos)
m_listTextRangeFormat.GetNext(pos)->Apply(textLayout);
CD2DSolidColorBrush* pDefaultBrush = new CD2DSolidColorBrush(pRenderTarget, m_crTextColor);
pDefaultBrush->Create(pRenderTarget);
CDrawingContext* pDrawingContext = new CDrawingContext(pRenderTarget, pDefaultBrush);
// call IDWriteTextLayout::Draw passing custom IDWriteTextRenderer
textLayout.Get()->Draw(pDrawingContext, m_textRenderer.Get(), m_fMargin, m_fMargin);
}
Demo project
So far, it contains an implementation of IDWriteTextRenderer which does the same as the default one. In a further article I will update it in order to perform custom rendering.
Download: MFC Support for DirectWrite Demo (Part 7).zip (733)
Notes
- You can find an excellent related article on Petzold Book Blog; see the link below.
Resources and related articles
- Codexpert blog: MFC Support for DirectWrite – Part 10: Outlined Text
- Petzold Book Blog: Character Formatting Extensions with DirectWrite
- MSDN: IDWriteTextLayout::Draw method
- MSDN: IDWriteTextRenderer interface
- MSDN: IDWritePixelSnapping interface