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)
This commit is contained in:
ocornut 2022-05-03 14:46:19 +02:00
parent e668890837
commit 6d27fecce1
5 changed files with 88 additions and 108 deletions

View File

@ -87,6 +87,9 @@ Other Changes:
- Stack Tool: Added option to copy item path to clipboard. (#4631) - 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 PathArcTo() emitting terminating vertices too close to arc vertices. (#4993) [@thedmd]
- DrawList: Fixed texture-based anti-aliasing path with RGBA textures (#5132, #3245) [@cfillion] - 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()) - 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). from causing an assertion when closing the group. (#5181).
- Misc: Fixed IsAnyItemHovered() returning false when using navigation. - Misc: Fixed IsAnyItemHovered() returning false when using navigation.

View File

@ -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. - 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. - 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.

184
imgui.cpp
View File

@ -12060,11 +12060,15 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeDat
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// - RenderViewportThumbnail() [Internal] // - RenderViewportThumbnail() [Internal]
// - RenderViewportsThumbnails() [Internal] // - RenderViewportsThumbnails() [Internal]
// - DebugTextEncoding()
// - MetricsHelpMarker() [Internal] // - MetricsHelpMarker() [Internal]
// - ShowFontAtlas() [Internal]
// - ShowMetricsWindow() // - ShowMetricsWindow()
// - DebugNodeColumns() [Internal] // - DebugNodeColumns() [Internal]
// - DebugNodeDrawList() [Internal] // - DebugNodeDrawList() [Internal]
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
// - DebugNodeFont() [Internal]
// - DebugNodeFontGlyph() [Internal]
// - DebugNodeStorage() [Internal] // - DebugNodeStorage() [Internal]
// - DebugNodeTabBar() [Internal] // - DebugNodeTabBar() [Internal]
// - DebugNodeViewport() [Internal] // - DebugNodeViewport() [Internal]
@ -12127,79 +12131,40 @@ static void RenderViewportsThumbnails()
ImGui::Dummy(bb_full.GetSize() * SCALE); 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(); Text("Text: \"%s\"", str);
if (font->FindGlyphNoFallback(c)) if (!BeginTable("list", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit))
ImGui::TextUnformatted(c_utf8); return;
else TableSetupColumn("Offset");
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "(not in font)"); TableSetupColumn("UTF-8");
TableSetupColumn("Glyph");
ImGui::TableNextColumn(); TableSetupColumn("Codepoint");
char utf8_code[] = "0x.. 0x.. 0x.. 0x.."; TableHeadersRow();
for (int byte_index = 0; c_utf8[byte_index]; byte_index++) 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<ImWchar> 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)
{
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);
}
}
else
{
for (const char* p = buf; p[0] != 0;)
{ {
unsigned int c; unsigned int c;
int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL); const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
char c_utf8[4 + 1]; TableNextColumn();
memcpy(c_utf8, p, c_utf8_len); Text("%d", (int)(p - str));
c_utf8[c_utf8_len] = 0; TableNextColumn();
ShowEncodingViewerChar(font, (ImWchar)c, c_utf8); for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
{
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
TextUnformatted("[missing]");
TableNextColumn();
Text("U+%04X", (int)c);
p += c_utf8_len; p += c_utf8_len;
} }
} EndTable();
ImGui::EndTable();
}
} }
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. // 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 // [DEBUG] List fonts in a font atlas and display its texture
namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
#endif {
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) void ImGui::ShowMetricsWindow(bool* p_open)
{ {
@ -12293,6 +12273,19 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// Tools // Tools
if (TreeNode("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! // Stack Tool is your best friend!
Checkbox("Show stack tool", &cfg->ShowStackTool); Checkbox("Show stack tool", &cfg->ShowStackTool);
SameLine(); 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. // 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..")) if (Button("Item Picker.."))
DebugStartItemPicker(); DebugStartItemPicker();
@ -12461,14 +12448,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
} }
// Details for Fonts // Details for Fonts
#ifndef IMGUI_DISABLE_DEMO_WINDOWS
ImFontAtlas* atlas = g.IO.Fonts; ImFontAtlas* atlas = g.IO.Fonts;
if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
{ {
ShowFontAtlas(atlas); ShowFontAtlas(atlas);
TreePop(); TreePop();
} }
#endif
// Details for Docking // Details for Docking
#ifdef IMGUI_HAS_DOCK #ifdef IMGUI_HAS_DOCK
@ -12628,25 +12613,6 @@ void ImGui::ShowMetricsWindow(bool* p_open)
End(); 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 // [DEBUG] Display contents of Columns
void ImGui::DebugNodeColumns(ImGuiOldColumns* 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); ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); 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)); draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
if (glyph) if (!glyph)
continue;
font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
if (glyph && IsMouseHoveringRect(cell_p1, cell_p2)) if (IsMouseHoveringRect(cell_p1, cell_p2))
{ {
BeginTooltip(); BeginTooltip();
Text("Codepoint: U+%04X", base + n); DebugNodeFontGlyph(font, glyph);
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);
EndTooltip(); EndTooltip();
} }
} }
@ -12878,6 +12840,16 @@ void ImGui::DebugNodeFont(ImFont* font)
TreePop(); 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 // [DEBUG] Display contents of ImGuiStorage
void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
{ {

View File

@ -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. 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 // 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. 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 // Memory Allocators

View File

@ -2871,6 +2871,7 @@ namespace ImGui
IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); 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 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 DebugNodeFont(ImFont* font);
IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
IMGUI_API void DebugNodeTable(ImGuiTable* table); IMGUI_API void DebugNodeTable(ImGuiTable* table);