From 553b1c301df1c4ffbb9fe5c47bfa41ea86d750b6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Dec 2024 18:52:30 +0100 Subject: [PATCH] Fonts: repack without full reload, discard rectangle, fixed CustomRect api with stable id, remove public BuildInit(). --- imgui.h | 5 +- imgui_draw.cpp | 207 ++++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 23 +++++- 3 files changed, 182 insertions(+), 53 deletions(-) diff --git a/imgui.h b/imgui.h index b733544ac..3013eb9e0 100644 --- a/imgui.h +++ b/imgui.h @@ -3529,7 +3529,6 @@ struct ImFontAtlas IMGUI_API void Clear(); // Clear all input and output. IMGUI_API void ClearCache(); // Clear cached glyphs - IMGUI_API void BuildInit(); // Build atlas, retrieve pixel data. // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). @@ -3583,7 +3582,7 @@ struct ImFontAtlas // - Note: this API may be redesigned later in order to support multi-monitor varying DPI settings. IMGUI_API int AddCustomRectRegular(int width, int height); IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0)); - ImFontAtlasCustomRect* GetCustomRectByIndex(int index) { IM_ASSERT(index >= 0); return &CustomRects[index]; } + IMGUI_API ImFontAtlasCustomRect* GetCustomRectByIndex(int index); // [Internal] IMGUI_API void CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const; @@ -3608,8 +3607,8 @@ struct ImFontAtlas ImVec2 TexUvScale; // = (1.0f/TexData->TexWidth, 1.0f/TexData->TexHeight) ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. - ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector Sources; // Source/configuration data + //ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines int TexNextUniqueID; // Next value to be stored in TexData->UniqueID ImDrawListSharedData* DrawListSharedData; // In principle this could become an array (e.g. multiple contexts using same atlas) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c21925de5..48f99cdf1 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2606,7 +2606,7 @@ void ImFontAtlas::ClearInputData() font->SourcesCount = 0; } Sources.clear(); - CustomRects.clear(); + //CustomRects.clear(); // Important: we leave TexReady untouched } @@ -2646,6 +2646,7 @@ void ImFontAtlas::Clear() ImFontAtlasBuildSetupFontLoader(this, font_loader); } +// FIXME-NEWATLAS: Too widespread purpose. Clarify each call site in current WIP demo. void ImFontAtlas::ClearCache() { int tex_w = (TexData && TexData->Status != ImTextureStatus_WantDestroy) ? TexData->Width : 0; @@ -2891,7 +2892,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) // Lazily create builder on the first call to AddFont if (Builder == NULL) - BuildInit(); + ImFontAtlasBuildInit(this); // Create new font if (!font_cfg->MergeMode) @@ -3066,11 +3067,24 @@ int ImFontAtlas::AddCustomRectRegular(int width, int height) { IM_ASSERT(width > 0 && width <= 0xFFFF); IM_ASSERT(height > 0 && height <= 0xFFFF); - ImFontAtlasCustomRect r; - r.Width = (unsigned short)width; - r.Height = (unsigned short)height; - CustomRects.push_back(r); - return CustomRects.Size - 1; // Return index + + if (DrawListSharedData && DrawListSharedData->RendererHasTextures) + { + ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height); + ImFontAtlasRect* r = ImFontAtlasPackGetRect(this, r_id); + ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h); + return r_id; + } + else + { + // FIXME-NEWATLAS-V1: Unfinished + ImFontAtlasCustomRect r; + r.Width = (unsigned short)width; + r.Height = (unsigned short)height; + //CustomRects.push_back(r); + //return CustomRects.Size - 1; // Return index + return -1; + } } int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset) @@ -3089,8 +3103,18 @@ int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int r.GlyphAdvanceX = advance_x; r.GlyphOffset = offset; r.Font = font; - CustomRects.push_back(r); - return CustomRects.Size - 1; // Return index + //CustomRects.push_back(r); + //return CustomRects.Size - 1; // Return index + return -1; +} + +ImFontAtlasCustomRect* ImFontAtlas::GetCustomRectByIndex(int idx) +{ + IM_STATIC_ASSERT(offsetof(ImFontAtlasCustomRect, X) == offsetof(ImFontAtlasRect, x)); + IM_STATIC_ASSERT(offsetof(ImFontAtlasCustomRect, Y) == offsetof(ImFontAtlasRect, y)); + IM_STATIC_ASSERT(offsetof(ImFontAtlasCustomRect, Width) == offsetof(ImFontAtlasRect, w)); + IM_STATIC_ASSERT(offsetof(ImFontAtlasCustomRect, Height) == offsetof(ImFontAtlasRect, h)); + return (ImFontAtlasCustomRect*)(void*)ImFontAtlasPackGetRect(this, idx); } void ImFontAtlas::CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const @@ -3121,35 +3145,12 @@ bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor curso return true; } -void ImFontAtlas::BuildInit() -{ - // Select builder - // - Note that we do not reassign to atlas->FontLoader, since it is likely to point to static data which - // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are - // using a hot-reloading scheme that messes up static data, store your own instance of ImFontLoader somewhere - // and point to it instead of pointing directly to return value of the GetBackendIOXXX functions. - if (FontLoader == NULL) - { -#ifdef IMGUI_ENABLE_FREETYPE - ImFontAtlasBuildSetupFontLoader(this, ImGuiFreeType::GetBackendIOForFreeType()); -#elif defined(IMGUI_ENABLE_STB_TRUETYPE) - ImFontAtlasBuildSetupFontLoader(this, ImFontAtlasGetFontLoaderForStbTruetype()); -#else - IM_ASSERT(0); // Invalid Build function -#endif - } - - // Create initial texture size - ImFontAtlasBuildAddTexture(this, 512, 128); - ImFontAtlasBuildInit(this); -} - bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); if (Builder == NULL) - BuildInit(); + ImFontAtlasBuildInit(this); // Default font is none are specified if (Sources.Size == 0) @@ -3222,6 +3223,8 @@ void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas) } } +// FIXME-NEWATLAS: Unused +#if 0 void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque) { ImTextureData* tex = atlas->TexData; @@ -3253,6 +3256,7 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa tex->Height = ImMax(tex->Height, pack_rects[i].y + pack_rects[i].h); } } +#endif // Render a white-colored bitmap encoded in a string void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char) @@ -3476,6 +3480,28 @@ void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFontConfig* sr font->LockSingleSrcConfigIdx = -1; } +void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font) +{ + for (ImFontGlyph& glyph : font->Glyphs) + if (glyph.PackId >= 0) + ImFontAtlasPackDiscardRect(atlas, glyph.PackId); + font->BuildClearGlyphs(); + + for (int src_n = 0; src_n < font->SourcesCount; src_n++) + { + ImFontConfig* src = (ImFontConfig*)(void*)&font->Sources[src_n]; + IM_ASSERT(src->SizePixels > 0.0f); + + // Reload font in backend + if (atlas->FontLoader && atlas->FontLoader->FontSrcDestroy != NULL) + atlas->FontLoader->FontSrcDestroy(atlas, src); + if (atlas->FontLoader && atlas->FontLoader->FontSrcInit != NULL) + atlas->FontLoader->FontSrcInit(atlas, src); + + ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, src); // Technically this is called for each source sub-font, tho 99.9% of the time the first one fills everything. + } +} + // Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData* void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data) { @@ -3588,29 +3614,40 @@ void ImFontAtlasBuildRepackTexture(ImFontAtlas* atlas, int w, int h) // FIXME-NEWATLAS-TESTS: Test calling RepackTexture with size too small to fits existing rects. - // Repack + copy pixels + // Repack, lose discarded rectangle, copy pixels // FIXME-NEWATLAS-V2: Repacking in batch would be beneficial to packing heuristic. ImFontAtlasPackInit(atlas); ImVector old_rects; + ImVector old_index = builder->RectsIndex; old_rects.swap(builder->Rects); - for (ImFontAtlasRect& old_r : old_rects) + + for (ImFontAtlasRectEntry& index_entry : builder->RectsIndex) { - ImFontAtlasRectId new_r_id = ImFontAtlasPackAddRect(atlas, old_r.w, old_r.h); + if (index_entry.Used == false) + continue; + ImFontAtlasRect& old_r = old_rects[index_entry.TargetIndex]; + if (old_r.w == 0 && old_r.h == 0) + continue; + ImFontAtlasRectId new_r_id = ImFontAtlasPackAddRect(atlas, old_r.w, old_r.h, &index_entry); if (new_r_id == -1) { // Undo, grow texture and try repacking again. // FIXME-NEWATLAS-TESTS: This is a very rarely exercised path! It needs to be automatically tested properly. IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize failed. Will grow.\n", new_tex->UniqueID); new_tex->WantDestroyNextFrame = true; - old_rects.swap(builder->Rects); + builder->Rects.swap(old_rects); + builder->RectsIndex = old_index; ImFontAtlasBuildSetTexture(atlas, old_tex); - ImFontAtlasBuildGrowTexture(atlas, w, h); + ImFontAtlasBuildGrowTexture(atlas, w, h); // Recurse return; } + IM_ASSERT(new_r_id == builder->RectsIndex.index_from_ptr(&index_entry)); ImFontAtlasRect* new_r = ImFontAtlasPackGetRect(atlas, new_r_id); ImFontAtlasTextureBlockCopy(old_tex, old_r.x, old_r.y, new_tex, new_r->x, new_r->y, new_r->w, new_r->h); } - IM_ASSERT(old_rects.Size == builder->Rects.Size); + IM_ASSERT(old_rects.Size == builder->Rects.Size + builder->RectsDiscardedCount); + builder->RectsDiscardedCount = 0; + builder->RectsDiscardedSurface = 0; // Patch glyphs UV for (ImFont* font : atlas->Fonts) @@ -3645,6 +3682,7 @@ void ImFontAtlasBuildGrowTexture(ImFontAtlas* atlas, int old_tex_w, int old_tex_ IM_ASSERT(ImIsPowerOfTwo(old_tex_w) && ImIsPowerOfTwo(old_tex_h)); // Grow texture so it follows roughly a square. + // FIXME-NEWATLAS-V1: Take account of RectsDiscardedSurface: may not need to grow. int new_tex_w = (old_tex_h < old_tex_w) ? old_tex_w : old_tex_w * 2; int new_tex_h = (old_tex_h < old_tex_w) ? old_tex_h * 2 : old_tex_h; @@ -3669,14 +3707,15 @@ void ImFontAtlasBuildCompactTexture(ImFontAtlas* atlas) // FIXME-NEWATLAS: Expose atlas->TexMinWidth etc. const int min_w = ImMax(builder->MaxRectSize.x, 512); const int min_h = builder->MaxRectSize.y; - const int surface_sqrt = (int)sqrtf((float)atlas->_PackedSurface); + const int surface_approx = atlas->_PackedSurface - builder->RectsDiscardedSurface; // Expected surface after repack + const int surface_sqrt = (int)sqrtf((float)surface_approx); int new_tex_w; int new_tex_h; if (min_w >= min_h) { new_tex_w = ImMax(min_w, ImUpperPowerOfTwo(surface_sqrt)); - new_tex_h = ImMax(min_h, (int)(atlas->_PackedSurface / new_tex_w)); + new_tex_h = ImMax(min_h, (int)((surface_approx + new_tex_w - 1) / new_tex_w)); if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0) new_tex_h = ImUpperPowerOfTwo(new_tex_h); } @@ -3685,10 +3724,10 @@ void ImFontAtlasBuildCompactTexture(ImFontAtlas* atlas) new_tex_h = ImMax(min_h, ImUpperPowerOfTwo(surface_sqrt)); if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0) new_tex_h = ImUpperPowerOfTwo(new_tex_h); - new_tex_w = ImMax(min_w, (int)(atlas->_PackedSurface / new_tex_h)); + new_tex_w = ImMax(min_w, (int)((surface_approx + new_tex_h - 1) / new_tex_h)); } - if (new_tex_w == old_tex_w && new_tex_h == old_tex_h) + if (builder->RectsDiscardedCount == 0 && new_tex_w == old_tex_w && new_tex_h == old_tex_h) return; ImFontAtlasBuildRepackTexture(atlas, new_tex_w, new_tex_h); @@ -3699,6 +3738,25 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) { ImFontAtlasBuilder* builder = atlas->Builder; + // Select Backend + // - Note that we do not reassign to atlas->FontBackendIO, since it is likely to point to static data which + // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are + // using a hot-reloading scheme that messes up static data, store your own instance of ImFontBackendIO somewhere + // and point to it instead of pointing directly to return value of the GetBackendIOXXX functions. + if (atlas->FontLoader == NULL) + { +#ifdef IMGUI_ENABLE_FREETYPE + ImFontAtlasBuildSetupFontLoader(atlas, ImGuiFreeType::GetFontLoader()); +#elif defined(IMGUI_ENABLE_STB_TRUETYPE) + ImFontAtlasBuildSetupFontLoader(atlas, ImFontAtlasGetFontLoaderForStbTruetype()); +#else + IM_ASSERT(0); // Invalid Build function +#endif + } + // Create initial texture size + if (atlas->TexData == NULL) + ImFontAtlasBuildAddTexture(atlas, 512, 128); + const bool builder_is_new = (builder == NULL); if (builder_is_new) builder = atlas->Builder = IM_NEW(ImFontAtlasBuilder)(); @@ -3736,7 +3794,7 @@ void ImFontAtlasBuildDestroy(ImFontAtlas* atlas) atlas->Builder = NULL; } -void ImFontAtlasPackInit(ImFontAtlas* atlas) +void ImFontAtlasPackInit(ImFontAtlas * atlas) { ImTextureData* tex = atlas->TexData; ImFontAtlasBuilder* builder = atlas->Builder; @@ -3751,9 +3809,52 @@ void ImFontAtlasPackInit(ImFontAtlas* atlas) builder->MaxRectBounds = ImVec2i(0, 0); } +// This is essentially a free-list pattern, it may be nice to wrap it into a dedicated type. +static ImFontAtlasRectId ImFontAtlasPackAllocRectEntry(ImFontAtlas* atlas, int rect_idx) +{ + ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; + int index_idx; + ImFontAtlasRectEntry* index_entry; + if (builder->RectsIndexFreeListStart < 0) + { + builder->RectsIndex.resize(builder->RectsIndex.Size + 1); + index_idx = builder->RectsIndex.Size - 1; + index_entry = &builder->RectsIndex[index_idx]; + } + else + { + index_idx = builder->RectsIndexFreeListStart; + index_entry = &builder->RectsIndex[index_idx]; + IM_ASSERT(index_entry->Used == false); + builder->RectsIndexFreeListStart = index_entry->TargetIndex; + } + index_entry->TargetIndex = rect_idx; + index_entry->Used = 1; + return (ImFontAtlasRectId)index_idx; +} + +// This is expected to be called in batches and followed by a repack +void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id) +{ + IM_ASSERT(id >= 0); + ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; + ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[id]; + IM_ASSERT(index_entry->Used && index_entry->TargetIndex >= 0); + + ImFontAtlasRect* rect = ImFontAtlasPackGetRect(atlas, id); + index_entry->Used = false; + index_entry->TargetIndex = builder->RectsIndexFreeListStart; + + const int pack_padding = atlas->TexGlyphPadding; + builder->RectsIndexFreeListStart = id; + builder->RectsDiscardedCount++; + builder->RectsDiscardedSurface += (rect->w + pack_padding) * (rect->h + pack_padding); + rect->w = rect->h = 0; // Clear rectangle so it won't be packed again +} + // Important: Calling this may recreate a new texture and therefore change atlas->TexData // FIXME-NEWATLAS-V2: Expose other glyph padding settings for custom alteration (e.g. drop shadows). See #7962 -ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h) +ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry) { IM_ASSERT(w > 0 && w <= 0xFFFF); IM_ASSERT(h > 0 && h <= 0xFFFF); @@ -3796,13 +3897,25 @@ ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h) atlas->_PackedRects++; builder->Rects.push_back(r); - return builder->Rects.Size - 1; + if (overwrite_entry != NULL) + { + // Write into an existing entry instead of adding one (used during repack) + IM_ASSERT(overwrite_entry->Used); + overwrite_entry->TargetIndex = builder->Rects.Size - 1; + return builder->RectsIndex.index_from_ptr(overwrite_entry); + } + else + { + return ImFontAtlasPackAllocRectEntry(atlas, builder->Rects.Size - 1); + } } ImFontAtlasRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id) { ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; - return &builder->Rects[id]; + ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[id]; + IM_ASSERT(index_entry->Used); + return &builder->Rects[index_entry->TargetIndex]; } ImFontGlyph* ImFont::BuildLoadGlyph(ImWchar codepoint) diff --git a/imgui_internal.h b/imgui_internal.h index 4e8384bee..67a4b50cb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -142,6 +142,7 @@ struct ImGuiTextIndex; // Maintain a line index for a text buffer. struct ImDrawDataBuilder; // Helper to build a ImDrawData instance struct ImDrawListSharedData; // Data shared between all ImDrawList instances struct ImFontAtlasRect; // Packed rectangle (same as ImTextureRect) +struct ImFontAtlasRectEntry; // Packed rectangle lookup entry struct ImFontAtlasBuilder; // Internal storage for incrementally packing and building a ImFontAtlas // ImGui @@ -3639,7 +3640,6 @@ IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype(); // [SECTION] ImFontAtlas internal API //----------------------------------------------------------------------------- -// Packed rectangle (same as ImTextureRect) struct ImFontAtlasRect { unsigned short x, y; @@ -3647,6 +3647,17 @@ struct ImFontAtlasRect }; typedef int ImFontAtlasRectId; // <0 when invalid +// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles) +// User are returned ImFontAtlasRectId values which are meant to be persistent. +// We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId. +// RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code. +// Having this also makes it easier to e.g. sort rectangles during repack. +struct ImFontAtlasRectEntry +{ + int TargetIndex : 31; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list. + unsigned int Used : 1; +}; + // Internal storage for incrementally packing and building a ImFontAtlas struct stbrp_context_opaque { char data[80]; }; struct stbrp_node; @@ -3655,7 +3666,11 @@ struct ImFontAtlasBuilder stbrp_context_opaque PackContext; // Actually 'stbrp_context' but we don't want to define this in the header file. ImVector PackNodes; ImVector Rects; + ImVector RectsIndex; // ImFontAtlasRectId -> index into Rects[] ImVector TempBuffer; // Misc scratch buffer + int RectsIndexFreeListStart;// First unused entry + int RectsDiscardedCount; + int RectsDiscardedSurface; ImVec2i MaxRectSize; // Largest rectangle to pack (de-facto used as a "minimum texture size") ImVec2i MaxRectBounds; // Bottom-right most used pixels bool LockDisableResize; // Disable resizing texture @@ -3665,7 +3680,7 @@ struct ImFontAtlasBuilder ImFontAtlasRectId PackIdMouseCursors; // White pixel + mouse cursors. Also happen to be fallback in case of packing failure. ImFontAtlasRectId PackIdLinesTexData; - ImFontAtlasBuilder() { memset(this, 0, sizeof(*this)); } + ImFontAtlasBuilder() { memset(this, 0, sizeof(*this)); RectsIndexFreeListStart = -1; } }; // FIXME-NEWATLAS: Cleanup @@ -3683,11 +3698,13 @@ IMGUI_API void ImFontAtlasBuildCompactTexture(ImFontAtlas* atlas); IMGUI_API bool ImFontAtlasBuildAddFont(ImFontAtlas* atlas, ImFontConfig* src); IMGUI_API void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFontConfig* src); +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); IMGUI_API void ImFontAtlasPackInit(ImFontAtlas* atlas); -IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h); +IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL); +IMGUI_API void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id); IMGUI_API ImFontAtlasRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id); IMGUI_API void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);