From 4fd148f4f972037421229ba343861a1c7497445d Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 9 Aug 2017 22:42:03 +0800 Subject: [PATCH] Added ImFontAtlas::GlyphRangesBuilder helper + doc --- extra_fonts/README.txt | 22 +++++++++++++++++++++- imgui.cpp | 24 +++++++++++++++++------- imgui.h | 15 ++++++++++++++- imgui_draw.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/extra_fonts/README.txt b/extra_fonts/README.txt index 82bc557a..9a311459 100644 --- a/extra_fonts/README.txt +++ b/extra_fonts/README.txt @@ -24,6 +24,7 @@ // Usage, e.g. ImGui::Text("%s Search", ICON_FA_SEARCH); + --------------------------------- FONTS LOADING INSTRUCTIONS --------------------------------- @@ -84,11 +85,27 @@ font->DisplayOffset.y += 1; // Render 1 pixel down +--------------------------------- + BUILDING CUSTOM GLYPH RANGES +--------------------------------- + + You can use the ImFontAtlas::GlyphRangesBuilder helper to create glyph ranges based on text input. + For exemple: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. + + ImVector ranges; + ImFontAtlas::GlyphRangesBuilder builder; + builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters) + builder.AddChar(0x7262); // Add a specific character + builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges + builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted) + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data); + + --------------------------------- REMAPPING CODEPOINTS --------------------------------- - All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese CP-1251 for Cyrillic) will not work. + All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese CP-1251 for Cyrillic) will NOT work! In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code. @@ -169,6 +186,9 @@ Inconsolata http://www.levien.com/type/myfonts/inconsolata.html + Google Noto Fonts (worldwide languages) + https://www.google.com/get/noto/ + Adobe Source Code Pro: Monospaced font family for user interface and coding environments https://github.com/adobe-fonts/source-code-pro diff --git a/imgui.cpp b/imgui.cpp index d8ed9097..5f937212 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -457,15 +457,25 @@ Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. - All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will not work. - In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. - You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code. - io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters - io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() - io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application + // Add default Japanese ranges + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); + + // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need) + ImVector ranges; + ImFontAtlas::GlyphRangesBuilder builder; + builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters) + builder.AddChar(0x7262); // Add a specific character + builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges + builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted) + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data); - As for text input, depends on you passing the right character code to io.AddInputCharacter(). The example applications do that. + All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. + Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work! + Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. + + Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that. + For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle. The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly. Q: How can I preserve my ImGui context across reloading a DLL? (loss of the global/static variables) A: Create your own context 'ctx = CreateContext()' + 'SetCurrentContext(ctx)' and your own font atlas 'ctx->GetIO().Fonts = new ImFontAtlas()' so you don't rely on the default globals. diff --git a/imgui.h b/imgui.h index 851a20ce..8552fea0 100644 --- a/imgui.h +++ b/imgui.h @@ -1350,7 +1350,7 @@ struct ImFontAtlas void SetTexID(ImTextureID id) { TexID = id; } // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) - // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create a UTF-8 string literally using the u8"Hello world" syntax. See FAQ for details. + // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs @@ -1358,6 +1358,19 @@ struct ImFontAtlas IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters + // Helpers to build glyph ranges from text data. Feed all your application strings/characters to it then call BuildRanges(). + struct GlyphRangesBuilder + { + ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) + GlyphRangesBuilder() { UsedChars.resize(0x10000 / 8); memset(UsedChars.Data, 0, 0x10000 / 8); } + bool GetBit(int n) { return (UsedChars[n >> 3] & (1 << (n & 7))) != 0; } + void SetBit(int n) { UsedChars[n >> 3] |= 1 << (n & 7); } // Set bit 'c' in the array + void AddChar(ImWchar c) { SetBit(c); } // Add character + IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) + IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault) to force add all of ASCII/Latin+Ext + IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges + }; + // Members // (Access texture data via GetTexData*() calls which will setup a default font for you.) ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 42a87b47..876fd7df 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1694,6 +1694,44 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai() return &ranges[0]; } +//----------------------------------------------------------------------------- +// ImFontAtlas::GlyphRangesBuilder +//----------------------------------------------------------------------------- + +void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end) +{ + while (text_end ? (text < text_end) : *text) + { + unsigned int c = 0; + int c_len = ImTextCharFromUtf8(&c, text, text_end); + text += c_len; + if (c_len == 0) + break; + if (c < 0x10000) + AddChar((ImWchar)c); + } +} + +void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges) +{ + for (; ranges[0]; ranges += 2) + for (ImWchar c = ranges[0]; c <= ranges[1]; c++) + AddChar(c); +} + +void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) +{ + for (int n = 0; n < 0x10000; n++) + if (GetBit(n)) + { + out_ranges->push_back((ImWchar)n); + while (n < 0x10000 && GetBit(n + 1)) + n++; + out_ranges->push_back((ImWchar)n); + } + out_ranges->push_back(0); +} + //----------------------------------------------------------------------------- // ImFont //-----------------------------------------------------------------------------