From cec3e945f02bb2fb2e12d1bbd984971ab4f2be37 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Dec 2024 16:21:07 +0100 Subject: [PATCH] Fonts: added ImFontAtlas::RemoveFont(), fixed various leaks. --- imgui.cpp | 6 ++++ imgui.h | 5 ++-- imgui_draw.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++--- imgui_internal.h | 1 + 4 files changed, 84 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 249456c7e..adb1e84f1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16484,6 +16484,12 @@ void ImGui::DebugNodeFont(ImFont* font) } if (SmallButton("Set as default")) GetIO().FontDefault = font; + if (font->ContainerAtlas->Fonts.Size > 1 && !font->ContainerAtlas->Locked) + { + SameLine(); + if (SmallButton("Remove")) + font->ContainerAtlas->RemoveFont(font); + } // Display details SetNextItemWidth(GetFontSize() * 8); diff --git a/imgui.h b/imgui.h index 7d04ebcd2..e237e90ae 100644 --- a/imgui.h +++ b/imgui.h @@ -3521,10 +3521,11 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. + IMGUI_API void RemoveFont(ImFont* font); // FIXME-NEWATLAS: Clarify meaning/purpose - IMGUI_API void Clear(); // Clear all input and output. - IMGUI_API void ClearCache(); // Clear cached glyphs + IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures) + IMGUI_API void ClearCache(); // Clear cached glyphs and textures. // As we are transitioning toward a new font system, we expect to obsolete those soon: IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. IMGUI_API void ClearFonts(); // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates). diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 13060843e..faabec1d5 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2476,6 +2476,8 @@ void ImTextureData::DestroyPixels() // - ImFontAtlas::AddFontFromMemoryTTF() // - ImFontAtlas::AddFontFromMemoryCompressedTTF() // - ImFontAtlas::AddFontFromMemoryCompressedBase85TTF() +// - ImFontAtlas::RemoveFont() +// - ImFontAtlasBuildNotifySetFont() //----------------------------------------------------------------------------- // - ImFontAtlas::AddCustomRectRegular() // - ImFontAtlas::AddCustomRectFontGlyph() @@ -2593,11 +2595,15 @@ void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); for (ImFontConfig& font_cfg : Sources) + { + if (FontLoader && FontLoader->FontSrcDestroy != NULL) + FontLoader->FontSrcDestroy(this, &font_cfg); if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas) { IM_FREE(font_cfg.FontData); font_cfg.FontData = NULL; } + } // When clearing this we lose access to the font name and other information used to build the font. for (ImFont* font : Fonts) @@ -2641,12 +2647,12 @@ void ImFontAtlas::ClearFonts() void ImFontAtlas::Clear() { //IM_DELETE(Builder); // FIXME-NEW-ATLAS: Clarify ClearXXX functions - const ImFontLoader* font_loader = FontLoader; - ImFontAtlasBuildSetupFontLoader(this, NULL); + //const ImFontLoader* font_loader = FontLoader; + //ImFontAtlasBuildSetupFontLoader(this, NULL); ClearInputData(); ClearTexData(); ClearFonts(); - ImFontAtlasBuildSetupFontLoader(this, font_loader); + //ImFontAtlasBuildSetupFontLoader(this, font_loader); } // FIXME-NEWATLAS: Too widespread purpose. Clarify each call site in current WIP demo. @@ -3041,6 +3047,59 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed return font; } +// We allow old_font == new_font which forces updating all values (e.g. sizes) +static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font) +{ + if (ImDrawListSharedData* shared_data = atlas->DrawListSharedData) + { + if (shared_data->Font == old_font) + shared_data->Font = new_font; + if (ImGuiContext* ctx = shared_data->Context) + { + if (ctx->IO.FontDefault == old_font) + ctx->IO.FontDefault = new_font; + if (ctx->Font == old_font) + { + ImGuiContext* curr_ctx = ImGui::GetCurrentContext(); + bool need_bind_ctx = ctx != curr_ctx; + if (need_bind_ctx) + ImGui::SetCurrentContext(ctx); + ImGui::SetCurrentFont(new_font); + if (need_bind_ctx) + ImGui::SetCurrentContext(curr_ctx); + } + } + } +} + +void ImFontAtlas::RemoveFont(ImFont* font) +{ + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); + ImFontAtlasBuildDiscardFontGlyphs(this, font); + + for (int src_n = 0; src_n < font->SourcesCount; src_n++) + { + ImFontConfig* src = (ImFontConfig*)(void*)&font->Sources[src_n]; + if (FontLoader && FontLoader->FontSrcDestroy != NULL) + FontLoader->FontSrcDestroy(this, src); + if (src->FontData != NULL && src->FontDataOwnedByAtlas) + IM_FREE(src->FontData); + } + + bool removed = Fonts.find_erase(font); + IM_ASSERT(removed); + + Sources.erase(font->Sources, font->Sources + font->SourcesCount); + ImFontAtlasBuildUpdatePointers(this); + + font->ContainerAtlas = NULL; + IM_DELETE(font); + + // Notify external systems + ImFont* new_current_font = Fonts.empty() ? NULL : Fonts[0]; + ImFontAtlasBuildNotifySetFont(this, font, new_current_font); +} + // FIXME-NEWATLAS-V1: Feature is broken for now. /* // Register custom rectangle glyphs @@ -3485,13 +3544,19 @@ void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFontConfig* sr font->LockSingleSrcConfigIdx = -1; } -void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font) +void ImFontAtlasBuildDiscardFontGlyphs(ImFontAtlas* atlas, ImFont* font) { for (ImFontGlyph& glyph : font->Glyphs) if (glyph.PackId >= 0) ImFontAtlasPackDiscardRect(atlas, glyph.PackId); font->BuildClearGlyphs(); + font->FallbackChar = font->EllipsisChar = 0; +} +// Discard old glyphs and reload font. Use if changing font size. +void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font) +{ + ImFontAtlasBuildDiscardFontGlyphs(atlas, font); for (int src_n = 0; src_n < font->SourcesCount; src_n++) { ImFontConfig* src = (ImFontConfig*)(void*)&font->Sources[src_n]; @@ -3505,6 +3570,9 @@ void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font) ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, src); // Technically this is called for each source sub-font, tho 99.9% of the time the first one fills everything. } + + // Notify external systems + ImFontAtlasBuildNotifySetFont(atlas, font, font); } // Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData* @@ -4023,11 +4091,13 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)src->FontData, src->FontNo); if (font_offset < 0) { + IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found."); return false; } if (!stbtt_InitFont(&bd_font_data->FontInfo, (unsigned char*)src->FontData, font_offset)) { + IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize."); return false; } diff --git a/imgui_internal.h b/imgui_internal.h index 48090ee90..f340b8e5e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3717,6 +3717,7 @@ IMGUI_API ImVec2i ImFontAtlasBuildGetTextureSizeEstimate(ImFontAtlas* IMGUI_API bool ImFontAtlasBuildAddFont(ImFontAtlas* atlas, ImFontConfig* src); IMGUI_API void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFontConfig* src); +IMGUI_API void ImFontAtlasBuildDiscardFontGlyphs(ImFontAtlas* atlas, ImFont* font); IMGUI_API void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font); IMGUI_API void ImFontAtlasBuildPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy IMGUI_API void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, int* out_oversample_h, int* out_oversample_v);