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 creates ImFont objects 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 in imgui_internal.h lines 3987–4011).
  • Texture Creation: The ImFontAtlas::Build() method generates a CPU-side bitmap (RGBA or Alpha8) and prepares it for backend upload as ImTextureRef.

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:

  1. Rect Packing: The ImFontAtlasBuilder calculates bounding boxes for each glyph using the configured OversampleH and OversampleV values, then packs these rectangles into the atlas texture.
  2. Rasterization: For each packed rectangle, the builder rasterizes the glyph bitmap using either stb_truetype or FreeType (if IMGUI_ENABLE_FREETYPE is defined).
  3. Texture Build: The ImFontAtlasBuildMain function (defined in imgui_internal.h line 4019) assembles the final bitmap and stores it in ImTextureData within 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 ProggyClean bitmap 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 in imgui_internal.h) handles rectangle packing and rasterization.
  • Reduce memory by disabling the default font (IMGUI_DISABLE_DEFAULT_FONT), lowering OversampleH/V to 1×1, and setting ImFontAtlasFlags_NoPowerOfTwoHeight.
  • Limit glyphs using ImFontGlyphRangesBuilder or rely on the dynamic font system (v1.92+) to load glyphs on demand.
  • Pre-allocate texture dimensions via TexMinWidth and TexMinHeight to 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →