DirectWrite allows inserting objects in a block of text, e.g. images, as shown in the picture.
Let’s see how can be done!
Implement IDWriteInlineObject interface methods to insert inline images
Once we have created a CD2DTextLayout object, remains the following to do:
- Define a class for implementing IDWriteInlineObject interface, e.g. CInlineImage. We can derive it from IDWriteInlineObject then override and implement IDWriteInlineObject and IUnknown methods. However, if we already are using MFC, it’s a little bit handier to derive from CCmdTarget, then use MFC’s interface maps DECLARE_INTERFACE_MAP, BEGIN_INTERFACE_PART and so on.
123456789101112131415161718192021class CInlineImage : public CCmdTarget{public:CInlineImage();virtual ~CInlineImage();HRESULT Create(CHwndRenderTarget* pRenderTarget, UINT nResID, LPCTSTR pszResType);HRESULT Create(CHwndRenderTarget* pRenderTarget, LPCTSTR pszFileName);BOOL IsValid();operator IDWriteInlineObject*();public:DECLARE_INTERFACE_MAP()BEGIN_INTERFACE_PART(InlineImage, IDWriteInlineObject)STDMETHOD(Draw)(void*, IDWriteTextRenderer*, FLOAT, FLOAT, BOOL, BOOL, IUnknown*);STDMETHOD(GetMetrics)(DWRITE_INLINE_OBJECT_METRICS*);STDMETHOD(GetOverhangMetrics)(DWRITE_OVERHANG_METRICS*);STDMETHOD(GetBreakConditions)(DWRITE_BREAK_CONDITION*, DWRITE_BREAK_CONDITION*);CHwndRenderTarget* m_pRenderTarget;CD2DBitmap* m_pBitmap;END_INTERFACE_PART(InlineImage)};
Detailed implementation can be found in the demo application attached to this article. Here is listed just an implementation example of overridden IDWriteInlineObject::Draw.
1234567891011121314151617STDMETHODIMP CInlineImage::XInlineImage::Draw(void* clientDrawingContext,IDWriteTextRenderer* textRenderer, FLOAT fOriginX, FLOAT fOriginY,BOOL bIsSideways, BOOL bIsRightToLeft, IUnknown* clientDrawingEffect){METHOD_PROLOGUE(CInlineImage, InlineImage);ASSERT_VALID(m_pRenderTarget);ASSERT_VALID(m_pBitmap);if(m_pRenderTarget->IsValid() && m_pBitmap->IsValid()){CD2DSizeF sizeBitmap = m_pBitmap->GetSize();m_pRenderTarget->DrawBitmap(m_pBitmap,CD2DRectF(fOriginX, fOriginY,fOriginX + sizeBitmap.width, fOriginY + sizeBitmap.height));}return S_OK;} - Create and initialize a CInlineImage object.
123456class CDirectWriteDemoView : public CView{// ...CInlineImage m_inlineImage;// ...};
12345678910111213int CDirectWriteDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct){// ...// Enable D2D support for this windowEnableD2DSupport();CHwndRenderTarget* pRenderTarget = GetRenderTarget();// create inline image objectsHRESULT hr = m_inlineImageRings.Create(pRenderTarget, IDB_RINGS, T("PNG"));ASSERT(SUCCEEDED(hr));// ...} - Pass the CInlineImage object to IDWriteTextLayout::SetInlineObject before drawing the text layout.
123456789101112131415LRESULT CDirectWriteDemoView::OnDraw2D(WPARAM wParam, LPARAM lParam){CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam;// ...// init the inline objects_InitInlineObjects(pRenderTarget, spTextLayout);// Draw the text in the render target.pRenderTarget->DrawTextLayout(CD2DPointF(), // upper-left corner of the textspTextLayout.get(), // text layout object&CD2DSolidColorBrush // brush used for text(pRenderTarget,D2D1::ColorF(D2D1::ColorF::DarkRed)));}
1234567891011121314void CDirectWriteDemoView::_InitInlineObjects(CHwndRenderTarget* pRenderTarget,const std::shared_ptr<CD2DTextLayout> spTextLayout){// ...CDirectWriteDemoDoc* pDoc = _GetDocument();DWRITE_TEXT_RANGE textRange;if(pDoc->GetTextRange(_T("[IMAGE:RINGS]"), textRange) && m_inlineImageRings.IsValid()){spTextLayout->Get()->SetInlineObject(m_inlineImageRings, textRange);}// ...}
Demo application
Download: MFC Support for DirectWrite Demo (Part 4).zip (1194)
Notes
- CInlineImage class implementation is intentionally left as simple as possible, for learning purpose. Of course, it can be improved, for example, by scaling images to fit the text height.
- Pictures used in the demo application are taken from http://www.icondrawer.com/gift-icons.php
Resources and related articles
- Codexpert: Direc2D and DirectWrite topics
- MSDN: How to Add Inline Objects to a Text Layout
- MSDN: IDWriteTextLayout::SetInlineObject method
- MSDN: IDWriteInlineObject interface
- Codeproject: An introduction to MFC’s COM Interface Macros
- MSDN TN038: MFC/OLE IUnknown Implementation