From 4c7dd443513fc32bd6474b82ebea9377cc055d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 27 Oct 2026 11:54:10 +0100 Subject: [PATCH] TextDrawer: Remove the current font and scale Drawing now takes the style and scale too, like measuring, so the text drawers have no font state left. UIContext keeps its current style and passes it, and fills it in from the theme if nothing has set one yet. PPGe passes its one font. Co-Authored-By: Claude Opus 5.5 --- Common/Render/Text/draw_text.cpp | 35 ++++++++------------- Common/Render/Text/draw_text.h | 27 +++++----------- Common/Render/Text/draw_text_android.cpp | 4 +-- Common/Render/Text/draw_text_android.h | 2 +- Common/Render/Text/draw_text_cocoa.h | 2 +- Common/Render/Text/draw_text_cocoa.mm | 10 +++--- Common/Render/Text/draw_text_sdl.cpp | 4 +-- Common/Render/Text/draw_text_sdl.h | 2 +- Common/Render/Text/draw_text_uwp.cpp | 4 +-- Common/Render/Text/draw_text_uwp.h | 2 +- Common/Render/Text/draw_text_win.cpp | 4 +-- Common/Render/Text/draw_text_win.h | 2 +- Common/UI/Context.cpp | 14 ++++----- Core/Util/PPGeDraw.cpp | 16 ++++------ UI/NativeApp.cpp | 4 --- unittest/UnitTest.cpp | 40 +++++++++--------------- 16 files changed, 64 insertions(+), 108 deletions(-) diff --git a/Common/Render/Text/draw_text.cpp b/Common/Render/Text/draw_text.cpp index f2a88343f6..e4c210b17d 100644 --- a/Common/Render/Text/draw_text.cpp +++ b/Common/Render/Text/draw_text.cpp @@ -90,23 +90,13 @@ const std::string &TextDrawer::WrapString(const FontStyle &style, float scale, s return entry.wrapped; } -void TextDrawer::SetOrCreateFont(const FontStyle &style) { - EnsureFont(style); - fontStyle_ = style; -} - -void TextDrawer::SetFontScale(float xscale, float yscale) { - fontScaleX_ = xscale; - fontScaleY_ = yscale; -} - float TextDrawer::CalculateDPIScale() const { if (ignoreGlobalDpi_) return dpiScale_; return g_display.dpi_scale_y; } -void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) { +void TextDrawer::DrawString(DrawBuffer &target, const FontStyle &style, float scaleX, float scaleY, std::string_view str, float x, float y, uint32_t color, int align) { using namespace Draw; if (str.empty()) { return; @@ -117,7 +107,7 @@ void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, f TextStringEntry *entry; - auto [iter, inserted] = cache_.try_emplace(LookupKey(fontStyle_, str, align & FLAG_DROP_SHADOW)); + auto [iter, inserted] = cache_.try_emplace(LookupKey(style, str, align & FLAG_DROP_SHADOW)); if (!inserted) { entry = iter->second.get(); entry->lastUsedFrame = frameCount_; @@ -152,7 +142,8 @@ void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, f // because we need white. Well, we could using swizzle, but not all our backends support that. TextureDesc desc{}; std::vector bitmapData; - if (!DrawStringBitmap(bitmapData, *entry, bitmapFormat, str, align, emoji)) { + EnsureFont(style); + if (!DrawStringBitmap(style, bitmapData, *entry, bitmapFormat, str, align, emoji)) { // Nothing drawn. The entry without a texture records that fact. return; } @@ -178,15 +169,15 @@ void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, f draw_->BindTexture(0, entry->texture); // Okay, the texture is bound, let's draw. - float w = (float)entry->width * (fontScaleX_ * dpiScale_); - float h = (float)entry->height * (fontScaleY_ * dpiScale_); + float w = (float)entry->width * (scaleX * dpiScale_); + float h = (float)entry->height * (scaleY * dpiScale_); float u = (float)(entry->width + entry->shadowPad * 2) / (float)entry->bmWidth; float v = (float)(entry->height + entry->shadowPad * 2) / (float)entry->bmHeight; DrawBuffer::DoAlign(align, &x, &y, &w, &h); // Align by the text, then grow the quad to include the shadow padding. - const float padX = entry->shadowPad * (fontScaleX_ * dpiScale_); - const float padY = entry->shadowPad * (fontScaleY_ * dpiScale_); + const float padX = entry->shadowPad * (scaleX * dpiScale_); + const float padY = entry->shadowPad * (scaleY * dpiScale_); target.DrawTexRect(x - padX, y - padY, x + w + padX, y + h + padY, 0.0f, 0.0f, u, v, color); target.Flush(true); } @@ -239,7 +230,7 @@ void TextDrawer::MeasureStringRect(const FontStyle &style, float scaleX, float s } } -void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align) { +void TextDrawer::DrawStringRect(DrawBuffer &target, const FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, uint32_t color, int align) { if (bounds.w < 0.0f || bounds.h < 0.0f) { return; } @@ -262,17 +253,18 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const } const int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT); - DrawString(target, wrap ? std::string_view(WrapString(fontStyle_, fontScaleX_, str, bounds.w, wrap)) : str, x, y, color, align); + DrawString(target, style, scaleX, scaleY, wrap ? std::string_view(WrapString(style, scaleX, str, bounds.w, wrap)) : str, x, y, color, align); } -bool TextDrawer::DrawStringBitmapRect(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align, bool fullColor) { +bool TextDrawer::DrawStringBitmapRect(const FontStyle &style, float scale, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align, bool fullColor) { // Clamp the size to something sane. if (str.size() > MAX_TEXT_LENGTH) { str = str.substr(0, MAX_TEXT_LENGTH); } const int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT); - return DrawStringBitmap(bitmapData, entry, texFormat, wrap ? std::string_view(WrapString(fontStyle_, fontScaleX_, str, bounds.w, wrap)) : str, align, fullColor); + EnsureFont(style); + return DrawStringBitmap(style, bitmapData, entry, texFormat, wrap ? std::string_view(WrapString(style, scale, str, bounds.w, wrap)) : str, align, fullColor); } void TextDrawer::AddDropShadow(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat srcFormat) const { @@ -311,7 +303,6 @@ void TextDrawer::ClearCache() { cache_.clear(); sizeCache_.clear(); wrapCache_.clear(); - fontStyle_ = {}; } void TextDrawer::OncePerFrame() { diff --git a/Common/Render/Text/draw_text.h b/Common/Render/Text/draw_text.h index d5033aee12..e337c5b8a6 100644 --- a/Common/Render/Text/draw_text.h +++ b/Common/Render/Text/draw_text.h @@ -71,25 +71,14 @@ public: virtual ~TextDrawer() = default; virtual bool IsReady() const { return true; } - // Sets the font and scale that drawing uses. - void SetOrCreateFont(const FontStyle &style); - void SetFontScale(float xscale, float yscale); - // Measuring doesn't depend on the current font and scale. + // The font and scale are passed to each call. The fonts are created as they're first used. void MeasureString(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float *w, float *h); void MeasureStringRect(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float maxWidth, float *w, float *h, int align = ALIGN_TOPLEFT); - // In the current font and scale. - void MeasureString(std::string_view str, float *w, float *h) { - MeasureString(fontStyle_, fontScaleX_, fontScaleY_, str, w, h); - } - void MeasureStringRect(std::string_view str, float maxWidth, float *w, float *h, int align = ALIGN_TOPLEFT) { - MeasureStringRect(fontStyle_, fontScaleX_, fontScaleY_, str, maxWidth, w, h, align); - } - - void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT); - void DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align); - virtual bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) = 0; - bool DrawStringBitmapRect(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align, bool fullColor); + void DrawString(DrawBuffer &target, const FontStyle &style, float scaleX, float scaleY, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT); + void DrawStringRect(DrawBuffer &target, const FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, uint32_t color, int align); + // Rasterizes at scale 1, wrapping to bounds as if drawn at scale. + bool DrawStringBitmapRect(const FontStyle &style, float scale, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align, bool fullColor); // Use for housekeeping like throwing out old strings. void OncePerFrame(); @@ -113,8 +102,9 @@ protected: // Creates the font for style, if it isn't already. virtual void EnsureFont(const FontStyle &style) = 0; - // Implementations of this must handle multi-line strings. EnsureFont has been called for style. + // Implementations of these must handle multi-line strings. EnsureFont has been called for style. virtual void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) = 0; + virtual bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) = 0; void ClearCache(); @@ -154,11 +144,8 @@ protected: Draw::DrawContext *draw_; int frameCount_ = 0; - float fontScaleX_ = 1.0f; - float fontScaleY_ = 1.0f; float dpiScale_ = 1.0f; bool ignoreGlobalDpi_ = false; - FontStyle fontStyle_{}; // We will clamp strings to this length to avoid various degenenerate behaviors.k static constexpr size_t MAX_TEXT_LENGTH = 16384; diff --git a/Common/Render/Text/draw_text_android.cpp b/Common/Render/Text/draw_text_android.cpp index b95b81cccf..38a43ab57e 100644 --- a/Common/Render/Text/draw_text_android.cpp +++ b/Common/Render/Text/draw_text_android.cpp @@ -124,13 +124,13 @@ void TextDrawerAndroid::MeasureStringInternal(const FontStyle &style, std::strin *h = (float)(size & 0xFFFF); } -bool TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { +bool TextDrawerAndroid::DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { if (str.empty()) { bitmapData.clear(); return false; } - auto iter = fontMap_.find(fontStyle_); + auto iter = fontMap_.find(style); if (iter == fontMap_.end()) { ERROR_LOG(Log::G3D, "Missing font"); return false; diff --git a/Common/Render/Text/draw_text_android.h b/Common/Render/Text/draw_text_android.h index 1156619929..4a0bb9a9e0 100644 --- a/Common/Render/Text/draw_text_android.h +++ b/Common/Render/Text/draw_text_android.h @@ -20,11 +20,11 @@ public: ~TextDrawerAndroid(); bool IsReady() const override; - bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; protected: void EnsureFont(const FontStyle &style) override; void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) override; + bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; bool SupportsColorEmoji() const override { return true; } void ClearFonts() override; diff --git a/Common/Render/Text/draw_text_cocoa.h b/Common/Render/Text/draw_text_cocoa.h index c61eec39ba..ebc5f5ac42 100644 --- a/Common/Render/Text/draw_text_cocoa.h +++ b/Common/Render/Text/draw_text_cocoa.h @@ -16,11 +16,11 @@ public: TextDrawerCocoa(Draw::DrawContext *draw); ~TextDrawerCocoa(); - bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; protected: void EnsureFont(const FontStyle &style) override; void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) override; + bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; bool SupportsColorEmoji() const override { return true; } void ClearFonts() override; diff --git a/Common/Render/Text/draw_text_cocoa.mm b/Common/Render/Text/draw_text_cocoa.mm index 3dfa3fff3e..c109a20cc3 100644 --- a/Common/Render/Text/draw_text_cocoa.mm +++ b/Common/Render/Text/draw_text_cocoa.mm @@ -223,13 +223,13 @@ void TextDrawerCocoa::MeasureStringInternal(const FontStyle &style, std::string_ *h = ceilf(extH); } -bool TextDrawerCocoa::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { +bool TextDrawerCocoa::DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { if (str.empty()) { bitmapData.clear(); return false; } - auto iter = fontMap_.find(fontStyle_); + auto iter = fontMap_.find(style); if (iter == fontMap_.end()) { return false; } @@ -258,10 +258,10 @@ bool TextDrawerCocoa::DrawStringBitmap(std::vector &bitmapData, TextStr // Figure out how big an image we need. // We re-use MeasureString here. float w, h; - MeasureString(str, &w, &h); + MeasureString(style, 1.0f, 1.0f, str, &w, &h); // Reverse the DPI scale that MeasureString baked in. - w /= (dpiScale_ * fontScaleX_); - h /= (dpiScale_ * fontScaleY_); + w /= dpiScale_; + h /= dpiScale_; int width = (int)ceilf(w); int height = (int)ceilf(h); diff --git a/Common/Render/Text/draw_text_sdl.cpp b/Common/Render/Text/draw_text_sdl.cpp index 2cde9fae30..ab93190da6 100644 --- a/Common/Render/Text/draw_text_sdl.cpp +++ b/Common/Render/Text/draw_text_sdl.cpp @@ -436,7 +436,7 @@ void TextDrawerSDL::MeasureStringInternal(const FontStyle &style, std::string_vi *h = (float)extH; } -bool TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { +bool TextDrawerSDL::DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { _dbg_assert_(!fullColor) if (str.empty()) { @@ -444,7 +444,7 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStrin return false; } - auto fontIter = fontMap_.find(fontStyle_); + auto fontIter = fontMap_.find(style); if (fontIter == fontMap_.end()) { ERROR_LOG(Log::G3D, "Font style not in map"); return false; diff --git a/Common/Render/Text/draw_text_sdl.h b/Common/Render/Text/draw_text_sdl.h index 9744002689..2abca5aab3 100644 --- a/Common/Render/Text/draw_text_sdl.h +++ b/Common/Render/Text/draw_text_sdl.h @@ -19,11 +19,11 @@ public: TextDrawerSDL(Draw::DrawContext *draw); ~TextDrawerSDL(); - bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; protected: void EnsureFont(const FontStyle &style) override; void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) override; + bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; bool SupportsColorEmoji() const override { return false; } void ClearFonts() override; diff --git a/Common/Render/Text/draw_text_uwp.cpp b/Common/Render/Text/draw_text_uwp.cpp index 9242fdbb7f..5b69551fa5 100644 --- a/Common/Render/Text/draw_text_uwp.cpp +++ b/Common/Render/Text/draw_text_uwp.cpp @@ -300,7 +300,7 @@ void TextDrawerUWP::MeasureStringInternal(const FontStyle &style, std::string_vi *h = (int)(metrics.height + 1.0f); } -bool TextDrawerUWP::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { +bool TextDrawerUWP::DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { if (str.empty()) { bitmapData.clear(); return false; @@ -310,7 +310,7 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector &bitmapData, TextStrin SIZE size; IDWriteTextFormat *format = nullptr; - auto iter = fontMap_.find(fontStyle_); + auto iter = fontMap_.find(style); if (iter != fontMap_.end()) { format = iter->second->textFmt; } diff --git a/Common/Render/Text/draw_text_uwp.h b/Common/Render/Text/draw_text_uwp.h index 95b7300078..c6c89898ee 100644 --- a/Common/Render/Text/draw_text_uwp.h +++ b/Common/Render/Text/draw_text_uwp.h @@ -20,11 +20,11 @@ public: TextDrawerUWP(Draw::DrawContext *draw); ~TextDrawerUWP(); - bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; protected: void EnsureFont(const FontStyle &style) override; void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) override; + bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; bool SupportsColorEmoji() const override { return true; } void ClearFonts() override; diff --git a/Common/Render/Text/draw_text_win.cpp b/Common/Render/Text/draw_text_win.cpp index 9caf064660..889b2e1a6e 100644 --- a/Common/Render/Text/draw_text_win.cpp +++ b/Common/Render/Text/draw_text_win.cpp @@ -154,14 +154,14 @@ void TextDrawerWin32::MeasureStringInternal(const FontStyle &style, std::string_ *h = extH; } -bool TextDrawerWin32::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { +bool TextDrawerWin32::DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) { if (str.empty()) { bitmapData.clear(); return false; } std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n")); - auto iter = fontMap_.find(fontStyle_); + auto iter = fontMap_.find(style); if (iter != fontMap_.end()) { SelectObject(ctx_->hDC, iter->second->hFont); } diff --git a/Common/Render/Text/draw_text_win.h b/Common/Render/Text/draw_text_win.h index 26ebc6c1ce..6388fd208f 100644 --- a/Common/Render/Text/draw_text_win.h +++ b/Common/Render/Text/draw_text_win.h @@ -16,11 +16,11 @@ public: TextDrawerWin32(Draw::DrawContext *draw); ~TextDrawerWin32(); - bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; protected: void EnsureFont(const FontStyle &style) override; void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) override; + bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override; bool SupportsColorEmoji() const override { return false; } void ClearFonts() override; diff --git a/Common/UI/Context.cpp b/Common/UI/Context.cpp index 26928a5a03..b4deb5af30 100644 --- a/Common/UI/Context.cpp +++ b/Common/UI/Context.cpp @@ -42,6 +42,10 @@ void UIContext::Init(Draw::DrawContext *thin3d, Draw::Pipeline *uipipe, Draw::Pi void UIContext::BeginFrame() { frameStartTime_ = time_now_d(); + // Text drawn before anything set a style uses the theme's. + if (fontStyle_->sizePts <= 0 && theme) { + *fontStyle_ = theme->uiFont; + } if (atlasInvalid_ || !uitexture_) { if (uitexture_) { uitexture_->Release(); @@ -266,10 +270,6 @@ void UIContext::SetFontScale(float scaleX, float scaleY) { void UIContext::SetFontStyle(const FontStyle &fontStyle) { *fontStyle_ = fontStyle; - if (textDrawer_) { - textDrawer_->SetFontScale(fontScaleX_, fontScaleY_); - textDrawer_->SetOrCreateFont(fontStyle); - } } static FontID AtlasFontFromStyle(const FontStyle &style) { @@ -314,8 +314,7 @@ void UIContext::DrawText(std::string_view str, float x, float y, uint32_t color, if (useFontTexture) Flush(); } else { - textDrawer_->SetFontScale(fontScaleX_, fontScaleY_); - textDrawer_->DrawString(*Draw(), str, x, y, color, align); + textDrawer_->DrawString(*Draw(), *fontStyle_, fontScaleX_, fontScaleY_, str, x, y, color, align); } RebindTexture(); } @@ -340,11 +339,10 @@ void UIContext::DrawTextRect(std::string_view str, const Bounds &bounds, uint32_ if (useFontTexture) Flush(); } else { - textDrawer_->SetFontScale(fontScaleX_, fontScaleY_); Bounds rounded = bounds; rounded.x = floorf(rounded.x); rounded.y = floorf(rounded.y); - textDrawer_->DrawStringRect(*Draw(), str, rounded, color, align); + textDrawer_->DrawStringRect(*Draw(), *fontStyle_, fontScaleX_, fontScaleY_, str, rounded, color, align); } RebindTexture(); } diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index bdac917e2d..98d8ee7030 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -115,6 +115,7 @@ static AtlasTextMetrics char_lines_metrics; static bool textDrawerInited = false; static TextDrawer *textDrawer = nullptr; +static const FontStyle textDrawerFont(FontFamily::SansSerif, 18, FontStyleFlags::Default); struct PPGeTextDrawerCacheKey { bool operator < (const PPGeTextDrawerCacheKey &other) const { if (align != other.align) @@ -769,9 +770,7 @@ static bool HasTextDrawer() { // Should we pass a draw_? Yes! UWP requires it. textDrawer = TextDrawer::Create(g_draw); if (textDrawer) { - textDrawer->SetFontScale(1.0f, 1.0f); textDrawer->SetForcedDPIScale(1.0f); - textDrawer->SetOrCreateFont(FontStyle(FontFamily::SansSerif, 18, FontStyleFlags::Default)); } textDrawerInited = true; return textDrawer != nullptr; @@ -786,11 +785,10 @@ void PPGeMeasureText(float *w, float *h, std::string_view text, float scale, int if (HasTextDrawer()) { float mw, mh; - textDrawer->SetFontScale(scale, scale); int dtalign = (WrapType & PPGE_LINE_WRAP_WORD) ? FLAG_WRAP_TEXT : 0; if (WrapType & PPGE_LINE_USE_ELLIPSIS) dtalign |= FLAG_ELLIPSIZE_TEXT; - textDrawer->MeasureStringRect(s, wrapWidth <= 0 ? 480.0f : wrapWidth, &mw, &mh, dtalign); + textDrawer->MeasureStringRect(textDrawerFont, scale, scale, s, wrapWidth <= 0 ? 480.0f : wrapWidth, &mw, &mh, dtalign); if (w) *w = mw; @@ -920,10 +918,9 @@ static PPGeTextDrawerImage PPGeGetTextImage(std::string_view text, const PPGeSty cacheItem->second.entry.lastUsedFrame = gpuStats.totals.numFlips; } else { std::vector bitmapData; - textDrawer->SetFontScale(style.scale, style.scale); Bounds b(0, 0, maxWidth, 272.0f); std::string cleaned = ReplaceAll(text, "\r", ""); - textDrawer->DrawStringBitmapRect(bitmapData, im.entry, Draw::DataFormat::R8_UNORM, cleaned.c_str(), b, tdalign, false); + textDrawer->DrawStringBitmapRect(textDrawerFont, style.scale, bitmapData, im.entry, Draw::DataFormat::R8_UNORM, cleaned.c_str(), b, tdalign, false); int bufwBytes = ((im.entry.bmWidth + 31) / 32) * 16; u32 sz = bufwBytes * (im.entry.bmHeight + 1); @@ -1125,16 +1122,15 @@ void PPGeDrawTextWrapped(std::string_view text, float x, float y, float wrapWidt float actualWidth, actualHeight; Bounds b(0, 0, wrapWidth <= 0 ? 480.0f - x : wrapWidth, wrapHeight); int tdalign = 0; - textDrawer->SetFontScale(style.scale, style.scale); - textDrawer->MeasureStringRect(s, b.w, &actualWidth, &actualHeight, tdalign | FLAG_WRAP_TEXT); + textDrawer->MeasureStringRect(textDrawerFont, style.scale, style.scale, s, b.w, &actualWidth, &actualHeight, tdalign | FLAG_WRAP_TEXT); // Check if we need to scale the text down to fit better. PPGeStyle adjustedStyle = style; if (wrapHeight != 0.0f && actualHeight > wrapHeight) { // Cheap way to get the line height. float oneLine, twoLines; - textDrawer->MeasureString("|", &actualWidth, &oneLine); - textDrawer->MeasureStringRect("|\n|", 480, &actualWidth, &twoLines); + textDrawer->MeasureString(textDrawerFont, style.scale, style.scale, "|", &actualWidth, &oneLine); + textDrawer->MeasureStringRect(textDrawerFont, style.scale, style.scale, "|\n|", 480, &actualWidth, &twoLines); float lineHeight = twoLines - oneLine; if (actualHeight > wrapHeight * maxScaleDown) { diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index aa04513b1c..40f037a2dd 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1005,10 +1005,6 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { ui_draw2d.Init(g_draw, texColorPipeline); uiContext->Init(g_draw, texColorPipeline, colorPipeline, &ui_draw2d); - if (uiContext->Text()) { - // This seems unnecessary. - // uiContext->Text()->SetOrCreateFont(FontStyle(FontID::invalid(), FontFamily::SansSerif, 20, FontStyleFlags::Default)); - } g_screenManager->setUIContext(uiContext); // Live before the queue is processed: a removal that comes in while the outputs' swapchains are being diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 35fa45989a..06f47a1aa4 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -3744,18 +3744,16 @@ bool TestFatShortNames() { class FakeTextDrawer : public TextDrawer { public: FakeTextDrawer() : TextDrawer(nullptr) {} - bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override { return false; } void TestDropShadow(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat srcFormat) { AddDropShadow(bitmapData, entry, srcFormat); } - const FontStyle &CurrentStyle() const { return fontStyle_; } - int measureCalls = 0; protected: void EnsureFont(const FontStyle &style) override {} + bool DrawStringBitmap(const FontStyle &style, std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override { return false; } void MeasureStringInternal(const FontStyle &style, std::string_view str, float *w, float *h) override { measureCalls++; std::vector lines; @@ -3774,49 +3772,40 @@ protected: bool TestTextDrawerCache() { FakeTextDrawer drawer; drawer.SetForcedDPIScale(1.0f); - drawer.SetOrCreateFont(FontStyle(FontFamily::SansSerif, 20, FontStyleFlags::Default)); + const FontStyle font20(FontFamily::SansSerif, 20, FontStyleFlags::Default); + const FontStyle font30(FontFamily::SansSerif, 30, FontStyleFlags::Default); float w, h; - drawer.MeasureString("hello", &w, &h); + drawer.MeasureString(font20, 1.0f, 1.0f, "hello", &w, &h); EXPECT_EQ_FLOAT(w, 50.0f); EXPECT_EQ_FLOAT(h, 20.0f); - drawer.MeasureString("hello", &w, &h); + drawer.MeasureString(font20, 1.0f, 1.0f, "hello", &w, &h); EXPECT_EQ_INT(drawer.measureCalls, 1); - // A different style is a different cache entry. - drawer.SetOrCreateFont(FontStyle(FontFamily::SansSerif, 30, FontStyleFlags::Default)); - drawer.MeasureString("hello", &w, &h); + // A different style is a different cache entry, but a different scale isn't. + drawer.MeasureString(font30, 1.0f, 1.0f, "hello", &w, &h); + EXPECT_EQ_INT(drawer.measureCalls, 2); + drawer.MeasureString(font30, 2.0f, 2.0f, "hello", &w, &h); + EXPECT_EQ_FLOAT(w, 100.0f); EXPECT_EQ_INT(drawer.measureCalls, 2); - // Measuring in another style and scale leaves the current ones alone. - const FontStyle small(FontFamily::SansSerif, 10, FontStyleFlags::Default); - drawer.MeasureString(small, 2.0f, 2.0f, "hello", &w, &h); - EXPECT_EQ_FLOAT(w, 100.0f); - EXPECT_EQ_INT(drawer.measureCalls, 3); - EXPECT_TRUE(drawer.CurrentStyle() == FontStyle(FontFamily::SansSerif, 30, FontStyleFlags::Default)); - drawer.MeasureString("hello", &w, &h); - EXPECT_EQ_FLOAT(w, 50.0f); - EXPECT_EQ_INT(drawer.measureCalls, 3); - const std::string_view para = "The quick brown fox jumps over the lazy dog, and then it does it again, and again, until the dog finally gets up and leaves."; - drawer.MeasureStringRect(para, 200.0f, &w, &h, FLAG_WRAP_TEXT); + drawer.MeasureStringRect(font30, 1.0f, 1.0f, para, 200.0f, &w, &h, FLAG_WRAP_TEXT); // The trailing space of a wrapped line is measured too. EXPECT_TRUE(w <= 210.0f); EXPECT_TRUE(h > 20.0f); float w2, h2; int callsBefore = drawer.measureCalls; - drawer.MeasureStringRect(para, 200.0f, &w2, &h2, FLAG_WRAP_TEXT); + drawer.MeasureStringRect(font30, 1.0f, 1.0f, para, 200.0f, &w2, &h2, FLAG_WRAP_TEXT); EXPECT_EQ_FLOAT(w, w2); EXPECT_EQ_FLOAT(h, h2); EXPECT_EQ_INT(drawer.measureCalls, callsBefore); // Scaling the font changes where the text wraps. - drawer.SetFontScale(0.5f, 0.5f); - drawer.MeasureStringRect(para, 200.0f, &w2, &h2, FLAG_WRAP_TEXT); + drawer.MeasureStringRect(font30, 0.5f, 0.5f, para, 200.0f, &w2, &h2, FLAG_WRAP_TEXT); EXPECT_TRUE(h2 < h * 0.5f); - drawer.SetFontScale(1.0f, 1.0f); - drawer.MeasureStringRect(para, 200.0f, &w, &h, FLAG_ELLIPSIZE_TEXT); + drawer.MeasureStringRect(font30, 1.0f, 1.0f, para, 200.0f, &w, &h, FLAG_ELLIPSIZE_TEXT); EXPECT_EQ_FLOAT(h, 20.0f); EXPECT_TRUE(w <= 200.0f); @@ -3830,7 +3819,6 @@ bool TestTextDrawerCache() { } drawer.TestDropShadow(bitmap, entry, Draw::DataFormat::R8_UNORM); }; - drawer.SetOrCreateFont(FontStyle(FontFamily::SansSerif, 24, FontStyleFlags::Default)); TextStringEntry entry1(0), entry2(0); std::vector bitmap; shadowTest(1.0f, entry1, bitmap);