Dear ImGui 字体系统完全指南:从内置字体、TTF 加载到 1.92 动态字体机制
【免费下载链接】imguiDear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies项目地址: https://gitcode.com/GitHub_Trending/im/imgui
本文基于 Dear ImGui 官方文档 docs/FONTS.md 编写,覆盖字体嵌入与加载、多字体合并、图标字体、FreeType 光栅化、彩色 Emoji、字形范围、UTF-8 与文件名两大经典陷阱,并结合当前仓库源码(imgui.h、imconfig.h、misc/freetype/imgui_freetype.h)补充实现细节。读完后你将能够:独立完成从嵌入式默认字体到外部 .TTF/.OTF 字体的全部加载场景,理解 1.92 版本引入的动态字体图集机制,并掌握字体问题的系统排错方法。
一、内置字体:为什么 ImGui 不需要文件系统
Dear ImGui 的源码内嵌了一份 ProggyClean.ttf(Tristan Grimmer 出品)的拷贝,这是一款 13 像素高、像素级精确的默认字体,但它不太适合缩放。仓库同时内嵌了一份 ProggyForever.ttf(Disco Hello 与 Tristan Grimmer 出品)的部分拷贝——这是模仿 ProggyClean 风格、可以良好缩放的新字体。
字体直接嵌入代码的目的是让 Dear ImGui 无需任何文件系统访问即可工作。如果项目中不用内置字体,可以在 imconfig.h 中定义IMGUI_DISABLE_DEFAULT_FONT,发布不含字体的二进制文件,节省约 26 KB。从源码可以确认该开关的三种粒度(imconfig.h):
//#define IMGUI_DISABLE_DEFAULT_FONT // 禁用全部默认字体,约 -9KB -14KB,AddFontDefaultXXX() 将断言 //#define IMGUI_DISABLE_DEFAULT_FONT_BITMAP // 仅禁用位图字体 ProggyClean,约 -9KB //#define IMGUI_DISABLE_DEFAULT_FONT_VECTOR // 仅禁用矢量字体 ProggyForever,约 -14KB三个入口函数的分工在 imgui.h 中有明确注释:
io.Fonts->AddFontDefaultVector(); // 加载内嵌的可缩放字体(ProggyForever),任意较大字号下推荐 io.Fonts->AddFontDefaultBitmap(); // 加载内嵌位图字体(ProggyClean,legacy),推荐 Size 13px 且无缩放 io.Fonts->AddFontDefault(); // legacy:按预期默认字号自动二选一自动选择的规则是:当style.FontSizeBase * style.FontScaleMain * style.FontSizeDpi >= 15时使用 ProggyForever,否则使用 ProggyClean。
除内置字体外,还可以加载外部 .TTF/.OTF 文件(见下文)。仓库的 misc/fonts/ 目录随附了几个推荐字体作为便利资源:Cousine-Regular.ttf、DroidSans.ttf、Karla-Regular.ttf、ProggyClean.ttf、ProggyTiny.ttf、Roboto-Medium.ttf。官方文档同时提醒:相比 2014 年最初引入时,如今的字体支持已经更好(ProggyForever 已内嵌),这些文件现在已非必要,未来可能移除。
二、排错:字体问题 90% 来自 5 个原因
官方文档指出,绝大多数字体与文本相关问题来自以下 4+1 件事,逐一排查即可定位。
原因 1:文件名错误(反斜杠或工作目录不对)
AddFontXXX()系列函数在文件名错误时会触发断言("Could not load font file!")。详见本文"文件名注意事项"与"UTF-8 编码"两节。
原因 2:非 ASCII 字符串的 UTF-8 编码不正确
见"UTF-8 编码注意事项"一节,可用编码查看器确认源码中字符串字面量的编码是否正确。
原因 3:缺少字形范围
自 1.92 起,只要后端是最新的,不再需要指定字形范围。1.92 之前,若要使用非 ASCII 字符,必须显式传入 glyph ranges 加载字体。这曾是 Dear ImGui 的一项约束:加载字体时需指定要加载哪些字符的字形,所有已加载字形会被预先渲染进单一纹理图集;调用io.Fonts->GetTexDataAsAlpha8()、io.Fonts->GetTexDataAsRGBA32()或io.Fonts->Build()任一都会构建图集,这通常由渲染后端完成(例如ImGui_ImplDX11_NewFrame()会调用)。如果使用自定义字形范围,必须保证数组是持久(persistent)的,在GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build()调用期间依然可用。
原因 4:字体图集纹理无法上传到 GPU
自 1.92 起(配合最新后端),图集是增量构建并动态调整大小的,这种情况会少很多。1.92 之前,若字形数量巨大或使用了多字体,纹理可能超出图形 API 的纹理尺寸限制。从源码结构看,一些显卡驱动存在纹理尺寸上限;开发 PC 应用时需注意用户的硬件限制可能低于开发机。纹理上传失败的典型表现是:所有字形甚至整个界面显示为空白白色矩形。
可行的缓解手段:
- 降低过采样,例如
font_config.OversampleH = 1,纹理尺寸减半但画质损失明显(OversampleH = 2 视觉上与 3 非常接近,而 1 的画质下降肉眼可辨); - 基于本地化源数据计算字形范围来缩小范围,用
ImFontGlyphRangesBuilder实现,并在需要新字符的帧之间重建图集——这是收益最大的一项; - 设置
io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;关闭纹理高度向上取整到 2 的幂的处理。源码中该标志定义见 imgui.h。
原因 5:减少启动时的纹理重建/拷贝
自 1.92 起,ImFontAtlas 初始创建时使用 512x128 的纹理,然后随着字形和字体的使用逐渐增大。图集增长伴随 alloc+copy,新旧两个尺寸的纹理会在短时间内(通常一帧)同时存在于内存。如果使用已知字体、希望减少初始增长,可以在初始化阶段设置TexMinWidth和TexMinHeight(源码定义见 imgui.h,均要求为 2 的幂,默认 512/128):
ImFontAtlas* atlas = io.Fonts; atlas->TexMinWidth = 1024; atlas->TexMinHeight = 1024; atlas->AddFont(...);三、1.92 新动态字体系统(2025 年 6 月)
v1.92 引入了新的动态字体系统,要求后端支持ImGuiBackendFlags_HasTextures特性,带来以下改变:
- 使用图标、亚洲语言和非英文语言的用户不再需要预构建全部字形——节省加载时间和内存,也减少了缺字问题,无需再指定字形范围;
- 可以随时调用
PushFont(nullptr, new_size)改变字体大小; - 打包自定义矩形更方便,像素可以立即写入;
- 以往任何字体更新都需要后端特定的调用重新上传纹理,且这些调用不跨后端可移植。现在可以缩放字体而无需任何后端特定调用;
- 可以把自定义 loader/后端插接到任意字体来源上。
四、DPI 处理
自 1.92 起(配合更新的后端):
- 设置
style.FontScaleDpi = your_content_scale;即可缩放所有字体; - 可在初始化时或每帧主循环开头调用
style.ScaleAllSizes(xxx)缩放尺寸/内边距; - macOS 风格的像素/backing 缩放自 1.92 起由更新的后端自动处理。
五、字体加载指令
选择基础字号
ImGuiStyle& style = ImGui::GetStyle(); style.FontSizeBase = 20.0f;加载默认字体
ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefaultVector(); // 加载内嵌可缩放字体io.Fonts->AddFontDefaultBitmap(); // 加载内嵌位图字体(legacy)io.Fonts->AddFontDefault(); // 加载内嵌字体(legacy:自动二选一)加载 .TTF/.OTF 文件
自 1.92 起(配合最新后端):不再需要传 size 参数:
ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("font.ttf");1.92 之前,或后端未更新时:
ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels);如果出现 "Could not load font file!" 断言,字体文件名大概率不正确,请仔细核对"文件名注意事项"。
加载多个字体
// Init ImGuiIO& io = ImGui::GetIO(); ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf"); ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf");在应用主循环中切换字体:
ImGui::Text("Hello"); // 使用默认字体(即第一个加载的字体) ImGui::PushFont(font2, 0.0f); // 切换字体,保持当前大小 ImGui::Text("Hello with another font"); ImGui::PopFont();高级选项:ImFontConfig
ImFontConfig config; config.OversampleH = 1.0f; ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config);注意ImFontConfig会被内部拷贝,可以放心使用局部变量。
合并多个字体为一个
自 1.92 起(配合最新后端):无需指定字形范围:
// 先加载一个字体 ImFont* font = io.Fonts->AddFontDefaultVector(); ImFontConfig config; config.MergeMode = true; io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 0.0f, &config); // 合并进第一个字体,例如添加亚洲字符 io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 0.0f, &config); // 合并进第一个字体,添加图标1.92 之前,或后端未更新时:
// 先加载一个字体 ImFont* font = io.Fonts->AddFontDefault(); // 添加字符范围并合并进上一个字体 // 注意:ranges 数组不会被 AddFont* 函数拷贝,且是惰性使用的, // 因此必须保证在 Build 或调用 GetTexDataAsRGBA32() 时它仍然可用。 static const ImWchar icons_ranges[] = { 0xf000, 0xf3ff, 0 }; // 不会被拷贝,保持在作用域内 ImFontConfig config; config.MergeMode = true; io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 18.0f, &config, io.Fonts->GetGlyphRangesJapanese()); // 合并进第一个字体 io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 18.0f, &config, icons_ranges); // 合并进第一个字体 io.Fonts->Build();通过第四个参数仅烘焙特定字体范围
自 1.92 起(配合最新后端):无需指定字形范围,所有GetGlyphRangesXXX()函数均已被标记过时。1.92 之前的典型用法:
// Basic Latin, Extended Latin io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesDefault()); // Default + 简体中文常用 2500 个汉字 io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); // Default + 平假名、片假名、半角、1946 个常用汉字 io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesJapanese());加载并使用日文字体示例
自 1.92 起(配合最新后端):
ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("NotoSansCJKjp-Medium.otf");1.92 之前,或后端未更新时:
ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("NotoSansCJKjp-Medium.otf", 20.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());界面代码:
ImGui::Text(u8"こんにちは!テスト %d", 123); if (ImGui::Button(u8"ロード")) { // do stuff } ImGui::InputText("string", buf, IM_COUNTOF(buf)); ImGui::SliderFloat("float", &f, 0.0f, 1.0f);(官方示例效果:深色/浅色样式,NotoSansCJKjp-Medium 字体 20px,圆角 5。)
六、从内存加载字体数据
ImFont* font = io.Fonts->AddFontFromMemoryTTF(data, data_size, size_pixels, ...);重要:AddFontFromMemoryTTF()默认将数据缓冲区的所有权转移给字体图集,销毁时图集会尝试释放它。这是为了避免不必要的拷贝,但官方文档认为这或许不是个好 API(未来版本会重新设计)。如果想保留数据所有权并自行释放,需要清除FontDataOwnedByAtlas字段(该字段定义见 imgui.h,AddFontFromMemoryTTF的注释见 imgui.h):
ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; ImFont* font = io.Fonts->AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg);重要:自 1.92 起,当使用FontDataOwnedByAtlas = false时,字体数据必须保持可用,直到atlas->RemoveFont(),更常见地是直到拥有该上下文或字体图集的程序关闭。1.92.0 中由于处理FontDataOwnedByAtlas = false存在 bug 这一事实尚未暴露,该 bug 已在 1.92.6 修复。
七、将字体数据嵌入源代码
- 编译并使用 misc/fonts/binary_to_compressed_c.cpp,生成可嵌入源码的压缩 C 风格数组;
- 该工具文件内自带使用说明文档;
- Windows 下的预编译版本 binary_to_compressed_c.exe 可在 demo 二进制发布包中找到(参见 docs/README.md);
- 该工具可选输出 Base85 编码以缩小源码体积,但实际二进制中的只读数组会大约增大 20%。
然后加载字体:
ImFont* font = io.Fonts->AddFontFromMemoryCompressedTTF(compressed_data, compressed_data_size, size_pixels, ...);或
ImFont* font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(compressed_data_base85, size_pixels, ...);八、图标字体
使用图标字体(如 FontAwesome 或 OpenFontIcons)是在 Dear ImGui 应用中使用图标的简单实用方式。常见模式是把图标字体合并进主字体,从而可以直接在字符串中嵌入图标,无需来回切换字体。
在 C++ 代码中引用图标的 UTF-8 码点,可以使用社区维护的 IconFontCppHeaders 头文件(提供形如ICON_FA_SEARCH的宏),这样ICON_FA_SEARCH就会渲染为一个"搜索"图标。
自 1.92 起(配合最新后端):无需指定字形范围,可以省略该参数。示例设置:
// 将图标合并进默认工具字体 #include "IconsFontAwesome.h" ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefaultVector(); ImFontConfig config; config.MergeMode = true; config.GlyphMinAdvanceX = 13.0f; // 若希望图标等宽,可设置此项 io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config);1.92 之前:
// 将图标合并进默认工具字体 #include "IconsFontAwesome.h" ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefault(); ImFontConfig config; config.MergeMode = true; config.GlyphMinAdvanceX = 13.0f; // 若希望图标等宽,可设置此项 static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config, icon_ranges);使用示例:
// 用法示例 ImGui::Text("%s among %d items", ICON_FA_SEARCH, count); ImGui::Button(ICON_FA_SEARCH " Search"); // C 字符串_字面量_可以在编译期拼接,例如 "hello" " world" // ICON_FA_SEARCH 定义为字符串字面量,所以这与 "A" "B" 拼成 "AB" 是同一机制等宽图标
若想让图标更像等宽、便于对齐,可以在加载图标字体时设置ImFontConfig::GlyphMinAdvanceX。注意:如果设置了GlyphMinAdvanceX,就必须在AddFontXXX()调用中传入font_size,因为 MinAdvanceX 值是针对给定字号指定的,否则会按比例缩放。
排除重叠范围
自 1.92 起(配合最新后端):字形范围被忽略——加载字形时,合并列表中的输入字体按顺序查询,第一个拥有该字形的字体负责加载它。
但若合并了多个字体,可能出现不期望的范围重叠。可以用ImFontConfig::GlyphExcludeRanges[]指定某个输入要忽略的范围:
// 添加字体源 1,但忽略 ICON_MIN_FA..ICON_MAX_FA 范围 static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig cfg1; cfg1.GlyphExcludeRanges = exclude_ranges; io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); // 添加字体源 2,它期望使用上面让出的范围 ImFontConfig cfg2; cfg2.MergeMode = true; io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2);另一个(玩具)示例:
// 从第一个字体中移除 'A'-'Z' static ImWchar exclude_ranges[] = { 'A', 'Z', 0 }; ImFontConfig cfg1; cfg1.GlyphExcludeRanges = exclude_ranges; io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); // 再加载另一个字体来填补空缺 ImFontConfig cfg2; cfg2.MergeMode = true; io.Fonts->AddFontFromFileTTF("Roboto-Medium.ttf", 0.0f, &cfg2);可以用Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool查看在多个字体源中均可用字形列表,帮助理解哪个字体输入提供了哪个字形。
九、FreeType 光栅化器(imgui_freetype)
- Dear ImGui 默认使用 imstb_truetype.h(stb_truetype)光栅化字体(支持可选过采样)。这种技术及其实现并不适合小字号渲染,小字可能略显模糊、难以阅读;
- 可以使用 misc/freetype/ 目录下的
imgui_freetype.cpp。编译时加入该文件,并在 imconfig.h 或构建系统中添加#define IMGUI_ENABLE_FREETYPE即可自动激活(开关定义见 imconfig.h); - FreeType 支持自动提示(auto-hinting),通常能改善小字号的可读性,在小分辨率下差别尤为明显;
- misc/freetype/ 目录内有更详细的使用文档;
- 正确的 sRGB 空间混合会对字体渲染质量产生重要影响。
从源码看,#define IMGUI_ENABLE_FREETYPE等价于手动调用io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())(见 misc/freetype/imgui_freetype.h)。FreeType 后端还提供了一组可全局(ImFontAtlas::FontLoaderFlags)或按字体(ImFontConfig::FontLoaderFlags,字段定义见 imgui.h)设置的标志(misc/freetype/imgui_freetype.h):
| 标志 | 含义 |
|---|---|
ImGuiFreeTypeLoaderFlags_NoHinting | 禁用 hinting,字形通常更"模糊" |
ImGuiFreeTypeLoaderFlags_NoAutoHint | 禁用自动 hinter |
ImGuiFreeTypeLoaderFlags_ForceAutoHint | 优先使用自动 hinter 而非字体自带 hinter |
ImGuiFreeTypeLoaderFlags_LightHinting | 轻量 hinting,仅沿 Y 轴吸附像素网格,字形更模糊但更接近原始形状 |
ImGuiFreeTypeLoaderFlags_MonoHinting | 强 hinting,仅适合单色输出 |
ImGuiFreeTypeLoaderFlags_Bold/Oblique | 人工加粗 / 仿斜体 |
ImGuiFreeTypeLoaderFlags_Monochrome | 禁用抗锯齿,与 MonoHinting 组合效果最佳 |
ImGuiFreeTypeLoaderFlags_LoadColor | 启用 FreeType 彩色分层字形 |
ImGuiFreeTypeLoaderFlags_Bitmap | 启用 FreeType 位图字形 |
默认 hinting 是开启的,且优先使用字体原生 hinter;关闭 FreeType 生成的字形会更模糊,接近 stb_truetype 的效果。
十、彩色字形 / Emoji
- 彩色 Emoji 渲染由 imgui_freetype 在 FreeType 2.10+ 下支持;
- 加载字体时需要
ImGuiFreeTypeBuilderFlags_LoadColor标志(即上表的ImGuiFreeTypeLoaderFlags_LoadColor); - Emoji 经常编码在 Unicode 高层(码点 > 0x10000),需要将 Dear ImGui 以
IMGUI_USE_WCHAR32编译(开关见 imconfig.h); - FreeType 目前并非支持所有类型的彩色字体;
- 皮肤色调修饰符(skin tone modifiers)等有状态 Unicode 特性不受文本渲染器支持。
示例(将 Windows Emoji 字体 seguemj 合并进常规字体):
io.Fonts->AddFontFromFileTTF("../../../imgui_dev/data/fonts/NotoSans-Regular.ttf", 16.0f); static ImWchar ranges[] = { 0x1, 0x1FFFF, 0 }; static ImFontConfig cfg; cfg.MergeMode = true; cfg.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor; io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg);十一、自定义字形范围
自 1.92 起(配合最新后端):无需指定字形范围,此节实用性大降。1.92 之前,可以使用ImFontGlyphRangesBuilder辅助类基于文本输入创建字形范围。例如:对于剧本内容已知的游戏,可以把整个剧本喂给它,只构建游戏实际用到的字符:
ImVector<ImWchar> ranges; ImFontGlyphRangesBuilder builder; builder.AddText("Hello world"); // 添加字符串("Hello world" 含 7 个不同字符) builder.AddChar(0x7262); // 添加特定字符 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // 添加一组默认范围 builder.BuildRanges(&ranges); // 构建最终结果(有序、去重) io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, nullptr, ranges.Data); io.Fonts->Build(); // 在 'ranges' 仍在作用域、未删除时构建图集十二、自定义彩色图标
自 1.92 起(配合最新后端):该系统已被重构。建议做法是创建自定义ImFontLoader并用它注册字体。AddCustomRectFontGlyph()已被废弃,因为其 API 在可缩放字体下不太合理。
1.92 之前的做法(BETA API,需要熟悉 dear imgui 和渲染后端):
- 使用
ImFontAtlas::AddCustomRect()和ImFontAtlas::AddCustomRectFontGlyph()API 注册会被打包进字体图集纹理的矩形。在构建图集之前注册,然后调用Build(); - 然后用
ImFontAtlas::GetCustomRect(int)查询矩形在纹理中的位置/尺寸,把任意图形数据 blit/copy 进去; - 该 API 是 beta 状态,因为很可能为支持多 DPI(多个不同 DPI 缩放的视口)而改变。
伪代码:
// 添加字体,然后为字形 'a' 和 'b' 注册两个 13x13 自定义矩形 ImFont* font = io.Fonts->AddFontDefaultVector(); int rect_ids[2]; rect_ids[0] = io.Fonts->AddCustomRectFontGlyph(font, 'a', 13, 13, 13+1); rect_ids[1] = io.Fonts->AddCustomRectFontGlyph(font, 'b', 13, 13, 13+1); // 构建图集 io.Fonts->Build(); // 以 RGBA 格式获取纹理 unsigned char* tex_pixels = nullptr; int tex_width, tex_height; io.Fonts->GetTexDataAsRGBA32(&tex_pixels, &tex_width, &tex_height); for (int rect_n = 0; rect_n < IM_COUNTOF(rect_ids); rect_n++) if (const ImTextureRect* rect = io.Fonts->GetCustomRect(rect_ids[rect_n])) { // 用红色像素填充自定义矩形(实际中应在此绘制/拷贝你的位图数据) for (int y = 0; y < rect->Height; y++) { ImU32* p = (ImU32*)tex_pixels + (rect->Y + y) * tex_width + (rect->X); for (int x = rect->Width; x > 0; x--) *p++ = IM_COL32(255, 0, 0, 255); } }十三、关于文件名
大量新 C/C++ 用户在加载字体时遇到问题,根源在于文件名错误——对"当前目录"的假设不对。
两点需要特别注意:
(1) C/C++ 及大多数编程语言中,字符串字面量里要用反斜杠\必须写成双反斜杠\\。而 Windows 恰好使用反斜杠作为路径分隔符,务必留意:
io.Fonts->AddFontFromFileTTF("MyFiles\MyImage01.jpg", ...); // 错误!! io.Fonts->AddFontFromFileTTF("MyFiles\\MyImage01.jpg", ...); // 正确在 Windows 下,某些情况也可以用/作为路径分隔符。
(2) 确保 IDE/调试器设置让可执行文件从正确的工作(当前)目录启动。Visual Studio 中可以在项目Properties > General > Debugging > Working Directory更改工作目录。人们常假设程序从项目根目录启动,而实际上默认通常从存放目标文件或可执行文件的目录启动:
io.Fonts->AddFontFromFileTTF("MyImage01.jpg", ...); // 相对文件名取决于运行时的 Working Directory! io.Fonts->AddFontFromFileTTF("../MyImage01.jpg", ...); // 从 Working Directory 的父目录加载十四、关于 UTF-8 编码
对于非 ASCII 字符显示,一个常见的用户问题是没有传入正确 UTF-8 编码的字符串。
(1) 提供函数ImGui::DebugTextEncoding(const char* text)可验证 UTF-8 字符串内容,是确认编码正确的便捷手段:
ImGui::SeparatorText("CORRECT"); ImGui::DebugTextEncoding(u8"こんにちは"); ImGui::SeparatorText("INCORRECT"); ImGui::DebugTextEncoding("こんにちは");也可以在Metrics/Debuggers->Tools->UTF-8 Encoding viewer找到同样工具从剪贴板粘贴验证,但这无法验证你的编译器所做的 UTF-8 编码。
(2) 编译器层面强制 UTF-8 的方式:
- Visual Studio 编译器:
/utf-8命令行标志; - Visual Studio 编译器:代码内
#pragma execution_character_set("utf-8"); - 自 2023 年 5 月起,仓库中所有示例的 Visual Studio 项目已改用
/utf-8。
或者,自 C++11 起,可以使用u8"my text"语法将字面量编码为 UTF-8:
ImGui::Text(u8"hello"); ImGui::Text(u8"こんにちは"); // 总是编码为 UTF-8 ImGui::Text("こんにちは"); // 编码取决于编译器设置/标志,可能不正确自 C++20 起,u8""的返回类型从const char*变为const char8_t*,不能直接转换为const char*,使用稍显繁琐:
ImGui::Text((const char*)u8"こんにちは");也可以用编译器选项关闭这一行为:MSVC 使用/Zc:char8_t-,Clang 和 GCC 使用-fno-char8_t。
十五、调试工具
Metrics/Debugger -> Fonts
可以使用Metrics/Debugger窗口(位于Demo>Tools)浏览字体,出问题时有用;也可以通过Demo->Tools->Style Editor->Fonts进入,Style Editor 的 Fonts 部分提供相同信息。
UTF-8 Encoding Viewer
可以使用Metrics/Debugger中的UTF-8 Encoding viewer验证 UTF-8 字符串内容。从 C/C++ 代码中,可以调用ImGui::DebugTextEncoding("my string");验证编码是否正确。
十六、仓库内置字体的版权与许可证
嵌入源码的字体:
- ProggyClean.ttf,Tristan Grimmer 出品,MIT License。推荐加载设置:Size = 13.0、GlyphOffset.y = +1、PixelSnapH = true;
- ProggyForever.ttf,Disco Hello 与 Tristan Grimmer 出品,MIT License。
misc/fonts/ 目录下的额外字体文件:
- Roboto-Medium.ttf,Christian Robetson 出品,Apache License 2.0;
- Cousine-Regular.ttf,Steve Matteson 出品,数字数据版权归 Google Corporation (2010),SIL Open Font License 1.1;
- DroidSans.ttf,Steve Matteson 出品,Apache License 2.0;
- ProggyTiny.ttf,Tristan Grimmer 出品,MIT License。推荐加载设置:Size = 10.0、GlyphOffset.y = +1;
- Karla-Regular.ttf,Jonathan Pinhorn 出品,SIL OPEN FONT LICENSE 1.1。
官方文档说明:这些字体文件在 2014 年引入时是有用的,如今随着字体支持改善(ProggyForever 内嵌)已大多不再必要,可能会最终移除。
十七、常用字体资源方向
- 图标字体:FontAwesome、OpenFontIcons、Google Icon Fonts、IcoMoon 定制图标字体构建器、IconFontCppHeaders 头文件生成等;
- 常规字体:Google Noto Fonts(全球语言)、Open Sans、日文字体 M+ 等;
- 等宽字体:Proggy Fonts、Sweet16、Noto Mono、Adobe Source Code Pro、Inconsolata 等,以及 Windows 自带的 Arial Unicode 等 Unicode 字体(全字符覆盖,但许可情况需自行确认)。
小结:一张速查表
| 场景 | 推荐做法 |
|---|---|
| 快速上手、无文件系统 | AddFontDefaultVector()(1.92 起推荐) |
| 像素风 UI、13px 不缩放 | AddFontDefaultBitmap() |
| 需要任意字号/缩放 | 1.92 动态系统 +AddFontFromFileTTF()(免 size 参数) |
| 缩小二进制体积 | imconfig.h 中IMGUI_DISABLE_DEFAULT_FONT(约省 26 KB) |
| 小字号更清晰 | misc/freetype/ +IMGUI_ENABLE_FREETYPE |
| 图标 | 图标字体 +MergeMode合并,GlyphMinAdvanceX等宽 |
| 全字符覆盖、无范围指定 | 1.92 动态字体(最新后端),否则ImFontGlyphRangesBuilder |
| 无文件部署 | binary_to_compressed_c.cpp 生成压缩数组 +AddFontFromMemoryCompressedTTF() |
| 排错 | 依次检查:文件名/工作目录、UTF-8 编码、字形范围、图集纹理大小 |
【免费下载链接】imguiDear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies项目地址: https://gitcode.com/GitHub_Trending/im/imgui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考