From bc051dcf91cb0e3c61ce20e582c91654d0049003 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Jun 2025 17:03:13 +0200 Subject: [PATCH 1/8] Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value. --- docs/CHANGELOG.txt | 3 +++ imgui.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fd705f1c8..3bf28c689 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,9 @@ Breaking changes: Other changes: +- Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value: + ImTextureData() was incorrectly cleared with zeroes. (#8745) [@rachit7645] +- Demo: Added "Text -> Font Size" demo section. (#8738) [@Demonese] - Backends: SDL3: avoid calling SDL_StartTextInput() again if already active. (#8727) [@morrazzzz] - Backends: OSX: added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress diff --git a/imgui.h b/imgui.h index e417731d0..b09d7ee82 100644 --- a/imgui.h +++ b/imgui.h @@ -3440,7 +3440,7 @@ struct ImTextureData bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions - ImTextureData() { memset(this, 0, sizeof(*this)); } + ImTextureData() { memset(this, 0, sizeof(*this)); TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); IMGUI_API void DestroyPixels(); From de7625b8c25b07214a7ed6540d2225a1547c2231 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 28 Jun 2025 16:45:58 +0200 Subject: [PATCH 2/8] Docs: tweak/fixed comments. (#8750, #8749) --- docs/CHANGELOG.txt | 12 +++++------- docs/FONTS.md | 2 +- imgui.cpp | 9 ++++++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3bf28c689..b548fb657 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -117,13 +117,11 @@ Breaking changes: `PushFont(NULL, some_size)` now keeps current change and changes size. - Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'. - Fonts: **IMPORTANT** on Font Merging: - - When searching for a glyph in multiple merged fonts: font inputs are now scanned in order - for the first font input which the desired glyph. This is technically a different behavior - than before! - - e.g. If you are merging fonts you may have glyphs that you expected to load from - Font Source 2 which exists in Font Source 1. After the update and when using a new backend, - those glyphs may now loaded from Font Source 1! - - You can use `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to ignore in given Input: + - When searching for a glyph in multiple merged fonts: we search for the FIRST font source + which contains the desired glyph. Because the user doesn't need to provide glyph ranges + any more, it is possible that a glyph that you expected to fetch from a secondary/merged + icon font may be erroneously fetched from the primary font. + - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source: // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig cfg1; diff --git a/docs/FONTS.md b/docs/FONTS.md index f8431507c..ce08f176d 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -387,7 +387,7 @@ io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg); ## Using Custom Glyph Ranges -🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is necessary, so this is not needed.** +🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is unnecessary. Therefore this is not really useful any more.** :rewind: You can use the `ImFontGlyphRangesBuilder` helper to create glyph ranges based on text input. For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. ```cpp diff --git a/imgui.cpp b/imgui.cpp index acdbf33e4..8aaa901fc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -416,9 +416,12 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site. - Kept inline single parameter function. Will obsolete. - Fonts: **IMPORTANT** on Font Merging: - - When searching for a glyph in multiple merged fonts: font inputs are now scanned in orderfor the first font input which the desired glyph. This is technically a different behavior than before! - - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1. After the update and when using a new backend, those glyphs may now loaded from Font Source 1! - - You can use `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to ignore in given Input: + - When searching for a glyph in multiple merged fonts: we search for the FIRST font source which contains the desired glyph. + Because the user doesn't need to provide glyph ranges any more, it is possible that a glyph that you expected to fetch from a secondary/merged icon font may be erroneously fetched from the primary font. + - When searching for a glyph in multiple merged fonts: we now search for the FIRST font source which contains the desired glyph. This is technically a different behavior than before! + - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1. + After the update and when using a new backend, those glyphs may now loaded from Font Source 1! + - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source: // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig cfg1; From d99ab9f90355adb3197f81eef9060b44d1076ae0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 28 Jun 2025 17:15:18 +0200 Subject: [PATCH 3/8] Backends: SDL2: undef Status for X11. (#8751) --- backends/imgui_impl_sdl2.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 9f5e0374a..18035d24f 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -118,6 +118,9 @@ #ifdef __EMSCRIPTEN__ #include #endif +#ifdef Status // X11 headers +#undef Status +#endif #if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__) #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1 From 8c61ee5498f1761c057f6aa8f87222c11b4c8eff Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 29 Jun 2025 18:05:28 +0200 Subject: [PATCH 4/8] Tables: fixed comments about DisableDefaultContextMenu. (#8746) --- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index eeee93778..22fbef4f2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2982,7 +2982,7 @@ struct IMGUI_API ImGuiTable bool IsSortSpecsDirty; bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag. bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted). - bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup() + bool DisableDefaultContextMenu; // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup() bool IsSettingsRequestLoad; bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data. bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index f6d516521..069c72995 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1251,7 +1251,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 11] Default context menu // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup(). - // - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup(). + // - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup(). // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu, // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options. if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table)) From 8ccfdf7ba05b74b9d80d8fad728c5510c5634c2c Mon Sep 17 00:00:00 2001 From: Aidan Sun Date: Sun, 29 Jun 2025 17:32:48 -0400 Subject: [PATCH 5/8] CI: Fixed dllimport/dllexport tests. (#8757) --- .github/workflows/build.yml | 20 ++++++-------------- docs/CHANGELOG.txt | 1 + 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 86872d21c..bc58e25bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,15 +51,11 @@ jobs: - name: Build example_null (mingw 64-bit, as DLL) shell: bash run: | - echo '#ifdef _EXPORT' > example_single_file.cpp - echo '# define IMGUI_API __declspec(dllexport)' >> example_single_file.cpp - echo '#else' >> example_single_file.cpp - echo '# define IMGUI_API __declspec(dllimport)' >> example_single_file.cpp - echo '#endif' >> example_single_file.cpp + echo '#define IMGUI_API __declspec(dllexport)' > example_single_file.cpp echo '#define IMGUI_IMPLEMENTATION' >> example_single_file.cpp echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp - g++ -I. -Wall -Wformat -D_EXPORT -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32 - g++ -I. -Wall -Wformat -o example_null.exe examples/example_null/main.cpp -L. -limgui + g++ -I. -Wall -Wformat -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32 + g++ -I. -Wall -Wformat -DIMGUI_API='__declspec(dllimport)' -o example_null.exe examples/example_null/main.cpp -L. -limgui rm -f example_null.exe libimgui.* example_single_file.* - name: Build example_null (extra warnings, msvc 64-bit) @@ -99,16 +95,12 @@ jobs: run: | call "%VS_PATH%\VC\Auxiliary\Build\vcvars64.bat" - echo #ifdef _EXPORT > example_single_file.cpp - echo # define IMGUI_API __declspec(dllexport) >> example_single_file.cpp - echo #else >> example_single_file.cpp - echo # define IMGUI_API __declspec(dllimport) >> example_single_file.cpp - echo #endif >> example_single_file.cpp + echo #define IMGUI_API __declspec(dllexport) > example_single_file.cpp echo #define IMGUI_IMPLEMENTATION >> example_single_file.cpp echo #include "misc/single_file/imgui_single_file.h" >> example_single_file.cpp - cl.exe /D_USRDLL /D_WINDLL /D_EXPORT /I. example_single_file.cpp /LD /FeImGui.dll /link - cl.exe /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp + cl.exe /D_USRDLL /D_WINDLL /I. example_single_file.cpp /LD /FeImGui.dll /link + cl.exe /DIMGUI_API=__declspec(dllimport) /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp - name: Build Win32 example_glfw_opengl2 shell: cmd diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b548fb657..216b61524 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other changes: - Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value: ImTextureData() was incorrectly cleared with zeroes. (#8745) [@rachit7645] - Demo: Added "Text -> Font Size" demo section. (#8738) [@Demonese] +- CI: Fixed dllimport/dllexport tests. (#8757) [@AidanSun05] - Backends: SDL3: avoid calling SDL_StartTextInput() again if already active. (#8727) [@morrazzzz] - Backends: OSX: added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress From b7e5d76c7928d66ace9116a9f180153a7c260b59 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Jun 2025 20:01:02 +0200 Subject: [PATCH 6/8] Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font loader at runtime without using internal API. (#8752, #8465) --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 7 ++++--- imgui.h | 5 +++-- imgui_draw.cpp | 9 +++++++-- misc/freetype/imgui_freetype.h | 6 +++--- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 216b61524..0b9d1d9a1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font + loader at runtime without using internal API. (#8752, #8465) - Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value: ImTextureData() was incorrectly cleared with zeroes. (#8745) [@rachit7645] - Demo: Added "Text -> Font Size" demo section. (#8738) [@Demonese] @@ -211,7 +213,7 @@ Breaking changes: - renamed/reworked ImFontBuilderIO into ImFontLoader, - renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader() - old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType() - - new: io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader(); + - new: io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader() - DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture(). - Fonts: (users of custom rectangles) - Renamed AddCustomRectRegular() to AddCustomRect(). (#8466) diff --git a/imgui.cpp b/imgui.cpp index 8aaa901fc..ddc20991e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -453,7 +453,8 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags. If you used runtime imgui_freetype selection rather than the default IMGUI_ENABLE_FREETYPE compile-time option: Renamed/reworked ImFontBuilderIO into ImFontLoader. Renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader(). - old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType() - - new: io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader() + - new: io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader() + - new: io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) to change dynamically at runtime [from 1.92.1] - Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV(). - The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h. - old: @@ -15902,7 +15903,7 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) #ifdef IMGUI_ENABLE_STB_TRUETYPE const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype(); if (RadioButton("stb_truetype", loader_current == loader_stbtruetype)) - ImFontAtlasBuildSetupFontLoader(atlas, loader_stbtruetype); + atlas->SetFontLoader(loader_stbtruetype); #else BeginDisabled(); RadioButton("stb_truetype", false); @@ -15913,7 +15914,7 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) #ifdef IMGUI_ENABLE_FREETYPE const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader(); if (RadioButton("FreeType", loader_current == loader_freetype)) - ImFontAtlasBuildSetupFontLoader(atlas, loader_freetype); + atlas->SetFontLoader(loader_freetype); if (loader_current == loader_freetype) { unsigned int loader_flags = atlas->FontLoaderFlags; diff --git a/imgui.h b/imgui.h index b09d7ee82..153ce5cad 100644 --- a/imgui.h +++ b/imgui.h @@ -3493,7 +3493,7 @@ struct ImFontConfig // [Internal] ImFontFlags Flags; // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates) ImFont* DstFont; // Target font (as we merging fonts, multiple ImFontConfig may target the same font) - const ImFontLoader* FontLoader; // Custom font backend for this source (other use one stored in ImFontAtlas) + const ImFontLoader* FontLoader; // Custom font backend for this source (default source is the one stored in ImFontAtlas) void* FontLoaderData; // Font loader opaque storage (per font config) IMGUI_API ImFontConfig(); @@ -3590,6 +3590,7 @@ struct ImFontAtlas IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures) IMGUI_API void CompactCache(); // Compact cached glyphs and texture. + IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime. // 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. @@ -3698,7 +3699,7 @@ struct ImFontAtlas int FontNextUniqueID; // Next value to be stored in ImFont->FontID ImVector DrawListSharedDatas; // List of users for this atlas. Typically one per Dear ImGui context. ImFontAtlasBuilder* Builder; // Opaque interface to our data that doesn't need to be public and may be discarded when rebuilding. - const ImFontLoader* FontLoader; // Font loader opaque interface (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE). Don't set directly! + const ImFontLoader* FontLoader; // Font loader opaque interface (default to use FreeType when IMGUI_ENABLE_FREETYPE is defined, otherwise default to use stb_truetype). Use SetFontLoader() to change this at runtime. const char* FontLoaderName; // Font loader name (for display e.g. in About box) == FontLoader->Name void* FontLoaderData; // Font backend opaque storage unsigned int FontLoaderFlags; // Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. Per-font override is also available in ImFontConfig). diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e835dcd75..704cc6d2f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2659,6 +2659,11 @@ void ImFontAtlas::CompactCache() ImFontAtlasTextureCompact(this); } +void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) +{ + ImFontAtlasBuildSetupFontLoader(this, font_loader); +} + void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); @@ -4178,9 +4183,9 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) if (atlas->FontLoader == NULL) { #ifdef IMGUI_ENABLE_FREETYPE - ImFontAtlasBuildSetupFontLoader(atlas, ImGuiFreeType::GetFontLoader()); + atlas->SetFontLoader(ImGuiFreeType::GetFontLoader()); #elif defined(IMGUI_ENABLE_STB_TRUETYPE) - ImFontAtlasBuildSetupFontLoader(atlas, ImFontAtlasGetFontLoaderForStbTruetype()); + atlas->SetFontLoader(ImFontAtlasGetFontLoaderForStbTruetype()); #else IM_ASSERT(0); // Invalid Build function #endif diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h index 4f7306790..85313699d 100644 --- a/misc/freetype/imgui_freetype.h +++ b/misc/freetype/imgui_freetype.h @@ -8,7 +8,7 @@ // Usage: // - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support // for imgui_freetype in imgui. It is equivalent to selecting the default loader with: -// io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader() +// io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) // Optional support for OpenType SVG fonts: // - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927. @@ -62,7 +62,7 @@ namespace ImGuiFreeType { // This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'. // If you need to dynamically select between multiple builders: - // - you can manually assign this builder with 'atlas->FontLoader = ImGuiFreeType::GetFontLoader()' + // - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())' // - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data. IMGUI_API const ImFontLoader* GetFontLoader(); @@ -75,7 +75,7 @@ namespace ImGuiFreeType // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader()' if you need runtime selection. + //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection. //static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE' #endif } From 51b3495ad8f7f4a3a4341bf0f9da87fd092a4efd Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Jun 2025 20:59:08 +0200 Subject: [PATCH 7/8] Fonts: set a maximum font size of 512.0f at ImGui:: API level to reduce edge cases. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 6 +++--- imgui_demo.cpp | 2 +- imgui_internal.h | 2 ++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0b9d1d9a1..99246de8f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,9 @@ Other changes: - Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font loader at runtime without using internal API. (#8752, #8465) +- Fonts: set a maximum font size of 512.0f at ImGui:: API level to reduce + edge cases (e.g. out of memory errors). ImDrawList:: API doesn't have the + constraint. (#8758) - Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value: ImTextureData() was incorrectly cleared with zeroes. (#8745) [@rachit7645] - Demo: Added "Text -> Font Size" demo section. (#8738) [@Demonese] diff --git a/imgui.cpp b/imgui.cpp index ddc20991e..f34ad7969 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8824,7 +8824,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet. // - We may support it better later and remove this rounding. final_size = GetRoundedFontSize(final_size); - final_size = ImMax(1.0f, final_size); + final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX); if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)) g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity; g.FontSize = final_size; @@ -15871,9 +15871,9 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work. SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later."); - DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 5.0f); + DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f); //BeginDisabled(io.ConfigDpiScaleFonts); - DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f); + DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f); //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten."); //EndDisabled(); if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 692eb2992..05f5e45f1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8282,7 +8282,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f); //BeginDisabled(GetIO().ConfigDpiScaleFonts); - DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f); + DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f); //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten."); //EndDisabled(); diff --git a/imgui_internal.h b/imgui_internal.h index 22fbef4f2..7ac0c2079 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3711,6 +3711,8 @@ typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are // [SECTION] ImFontAtlas internal API //----------------------------------------------------------------------------- +#define IMGUI_FONT_SIZE_MAX (512.0f) + // Helpers: ImTextureRef ==/!= operators provided as convenience // (note that _TexID and _TexData are never set simultaneously) inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; } From fd75bdccb033e74afeba44f500edc8fcef7eb023 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Jun 2025 21:16:20 +0200 Subject: [PATCH 8/8] Fonts: for large size fonts, layout/size calculation only load glyphs metrics. Actual glyphs are renderer+packed when used by drawing functions. (#8758, #8465) (Breaking) breaks signature of ImFontLoader::FontBakedLoadGlyph, sorry. --- docs/CHANGELOG.txt | 2 + imgui_draw.cpp | 90 ++++++++++++++++++++++++++------ imgui_internal.h | 6 ++- misc/freetype/imgui_freetype.cpp | 18 +++++-- 4 files changed, 93 insertions(+), 23 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 99246de8f..625a5b927 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,8 @@ Other changes: - Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font loader at runtime without using internal API. (#8752, #8465) +- Fonts: for large size fonts, layout/size calculation only load glyphs metrics. + Actual glyphs are renderer+packed when used by drawing functions. (#8758, #8465) - Fonts: set a maximum font size of 512.0f at ImGui:: API level to reduce edge cases (e.g. out of memory errors). ImDrawList:: API doesn't have the constraint. (#8758) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 704cc6d2f..374088573 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2560,6 +2560,7 @@ void ImTextureData::DestroyPixels() //----------------------------------------------------------------------------- // - ImFontBaked_BuildGrowIndex() // - ImFontBaked_BuildLoadGlyph() +// - ImFontBaked_BuildLoadGlyphAdvanceX() // - ImFontAtlasDebugLogTextureRequests() //----------------------------------------------------------------------------- // - ImFontAtlasGetFontLoaderForStbTruetype() @@ -4409,7 +4410,7 @@ static void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font, *c = (ImWchar)font->RemapPairs.GetInt((ImGuiID)*c, (int)*c); } -static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint) +static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x) { ImFont* font = baked->ContainerFont; ImFontAtlas* atlas = font->ContainerAtlas; @@ -4442,13 +4443,25 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)) { - ImFontGlyph glyph_buf; - if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf)) + if (only_load_advance_x == NULL) { - // FIXME: Add hooks for e.g. #7962 - glyph_buf.Codepoint = src_codepoint; - glyph_buf.SourceIdx = src_n; - return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf); + ImFontGlyph glyph_buf; + if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL)) + { + // FIXME: Add hooks for e.g. #7962 + glyph_buf.Codepoint = src_codepoint; + glyph_buf.SourceIdx = src_n; + return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf); + } + } + 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)) + { + ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x); + return NULL; + } } } loader_user_data_p += loader->FontBakedSrcLoaderDataSize; @@ -4468,12 +4481,27 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep return NULL; } +static float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint) +{ + if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE) + { + // First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API. + float only_advance_x = 0.0f; + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, &only_advance_x); + return glyph ? glyph->AdvanceX : only_advance_x; + } + else + { + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, NULL); + return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX; + } +} + // The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b IM_MSVC_RUNTIME_CHECKS_OFF static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint) { - ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint); - return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX; + return ImFontBaked_BuildLoadGlyphAdvanceX(baked, (ImWchar)codepoint); } IM_MSVC_RUNTIME_CHECKS_RESTORE @@ -4594,7 +4622,7 @@ 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) +static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, 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; @@ -4616,7 +4644,14 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC int advance, lsb; stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1); stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb); - const bool is_visible = (x0 != x1 && y0 != y1); + + // Load metrics only mode + if (out_advance_x != NULL) + { + IM_ASSERT(out_glyph == NULL); + *out_advance_x = advance * scale_for_layout; + return true; + } // Prepare glyph out_glyph->Codepoint = codepoint; @@ -4624,6 +4659,7 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC // Pack and retrieve position inside texture atlas // (generally based on stbtt_PackFontRangesRenderIntoRects) + const bool is_visible = (x0 != x1 && y0 != y1); if (is_visible) { const int w = (x1 - x0 + oversample_h - 1); @@ -5124,6 +5160,29 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked return glyph; } +// FIXME: Code is duplicated with code above. +void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x) +{ + IM_UNUSED(atlas); + if (src != NULL) + { + // Clamp & recenter if needed + const float ref_size = baked->ContainerFont->Sources[0]->SizePixels; + const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; + advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale); + + // Snap to pixel + if (src->PixelSnapH) + advance_x = IM_ROUND(advance_x); + + // Bake spacing + advance_x += src->GlyphExtraAdvanceX; + } + + ImFontBaked_BuildGrowIndex(baked, codepoint + 1); + baked->IndexAdvanceX[codepoint] = advance_x; +} + // Copy to texture, post-process and queue update for backend void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch) { @@ -5151,7 +5210,7 @@ ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c) if (i != IM_FONTGLYPH_INDEX_UNUSED) return &Glyphs.Data[i]; } - ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c); + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL); return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex]; } @@ -5167,7 +5226,7 @@ ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c) return &Glyphs.Data[i]; } LockLoadingFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites. - ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c); + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL); LockLoadingFallback = false; return glyph; } @@ -5210,10 +5269,7 @@ float ImFontBaked::GetCharAdvance(ImWchar c) if (x >= 0.0f) return x; } - - // Same as BuildLoadGlyphGetAdvanceOrFallback(): - const ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c); - return glyph ? glyph->AdvanceX : FallbackAdvanceX; + return ImFontBaked_BuildLoadGlyphAdvanceX(this, c); } IM_MSVC_RUNTIME_CHECKS_RESTORE diff --git a/imgui_internal.h b/imgui_internal.h index 7ac0c2079..12fdac568 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3691,7 +3691,7 @@ struct ImFontLoader bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint); 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); + bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x); // 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? @@ -3711,7 +3711,8 @@ typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are // [SECTION] ImFontAtlas internal API //----------------------------------------------------------------------------- -#define IMGUI_FONT_SIZE_MAX (512.0f) +#define IMGUI_FONT_SIZE_MAX (512.0f) +#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE (128.0f) // Helpers: ImTextureRef ==/!= operators provided as convenience // (note that _TexID and _TexData are never set simultaneously) @@ -3829,6 +3830,7 @@ IMGUI_API ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, IMGUI_API ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id); IMGUI_API void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked); IMGUI_API ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph); +IMGUI_API void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x); IMGUI_API void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph); IMGUI_API void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch); diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index e5f2818db..33f259365 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -475,7 +475,7 @@ void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE() } -bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph) +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) { ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); @@ -494,9 +494,20 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src if (metrics == nullptr) return false; - // Render glyph into a bitmap (currently held by FreeType) FT_Face face = bd_font_data->FtFace; FT_GlyphSlot slot = face->glyph; + const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; + + // Load metrics only mode + const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; + if (out_advance_x != NULL) + { + IM_ASSERT(out_glyph == NULL); + *out_advance_x = advance_x; + return true; + } + + // Render glyph into a bitmap (currently held by FreeType) FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL; FT_Error error = FT_Render_Glyph(slot, render_mode); const FT_Bitmap* ft_bitmap = &slot->bitmap; @@ -506,11 +517,10 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src const int w = (int)ft_bitmap->width; const int h = (int)ft_bitmap->rows; const bool is_visible = (w != 0 && h != 0); - const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; // Prepare glyph out_glyph->Codepoint = codepoint; - out_glyph->AdvanceX = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; + out_glyph->AdvanceX = advance_x; // Pack and retrieve position inside texture atlas if (is_visible)