From 85ca885d412b12a3b339866d583edad163bd1930 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 2 Oct 2023 09:50:49 +0200 Subject: [PATCH 1/4] Automatic addition of missing glyphs into imgui atlas, Only do the automatic glyph addition for the default font --- src/imgui/imgui_draw.cpp | 13 ++++++++-- src/slic3r/GUI/ImGuiWrapper.cpp | 45 ++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/imgui/imgui_draw.cpp b/src/imgui/imgui_draw.cpp index ab184928f7..62a470233b 100644 --- a/src/imgui/imgui_draw.cpp +++ b/src/imgui/imgui_draw.cpp @@ -3282,11 +3282,20 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const { - if (c >= (size_t)IndexLookup.Size) + // PrusaSlicer extension: call the following function whenever the fallback is needed. + // The goal is to not modify ImGui code too much. + void imgui_rendered_fallback_glyph(ImWchar c); + + + if (c >= (size_t)IndexLookup.Size) { + imgui_rendered_fallback_glyph(c); return FallbackGlyph; + } const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) + if (i == (ImWchar)-1) { + imgui_rendered_fallback_glyph(c); return FallbackGlyph; + } return &Glyphs.Data[i]; } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 61e8dd7fa9..43cfde0ad5 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -46,6 +46,28 @@ // suggest location #include "libslic3r/ClipperUtils.hpp" // Slic3r::intersection + +// Following two sets keeps characters that ImGui tried to render, but they were not in the atlas, +// and ones that we already tried to add into the atlas. +std::set s_missing_chars; +std::set s_fixed_chars; + +// This is a free function that ImGui calls when it renders +// a fallback glyph for c. +void imgui_rendered_fallback_glyph(ImWchar c) +{ + if (ImGui::GetIO().Fonts->Fonts[0] == ImGui::GetFont()) { + // Only do this when we are using the default ImGui font. Otherwise this would conflict with + // EmbossStyleManager's font handling and we would load glyphs needlessly. + if (s_fixed_chars.find(c) == s_fixed_chars.end()) { + // Do not add this if we already tried to fix it. We don't want to + // rebuild the atlas in every frame when the glyph is not available at all. + s_missing_chars.emplace(c); + } + } +} + + namespace Slic3r { namespace GUI { @@ -371,6 +393,16 @@ void ImGuiWrapper::render() ImGui::Render(); render_draw_data(ImGui::GetDrawData()); m_new_frame_open = false; + + if (! s_missing_chars.empty()) { + // If there were some characters that ImGui was unable to render, we will destroy current font. + // It will be rebuilt in the next call of new_frame including these. We also move all characters + // that we already added this way into the list of missing chars again, so all are added at once. + destroy_font(); + for (ImWchar c : s_fixed_chars) + s_missing_chars.emplace(c); + s_fixed_chars.clear(); + } } bool ImGuiWrapper::button(const std::string& label, const ImVec2 &size, bool enable) @@ -1102,13 +1134,20 @@ void ImGuiWrapper::init_font(bool compress) builder.AddChar(ImWchar(0x2026)); // … if (m_font_cjk) { - // This is a temporary fix of https://github.com/prusa3d/PrusaSlicer/issues/8171. The translation - // contains characters not in the ImGui ranges for simplified Chinese. For now, just add them manually. - // In future, it might be worth to parse the dictionary and add all the necessary characters. + // https://github.com/prusa3d/PrusaSlicer/issues/8171: The translation + // contains characters not in the ImGui ranges for simplified Chinese. Add them manually. + // This should no longer be needed because the following block would add them automatically. builder.AddChar(ImWchar(0x5ED3)); builder.AddChar(ImWchar(0x8F91)); } + // Add the characters that that needed the fallback character. + for (ImWchar c : s_missing_chars) { + builder.AddChar(c); + s_fixed_chars.emplace(c); + } + s_missing_chars.clear(); + #ifdef __APPLE__ if (m_font_cjk) // Apple keyboard shortcuts are only contained in the CJK fonts. From dbc548b2aa9f12077c0104c5877cbc1ced79fb09 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 5 Jun 2024 15:55:39 +0200 Subject: [PATCH 2/4] Automatic addition of missing glyphs: fix issues on DPI change --- src/slic3r/GUI/ImGuiWrapper.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 43cfde0ad5..a9377d99e9 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -396,12 +396,8 @@ void ImGuiWrapper::render() if (! s_missing_chars.empty()) { // If there were some characters that ImGui was unable to render, we will destroy current font. - // It will be rebuilt in the next call of new_frame including these. We also move all characters - // that we already added this way into the list of missing chars again, so all are added at once. + // It will be rebuilt in the next call of new_frame including these. destroy_font(); - for (ImWchar c : s_fixed_chars) - s_missing_chars.emplace(c); - s_fixed_chars.clear(); } } @@ -1553,6 +1549,13 @@ void ImGuiWrapper::destroy_font() io.Fonts->TexID = 0; glsafe(::glDeleteTextures(1, &m_font_texture)); m_font_texture = 0; + + // We have destroyed current font, including all characters that we may have added dynamically. + // Move move all characters that we already added into the list of missing chars again, + // so they are all added at once. + for (ImWchar c : s_fixed_chars) + s_missing_chars.emplace(c); + s_fixed_chars.clear(); } } From ccd891044278f3a4e7e3dc64621bad2cb929ae9c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 6 Jun 2024 11:18:41 +0200 Subject: [PATCH 3/4] Automatic addition of missing glyphs: load CJK when a glyph is not found --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 135 ++++++++++++++++++-------------- src/slic3r/GUI/ImGuiWrapper.hpp | 3 +- 3 files changed, 78 insertions(+), 62 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fff19f2d84..4b762a4496 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1823,7 +1823,7 @@ void GLCanvas3D::render() #ifdef SHOW_IMGUI_DEMO_WINDOW if (show_imgui_demo_window) ImGui::ShowDemoWindow(); -#endif // SHOW_IMGUI_DEMO_WINDOW +#endif // SHOW_IMGUI_DEMO_WINDOW const bool is_looking_downward = camera.is_looking_downward(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index a9377d99e9..bd513bc1d7 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -51,6 +51,7 @@ // and ones that we already tried to add into the atlas. std::set s_missing_chars; std::set s_fixed_chars; +bool s_font_cjk; // This is a free function that ImGui calls when it renders // a fallback glyph for c. @@ -59,10 +60,21 @@ void imgui_rendered_fallback_glyph(ImWchar c) if (ImGui::GetIO().Fonts->Fonts[0] == ImGui::GetFont()) { // Only do this when we are using the default ImGui font. Otherwise this would conflict with // EmbossStyleManager's font handling and we would load glyphs needlessly. - if (s_fixed_chars.find(c) == s_fixed_chars.end()) { - // Do not add this if we already tried to fix it. We don't want to - // rebuild the atlas in every frame when the glyph is not available at all. + auto it = s_fixed_chars.find(c); + if (it == s_fixed_chars.end()) { + // This is the first time we are trying to fix this character. s_missing_chars.emplace(c); + } else { + // We already tried to add this, but it is still not there. There is a chance + // that loading the CJK font would make this available. + if (! s_font_cjk) { + s_font_cjk = true; + s_missing_chars.emplace(c); + s_fixed_chars.erase(it); + } else { + // We did everything we could. The glyph was not available. + // Do not try to add it anymore. + } } } } @@ -170,6 +182,47 @@ ImGuiWrapper::ImGuiWrapper() init_style(); ImGui::GetIO().IniFilename = nullptr; + + static const ImWchar ranges_latin2[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x0100, 0x017F, // Latin Extended-A + 0, + }; + static const ImWchar ranges_turkish[] = { + 0x0020, 0x01FF, // Basic Latin + Latin Supplement + 0x0100, 0x017F, // Latin Extended-A + 0x0180, 0x01FF, // Turkish + 0, + }; + static const ImWchar ranges_vietnamese[] = + { + 0x0020, 0x00FF, // Basic Latin + 0x0102, 0x0103, + 0x0110, 0x0111, + 0x0128, 0x0129, + 0x0168, 0x0169, + 0x01A0, 0x01A1, + 0x01AF, 0x01B0, + 0x1EA0, 0x1EF9, + 0, + }; + + m_lang_glyphs_info.emplace_back("cs", ranges_latin2, false); + m_lang_glyphs_info.emplace_back("pl", ranges_latin2, false); + m_lang_glyphs_info.emplace_back("hu", ranges_latin2, false); + m_lang_glyphs_info.emplace_back("sl", ranges_latin2, false); + m_lang_glyphs_info.emplace_back("ru", ImGui::GetIO().Fonts->GetGlyphRangesCyrillic(), false); // Default + about 400 Cyrillic characters + m_lang_glyphs_info.emplace_back("uk", ImGui::GetIO().Fonts->GetGlyphRangesCyrillic(), false); + m_lang_glyphs_info.emplace_back("be", ImGui::GetIO().Fonts->GetGlyphRangesCyrillic(), false); + m_lang_glyphs_info.emplace_back("tr", ranges_turkish, false); + m_lang_glyphs_info.emplace_back("vi", ranges_vietnamese, false); + m_lang_glyphs_info.emplace_back("ja", ImGui::GetIO().Fonts->GetGlyphRangesJapanese(), true); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs + m_lang_glyphs_info.emplace_back("ko", ImGui::GetIO().Fonts->GetGlyphRangesKorean(), true); // Default + Korean characters + m_lang_glyphs_info.emplace_back("zh_TW",ImGui::GetIO().Fonts->GetGlyphRangesChineseFull(), true); // Traditional Chinese: Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs + m_lang_glyphs_info.emplace_back("zh", ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon(), true); // Simplified Chinese: Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese + m_lang_glyphs_info.emplace_back("th", ImGui::GetIO().Fonts->GetGlyphRangesThai(), false); + m_lang_glyphs_info.emplace_back("else", ImGui::GetIO().Fonts->GetGlyphRangesDefault(), false); } ImGuiWrapper::~ImGuiWrapper() @@ -189,62 +242,18 @@ void ImGuiWrapper::set_language(const std::string &language) } const ImWchar *ranges = nullptr; - size_t idx = language.find('_'); - std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx); - static const ImWchar ranges_latin2[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x0100, 0x017F, // Latin Extended-A - 0, - }; - static const ImWchar ranges_turkish[] = { - 0x0020, 0x01FF, // Basic Latin + Latin Supplement - 0x0100, 0x017F, // Latin Extended-A - 0x0180, 0x01FF, // Turkish - 0, - }; - static const ImWchar ranges_vietnamese[] = - { - 0x0020, 0x00FF, // Basic Latin - 0x0102, 0x0103, - 0x0110, 0x0111, - 0x0128, 0x0129, - 0x0168, 0x0169, - 0x01A0, 0x01A1, - 0x01AF, 0x01B0, - 0x1EA0, 0x1EF9, - 0, - }; - m_font_cjk = false; - if (lang == "cs" || lang == "pl" || lang == "hu" || lang == "sl") { - ranges = ranges_latin2; - } else if (lang == "ru" || lang == "uk" || lang == "be") { - ranges = ImGui::GetIO().Fonts->GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters - } else if (lang == "tr") { - ranges = ranges_turkish; - } else if (lang == "vi") { - ranges = ranges_vietnamese; - } else if (lang == "ja") { - ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs - m_font_cjk = true; - } else if (lang == "ko") { - ranges = ImGui::GetIO().Fonts->GetGlyphRangesKorean(); // Default + Korean characters - m_font_cjk = true; - } else if (lang == "zh") { - ranges = (language == "zh_TW") ? - // Traditional Chinese - // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs - ImGui::GetIO().Fonts->GetGlyphRangesChineseFull() : - // Simplified Chinese - // Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese - ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon(); - m_font_cjk = true; - } else if (lang == "th") { - ranges = ImGui::GetIO().Fonts->GetGlyphRangesThai(); // Default + Thai characters - } else { - ranges = ImGui::GetIO().Fonts->GetGlyphRangesDefault(); // Basic Latin, Extended Latin + + // Get glyph ranges for current language, std CLK flag to inform which font files need to be loaded. + for (const auto& [lang_str, lang_ranges, lang_cjk] : m_lang_glyphs_info) { + if (boost::istarts_with(language, lang_str) || lang_str == "else") { + ranges = lang_ranges; + s_font_cjk = lang_cjk; + } } + s_missing_chars.clear(); + s_fixed_chars.clear(); + if (ranges != m_glyph_ranges) { m_glyph_ranges = ranges; destroy_font(); @@ -1129,7 +1138,7 @@ void ImGuiWrapper::init_font(bool compress) builder.AddChar(ImWchar(0x2026)); // … - if (m_font_cjk) { + if (s_font_cjk) { // https://github.com/prusa3d/PrusaSlicer/issues/8171: The translation // contains characters not in the ImGui ranges for simplified Chinese. Add them manually. // This should no longer be needed because the following block would add them automatically. @@ -1145,7 +1154,7 @@ void ImGuiWrapper::init_font(bool compress) s_missing_chars.clear(); #ifdef __APPLE__ - if (m_font_cjk) + if (s_font_cjk) // Apple keyboard shortcuts are only contained in the CJK fonts. builder.AddRanges(ranges_keyboard_shortcuts); #endif @@ -1153,7 +1162,13 @@ void ImGuiWrapper::init_font(bool compress) //FIXME replace with io.Fonts->AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, m_font_size, nullptr, ranges.Data); //https://github.com/ocornut/imgui/issues/220 - ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + (m_font_cjk ? "NotoSansCJK-Regular.ttc" : "NotoSans-Regular.ttf")).c_str(), m_font_size, nullptr, ranges.Data); + ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + "NotoSans-Regular.ttf").c_str(), m_font_size, nullptr, ranges.Data); + if (s_font_cjk) { + ImFontConfig config; + config.MergeMode = true; + io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + "NotoSansCJK-Regular.ttc").c_str(), m_font_size, &config, ranges.Data); + } + if (font == nullptr) { font = io.Fonts->AddFontDefault(); if (font == nullptr) { diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 052eba2a3c..9873318099 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -31,9 +31,10 @@ namespace GUI { class ImGuiWrapper { + std::vector> m_lang_glyphs_info; // language prefix, ranges, whether it needs CLK font + const ImWchar* m_notosans_regular_ranges; const ImWchar* m_glyph_ranges{ nullptr }; // Chinese, Japanese, Korean - bool m_font_cjk{ false }; float m_font_size{ 18.0 }; unsigned m_font_texture{ 0 }; float m_style_scaling{ 1.0 }; From 5fac6934ec9d3707cb60590bc10683ab4eef5db2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 6 Jun 2024 13:15:10 +0200 Subject: [PATCH 4/4] Automatic addition of missing glyphs: require extra frame when chars are added --- src/slic3r/GUI/ImGuiWrapper.cpp | 11 +---------- src/slic3r/GUI/ImGuiWrapper.hpp | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index bd513bc1d7..d0b0ea9c3b 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -407,6 +407,7 @@ void ImGuiWrapper::render() // If there were some characters that ImGui was unable to render, we will destroy current font. // It will be rebuilt in the next call of new_frame including these. destroy_font(); + this->set_requires_extra_frame(); } } @@ -1176,16 +1177,6 @@ void ImGuiWrapper::init_font(bool compress) } } -#ifdef __APPLE__ - ImFontConfig config; - config.MergeMode = true; - if (! m_font_cjk) { - // Apple keyboard shortcuts are only contained in the CJK fonts. - [[maybe_unused]]ImFont *font_cjk = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSansCJK-Regular.ttc").c_str(), m_font_size, &config, ranges_keyboard_shortcuts); - assert(font_cjk != nullptr); - } -#endif - float font_scale = m_font_size/15; int icon_sz = lround(16 * font_scale); // default size of icon is 16 px diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 9873318099..37c1bec55f 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -32,7 +32,6 @@ namespace GUI { class ImGuiWrapper { std::vector> m_lang_glyphs_info; // language prefix, ranges, whether it needs CLK font - const ImWchar* m_notosans_regular_ranges; const ImWchar* m_glyph_ranges{ nullptr }; // Chinese, Japanese, Korean float m_font_size{ 18.0 };