diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index cb18046d9..c8b2bcfb8 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -19,6 +19,7 @@ #include "engine/point.hpp" #include "palette.h" #include "utils/display.h" +#include "utils/language.h" #include "utils/sdl_compat.h" #include "utils/utf8.hpp" @@ -33,7 +34,8 @@ std::unordered_map> FontKerns; std::array FontSizes = { 12, 24, 30, 42, 46, 22 }; std::array CJKWidth = { 17, 24, 28, 41, 47, 16 }; std::array HangulWidth = { 15, 20, 24, 35, 39, 15 }; -std::array LineHeights = { 12, 26, 38, 42, 50, 22 }; +constexpr std::array LineHeights = { 12, 26, 38, 42, 50, 22 }; +constexpr int SmallFontTallLineHeight = 16; std::array BaseLineOffset = { -3, -2, -3, -6, -7, 3 }; std::array ColorTranlations = { @@ -105,6 +107,11 @@ text_color GetColorFromFlags(UiFlags flags) return ColorWhitegold; } +uint16_t GetUnicodeRow(char32_t codePoint) +{ + return static_cast(codePoint) >> 8; +} + bool IsCJK(uint16_t row) { return row >= 0x4e && row <= 0x9f; @@ -115,6 +122,11 @@ bool IsHangul(uint16_t row) return row >= 0xac && row <= 0xd7; } +bool IsSmallFontTallRow(uint16_t row) +{ + return IsCJK(row) || IsHangul(row); +} + std::array *LoadFontKerning(GameFontTables size, uint16_t row) { uint32_t fontId = (size << 16) | row; @@ -257,6 +269,63 @@ private: std::size_t next_; }; +bool ContainsSmallFontTallCodepoints(string_view text) +{ + while (!text.empty()) { + const char32_t next = ConsumeFirstUtf8CodePoint(&text); + if (next == Utf8DecodeError) + break; + if (next == ZWSP) + continue; + if (IsSmallFontTallRow(GetUnicodeRow(next))) + return true; + } + return false; +} + +int GetLineHeight(string_view text, unsigned fontIndex) +{ + if (fontIndex == 0 && IsSmallFontTall() && ContainsSmallFontTallCodepoints(text)) { + return SmallFontTallLineHeight; + } + return LineHeights[fontIndex]; +} + +int GetLineHeight(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, unsigned fontIndex) +{ + constexpr std::array LineHeights = { 12, 26, 38, 42, 50, 22 }; + if (fontIndex == 0 && IsSmallFontTall()) { + char32_t prev = U'\0'; + char32_t next; + FmtArgParser fmtArgParser { fmt, args, argsLen }; + string_view rest = fmt; + while (!rest.empty()) { + if ((prev == U'{' || prev == U'}') && static_cast(prev) == rest[0]) { + rest.remove_prefix(1); + continue; + } + const std::optional fmtArgPos = fmtArgParser(rest); + if (fmtArgPos) { + if (ContainsSmallFontTallCodepoints(args[*fmtArgPos].GetFormatted())) + return true; + prev = U'\0'; + continue; + } + + next = ConsumeFirstUtf8CodePoint(&rest); + if (next == Utf8DecodeError) + break; + if (next == ZWSP) { + prev = next; + continue; + } + if (IsSmallFontTallRow(GetUnicodeRow(next))) + return SmallFontTallLineHeight; + } + } + return LineHeights[fontIndex]; +} + int DoDrawString(const Surface &out, string_view text, Rectangle rect, Point &characterPosition, int spacing, int lineHeight, int lineWidth, int rightMargin, int bottomMargin, UiFlags flags, GameFontTables size, text_color color) @@ -274,7 +343,7 @@ int DoDrawString(const Surface &out, string_view text, Rectangle rect, Point &ch if (next == ZWSP) continue; - uint32_t unicodeRow = next >> 8; + const uint32_t unicodeRow = GetUnicodeRow(next); if (unicodeRow != currentUnicodeRow || font == nullptr) { kerning = LoadFontKerning(size, unicodeRow); font = LoadFont(size, color, unicodeRow); @@ -349,7 +418,7 @@ int GetLineWidth(string_view text, GameFontTables size, int spacing, int *charac break; uint8_t frame = next & 0xFF; - uint32_t unicodeRow = next >> 8; + const uint32_t unicodeRow = GetUnicodeRow(next); if (unicodeRow != currentUnicodeRow || kerning == nullptr) { kerning = LoadFontKerning(size, unicodeRow); currentUnicodeRow = unicodeRow; @@ -400,7 +469,7 @@ int GetLineWidth(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen break; uint8_t frame = next & 0xFF; - uint32_t unicodeRow = next >> 8; + const uint32_t unicodeRow = GetUnicodeRow(next); if (unicodeRow != currentUnicodeRow || kerning == nullptr) { kerning = LoadFontKerning(size, unicodeRow); currentUnicodeRow = unicodeRow; @@ -465,7 +534,7 @@ std::string WordWrapString(string_view text, size_t width, GameFontTables size, if (codepoint != ZWSP) { uint8_t frame = codepoint & 0xFF; - uint32_t unicodeRow = codepoint >> 8; + const uint32_t unicodeRow = GetUnicodeRow(codepoint); if (unicodeRow != currentUnicodeRow || kerning == nullptr) { kerning = LoadFontKerning(size, unicodeRow); currentUnicodeRow = unicodeRow; @@ -535,7 +604,7 @@ uint32_t DrawString(const Surface &out, string_view text, const Rectangle &rect, const int bottomMargin = rect.size.height != 0 ? std::min(rect.position.y + rect.size.height, out.h()) : out.h(); if (lineHeight == -1) - lineHeight = LineHeights[size]; + lineHeight = GetLineHeight(text, size); if (HasAnyOf(flags, UiFlags::VerticalCenter)) { int textHeight = (std::count(text.cbegin(), text.cend(), '\n') + 1) * lineHeight; @@ -579,7 +648,7 @@ void DrawStringWithColors(const Surface &out, string_view fmt, DrawStringFormatA const int bottomMargin = rect.size.height != 0 ? std::min(rect.position.y + rect.size.height, out.h()) : out.h(); if (lineHeight == -1) - lineHeight = LineHeights[size]; + lineHeight = GetLineHeight(fmt, args, argsLen, size); if (HasAnyOf(flags, UiFlags::VerticalCenter)) { int textHeight = (CountNewlines(fmt, args, argsLen) + 1) * lineHeight; @@ -618,7 +687,7 @@ void DrawStringWithColors(const Surface &out, string_view fmt, DrawStringFormatA continue; } - uint32_t unicodeRow = next >> 8; + const uint32_t unicodeRow = GetUnicodeRow(next); if (unicodeRow != currentUnicodeRow || font == nullptr) { kerning = LoadFontKerning(size, unicodeRow); font = LoadFont(size, color, unicodeRow);