// This method takes an attributed string and outputs a GlyphLayout data // structure that contains the glyph number and location of each inidividual glyph void getGlyphLayout (const AttributedString& text, GlyphLayout& glyphLayout) { // For now we are creating the DirectWrite Factory, System Font Collection, // D2D Factory and GDI Render target every time we layout text. // This is inefficient and we may be loading and unloading libraries each layout. // These four things should be created once at application startup and be destroyed // when the application exits. I'm not sure where the best place to do this so // for now I will just use the inefficient method. IDWriteFactory* dwFactory = nullptr; HRESULT hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwFactory)); IDWriteFontCollection* dwFontCollection = nullptr; hr = dwFactory->GetSystemFontCollection (&dwFontCollection); // To add color to text, we need to create a D2D render target // Since we are not actually rendering to a D2D context we create a temporary GDI render target ID2D1Factory *d2dFactory = nullptr; hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory); D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_SOFTWARE, D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 0, 0, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, D2D1_FEATURE_LEVEL_DEFAULT ); ID2D1DCRenderTarget* d2dDCRT = nullptr; hr = d2dFactory->CreateDCRenderTarget (&d2dRTProp, &d2dDCRT); // Initially we set the paragraph up with a default font and then apply the attributed string ranges later Font defaultFont; const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (defaultFont, *dwFontCollection); // We should probably be detecting the locale instead of hard coding it to en-us String localeName("en-us"); // We multiply the font height by the size factor so we layout text at the correct size IDWriteTextFormat* dwTextFormat = nullptr; hr = dwFactory->CreateTextFormat ( defaultFont.getTypefaceName().toWideCharPointer(), dwFontCollection, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, localeName.toWideCharPointer(), &dwTextFormat ); // Paragraph Attributes // Set Paragraph Alignment if (text.getTextAlignment() == AttributedString::left) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING); if (text.getTextAlignment() == AttributedString::right) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_TRAILING); if (text.getTextAlignment() == AttributedString::center) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_CENTER); // DirectWrite cannot justify text, default to left alignment if (text.getTextAlignment() == AttributedString::justified) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING); // Set Word Wrap if (text.getWordWrap() == AttributedString::none) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_NO_WRAP); if (text.getWordWrap() == AttributedString::byWord) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP); // DirectWrite does not support wrapping by character, default to wrapping by word if (text.getWordWrap() == AttributedString::byChar) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP); // DirectWrite does not automatically set reading direction // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) dwTextFormat->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); IDWriteTextLayout* dwTextLayout = nullptr; hr = dwFactory->CreateTextLayout ( text.getText().toWideCharPointer(), text.getText().length(), dwTextFormat, glyphLayout.getWidth(), glyphLayout.getHeight(), &dwTextLayout ); // Character Attributes int numCharacterAttributes = text.getCharAttributesSize(); for (int i = 0; i < numCharacterAttributes; ++i) { Attr* attr = text.getCharAttribute (i); // Character Range Error Checking if (attr->range.getStart() > text.getText().length()) continue; if (attr->range.getEnd() > text.getText().length()) attr->range.setEnd (text.getText().length()); if (attr->attribute == Attr::font) { AttrFont* attrFont = static_cast<AttrFont*>(attr); DWRITE_TEXT_RANGE dwRange; dwRange.startPosition = attrFont->range.getStart(); dwRange.length = attrFont->range.getLength(); dwTextLayout->SetFontFamilyName (attrFont->font.getTypefaceName().toWideCharPointer(), dwRange); // We multiply the font height by the size factor so we layout text at the correct size const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (attrFont->font, *dwFontCollection); dwTextLayout->SetFontSize (attrFont->font.getHeight() * fontHeightToEmSizeFactor, dwRange); } if (attr->attribute == Attr::foregroundColour) { AttrColour* attrColour = static_cast<AttrColour*>(attr); DWRITE_TEXT_RANGE dwRange; dwRange.startPosition = attrColour->range.getStart(); dwRange.length = attrColour->range.getLength(); ID2D1SolidColorBrush* d2dBrush = nullptr; d2dDCRT->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF(attrColour->colour.getFloatRed(), attrColour->colour.getFloatGreen(), attrColour->colour.getFloatBlue(), attrColour->colour.getFloatAlpha())), &d2dBrush); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours dwTextLayout->SetDrawingEffect (d2dBrush, dwRange); safeRelease (&d2dBrush); } } UINT32 actualLineCount = 0; hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); // Preallocate GlyphLayout Line Array glyphLayout.setNumLines (actualLineCount); HeapBlock <DWRITE_LINE_METRICS> dwLineMetrics (actualLineCount); hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount); int location = 0; // Create GlyphLine structures for each line in the layout for (UINT32 i = 0; i < actualLineCount; ++i) { // Get string range Range<int> lineStringRange (location, (int) location + dwLineMetrics[i].length); location = dwLineMetrics[i].length; GlyphLine* glyphLine = new GlyphLine(); glyphLine->setStringRange (lineStringRange); glyphLayout.addGlyphLine (glyphLine); } // To copy glyph data from DirectWrite into our own data structures we must create our // own CustomTextRenderer. Instead of passing the draw method an actual graphics context, // we pass it the GlyphLayout object that needs to be filled with glyphs. CustomDirectWriteTextRenderer* textRenderer = nullptr; textRenderer = new CustomDirectWriteTextRenderer(); hr = dwTextLayout->Draw ( &glyphLayout, textRenderer, glyphLayout.getX(), glyphLayout.getY() ); safeRelease (&textRenderer); safeRelease (&dwTextLayout); safeRelease (&dwTextFormat); safeRelease (&d2dDCRT); safeRelease (&d2dFactory); safeRelease (&dwFontCollection); safeRelease (&dwFactory); }
// Measure text FW1_RECTF STDMETHODCALLTYPE CFW1FontWrapper::MeasureString( const WCHAR *pszString, const WCHAR *pszFontFamily, FLOAT FontSize, const FW1_RECTF *pLayoutRect, UINT Flags ) { FW1_RECTF stringRect = {pLayoutRect->Left, pLayoutRect->Top, pLayoutRect->Left, pLayoutRect->Top}; IDWriteTextLayout *pTextLayout = createTextLayout(pszString, pszFontFamily, FontSize, pLayoutRect, Flags); if(pTextLayout != NULL) { // Get measurements /*DWRITE_OVERHANG_METRICS overhangMetrics; HRESULT hResult = pTextLayout->GetOverhangMetrics(&overhangMetrics); if(SUCCEEDED(hResult)) { stringRect.Left = floor(pLayoutRect->Left - overhangMetrics.left); stringRect.Top = floor(pLayoutRect->Top - overhangMetrics.top); stringRect.Right = ceil(pLayoutRect->Left + overhangMetrics.right); stringRect.Bottom = ceil(pLayoutRect->Top + overhangMetrics.bottom); }*/ DWRITE_TEXT_METRICS textMetrics; HRESULT hResult = pTextLayout->GetMetrics(&textMetrics); if(SUCCEEDED(hResult)) { stringRect.Left = textMetrics.left; stringRect.Top = textMetrics.top; stringRect.Right = textMetrics.left + textMetrics.widthIncludingTrailingWhitespace; stringRect.Bottom = textMetrics.top + textMetrics.height; } pTextLayout->Release(); } return stringRect; }
// Draw text void STDMETHODCALLTYPE CFW1FontWrapper::DrawString( ID3D11DeviceContext *pContext, const WCHAR *pszString, const WCHAR *pszFontFamily, FLOAT FontSize, const FW1_RECTF *pLayoutRect, UINT32 Color, const FW1_RECTF *pClipRect, const FLOAT *pTransformMatrix, UINT Flags ) { IDWriteTextLayout *pTextLayout = createTextLayout(pszString, pszFontFamily, FontSize, pLayoutRect, Flags); if(pTextLayout != NULL) { // Draw DrawTextLayout( pContext, pTextLayout, pLayoutRect->Left, pLayoutRect->Top, Color, pClipRect, pTransformMatrix, Flags ); pTextLayout->Release(); } }
bool CanvasD2D::MeasureTextLinesW(const WCHAR* str, UINT strLen, const TextFormat& format, Gdiplus::RectF& rect, UINT& lines) { ((TextFormatD2D&)format).m_TextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP); IDWriteTextLayout* textLayout; HRESULT hr = c_DWFactory->CreateTextLayout( str, strLen, ((TextFormatD2D&)format).m_TextFormat, rect.Width, 10000, &textLayout); if (SUCCEEDED(hr)) { DWRITE_TEXT_METRICS metrics; textLayout->GetMetrics(&metrics); rect.Width = metrics.width + 5.0f; rect.Height = metrics.height + 1.0f; // 1.0f to get same result as GDI+. lines = metrics.lineCount; textLayout->Release(); return true; } return false; }
touchmind::VISITOR_RESULT operator()(std::shared_ptr<touchmind::model::node::NodeModel> node) { HRESULT hr = S_OK; m_pXPSPrint->_PrintNode(node); if (FAILED(hr)) { LOG(SEVERITY_LEVEL_WARN) << L"_PrintNodeBody failed at node(" << node->GetId() << L", hr = " << hr; return touchmind::VISITOR_RESULT_STOP; } XpsDWriteTextRenderer* pTextRenderer = m_pXPSPrint->GetTextRenderer(); touchmind::view::node::NodeViewManager *pNodeViewManager = m_pXPSPrint->GetNodeViewManager(); control::DWriteEditControlManager *pEditControlManager = m_pXPSPrint->GetEditControlManager(); control::EDIT_CONTROL_INDEX editControlIndex = pNodeViewManager->GetEditControlIndexFromNodeId(node->GetId()); std::shared_ptr<control::DWriteEditControl> pEditControl = pEditControlManager->GetEditControl(editControlIndex); if (pEditControl != nullptr) { IDWriteTextLayout* pTextLayout = pEditControl->GetTextLayout(); if (pTextLayout != nullptr) { pTextLayout->Draw(nullptr, pTextRenderer, node->GetX() + m_pXPSPrint->GetConfiguration()->GetInsets().left, node->GetY() + m_pXPSPrint->GetConfiguration()->GetInsets().top); } else { LOG(SEVERITY_LEVEL_WARN) << L"TextLayout is null"; } } else { LOG(SEVERITY_LEVEL_WARN) << L"Could not found EditControl for Node(" << node->GetId() << L")"; } return touchmind::VISITOR_RESULT_CONTINUE; }
// Create geometry from a string void STDMETHODCALLTYPE CFW1FontWrapper::AnalyzeString( ID3D11DeviceContext *pContext, const WCHAR *pszString, const WCHAR *pszFontFamily, FLOAT FontSize, const FW1_RECTF *pLayoutRect, UINT32 Color, UINT Flags, IFW1TextGeometry *pTextGeometry ) { IDWriteTextLayout *pTextLayout = createTextLayout(pszString, pszFontFamily, FontSize, pLayoutRect, Flags); if(pTextLayout != NULL) { AnalyzeTextLayout( pContext, pTextLayout, pLayoutRect->Left, pLayoutRect->Top, Color, Flags, pTextGeometry ); pTextLayout->Release(); } }
IDWriteTextLayout* createTextLayout(IDWriteFactory1* writeFactory, IDWriteTextFormat* textFormat, const Int2& layoutSize, float scale, const wchar_t* wtext) { IDWriteTextLayout* textLayout; // Create a DirectWrite Text Layout object. DX::ThrowIfFailed( writeFactory->CreateTextLayout( wtext, // Text to be displayed (UINT32)wcslen(wtext), // Length of the text textFormat, // DirectWrite Text Format object layoutSize.width() / scale, // Width of the Text Layout layoutSize.height() / scale, // Height of the Text Layout &textLayout ) ); // Create a text range corresponding to the entire string. DWRITE_TEXT_RANGE textRange = { 0 }; textRange.length = static_cast<UINT32>(wcslen(wtext)); textRange.startPosition = 0; // Set the font size and weight on the text range. textLayout->SetFontSize(100.f, textRange); textLayout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange); //m_textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); return textLayout; }
void DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len, int x, int y, int w, int h, int cellWidth, COLORREF color) { HRESULT hr = S_OK; IDWriteBitmapRenderTarget *bmpRT = NULL; // Skip when any fonts are not set. if (mTextFormat == NULL) return; // Check possibility of zero divided error. if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f) return; if (SUCCEEDED(hr)) hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT); if (SUCCEEDED(hr)) { IDWriteTextLayout *textLayout = NULL; HDC memdc = bmpRT->GetMemoryDC(); BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY); hr = mDWriteFactory->CreateGdiCompatibleTextLayout( text, len, mTextFormat, PixelsToDipsX(w), PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout); if (SUCCEEDED(hr)) { DWRITE_TEXT_RANGE textRange = { 0, (UINT32)len }; textLayout->SetFontWeight(mFontWeight, textRange); textLayout->SetFontStyle(mFontStyle, textRange); } if (SUCCEEDED(hr)) { GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT, mRenderingParams); GdiTextRendererContext data = { color, PixelsToDipsX(cellWidth), 0.0f }; textLayout->Draw(&data, renderer, 0, 0); SafeRelease(&renderer); } BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY); SafeRelease(&textLayout); } SafeRelease(&bmpRT); }
void ClixRenderer::DText(std::wstring text, float x, float y, float w, float h, float size, CColor color) { IDWriteTextLayout* textLayout; D2Brush->SetColor(CColor::ColorF(color)); pDWriteFactory->CreateTextLayout(text.c_str(), text.length(), pTextFormat, w, h, &textLayout); DWRITE_TEXT_RANGE range = { 0, text.length() }; textLayout->SetFontSize(size, range); D2Render->DrawTextLayout(D2D1::Point2F(x, y), textLayout, D2Brush); textLayout->Release(); }
int CDirectWriteRenderer::GetFitCharCount(LPCWSTR pText, int Length, int Width, CDirectWriteFont &Font) { if (pText == nullptr || Length == 0) return 0; if (m_pRenderTarget == nullptr) return 0; int FitCharCount = 0; IDWriteFactory *pFactory = m_System.GetDWriteFactory(); if (pFactory != nullptr) { IDWriteTextFormat *pTextFormat = Font.GetTextFormat(); if (pTextFormat != nullptr) { IDWriteTextLayout *pTextLayout; pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); if (Length < 0) Length = ::lstrlenW(pText); HRESULT hr = pFactory->CreateTextLayout( pText, Length, pTextFormat, static_cast<float>(Width), m_pRenderTarget->GetSize().height, &pTextLayout); if (SUCCEEDED(hr)) { Util::CTempBuffer<DWRITE_CLUSTER_METRICS, 256> ClusterMetrics(Length); UINT32 ClusterCount; hr = pTextLayout->GetClusterMetrics(ClusterMetrics.GetBuffer(), Length, &ClusterCount); if (SUCCEEDED(hr)) { float Pos = 0.0f; for (UINT32 i = 0; i < ClusterCount; i++) { Pos += ClusterMetrics[i].width; if (static_cast<int>(std::ceil(Pos)) > Width) break; FitCharCount += ClusterMetrics[i].length; } } pTextLayout->Release(); } pTextFormat->Release(); } pFactory->Release(); } return FitCharCount; }
bool CDirectWriteRenderer::GetTextMetrics( LPCWSTR pText, int Length, CDirectWriteFont &Font, TextMetrics *pMetrics) { if (pText == nullptr || pMetrics == nullptr) return false; if (m_pRenderTarget == nullptr) return false; HRESULT hr = E_UNEXPECTED; IDWriteFactory *pFactory = m_System.GetDWriteFactory(); if (pFactory != nullptr) { IDWriteTextFormat *pTextFormat = Font.GetTextFormat(); if (pTextFormat != nullptr) { D2D1_SIZE_F Size; IDWriteTextLayout *pTextLayout; pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); if (Length < 0) Length = ::lstrlenW(pText); Size = m_pRenderTarget->GetSize(); hr = pFactory->CreateTextLayout( pText, Length, pTextFormat, Size.width, Size.height, &pTextLayout); if (SUCCEEDED(hr)) { DWRITE_TEXT_METRICS Metrics; hr = pTextLayout->GetMetrics(&Metrics); if (SUCCEEDED(hr)) { pMetrics->Width = Metrics.width; pMetrics->WidthIncludingTrailingWhitespace = Metrics.widthIncludingTrailingWhitespace; pMetrics->Height = Metrics.height; } pTextLayout->Release(); } pTextFormat->Release(); } pFactory->Release(); } return SUCCEEDED(hr); }
void MWinDeviceImpl::SetTabStops( uint32 inTabWidth) { if (mTextLayout == nil) THROW(("SetText must be called first!")); mTextLayout->SetIncrementalTabStop(inTabWidth * 96.f / 72.f); }
void DrawNormal(const Chat &chat, int vpos) { int x = static_cast<int>(rcWnd_.right - (float)(chat.width + rcWnd_.right) * (vpos - chat.vpos) / VPOS_LEN); int y = static_cast<int>(((float)yPitch_ * CNJIni::GetSettings()->commentLineMargin) * (float)chat.line); IDWriteTextLayout *pLayout; pDWriteFactory_->CreateTextLayout(chat.text.c_str(), chat.text.length(), pTextFormat_, 1920, 1200, &pLayout); D2D1_POINT_2F pt = {static_cast<FLOAT>(x) / dpiScaleX_, static_cast<FLOAT>(y) / dpiScaleY_}; ID2D1SolidColorBrush *pColor; pRT_->CreateSolidColorBrush( D2D1::ColorF(bgr2rgb(chat.color)), &pColor ); renderer_->SetColorBrush(pColor); pLayout->Draw(NULL, pTextRenderer_, pt.x, pt.y); pColor->Release(); pLayout->Release(); }
// Create text layout from string IDWriteTextLayout* CFW1FontWrapper::createTextLayout( const WCHAR *pszString, const WCHAR *pszFontFamily, FLOAT fontSize, const FW1_RECTF *pLayoutRect, UINT flags ) { if(m_defaultTextInited) { UINT32 stringLength = 0; while(pszString[stringLength] != 0) ++stringLength; // Create DWrite text layout for the string IDWriteTextLayout *pTextLayout; HRESULT hResult = m_pDWriteFactory->CreateTextLayout( pszString, stringLength, m_pDefaultTextFormat, pLayoutRect->Right - pLayoutRect->Left, pLayoutRect->Bottom - pLayoutRect->Top, &pTextLayout ); if(SUCCEEDED(hResult)) { // Layout settings DWRITE_TEXT_RANGE allText = {0, stringLength}; pTextLayout->SetFontSize(fontSize, allText); if(pszFontFamily != NULL) pTextLayout->SetFontFamilyName(pszFontFamily, allText); if((flags & FW1_NOWORDWRAP) != 0) pTextLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); if(flags & FW1_RIGHT) pTextLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING); else if(flags & FW1_CENTER) pTextLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); if(flags & FW1_BOTTOM) pTextLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR); else if(flags & FW1_VCENTER) pTextLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); return pTextLayout; } } return NULL; }
bool CanvasD2D::MeasureTextW(const WCHAR* str, UINT strLen, const TextFormat& format, Gdiplus::RectF& rect) { IDWriteTextLayout* textLayout; HRESULT hr = c_DWFactory->CreateTextLayout( str, strLen, ((TextFormatD2D&)format).m_TextFormat, 10000, 10000, &textLayout); if (SUCCEEDED(hr)) { DWRITE_TEXT_METRICS metrics; textLayout->GetMetrics(&metrics); rect.Width = metrics.width + 5.0f; rect.Height = metrics.height + 1.0f; // 1.0f to get same result as GDI+. textLayout->Release(); return true; } return false; }
Glyph* D3DGlyphProvider::getGlyph(UChar ch, float emsize) { HRESULT hr = S_OK; IDWriteTextLayout* ptextlayout = NULL; ASSERT_PTR(mpTextFormat); hr = mpDWriteFactory->CreateTextLayout(&ch, 1, mpTextFormat, 512, 512, &ptextlayout); if ( SUCCEEDED(hr) ) { DWRITE_TEXT_RANGE range = { 0, 0 }; ptextlayout->SetFontSize(emsize, range); AutoPtr<Glyph> glyph = new Glyph(); hr = ptextlayout->Draw(glyph.getPointer(), mpTextRenderer, 0, 0); if ( SUCCEEDED(hr) ) { return glyph.release(); } } return NULL; }
void DrawShita(const Chat &chat) { RECT rc; rc.top = rcWnd_.bottom - yPitch_ - static_cast<LONG>(chat.line * (int)((float)yPitch_ * CNJIni::GetSettings()->commentLineMargin)); rc.bottom = rc.top + yPitch_; rc.left = 0; rc.right = rcWnd_.right; D2D1_RECT_F layoutRect = D2D1::RectF( static_cast<FLOAT>(rc.left) / dpiScaleX_, static_cast<FLOAT>(rc.top) / dpiScaleY_, static_cast<FLOAT>(rc.right) / dpiScaleX_, static_cast<FLOAT>(rc.bottom) / dpiScaleY_ ); pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); IDWriteTextLayout *pLayout; pDWriteFactory_->CreateTextLayout( chat.text.c_str(), chat.text.length(), pTextFormat_, static_cast<FLOAT>(rc.right) / dpiScaleX_, static_cast<FLOAT>(yPitch_) / dpiScaleY_, &pLayout ); D2D1_POINT_2F pt = {static_cast<FLOAT>(rc.left) / dpiScaleX_, static_cast<FLOAT>(rc.top) / dpiScaleY_}; ID2D1SolidColorBrush *pColor; pRT_->CreateSolidColorBrush( D2D1::ColorF(bgr2rgb(chat.color)), &pColor ); renderer_->SetColorBrush(pColor); pLayout->Draw(NULL, pTextRenderer_, pt.x, pt.y); pColor->Release(); pLayout->Release(); pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); }
void addAttributedRange (const AttributedString::Attribute& attr, IDWriteTextLayout& textLayout, const int textLen, ID2D1RenderTarget& renderTarget, IDWriteFontCollection& fontCollection) { DWRITE_TEXT_RANGE range; range.startPosition = attr.range.getStart(); range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart()); if (const Font* const font = attr.getFont()) { const String familyName (FontStyleHelpers::getConcreteFamilyName (*font)); BOOL fontFound = false; uint32 fontIndex; fontCollection.FindFamilyName (familyName.toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; ComSmartPtr<IDWriteFontFamily> fontFamily; HRESULT hr = fontCollection.GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); ComSmartPtr<IDWriteFont> dwFont; uint32 fontFacesCount = 0; fontFacesCount = fontFamily->GetFontCount(); for (int i = fontFacesCount; --i >= 0;) { hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); if (font->getTypefaceStyle() == getFontFaceName (dwFont)) break; } textLayout.SetFontFamilyName (familyName.toWideCharPointer(), range); textLayout.SetFontWeight (dwFont->GetWeight(), range); textLayout.SetFontStretch (dwFont->GetStretch(), range); textLayout.SetFontStyle (dwFont->GetStyle(), range); const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont); textLayout.SetFontSize (font->getHeight() * fontHeightToEmSizeFactor, range); } if (const Colour* const colour = attr.getColour()) { ComSmartPtr<ID2D1SolidColorBrush> d2dBrush; renderTarget.CreateSolidColorBrush (D2D1::ColorF (colour->getFloatRed(), colour->getFloatGreen(), colour->getFloatBlue(), colour->getFloatAlpha()), d2dBrush.resetAndGetPointerAddress()); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours textLayout.SetDrawingEffect (d2dBrush, range); } }
void MWinDeviceImpl::SetText( const string& inText) { if (mTextLayout != nil) mTextLayout->Release(); wstring s(c2w(inText)); THROW_IF_HRESULT_ERROR( sDWFactory->CreateTextLayout( s.c_str(), s.length(), GetTextFormat(), 99999.0f, 99999.0f, &mTextLayout )); }
HRESULT Layout::Draw(float pageWidthInDips, ID2D1RenderTarget* renderTarget, ID2D1Brush* textBrush) { HRESULT hr = S_OK; static UINT const fontResourceIDs[] = { IDR_FONT_PERICLES, IDR_FONT_KOOTENAY }; // Create a custom font collection comprising our two font resources. We could have done this // in the constructor rather than every time. However, if you set break points on the loader // callbacks you'll find they're only called the first time the font collection is created. // Thereafter the font collection data is cached so recreating it is quite fast. hr = fontContext_.Initialize(); if (FAILED(hr)) return hr; IDWriteFontCollection* fontCollection = NULL; hr = fontContext_.CreateFontCollection( fontResourceIDs, sizeof(fontResourceIDs), &fontCollection ); if (FAILED(hr)) return hr; // Set up for first paragraph. float const columnWidth = std::max<float>(minColumnWidth_, pageWidthInDips - leftMargin_ - rightMargin_); float y = 0; float spaceBefore = 0; IDWriteTextFormat* textFormat = NULL; size_t const formatCount = sizeof(formats_) / sizeof(formats_[0]); size_t const paragraphCount = sizeof(paragraphs_) / sizeof(paragraphs_[0]); // Iterate over all the paragraphs. for (size_t i = 0; i < paragraphCount; ++i) { // We create a different text format object for the first formatCount // paragraphs. After that we reuse the last text format object. if (i < formatCount) { // Create the text format object, specifying both the family name and the // custom font collection in which to look for the family name. DirectWrite // will only look for the family name in the specified collection so there // is no ambiguity even if the system font collection happens to have a font // with the same family name. SafeRelease(&textFormat); hr = g_dwriteFactory->CreateTextFormat( formats_[i].familyName, fontCollection, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, formats_[i].pointSize * (96.0f / 72), L"en-us", &textFormat ); spaceBefore = formats_[i].spaceBefore * (96.0f / 72); } IDWriteTextLayout* textLayout = NULL; if (SUCCEEDED(hr)) { // Load the string. int const maxLength = 512; wchar_t charBuffer[maxLength]; int stringLength = LoadString(g_instance, paragraphs_[i], charBuffer, maxLength); // Create the text layout object. hr = g_dwriteFactory->CreateTextLayout( charBuffer, stringLength, textFormat, columnWidth, 0, &textLayout ); } if (SUCCEEDED(hr)) { // Draw the text and update the y coordinate. y += spaceBefore; renderTarget->DrawTextLayout( D2D1::Point2F(leftMargin_, y), textLayout, textBrush ); DWRITE_TEXT_METRICS metrics; hr = textLayout->GetMetrics(&metrics); y += metrics.height; } SafeRelease(&textLayout); if (FAILED(hr)) break; } SafeRelease(&textFormat); SafeRelease(&fontCollection); return hr; }
int nf_print(void * bitmap, uint16_t w, uint16_t h, nf_font_t font, nf_feature_t * features, size_t features_count, nf_aabb_t * result_rect, const char * text, ...) { if(!bitmap) { NF_ERROR("can't print with invalid bitmap\n"); return -1; } if(!font) { NF_ERROR("can't print with invalid font\n"); return -1; } if(!text) { NF_ERROR("can't print with invalid text\n"); return -1; } // figure out text rendering settings HRESULT hr = 0; D2D1_COLOR_F bg_color = D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f); D2D1_COLOR_F fg_color = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); DWRITE_WORD_WRAPPING text_wrap = DWRITE_WORD_WRAPPING_WRAP; DWRITE_TEXT_ALIGNMENT text_alignment = DWRITE_TEXT_ALIGNMENT_LEADING; DWRITE_PARAGRAPH_ALIGNMENT parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_NEAR; D2D1_TEXT_ANTIALIAS_MODE text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; float ppi_x = 0.0f, ppi_y = 0.0f; ctx.d2d_factory->GetDesktopDpi(&ppi_x, &ppi_y); size_t len = strlen(text) + 1; WCHAR * wtext = (WCHAR*)alloca(len * sizeof(WCHAR)); size_t wlen = mbstowcs(wtext, text, len); if(wlen == (size_t)-1) { NF_ERROR("failed to convert text to wchar, text : '%s'\n", text); return -1; } IDWriteTextLayout * layout = NULL; if(FAILED(hr = ctx.dw_factory->CreateTextLayout( wtext, wlen, (IDWriteTextFormat*)font, w, h, &layout))) { nf_explain_hr(hr, "can't create dwrite text layout"); return -1; } for(size_t i = 0; i < features_count; ++i) { DWRITE_TEXT_RANGE range; range.startPosition = features[i].range.start; range.length = features[i].range.end - features[i].range.start + 1; switch(features[i].type) { case NF_FEATURE_BOLD: layout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, range); break; case NF_FEATURE_UNDERLINE: layout->SetUnderline(true, range); break; case NF_FEATURE_ITALIC: layout->SetFontStyle(DWRITE_FONT_STYLE_ITALIC, range); break; case NF_FEATURE_WRAP: text_wrap = DWRITE_WORD_WRAPPING_WRAP; break; case NF_FEATURE_NO_WRAP: text_wrap = DWRITE_WORD_WRAPPING_NO_WRAP; break; case NF_FEATURE_ALIGN_LEFT: text_alignment = DWRITE_TEXT_ALIGNMENT_LEADING; break; case NF_FEATURE_ALIGN_CENTER: text_alignment = DWRITE_TEXT_ALIGNMENT_CENTER; break; case NF_FEATURE_ALIGN_RIGHT: text_alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break; case NF_FEATURE_ALIGN_JUSTIFIED: text_alignment = DWRITE_TEXT_ALIGNMENT_JUSTIFIED; break; case NF_FEATURE_ALIGN_PARAGRAPH_LEFT: parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_NEAR; break; case NF_FEATURE_ALIGN_PARAGRAPH_CENTER: parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_CENTER; break; case NF_FEATURE_ALIGN_PARAGRAPH_RIGHT: parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_FAR; break; case NF_FEATURE_AA_DISABLED: text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; break; case NF_FEATURE_AA_WIN_CLEARTYPE: text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; break; case NF_FEATURE_AA_WIN_GREYSCALE: text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; break; case NF_FEATURE_PPI: ppi_x = features[i].ppi.x; ppi_y = features[i].ppi.y; break; case NF_FEATURE_COLOR_BG: bg_color = D2D1::ColorF( features[i].color.r, features[i].color.g, features[i].color.b, features[i].color.a); break; case NF_FEATURE_COLOR_TEXT: fg_color = D2D1::ColorF( features[i].color.r, features[i].color.g, features[i].color.b, features[i].color.a); break; default: break; } } layout->SetWordWrapping(text_wrap); layout->SetTextAlignment(text_alignment); layout->SetParagraphAlignment(parg_alignment); ctx.d2d_rt->SetDpi(ppi_x, ppi_y); ctx.d2d_rt->SetTextAntialiasMode(text_aa_mode); ctx.d2d_brush->SetColor(fg_color); // figure our result metrics // TODO does this call actually rasterizes text? DWRITE_TEXT_METRICS text_metrics; layout->GetMetrics(&text_metrics); float clip_x1 = text_metrics.left; float clip_y1 = text_metrics.top; float clip_x2 = text_metrics.left + text_metrics.width; float clip_y2 = text_metrics.top + text_metrics.height; clip_x1 = clip_x1 < 0 ? 0 : (clip_x1 >= w ? w - 1 : clip_x1); clip_y1 = clip_y1 < 0 ? 0 : (clip_y1 >= h ? h - 1 : clip_y1); clip_x2 = clip_x2 < 0 ? 0 : (clip_x2 >= w ? w - 1 : clip_x2); clip_y2 = clip_y2 < 0 ? 0 : (clip_y2 >= h ? h - 1 : clip_y2); float clip_w = clip_x2 - clip_x1 + 1.0f; float clip_h = clip_y2 - clip_y1 + 1.0f; nf_aabb_t aabb; aabb.x = clip_x1; aabb.y = clip_y1; aabb.w = clip_w; aabb.h = clip_h; if(result_rect) *result_rect = aabb; // render text ctx.d2d_rt->BeginDraw(); ctx.d2d_rt->Clear(bg_color); ctx.d2d_rt->DrawTextLayout(D2D1::Point2F(), layout, ctx.d2d_brush); ctx.d2d_rt->EndDraw(); layout->Release(); layout = NULL; // read texture from d3d ctx.d3d_device->CopyResource(ctx.d3d_texture2, ctx.d3d_texture1); D3D10_MAPPED_TEXTURE2D mapped = {0}; if(FAILED(hr = ctx.d3d_texture2->Map(0, D3D10_MAP_READ, 0, &mapped))) { nf_explain_hr(hr, "can't map d3d texture"); return -1; } for(size_t j = aabb.y; j < aabb.y + aabb.h; ++j) // hardcoded BGRA8 format memcpy( (uint8_t*)bitmap + (j * w + aabb.x) * 4, (uint8_t*)mapped.pData + j * mapped.RowPitch + aabb.x, aabb.w * 4); ctx.d3d_texture2->Unmap(0); return 0; }