From da7a32c6ea92242c99412c2702ad59df36007de4 Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Mon, 25 Mar 2024 08:29:53 +0100 Subject: Added support for HiDPI fonts * TrueTypeFont class now takes into account the graphics scale, in order to render an appropriate higher-resolution texture. * Removed TrueTypeFont::fontCounter, since TTF_Init/TTF_Quit already keep a counter. * Avoid copying the rendered string needlessly, when it already exists in the cache. Avoid another copy, when inserting a new chunk into the cache. --- src/client.cpp | 5 +- src/graphics.h | 6 ++ src/gui/gui.cpp | 10 ++- src/gui/gui.h | 4 +- src/gui/truetypefont.cpp | 154 ++++++++++++++++++++++++++--------------------- src/gui/truetypefont.h | 8 +++ src/openglgraphics.cpp | 11 ++-- src/openglgraphics.h | 2 +- src/sdlgraphics.cpp | 1 + 9 files changed, 120 insertions(+), 81 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 7d887436..8e144e8e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1339,12 +1339,9 @@ void Client::checkGraphicsSize() const int width = graphics->getWidth(); const int height = graphics->getHeight(); - const auto guiTop = gui->getTop(); - if (guiTop->getWidth() == width && guiTop->getHeight() == height) + if (!gui->videoResized(width, height)) return; - gui->videoResized(width, height); - if (mDesktop) mDesktop->setSize(width, height); diff --git a/src/graphics.h b/src/graphics.h index 79513e2c..55f67568 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -202,6 +202,11 @@ class Graphics : public gcn::Graphics */ int getHeight() const { return mHeight; } + /** + * Returns the graphics scale. + */ + float getScale() const { return mScale; } + /** * Converts a window coordinate to a logical coordinate. Used for * converting mouse coordinates. @@ -237,6 +242,7 @@ class Graphics : public gcn::Graphics protected: int mWidth = 0; int mHeight = 0; + float mScale = 1.0f; gcn::Color mColor; }; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 68f43cc3..ae74cab2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -106,6 +106,9 @@ Gui::Gui(Graphics *graphics) std::string fontFile = branding.getValue("font", "fonts/dejavusans.ttf"); std::string path = resman->getPath(fontFile); + // Initialize the font scale before creating the fonts + TrueTypeFont::updateFontScale(graphics->getScale()); + try { mGuiFont = new TrueTypeFont(path, fontSize); @@ -222,17 +225,20 @@ void Gui::draw() mGraphics->_endDraw(); } -void Gui::videoResized(int width, int height) +bool Gui::videoResized(int width, int height) { + TrueTypeFont::updateFontScale(static_cast(mGraphics)->getScale()); + auto *top = static_cast(getTop()); int oldWidth = top->getWidth(); int oldHeight = top->getHeight(); if (oldWidth == width && oldHeight == height) - return; + return false; top->setSize(width, height); top->adjustAfterResize(oldWidth, oldHeight); + return true; } void Gui::setUseCustomCursor(bool customCursor) diff --git a/src/gui/gui.h b/src/gui/gui.h index b731514f..29dcdef2 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -86,8 +86,10 @@ class Gui : public gcn::Gui /** * Called when the application window has been resized. + * + * Returns whether the top widget changed size. */ - void videoResized(int width, int height); + bool videoResized(int width, int height); gcn::FocusHandler *getFocusHandler() const { return mFocusHandler; } diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp index adb49780..30037f84 100644 --- a/src/gui/truetypefont.cpp +++ b/src/gui/truetypefont.cpp @@ -29,6 +29,9 @@ #include #include +#include +#include + const unsigned int CACHE_SIZE = 256; static const char *getSafeUtf8String(const std::string &text) @@ -42,64 +45,55 @@ static const char *getSafeUtf8String(const std::string &text) return buf; } -class TextChunk +bool operator==(SDL_Color lhs, SDL_Color rhs) { - public: - TextChunk(const std::string &text, const gcn::Color &color) : - text(text), color(color) - { - } - - ~TextChunk() - { - delete img; - } - - bool operator==(const TextChunk &chunk) const - { - return (chunk.text == text && chunk.color == color); - } + return (lhs.r == rhs.r && + lhs.g == rhs.g && + lhs.b == rhs.b && + lhs.a == rhs.a); +} - void generate(TTF_Font *font) - { - SDL_Color sdlCol; - sdlCol.r = color.r; - sdlCol.g = color.g; - sdlCol.b = color.b; - sdlCol.a = color.a; +class TextChunk +{ +public: + TextChunk(const std::string &text, SDL_Color color) + : text(text) + , color(color) + {} - const char *str = getSafeUtf8String(text); - SDL_Surface *surface = TTF_RenderUTF8_Blended( - font, str, sdlCol); + void generate(TTF_Font *font) + { + SDL_Surface *surface = TTF_RenderUTF8_Blended(font, + getSafeUtf8String(text), + color); - if (!surface) - { - img = nullptr; - return; - } + if (!surface) + return; - img = Image::load(surface); + img.reset(Image::load(surface)); - SDL_FreeSurface(surface); - } + SDL_FreeSurface(surface); + } - Image *img = nullptr; - std::string text; - gcn::Color color; + std::unique_ptr img; + const std::string text; + const SDL_Color color; }; -static int fontCounter; +std::list TrueTypeFont::mFonts; +float TrueTypeFont::mScale = 1.0f; TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style) + : mFilename(filename) + , mPointSize(size) { - if (fontCounter == 0 && TTF_Init() == -1) + if (TTF_Init() == -1) { throw GCN_EXCEPTION("Unable to initialize SDL_ttf: " + std::string(TTF_GetError())); } - ++fontCounter; - mFont = TTF_OpenFont(filename.c_str(), size); + mFont = TTF_OpenFont(filename.c_str(), size * mScale); if (!mFont) { @@ -108,15 +102,18 @@ TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style) } TTF_SetFontStyle(mFont, style); + + mFonts.push_back(this); } TrueTypeFont::~TrueTypeFont() { - TTF_CloseFont(mFont); - --fontCounter; + mFonts.remove(this); + + if (mFont) + TTF_CloseFont(mFont); - if (fontCounter == 0) - TTF_Quit(); + TTF_Quit(); } void TrueTypeFont::drawString(gcn::Graphics *graphics, @@ -126,26 +123,25 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics, if (text.empty()) return; - auto *g = dynamic_cast(graphics); + auto *g = static_cast(graphics); + const gcn::Color col = g->getColor(); - if (!g) - throw "Not a valid graphics object!"; - - gcn::Color col = g->getColor(); - const float alpha = col.a / 255.0f; - - /* The alpha value is ignored at string generation so avoid caching the + /* The alpha value is ignored at image generation to avoid caching the * same text with different alpha values. */ - col.a = 255; - - TextChunk chunk(text, col); + const SDL_Color color = { + static_cast(col.r), + static_cast(col.g), + static_cast(col.b), + 255 + }; bool found = false; for (auto i = mCache.begin(); i != mCache.end(); ++i) { - if (chunk == (*i)) + auto &chunk = *i; + if (chunk.text == text && chunk.color == color) { // Raise priority: move it to front mCache.splice(mCache.begin(), mCache, i); @@ -154,19 +150,42 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics, } } - // Surface not found if (!found) { if (mCache.size() >= CACHE_SIZE) mCache.pop_back(); - mCache.push_front(chunk); + mCache.emplace_front(text, color); mCache.front().generate(mFont); } - if (mCache.front().img) + if (auto img = mCache.front().img.get()) + { + img->setAlpha(col.a / 255.0f); + g->drawRescaledImageF(img, 0, 0, x, y, + img->getWidth(), + img->getHeight(), + img->getWidth() / mScale, + img->getHeight() / mScale); + } +} + +void TrueTypeFont::updateFontScale(float scale) +{ + if (mScale == scale) + return; + + mScale = scale; + + for (auto font : mFonts) { - mCache.front().img->setAlpha(alpha); - g->drawImage(mCache.front().img, x, y); +#if SDL_TTF_VERSION_ATLEAST(2, 0, 18) + TTF_SetFontSize(font->mFont, font->mPointSize * mScale); +#else + TTF_CloseFont(font->mFont); + font->mFont = TTF_OpenFont(font->mFilename.c_str(), font->mPointSize * mScale); +#endif + + font->mCache.clear(); } } @@ -180,23 +199,22 @@ int TrueTypeFont::getWidth(const std::string &text) const // Assumption is that TTF::draw will be called next mCache.splice(mCache.begin(), mCache, i); if (i->img) - return i->img->getWidth(); + return std::ceil(i->img->getWidth() / mScale); return 0; } } int w, h; - const char *str = getSafeUtf8String(text); - TTF_SizeUTF8(mFont, str, &w, &h); - return w; + TTF_SizeUTF8(mFont, getSafeUtf8String(text), &w, &h); + return std::ceil(w / mScale); } int TrueTypeFont::getHeight() const { - return TTF_FontHeight(mFont); + return std::ceil(TTF_FontHeight(mFont) / mScale); } int TrueTypeFont::getLineHeight() const { - return TTF_FontLineSkip(mFont); + return std::ceil(TTF_FontLineSkip(mFont) / mScale); } diff --git a/src/gui/truetypefont.h b/src/gui/truetypefont.h index d42400d8..b3ebcc73 100644 --- a/src/gui/truetypefont.h +++ b/src/gui/truetypefont.h @@ -72,11 +72,19 @@ class TrueTypeFont : public gcn::Font const std::string &text, int x, int y) override; + static void updateFontScale(float scale); + private: + const std::string mFilename; TTF_Font *mFont; + const int mPointSize; + // Word surfaces cache mutable std::list mCache; + + static std::list mFonts; + static float mScale; }; #endif diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp index 9e77e5f7..56c16586 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -131,7 +131,7 @@ void OpenGLGraphics::setReduceInputLag(bool reduceInputLag) void OpenGLGraphics::updateSize(int windowWidth, int windowHeight, float scale) { - mScale = scale; + mUserScale = scale; int drawableWidth; int drawableHeight; @@ -142,11 +142,12 @@ void OpenGLGraphics::updateSize(int windowWidth, int windowHeight, float scale) float displayScaleX = windowWidth > 0 ? static_cast(drawableWidth) / windowWidth : 1.0f; float displayScaleY = windowHeight > 0 ? static_cast(drawableHeight) / windowHeight : 1.0f; - mScaleX = mScale * displayScaleX; - mScaleY = mScale * displayScaleY; + mScaleX = mUserScale * displayScaleX; + mScaleY = mUserScale * displayScaleY; mWidth = std::ceil(drawableWidth / mScaleX); mHeight = std::ceil(drawableHeight / mScaleY); + mScale = mScaleX; glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -557,8 +558,8 @@ void OpenGLGraphics::updateScreen() void OpenGLGraphics::windowToLogical(int windowX, int windowY, float &logicalX, float &logicalY) const { - logicalX = windowX / mScale; - logicalY = windowY / mScale; + logicalX = windowX / mUserScale; + logicalY = windowY / mUserScale; } SDL_Surface *OpenGLGraphics::getScreenshot() diff --git a/src/openglgraphics.h b/src/openglgraphics.h index dbeab80a..654a7194 100644 --- a/src/openglgraphics.h +++ b/src/openglgraphics.h @@ -126,7 +126,7 @@ class OpenGLGraphics final : public Graphics GLfloat *mFloatTexArray; GLint *mIntTexArray; GLint *mIntVertArray; - float mScale = 1.0f; + float mUserScale = 1.0f; float mScaleX = 1.0f; float mScaleY = 1.0f; bool mAlpha = false; diff --git a/src/sdlgraphics.cpp b/src/sdlgraphics.cpp index dc56496e..a2fc2268 100644 --- a/src/sdlgraphics.cpp +++ b/src/sdlgraphics.cpp @@ -101,6 +101,7 @@ void SDLGraphics::updateSize(int windowWidth, int windowHeight, float scale) mWidth = std::ceil(mWidth / scaleX); mHeight = std::ceil(mHeight / scaleY); + mScale = scaleX; SDL_RenderSetScale(mRenderer, scaleX, scaleY); } -- cgit v1.2.3-60-g2f50