From 94717f38fa766d1972a457808bbd3f37cd66ca00 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 24 Dec 2023 01:53:49 +0100 Subject: [PATCH 1/3] FreeType: Calculate font size correctly depending on whether it is a pixel or a true type font. Fix small rendering issues. Fix #2953 --- src/font.cpp | 71 +++++++++++++++++++++++++++------------- src/game_interpreter.cpp | 8 ----- src/game_windows.cpp | 2 +- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/font.cpp b/src/font.cpp index 7866835424..7226cdaa84 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -28,10 +28,11 @@ #include "main_data.h" #ifdef HAVE_FREETYPE -# include -# include FT_FREETYPE_H -# include FT_BITMAP_H -# include FT_MODULE_H +# include +# include FT_FREETYPE_H +# include FT_BITMAP_H +# include FT_MODULE_H +# include FT_TRUETYPE_TABLES_H #endif #ifdef HAVE_HARFBUZZ @@ -145,6 +146,8 @@ namespace { void vApplyStyle(const Style& style) override; private: + void SetSize(int height, bool create); + FT_Face face = nullptr; std::vector ft_buffer; // Freetype uses the baseline as 0 and the built-in fonts the top @@ -287,20 +290,7 @@ FTFont::FTFont(Filesystem_Stream::InputStream is, int size, bool bold, bool ital } } - if (FT_HAS_COLOR(face)) { - // FIXME: Find the best size - FT_Select_Size(face, 0); - } else { - FT_Set_Pixel_Sizes(face, 0, size); - } - -#ifdef HAVE_HARFBUZZ - hb_buffer = hb_buffer_create(); - hb_font = hb_ft_font_create_referenced(face); - hb_ft_font_set_funcs(hb_font); -#endif - - baseline_offset = static_cast(size * (10.0 / 12.0)); + SetSize(size, true); if (!strcmp(face->family_name, "RM2000") || !strcmp(face->family_name, "RMG2000")) { // Workaround for bad kerning in RM2000 and RMG2000 fonts @@ -501,21 +491,56 @@ void FTFont::vApplyStyle(const Style& style) { return; } + SetSize(style.size, false); +} + +void FTFont::SetSize(int height, bool create) { if (FT_HAS_COLOR(face)) { // FIXME: Find the best size FT_Select_Size(face, 0); + } else if (FT_IS_SCALABLE(face)) { + // Calculate the pt size from px + auto table_os2 = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2)); + auto table_hori = static_cast(FT_Get_Sfnt_Table(face, ft_sfnt_hhea)); + + if (table_os2 && table_hori) { + int units; + if (table_os2->usWinAscent + table_os2->usWinDescent == 0) { + units = table_hori->Ascender - table_hori->Descender; + } else { + units = table_os2->usWinAscent + table_os2->usWinDescent; + } + + int pt = FT_MulDiv(face->units_per_EM, height, units); + if (FT_MulDiv(units, pt, face->units_per_EM) > height) { + --pt; + } + + height = std::max(1, pt); + } + + FT_Set_Pixel_Sizes(face, 0, height); } else { - FT_Set_Pixel_Sizes(face, 0, style.size); + FT_Set_Pixel_Sizes(face, 0, face->available_sizes->height); } #ifdef HAVE_HARFBUZZ - // Without this the sizes become desynchronized - hb_font_destroy(hb_font); + if (create) { + hb_buffer = hb_buffer_create(); + } else { + // Without this the sizes become desynchronized + hb_font_destroy(hb_font); + } hb_font = hb_ft_font_create_referenced(face); hb_ft_font_set_funcs(hb_font); #endif -} + baseline_offset = static_cast(FT_MulFix(face->ascender, face->size->metrics.y_scale) / 64); + if (baseline_offset == 0) { + // FIXME: Becomes 0 for FON files. How is the baseline calculated for them? + baseline_offset = static_cast(height * (10.0 / 12.0)); + } +} #endif FontRef Font::Default() { @@ -567,7 +592,7 @@ FontRef Font::CreateFtFont(Filesystem_Stream::InputStream is, int size, bool bol FreeFontMemory(); - std::string key = ToString(is.GetName()) + ":" + (bold ? "T" : "F") + (italic ? "T" : "F"); + std::string key = ToString(is.GetName()) + ":" + std::to_string(size) + ":" + (bold ? "T" : "F") + (italic ? "T" : "F"); auto it = ft_cache.find(key); diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index a12785e08e..acb5227440 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4324,14 +4324,6 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con text.line_spacing = (flags & (0xFF << 16)) >> 16; text.font_size = ValueOrVariableBitfield(com.parameters[0], 5, com.parameters[20]); - // Windows uses pt but we use px - int font_px = Utils::RoundTo(text.font_size * (72 / 96.0)); - // Maniac Patch appears to confuse pt and px causing large padding everywhere - // The next two lines emulate this problem - text.position_y = text.font_size - font_px; - text.line_spacing += text.font_size - font_px; - - text.font_size = font_px; // maniacs stores string parsing modes in the delimiters // x01 -> literal string diff --git a/src/game_windows.cpp b/src/game_windows.cpp index 4ca722385a..de678dc217 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -380,7 +380,7 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { auto style_guard = apply_style(font, text); int x = text.position_x; - int y = text.position_y; + int y = text.position_y + 2; // +2 to match the offset RPG_RT uses int text_color = 0; for (const auto& line: pm.GetLines()) { std::u32string line32; From b9221337dfd8b0c46adf28bfc897e9fade7531e4 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 24 Dec 2023 15:31:54 +0100 Subject: [PATCH 2/3] FtFont: Use floating point and ceil to align glyphs correctly. Finally looks very close to Windows. --- src/font.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/font.cpp b/src/font.cpp index 7226cdaa84..d6ac64f2b6 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -347,8 +347,8 @@ Rect FTFont::vGetSize(char32_t glyph) const { FT_GlyphSlot slot = face->glyph; Point advance; - advance.x = slot->advance.x / 64; - advance.y = slot->advance.y / 64; + advance.x = std::ceil(slot->advance.x / 64.0); + advance.y = std::ceil(slot->advance.y / 64.0); if (EP_UNLIKELY(rm2000_workaround)) { advance.x = 6; @@ -427,8 +427,8 @@ Font::GlyphRet FTFont::vRenderShaped(char32_t glyph) const { Point advance; Point offset; - advance.x = slot->advance.x / 64; - advance.y = slot->advance.y / 64; + advance.x = std::ceil(slot->advance.x / 64.0); + advance.y = std::ceil(slot->advance.y / 64.0); offset.x = slot->bitmap_left; offset.y = slot->bitmap_top - baseline_offset; @@ -474,8 +474,8 @@ std::vector FTFont::vShape(U32StringView txt) const { advance.y = s.height; ret.push_back({txt[info.cluster], advance, offset, true}); } else { - advance.x = pos.x_advance / 64; - advance.y = pos.y_advance / 64; + advance.x = std::ceil(pos.x_advance / 64.0); + advance.y = std::ceil(pos.y_advance / 64.0); offset.x = pos.x_offset / 64; offset.y = pos.y_offset / 64; ret.push_back({static_cast(info.codepoint), advance, offset, false}); @@ -535,7 +535,7 @@ void FTFont::SetSize(int height, bool create) { hb_ft_font_set_funcs(hb_font); #endif - baseline_offset = static_cast(FT_MulFix(face->ascender, face->size->metrics.y_scale) / 64); + baseline_offset = FT_MulFix(face->ascender, face->size->metrics.y_scale) / 64; if (baseline_offset == 0) { // FIXME: Becomes 0 for FON files. How is the baseline calculated for them? baseline_offset = static_cast(height * (10.0 / 12.0)); From 7f92d9715ea5fd8d1bc649c18c5382a242da17d9 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 4 Jan 2024 15:42:53 +0100 Subject: [PATCH 3/3] FtFont: Use round instead of ceil. Gives better results. --- src/font.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/font.cpp b/src/font.cpp index d6ac64f2b6..415e53ad59 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -347,8 +347,8 @@ Rect FTFont::vGetSize(char32_t glyph) const { FT_GlyphSlot slot = face->glyph; Point advance; - advance.x = std::ceil(slot->advance.x / 64.0); - advance.y = std::ceil(slot->advance.y / 64.0); + advance.x = Utils::RoundTo(slot->advance.x / 64.0); + advance.y = Utils::RoundTo(slot->advance.y / 64.0); if (EP_UNLIKELY(rm2000_workaround)) { advance.x = 6; @@ -427,8 +427,8 @@ Font::GlyphRet FTFont::vRenderShaped(char32_t glyph) const { Point advance; Point offset; - advance.x = std::ceil(slot->advance.x / 64.0); - advance.y = std::ceil(slot->advance.y / 64.0); + advance.x = Utils::RoundTo(slot->advance.x / 64.0); + advance.y = Utils::RoundTo(slot->advance.y / 64.0); offset.x = slot->bitmap_left; offset.y = slot->bitmap_top - baseline_offset; @@ -474,10 +474,10 @@ std::vector FTFont::vShape(U32StringView txt) const { advance.y = s.height; ret.push_back({txt[info.cluster], advance, offset, true}); } else { - advance.x = std::ceil(pos.x_advance / 64.0); - advance.y = std::ceil(pos.y_advance / 64.0); - offset.x = pos.x_offset / 64; - offset.y = pos.y_offset / 64; + advance.x = Utils::RoundTo(pos.x_advance / 64.0); + advance.y = Utils::RoundTo(pos.y_advance / 64.0); + offset.x = Utils::RoundTo(pos.x_offset / 64.0); + offset.y = Utils::RoundTo(pos.y_offset / 64.0); ret.push_back({static_cast(info.codepoint), advance, offset, false}); } }