diff options
author | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-07-10 13:44:10 +0200 |
---|---|---|
committer | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-07-15 11:40:25 +0200 |
commit | f83b1d648fc6ec2c64fd2c3483fede23c973c20f (patch) | |
tree | 04dd7950cbea1331870190d1caf9061011dfca05 /src/gui/truetypefont.cpp | |
parent | 32bcf41f01d4ed4a3a08030095611e75d6186d60 (diff) | |
download | mana-master.tar.gz mana-master.tar.bz2 mana-master.tar.xz mana-master.zip |
Rather than rendering the same texture 4 additional times, render a
specific outline version of the text. While reducing the number of times
the text is drawn, it does increase font texture use.
Result is a generally prettier outline due to rendering it properly at
sharp corners of the characters. The shadow for outlined text is now
also thicker, as appropriate.
As part of this change, the `TextRenderer` class was merged into the
`Graphics` and `TrueTypeFont` classes. There seemed to be little point
in having a separate function for this, apart from needing more static
casts to `Graphics*`.
Also fixed an issue where the font style was not being restored after
adjusting the font scale, when using an older SDL_ttf than 2.0.18.
Closes #87
Diffstat (limited to 'src/gui/truetypefont.cpp')
-rw-r--r-- | src/gui/truetypefont.cpp | 145 |
1 files changed, 100 insertions, 45 deletions
diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp index 444641b5..c85d08c0 100644 --- a/src/gui/truetypefont.cpp +++ b/src/gui/truetypefont.cpp @@ -53,6 +53,7 @@ bool operator==(SDL_Color lhs, SDL_Color rhs) lhs.a == rhs.a); } + class TextChunk { public: @@ -60,24 +61,49 @@ public: : text(text) {} - void generate(TTF_Font *font) + void render(Graphics *graphics, + int x, int y, + TTF_Font *font, + std::unique_ptr<Image> &img, + float scale); + + const std::string text; + std::unique_ptr<Image> regular; + std::unique_ptr<Image> outlined; +}; + +void TextChunk::render(Graphics *graphics, + int x, int y, + TTF_Font *font, + std::unique_ptr<Image> &img, + float scale) +{ + if (!img) { // Always render in white, we'll use color modulation when rendering + constexpr SDL_Color white = { 255, 255, 255, 255 }; SDL_Surface *surface = TTF_RenderUTF8_Blended(font, getSafeUtf8String(text), - SDL_Color { 255, 255, 255, 255 }); + white); - if (!surface) - return; + if (surface) + { + img.reset(Image::load(surface)); + SDL_FreeSurface(surface); + } + } - img.reset(Image::load(surface)); + if (img) + { + graphics->drawRescaledImageF(img.get(), 0, 0, x, y, + img->getWidth(), + img->getHeight(), + img->getWidth() / scale, + img->getHeight() / scale, true); - SDL_FreeSurface(surface); } +} - std::unique_ptr<Image> img; - const std::string text; -}; std::list<TrueTypeFont*> TrueTypeFont::mFonts; float TrueTypeFont::mScale = 1.0f; @@ -85,6 +111,7 @@ float TrueTypeFont::mScale = 1.0f; TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style) : mFilename(filename) , mPointSize(size) + , mStyle(style) { if (TTF_Init() == -1) { @@ -93,14 +120,18 @@ TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style) } mFont = TTF_OpenFont(filename.c_str(), size * mScale); + mFontOutline = TTF_OpenFont(filename.c_str(), size * mScale); - if (!mFont) + if (!mFont || !mFontOutline) { throw GCN_EXCEPTION("SDLTrueTypeFont::SDLTrueTypeFont: " + std::string(TTF_GetError())); } TTF_SetFontStyle(mFont, style); + TTF_SetFontStyle(mFontOutline, style); + + TTF_SetFontOutline(mFontOutline, static_cast<int>(mScale)); mFonts.push_back(this); } @@ -112,6 +143,9 @@ TrueTypeFont::~TrueTypeFont() if (mFont) TTF_CloseFont(mFont); + if (mFontOutline) + TTF_CloseFont(mFontOutline); + TTF_Quit(); } @@ -123,37 +157,41 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics, return; auto *g = static_cast<Graphics *>(graphics); + TextChunk &chunk = getChunk(text); - bool found = false; + chunk.render(g, x, y, mFont, chunk.regular, mScale); +} - for (auto i = mCache.begin(); i != mCache.end(); ++i) - { - auto &chunk = *i; - if (chunk.text == text) - { - // Raise priority: move it to front - mCache.splice(mCache.begin(), mCache, i); - found = true; - break; - } - } +void TrueTypeFont::drawString(Graphics *graphics, + const std::string &text, + int x, int y, + const std::optional<gcn::Color> &outlineColor, + const std::optional<gcn::Color> &shadowColor) +{ + if (text.empty()) + return; + + auto *g = static_cast<Graphics *>(graphics); + auto color = graphics->getColor(); + TextChunk &chunk = getChunk(text); - if (!found) + if (shadowColor) { - if (mCache.size() >= CACHE_SIZE) - mCache.pop_back(); - mCache.emplace_front(text); - mCache.front().generate(mFont); + g->setColor(*shadowColor); + if (outlineColor) + chunk.render(g, x, y, mFontOutline, chunk.outlined, mScale); + else + chunk.render(g, x + 1, y + 1, mFont, chunk.regular, mScale); } - if (auto img = mCache.front().img.get()) + if (outlineColor) { - g->drawRescaledImageF(img, 0, 0, x, y, - img->getWidth(), - img->getHeight(), - img->getWidth() / mScale, - img->getHeight() / mScale, true); + g->setColor(*outlineColor); + chunk.render(g, x - 1, y - 1, mFontOutline, chunk.outlined, mScale); } + + g->setColor(color); + chunk.render(g, x, y, mFont, chunk.regular, mScale); } void TrueTypeFont::updateFontScale(float scale) @@ -167,9 +205,16 @@ void TrueTypeFont::updateFontScale(float scale) { #if SDL_TTF_VERSION_ATLEAST(2, 0, 18) TTF_SetFontSize(font->mFont, font->mPointSize * mScale); + TTF_SetFontSize(font->mFontOutline, font->mPointSize * mScale); + TTF_SetFontOutline(font->mFontOutline, mScale); #else TTF_CloseFont(font->mFont); + TTF_CloseFont(font->mFontOutline); font->mFont = TTF_OpenFont(font->mFilename.c_str(), font->mPointSize * mScale); + font->mFontOutline = TTF_OpenFont(font->mFilename.c_str(), font->mPointSize * mScale); + TTF_SetFontStyle(font->mFont, font->mStyle); + TTF_SetFontStyle(font->mFontOutline, font->mStyle); + TTF_SetFontOutline(font->mFontOutline, mScale); #endif font->mCache.clear(); @@ -178,19 +223,11 @@ void TrueTypeFont::updateFontScale(float scale) int TrueTypeFont::getWidth(const std::string &text) const { - for (auto i = mCache.begin(); i != mCache.end(); i++) - { - if (i->text == text) - { - // Raise priority: move it to front - // Assumption is that TTF::draw will be called next - mCache.splice(mCache.begin(), mCache, i); - if (i->img) - return std::ceil(i->img->getWidth() / mScale); - return 0; - } - } + TextChunk &chunk = getChunk(text); + if (auto img = chunk.regular.get()) + return std::ceil(img->getWidth() / mScale); + // If the image wasn't created yet, just calculate the width of the text int w, h; TTF_SizeUTF8(mFont, getSafeUtf8String(text), &w, &h); return std::ceil(w / mScale); @@ -205,3 +242,21 @@ int TrueTypeFont::getLineHeight() const { return std::ceil(TTF_FontLineSkip(mFont) / mScale); } + +TextChunk &TrueTypeFont::getChunk(const std::string &text) const +{ + for (auto i = mCache.begin(); i != mCache.end(); i++) + { + if (i->text == text) + { + // Raise priority: move it to front + mCache.splice(mCache.begin(), mCache, i); + return *i; + } + } + + if (mCache.size() >= CACHE_SIZE) + mCache.pop_back(); + + return mCache.emplace_front(text); +} |