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 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); } }
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; }