From 6d27fecce1ea2cb10ca956cd67b8179cb76b35c3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 May 2022 14:46:19 +0200 Subject: [PATCH] Debug: added DebugTextEncoding() to help diagnose between text encoding issues and font loading issues. Simplified code + extracted DebugNodeFontGlyph(). Helper to diagnose issues such as #4866, #3558, #3436, #2233, #1880, #1780, #905, #832, #762, #726, #609, #565, #307) --- docs/CHANGELOG.txt | 3 + docs/FONTS.md | 6 +- imgui.cpp | 184 +++++++++++++++++++-------------------------- imgui.h | 2 +- imgui_internal.h | 1 + 5 files changed, 88 insertions(+), 108 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1a818132..a7cc0453 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -87,6 +87,9 @@ Other Changes: - Stack Tool: Added option to copy item path to clipboard. (#4631) - Drawlist: Fixed PathArcTo() emitting terminating vertices too close to arc vertices. (#4993) [@thedmd] - DrawList: Fixed texture-based anti-aliasing path with RGBA textures (#5132, #3245) [@cfillion] +- Debug: Added DebugTextEncoding() function to facilitate diagnosing issues when not sure about whether + you have a UTF-8 text encoding issue or a font loading issue. [@LaMarche05, @ocornut] +- Metrics: Added a "UTF-8 Encoding Viewer" section using the aforementioned DebugTextEncoding() function. - Misc: Fixed calling GetID("label") _before_ a widget emitting this item inside a group (such as InputInt()) from causing an assertion when closing the group. (#5181). - Misc: Fixed IsAnyItemHovered() returning false when using navigation. diff --git a/docs/FONTS.md b/docs/FONTS.md index de7a1c99..a0354674 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -29,7 +29,11 @@ In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) fo - You can use the `Metrics/Debugger` window (available in `Demo>Tools`) to browse your fonts and understand what's going on if you have an issue. You can also reach it in `Demo->Tools->Style Editor->Fonts`. The same information are also available in the Style Editor under Fonts. -![imgui_capture_0008](https://user-images.githubusercontent.com/8225057/135429892-0e41ef8d-33c5-4991-bcf6-f997a0bcfd6b.png) +![Fonts debugging](https://user-images.githubusercontent.com/8225057/135429892-0e41ef8d-33c5-4991-bcf6-f997a0bcfd6b.png) + +- You can use the `UTF-8 Encoding viewer` in `Metrics/Debugger` to verify the content of your UTF-8 strings. From C/C++ code, you can call `ImGui::DebugTextEncoding("my string");` function to verify that your UTF-8 encoding is correct. + +![UTF-8 Encoding viewer](https://user-images.githubusercontent.com/8225057/166505963-8a0d7899-8ee8-4558-abb2-1ae523dc02f9.png) - All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. diff --git a/imgui.cpp b/imgui.cpp index 3ab6aece..06a5ff47 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12060,11 +12060,15 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeDat //----------------------------------------------------------------------------- // - RenderViewportThumbnail() [Internal] // - RenderViewportsThumbnails() [Internal] +// - DebugTextEncoding() // - MetricsHelpMarker() [Internal] +// - ShowFontAtlas() [Internal] // - ShowMetricsWindow() // - DebugNodeColumns() [Internal] // - DebugNodeDrawList() [Internal] // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] +// - DebugNodeFont() [Internal] +// - DebugNodeFontGlyph() [Internal] // - DebugNodeStorage() [Internal] // - DebugNodeTabBar() [Internal] // - DebugNodeViewport() [Internal] @@ -12127,79 +12131,40 @@ static void RenderViewportsThumbnails() ImGui::Dummy(bb_full.GetSize() * SCALE); } -static void ShowEncodingViewerChar(ImFont* font, ImWchar c, const char* c_utf8) +// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct. +void ImGui::DebugTextEncoding(const char* str) { - ImGui::TableNextColumn(); - if (font->FindGlyphNoFallback(c)) - ImGui::TextUnformatted(c_utf8); - else - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "(not in font)"); - - ImGui::TableNextColumn(); - char utf8_code[] = "0x.. 0x.. 0x.. 0x.."; - for (int byte_index = 0; c_utf8[byte_index]; byte_index++) + Text("Text: \"%s\"", str); + if (!BeginTable("list", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) + return; + TableSetupColumn("Offset"); + TableSetupColumn("UTF-8"); + TableSetupColumn("Glyph"); + TableSetupColumn("Codepoint"); + TableHeadersRow(); + for (const char* p = str; *p != 0; ) { - if (byte_index > 0) - utf8_code[byte_index * 5 - 1] = ' '; - ImFormatString(utf8_code + (byte_index * 5) + 2, 3, "%02X", (int)(unsigned char)c_utf8[byte_index]); - } - ImGui::TextUnformatted(utf8_code); - ImGui::TableNextColumn(); - ImGui::Text("U+%04X", (int)c); -} - -static void ShowUTF8EncodingViewer() -{ - static char buf[256] = ""; - static ImFontGlyphRangesBuilder range_builder; - static ImVector ranges; - static bool unique_glyphs = false; - - ImGui::SetNextItemWidth(-FLT_MIN); - - bool rebuild = false; - rebuild |= ImGui::InputText("##Sample Text", buf, IM_ARRAYSIZE(buf)); - rebuild |= ImGui::Checkbox("Sorted unique glyphs", &unique_glyphs); - if (rebuild && unique_glyphs) - { - range_builder.Clear(); - range_builder.AddText(buf); - ranges.clear(); - range_builder.BuildRanges(&ranges); - } - if (ImGui::BeginTable("list", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY, ImVec2(0.0f, ImGui::GetFontSize() * 15))) - { - ImGui::TableSetupColumn("Glyph"); - ImGui::TableSetupColumn("UTF-8"); - ImGui::TableSetupColumn("Codepoint"); - ImGui::TableHeadersRow(); - - ImFont* font = ImGui::GetFont(); - if (unique_glyphs) + unsigned int c; + const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL); + TableNextColumn(); + Text("%d", (int)(p - str)); + TableNextColumn(); + for (int byte_index = 0; byte_index < c_utf8_len; byte_index++) { - for (int range_index = 0; range_index < ranges.Size && ranges[range_index] != 0; range_index += 2) - for (ImWchar c = ranges[range_index]; c <= ranges[range_index + 1]; c++) - { - char c_utf8[4 + 1]; - ImTextStrToUtf8(c_utf8, IM_ARRAYSIZE(c_utf8), &c, &c + 1); - ShowEncodingViewerChar(font, c, c_utf8); - } + if (byte_index > 0) + SameLine(); + Text("0x%02X", (int)(unsigned char)p[byte_index]); } + TableNextColumn(); + if (GetFont()->FindGlyphNoFallback((ImWchar)c)) + TextUnformatted(p, p + c_utf8_len); else - { - for (const char* p = buf; p[0] != 0;) - { - unsigned int c; - int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL); - char c_utf8[4 + 1]; - memcpy(c_utf8, p, c_utf8_len); - c_utf8[c_utf8_len] = 0; - ShowEncodingViewerChar(font, (ImWchar)c, c_utf8); - p += c_utf8_len; - } - } - ImGui::EndTable(); + TextUnformatted("[missing]"); + TableNextColumn(); + Text("U+%04X", (int)c); + p += c_utf8_len; } + EndTable(); } // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. @@ -12216,9 +12181,24 @@ static void MetricsHelpMarker(const char* desc) } } -#ifndef IMGUI_DISABLE_DEMO_WINDOWS -namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } -#endif +// [DEBUG] List fonts in a font atlas and display its texture +void ImGui::ShowFontAtlas(ImFontAtlas* atlas) +{ + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + PushID(font); + DebugNodeFont(font); + PopID(); + } + if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); + TreePop(); + } +} void ImGui::ShowMetricsWindow(bool* p_open) { @@ -12293,6 +12273,19 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { + bool show_encoding_viewer = TreeNode("UTF-8 Encoding viewer"); + SameLine(); + MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct."); + if (show_encoding_viewer) + { + static char buf[100] = ""; + SetNextItemWidth(-FLT_MIN); + InputText("##Text", buf, IM_ARRAYSIZE(buf)); + if (buf[0] != 0) + DebugTextEncoding(buf); + TreePop(); + } + // Stack Tool is your best friend! Checkbox("Show stack tool", &cfg->ShowStackTool); SameLine(); @@ -12360,12 +12353,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } - if (TreeNode("UTF-8 Encoding viewer")) - { - ShowUTF8EncodingViewer(); - TreePop(); - } - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. if (Button("Item Picker..")) DebugStartItemPicker(); @@ -12461,14 +12448,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) } // Details for Fonts -#ifndef IMGUI_DISABLE_DEMO_WINDOWS ImFontAtlas* atlas = g.IO.Fonts; if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) { ShowFontAtlas(atlas); TreePop(); } -#endif // Details for Docking #ifdef IMGUI_HAS_DOCK @@ -12628,25 +12613,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) End(); } -// [DEBUG] List fonts in a font atlas and display its texture -void ImGui::ShowFontAtlas(ImFontAtlas* atlas) -{ - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - PushID(font); - DebugNodeFont(font); - PopID(); - } - if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); - Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); - TreePop(); - } -} - // [DEBUG] Display contents of Columns void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) { @@ -12856,17 +12822,13 @@ void ImGui::DebugNodeFont(ImFont* font) ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); - if (glyph) - font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); - if (glyph && IsMouseHoveringRect(cell_p1, cell_p2)) + if (!glyph) + continue; + font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); + if (IsMouseHoveringRect(cell_p1, cell_p2)) { BeginTooltip(); - Text("Codepoint: U+%04X", base + n); - Separator(); - Text("Visible: %d", glyph->Visible); - Text("AdvanceX: %.1f", glyph->AdvanceX); - Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + DebugNodeFontGlyph(font, glyph); EndTooltip(); } } @@ -12878,6 +12840,16 @@ void ImGui::DebugNodeFont(ImFont* font) TreePop(); } +void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph) +{ + Text("Codepoint: U+%04X", glyph->Codepoint); + Separator(); + Text("Visible: %d", glyph->Visible); + Text("AdvanceX: %.1f", glyph->AdvanceX); + Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); +} + // [DEBUG] Display contents of ImGuiStorage void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) { diff --git a/imgui.h b/imgui.h index 612d3017..584a1115 100644 --- a/imgui.h +++ b/imgui.h @@ -919,7 +919,7 @@ namespace ImGui IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. // Debug Utilities - // - This is used by the IMGUI_CHECKVERSION() macro. + IMGUI_API void DebugTextEncoding(const char* text); IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. // Memory Allocators diff --git a/imgui_internal.h b/imgui_internal.h index ed9d68df..4ad43dee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2871,6 +2871,7 @@ namespace ImGui IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); IMGUI_API void DebugNodeFont(ImFont* font); + IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph); IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTable(ImGuiTable* table);