void FontRendererImpl::DrawText(const std::string& text, const Rect& rect, const RGBA& color, float fontSize, float fontScale, const std::string& fontRef) { // wait for a swap to complete FrpSeqAllocatorWaitForSwap(); m_mutex.lock(); // create or find a text format ComPtr<IDWriteTextFormat> textFormat; auto formatKey = std::make_pair(fontRef, fontSize); auto formatIter = m_textFormatCache.find(formatKey); if (formatIter != m_textFormatCache.end()) { textFormat = formatIter->second; } else { m_dwFactory->CreateTextFormat(ToWide(fontRef).c_str(), nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, fontSize, L"en-us", textFormat.GetAddressOf()); m_textFormatCache[formatKey] = textFormat; } // create or find a cached text layout ComPtr<IDWriteTextLayout> textLayout; auto layoutKey = std::make_pair(textFormat.Get(), std::make_pair(color.AsARGB(), text)); auto layoutIter = m_textLayoutCache.find(layoutKey); if (layoutIter != m_textLayoutCache.end()) { textLayout = layoutIter->second; } else { std::wstring wideText = ToWide(text); // parse colors and the lot std::wstring noColorTextString; std::vector<DWRITE_TEXT_RANGE> textRanges; std::vector<RGBA> textColors; { std::wstringstream noColorText; int count = 0; static const RGBA colors[] = { RGBA(0, 0, 0), RGBA(255, 0, 0), RGBA(0, 255, 0), RGBA(255, 255, 0), RGBA(0, 0, 255), RGBA(0, 255, 255), RGBA(255, 0, 255), RGBA(255, 255, 255), RGBA(100, 0, 0), RGBA(0, 0, 100) }; textRanges.reserve(50); textColors.reserve(50); textRanges.push_back({ 0, UINT32_MAX }); textColors.push_back(color); for (int i = 0; i < wideText.length(); i++) { if (wideText[i] == '^' && (i + 1) < wideText.length() && isdigit(wideText[i + 1])) { textRanges.back().length = count - textRanges.back().startPosition; textRanges.push_back({ (UINT32)count, UINT32_MAX }); textColors.push_back(colors[wideText[i + 1] - '0']); ++i; continue; } noColorText << wideText[i]; ++count; } textRanges.back().length = count - textRanges.back().startPosition; noColorTextString = noColorText.str(); } m_dwFactory->CreateTextLayout(noColorTextString.c_str(), static_cast<UINT32>(noColorTextString.length()), textFormat.Get(), rect.Width(), rect.Height(), textLayout.GetAddressOf()); m_textLayoutCache[layoutKey] = textLayout; // set effect for (size_t i : irange(textRanges.size())) { DWRITE_TEXT_RANGE effectRange = textRanges[i]; RGBA color = textColors[i]; static thread_local std::map<uint32_t, ComPtr<FrDrawingEffect>> effects; auto it = effects.find(color.AsARGB()); if (it == effects.end()) { ComPtr<FrDrawingEffect> effect = Make<FrDrawingEffect>(); effect->SetColor(textColors[i]); it = effects.insert({ color.AsARGB(), effect }).first; } check(SUCCEEDED(textLayout->SetDrawingEffect((IUnknown*)it->second.Get(), effectRange))); } } // draw auto drawingContext = new FrDrawingContext(); textLayout->Draw(drawingContext, m_textRenderer.Get(), rect.Left(), rect.Top()); auto numRuns = drawingContext->glyphRuns.size(); if (numRuns) { for (auto& run : drawingContext->glyphRuns) { m_queuedRenderables.push_back(std::make_unique<FrGlyphRunRenderable>(run)); //m_queuedGlyphRuns.push_back(run); } } delete drawingContext; m_mutex.unlock(); }