From 09d740ba3292a8609481a17e5b20f16df3f34377 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Sep 2026 21:17:16 +0200 Subject: [PATCH] (Breaking) Fonts: refactor ImFontLoader to allow loading from glyph_index. (#9559, #5647) cc #9540, #8466, #8465 --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 3 ++- imgui_draw.cpp | 45 ++++++++++++++++---------------- imgui_internal.h | 5 ++-- misc/freetype/imgui_freetype.cpp | 40 ++++++++++++---------------- 5 files changed, 47 insertions(+), 49 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 30afc33be..55e2245db 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -81,6 +81,9 @@ Other Changes: ProggyClean on most Apple/Retina setups by default. - imgui_freetype: Fixed blurryness rendering `AddFontDefaultVector()`. Caused by a non-pixel aligned offset caused by `ExtraSizeScale`. (#9348, #9233) [@redpartizan, @RDMCz] + - (Internal) ImFontLoader: reworked `FontSrcContainsGlyph`, `FontBakedLoadGlyph` signatures + to take raw glyph index instead of codepoint. Added `FontSrcGetGlyphIndexFromCodepoint`. + (#9559, #5647, #9540, #8466, #8465) - ColorEdit: - ColorButton, ColorEdit, ColorPicker: cancel-out alpha caused by BeginDisabled() so disabled color buttons have the same color as non-disabled oness. (#9511) diff --git a/imgui.cpp b/imgui.cpp index fb86370b9..62688197d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -17710,7 +17710,8 @@ static int CalcFontGlyphSrcOverlapMask(ImFontAtlas* atlas, ImFont* font, unsigne for (int src_n = 0; src_n < font->Sources.Size; src_n++) { ImFontConfig* src = font->Sources[src_n]; - if (!(src->FontLoader ? src->FontLoader : atlas->FontLoader)->FontSrcContainsGlyph(atlas, src, (ImWchar)codepoint)) + const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; + if (loader->FontSrcGetGlyphIndexFromCodepoint(atlas, src, (ImWchar)codepoint) == 0) continue; mask |= (1 << src_n); count++; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 86a35d731..1c8e54863 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3123,7 +3123,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) } if (font_cfg->FontLoader != NULL) { - IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL); + IM_ASSERT(font_cfg->FontLoader->FontSrcGetGlyphIndexFromCodepoint != NULL && font_cfg->FontLoader->FontBakedLoadGlyph != NULL); IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet. } // | Target w/ Implicit RefSize | Target w/ Explicit RefSize | @@ -4606,10 +4606,15 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep if (src->GlyphExcludeRanges && !ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)) continue; + int glyph_index = loader->FontSrcGetGlyphIndexFromCodepoint(atlas, src, codepoint); + if (glyph_index == 0) + continue; + if (only_load_advance_x == NULL) { ImFontGlyph glyph_buf; - if (!loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL)) + glyph_buf.Codepoint = codepoint; + if (!loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, glyph_index, &glyph_buf, NULL)) continue; // FIXME: Add hooks for e.g. #7962 glyph_buf.Codepoint = src_codepoint; @@ -4619,7 +4624,7 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep else { // Special mode but only loading glyphs metrics. Will rasterize and pack later. - if (!loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x)) + if (!loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, glyph_index, NULL, only_load_advance_x)) continue; ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x); return NULL; @@ -4707,10 +4712,8 @@ struct ImGui_ImplStbTrueType_FontSrcData float ScaleFactor; }; -static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) +static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas*, ImFontConfig* src) { - IM_UNUSED(atlas); - ImGui_ImplStbTrueType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplStbTrueType_FontSrcData); IM_ASSERT(src->FontLoaderData == NULL); @@ -4742,29 +4745,27 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* return true; } -static void ImGui_ImplStbTrueType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) +static void ImGui_ImplStbTrueType_FontSrcDestroy(ImFontAtlas*, ImFontConfig* src) { - IM_UNUSED(atlas); ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; IM_DELETE(bd_font_data); src->FontLoaderData = NULL; } -static bool ImGui_ImplStbTrueType_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) +static int ImGui_ImplStbTrueType_FontSrcGetGlyphIndexFromCodepoint(ImFontAtlas*, ImFontConfig* src, ImWchar codepoint) { - IM_UNUSED(atlas); - ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; - IM_ASSERT(bd_font_data != NULL); - - int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint); - return glyph_index != 0; + return stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint); } -static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*) +static bool ImGui_ImplStbTrueType_FontSrcContainsGlyph(ImFontAtlas*, ImFontConfig* src, int glyph_index) { - IM_UNUSED(atlas); + ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; + return glyph_index > 0 && glyph_index < bd_font_data->FontInfo.numGlyphs; +} +static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas*, ImFontConfig* src, ImFontBaked* baked, void*) +{ ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; if (src->MergeMode == false) { @@ -4780,14 +4781,12 @@ static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig return true; } -static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) +static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, int glyph_index, ImFontGlyph* out_glyph, float* out_advance_x) { // Search for first font which has the glyph ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; IM_ASSERT(bd_font_data); - int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint); - if (glyph_index == 0) - return false; + IM_ASSERT(glyph_index != 0); // Fonts unit to pixels int oversample_h, oversample_v; @@ -4812,7 +4811,6 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC } // Prepare glyph - out_glyph->Codepoint = codepoint; out_glyph->AdvanceX = advance * scale_for_layout; // Pack and retrieve position inside texture atlas @@ -4874,6 +4872,7 @@ const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype() loader.Name = "stb_truetype"; loader.FontSrcInit = ImGui_ImplStbTrueType_FontSrcInit; loader.FontSrcDestroy = ImGui_ImplStbTrueType_FontSrcDestroy; + loader.FontSrcGetGlyphIndexFromCodepoint = ImGui_ImplStbTrueType_FontSrcGetGlyphIndexFromCodepoint; loader.FontSrcContainsGlyph = ImGui_ImplStbTrueType_FontSrcContainsGlyph; loader.FontBakedInit = ImGui_ImplStbTrueType_FontBakedInit; loader.FontBakedDestroy = NULL; @@ -5405,7 +5404,7 @@ bool ImFont::IsGlyphInFont(ImWchar c) for (ImFontConfig* src : Sources) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; - if (loader->FontSrcContainsGlyph != NULL && loader->FontSrcContainsGlyph(atlas, src, c)) + if (loader->FontSrcGetGlyphIndexFromCodepoint(atlas, src, c) != 0) return true; } return false; diff --git a/imgui_internal.h b/imgui_internal.h index 0ffadedc3..b0f8856f8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3932,10 +3932,11 @@ struct ImFontLoader void (*LoaderShutdown)(ImFontAtlas* atlas); bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src); void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src); - bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint); + int (*FontSrcGetGlyphIndexFromCodepoint)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint); // Required + bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, int glyph_index); bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); - bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x); + bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, int glyph_index, ImFontGlyph* out_glyph, float* out_advance_x); // Required // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations. // FIXME: At this point the two other types of buffers may be managed by core to be consistent? diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 2e2e78f7d..375863fdf 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -221,17 +221,14 @@ void ImGui_ImplFreeType_FontSrcData::CloseFont() } } -static const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t codepoint) +static const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t glyph_index) { - uint32_t glyph_index = FT_Get_Char_Index(src_data->FtFace, codepoint); - if (glyph_index == 0) - return nullptr; - // If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts. // - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076 // - https://github.com/ocornut/imgui/issues/4567 // - https://github.com/ocornut/imgui/issues/4566 // You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version. + IM_ASSERT(glyph_index != 0); FT_Error error = FT_Load_Glyph(src_data->FtFace, glyph_index, src_data->LoadFlags); if (error) return nullptr; @@ -417,17 +414,15 @@ static bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src return true; } -static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) +static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas*, ImFontConfig* src) { - IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; IM_DELETE(bd_font_data); src->FontLoaderData = nullptr; } -static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) +static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas*, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) { - IM_UNUSED(atlas); float size = baked->Size; const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; if (src->MergeMode && src->SizePixels != 0.0f) @@ -475,9 +470,8 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s return true; } -static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) +static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas*, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) { - IM_UNUSED(atlas); IM_UNUSED(baked); IM_UNUSED(src); ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; @@ -486,13 +480,9 @@ static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE() } -static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) +static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, int glyph_index, ImFontGlyph* out_glyph, float* out_advance_x) { ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; - uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); - if (glyph_index == 0) - return false; - if (bd_font_data->BakedLastActivated != baked) // <-- could use id { // Activate current size @@ -501,7 +491,7 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf bd_font_data->BakedLastActivated = baked; } - const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint); + const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, glyph_index); if (metrics == nullptr) return false; @@ -530,7 +520,6 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf const bool is_visible = (w != 0 && h != 0); // Prepare glyph - out_glyph->Codepoint = codepoint; out_glyph->AdvanceX = advance_x; // Pack and retrieve position inside texture atlas @@ -573,12 +562,16 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf return true; } -static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) +static int ImGui_ImplFreetype_FontSrcGetGlyphIndexFromCodepoint(ImFontAtlas*, ImFontConfig* src, ImWchar codepoint) { - IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; - int glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); - return glyph_index != 0; + return (int)FT_Get_Char_Index(bd_font_data->FtFace, codepoint); +} + +static bool ImGui_ImplFreeType_FontSrcContainsGlyph(ImFontAtlas*, ImFontConfig* src, int glyph_index) +{ + ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; + return glyph_index > 0 && glyph_index < bd_font_data->FtFace->num_glyphs; } const ImFontLoader* ImGuiFreeType::GetFontLoader() @@ -589,7 +582,8 @@ const ImFontLoader* ImGuiFreeType::GetFontLoader() loader->LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown; loader->FontSrcInit = ImGui_ImplFreeType_FontSrcInit; loader->FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy; - loader->FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph; + loader->FontSrcGetGlyphIndexFromCodepoint = ImGui_ImplFreetype_FontSrcGetGlyphIndexFromCodepoint; + loader->FontSrcContainsGlyph = ImGui_ImplFreeType_FontSrcContainsGlyph; loader->FontBakedInit = ImGui_ImplFreeType_FontBakedInit; loader->FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy; loader->FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph;