How the ImGui Font Atlas Works and How to Optimize Its Size
The ImGui font atlas (ImFontAtlas) packs all registered font glyphs into a single CPU-side bitmap texture that gets uploaded to the GPU, and you can minimize its memory footprint by reducing oversampling, limiting glyph ranges, and leveraging the dynamic font system introduced in v1.92.
The ImFontAtlas structure in the ocornut/imgui repository serves as the central manager for all text rendering within an ImGui context, consolidating every required glyph into a unified texture atlas. Understanding how this packing mechanism works in imgui_internal.h and imgui.h is essential for keeping memory usage low and avoiding costly texture reallocations in immediate-mode GUI applications.
What Is the ImGui Font Atlas?
ImFontAtlas is the core structure that stores all fonts used by an ImGui context. It lives inside ImGuiIO::Fonts and manages the entire lifecycle of font rendering:
- Font Registration: The
AddFont...family of methods createsImFontobjects and registers them with the atlas. - Glyph Packing: Each glyph is turned into a rectangular region and packed into a single texture using the
ImFontAtlasBuilder(defined inimgui_internal.hlines 3987–4011). - Texture Creation: The
ImFontAtlas::Build()method generates a CPU-side bitmap (RGBA or Alpha8) and prepares it for backend upload asImTextureRef.
The atlas texture grows incrementally, starting at 512×128 pixels, and resizes automatically when new glyphs are requested.
How the Font Atlas Packs Glyphs
The packing process occurs in three distinct stages as implemented in the source code:
- Rect Packing: The
ImFontAtlasBuildercalculates bounding boxes for each glyph using the configuredOversampleHandOversampleVvalues, then packs these rectangles into the atlas texture. - Rasterization: For each packed rectangle, the builder rasterizes the glyph bitmap using either stb_truetype or FreeType (if
IMGUI_ENABLE_FREETYPEis defined). - Texture Build: The
ImFontAtlasBuildMainfunction (defined inimgui_internal.hline 4019) assembles the final bitmap and stores it inImTextureDatawithin the atlas.
Runtime text rendering (ImDrawList::AddText, ImGui::Text) fetches UV coordinates from this packed texture to render individual glyphs.
Why the Atlas Texture Can Grow Too Large
Several default behaviors in the ImGui font atlas can cause excessive memory consumption:
- Oversampling: Each glyph is rasterized with
OversampleH/OversampleV(default 2×2), quadrupling the base pixel requirements. - Unicode Ranges: Loading fonts with full Unicode ranges (e.g., CJK) creates thousands of glyphs.
- Power-of-Two Height: By default, the atlas height rounds up to the next power of two unless you set
ImFontAtlasFlags_NoPowerOfTwoHeight. - Default Embedded Font: The bundled
ProggyCleanbitmap font (~26 KB) is added automatically unless explicitly disabled.
How to Optimize the ImGui Font Atlas Size
Remove the Default Bitmap Font
Disable the embedded default font to reclaim ~26 KB of texture space:
// In imconfig.h, before including imgui.h:
#define IMGUI_DISABLE_DEFAULT_FONT
Reduce Oversampling
Lower the ImFontConfig oversampling values to halve texture dimensions:
ImFontConfig cfg;
cfg.OversampleH = 1;
cfg.OversampleV = 1;
io.Fonts->AddFontFromFileTTF("myfont.ttf", 16.0f, &cfg);
Limit Glyph Ranges
Use ImFontGlyphRangesBuilder to include only characters your application actually needs:
ImFontGlyphRangesBuilder builder;
builder.AddText("Hello World"); // Only these glyphs
ImVector<ImWchar> ranges;
builder.BuildRanges(&ranges);
io.Fonts->AddFontFromFileTTF("myfont.ttf", 16.0f, nullptr, ranges.Data);
Avoid Power-of-Two Height
Allow the atlas to use exact dimensions rather than rounding up:
io.Fonts->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;
Pre-Allocate Texture Size
Prevent early growth reallocations by setting minimum texture dimensions:
ImFontAtlas* atlas = io.Fonts;
atlas->TexMinWidth = 1024;
atlas->TexMinHeight = 1024;
Use the Dynamic Font System (v1.92+)
Starting with Dear ImGui v1.92, glyphs are added on demand. Simply load fonts without explicit ranges, and the backend requests glyphs lazily:
// No ranges needed - glyphs loaded as encountered
io.Fonts->AddFontFromFileTTF("myfont.ttf", 16.0f);
Prefer FreeType Over stb_truetype
FreeType rasterizes at exact sizes, often allowing lower oversampling:
// In imconfig.h:
#define IMGUI_ENABLE_FREETYPE
Merge Fonts Efficiently
Keep primary fonts small and merge supplemental fonts only for missing glyphs:
ImFontConfig cfg;
cfg.MergeMode = true;
io.Fonts->AddFontDefault(); // Base UI font
io.Fonts->AddFontFromFileTTF("CJK_Supplement.otf", 16.0f, &cfg);
Pack Custom Rectangles
For icons, use custom rectangles instead of loading entire icon fonts:
int rectId = io.Fonts->AddCustomRectFontGlyph(font, ICON_FA_STAR, 16, 16, 16);
Practical Implementation Example
Complete workflow combining multiple optimization techniques:
// 1. Configure atlas settings
ImGuiIO& io = ImGui::GetIO();
ImFontAtlas* atlas = io.Fonts;
// Disable power-of-two height (saves VRAM)
atlas->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;
// Pre-allocate to avoid reallocations
atlas->TexMinWidth = 1024;
atlas->TexMinHeight = 1024;
// 2. Load primary font with reduced oversampling
ImFontConfig cfg;
cfg.OversampleH = 1;
cfg.OversampleV = 1;
ImFont* uiFont = atlas->AddFontFromFileTTF("ProggyVector.ttf", 0.0f, &cfg);
// 3. Add specific glyph ranges only
ImFontGlyphRangesBuilder rangeBuilder;
rangeBuilder.AddText(u8"こんにちは世界");
ImVector<ImWchar> ranges;
rangeBuilder.BuildRanges(&ranges);
cfg.MergeMode = true;
atlas->AddFontFromFileTTF("NotoSansCJKjp-Medium.otf", 0.0f, &cfg, ranges.Data);
// 4. Build and upload
atlas->Build();
Summary
- ImFontAtlas stores all glyphs in a single texture managed through
ImGuiIO::Fonts. - The packing algorithm in
ImFontAtlasBuilder(defined inimgui_internal.h) handles rectangle packing and rasterization. - Reduce memory by disabling the default font (
IMGUI_DISABLE_DEFAULT_FONT), loweringOversampleH/Vto 1×1, and settingImFontAtlasFlags_NoPowerOfTwoHeight. - Limit glyphs using
ImFontGlyphRangesBuilderor rely on the dynamic font system (v1.92+) to load glyphs on demand. - Pre-allocate texture dimensions via
TexMinWidthandTexMinHeightto prevent runtime reallocations. - Consider FreeType for better quality at lower oversampling values compared to stb_truetype.
Frequently Asked Questions
What is the default size of the ImGui font atlas texture?
The atlas starts at 512×128 pixels and grows dynamically as more glyphs are added. According to the source code in imgui_internal.h, the texture expands automatically when the packing algorithm runs out of space, which can trigger costly GPU texture reallocations if not pre-configured.
How does oversampling affect the atlas texture size?
Oversampling multiplies the texture size by the product of OversampleH and OversampleV. The default 2×2 configuration creates four times as many pixels per glyph compared to 1×1. Reducing these values to 1 in ImFontConfig cuts the atlas memory footprint by up to 75% for large font sets.
Can I load fonts without pre-building all glyphs?
Yes, starting with Dear ImGui v1.92, you can use the dynamic font system by calling AddFontFromFileTTF without specifying explicit glyph ranges. The backend requests glyphs lazily as they are encountered in text, keeping the initial atlas size minimal and growing it only when new characters appear.
Is FreeType better than stb_truetype for ImGui font atlases?
FreeType often produces higher quality text at lower oversampling values, which can reduce atlas size. To enable it, define IMGUI_ENABLE_FREETYPE in your build configuration and include misc/freetype/imgui_freetype.h. FreeType also supports color glyphs and advanced hinting, though it adds a dependency on the FreeType library.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →