diff --git a/imgui.cpp b/imgui.cpp index 47684bd34..028e60b61 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3751,7 +3751,7 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex return ImVec2(0.0f, font_size); ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); - // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field) + // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) const float font_scale = font_size / font->FontSize; const float character_spacing_x = 1.0f * font_scale; if (text_size.x > 0.0f) @@ -8750,7 +8750,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Password pushes a temporary font with only a fallback glyph if (is_password) { - const ImFont::Glyph* glyph = g.Font->FindGlyph('*'); + const ImFontGlyph* glyph = g.Font->FindGlyph('*'); ImFont* password_font = &g.InputTextPasswordFont; password_font->FontSize = g.Font->FontSize; password_font->Scale = g.Font->Scale; @@ -8759,8 +8759,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 password_font->Descent = g.Font->Descent; password_font->ContainerAtlas = g.Font->ContainerAtlas; password_font->FallbackGlyph = glyph; - password_font->FallbackXAdvance = glyph->XAdvance; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty()); + password_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); PushFont(password_font); } diff --git a/imgui.h b/imgui.h index 04bf8538b..a60d83d00 100644 --- a/imgui.h +++ b/imgui.h @@ -905,7 +905,7 @@ struct ImGuiIO ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are negative, so a disappearing/reappearing mouse won't have a huge delta for one frame. //------------------------------------------------------------------ - // [Private] ImGui will maintain those fields. Forward compatibility not guaranteed! + // [Internal] ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ ImVec2 MousePosPrev; // Previous mouse position temporary storage (nb: not for public use, set to MousePos in NewFrame()) @@ -1375,7 +1375,7 @@ struct ImFontConfig float SizePixels; // // Size in pixels for rasterizer. int OversampleH, OversampleV; // 3, 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. - ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. + ImVec2 GlyphExtraSpacing; // 1, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. const ImWchar* GlyphRanges; // NULL // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. @@ -1389,6 +1389,14 @@ struct ImFontConfig IMGUI_API ImFontConfig(); }; +struct ImFontGlyph +{ + ImWchar Codepoint; // 0x0000..0xFFFF + float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in) + float X0, Y0, X1, Y1; // Glyph corners + float U0, V0, U1, V1; // Texture coordinates +}; + // Load and rasterize multiple TTF/OTF fonts into a same texture. // Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering. // We also add custom graphic data into the texture that serves for ImGui. @@ -1412,25 +1420,29 @@ struct ImFontAtlas IMGUI_API void ClearFonts(); // Clear the ImGui-side font data (glyphs storage, UV coordinates) IMGUI_API void Clear(); // Clear all - // Retrieve texture data - // User is in charge of copying the pixels into graphics memory, then call SetTextureUserID() - // After loading the texture into your graphic system, store your texture handle in 'TexID' (ignore if you aren't using multiple fonts nor images) - // RGBA32 format is provided for convenience and high compatibility, but note that all RGB pixels are white, so 75% of the memory is wasted. + // Build atlas, retrieve pixel data. + // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). + // RGBA32 format is provided for convenience and compatibility, but note that unless you use CustomRect to draw color data, the RGB pixels emitted from Fonts will all be white (~75% of waste). // Pitch = Width * BytesPerPixels + IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - void SetTexID(ImTextureID id) { TexID = id; } + void SetTexID(ImTextureID id) { TexID = id; } + + //------------------------------------------- + // Glyph Ranges + //------------------------------------------- // 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 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 - IMGUI_API const ImWchar* GetGlyphRangesChinese(); // Japanese + full set of about 21000 CJK Unified Ideographs + IMGUI_API const ImWchar* GetGlyphRangesChinese(); // Default + Japanese + full set of about 21000 CJK Unified Ideographs 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(). + // Helpers to build glyph ranges from text data. Feed your application strings/characters to it then call BuildRanges(). struct GlyphRangesBuilder { ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) @@ -1443,57 +1455,64 @@ struct ImFontAtlas IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges }; + //------------------------------------------- + // Custom Rectangles/Glyphs API + //------------------------------------------- + + // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. After calling Build(), you can query the rectangle position and render your pixels. + // You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. + struct CustomRect + { + unsigned int ID; // Input // User ID. Use <0x10000 to map into a font glyph, >=0x10000 for other/internal/custom texture data. + unsigned short Width, Height; // Input // Desired rectangle dimension + unsigned short X, Y; // Output // Packed position in Atlas + float GlyphAdvanceX; // Input // For custom font glyphs only (ID<0x10000): glyph xadvance + ImVec2 GlyphOffset; // Input // For custom font glyphs only (ID<0x10000): glyph display offset + ImFont* Font; // Input // For custom font glyphs only (ID<0x10000): target font + CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0,0); Font = NULL; } + bool IsPacked() const { return X != 0xFFFF; } + }; + + IMGUI_API int AddCustomRectRegular(unsigned int id, int width, int height); // Id needs to be >= 0x10000. Id >= 0x80000000 are reserved for ImGui and ImDrawList + IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0,0)); // Id needs to be < 0x10000 to register a rectangle to map into a specific font. + IMGUI_API void ClearCustomRects(); + IMGUI_API void CalcCustomRectUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max); + const CustomRect* GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; } + + //------------------------------------------- // 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. + int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. + int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. + + // [Internal] + // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 int TexWidth; // Texture width calculated during Build(). int TexHeight; // Texture height calculated during Build(). - int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. - int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. - - // [Private] User rectangle for packing custom texture data into the atlas. - struct CustomRect - { - unsigned int ID; // Input // User ID. <0x10000 for font mapped data (WIP/UNSUPPORTED), >=0x10000 for other texture data - unsigned short Width, Height; // Input // Desired rectangle dimension - unsigned short X, Y; // Output // Packed position in Atlas - CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; } - bool IsPacked() const { return X != 0xFFFF; } - }; - - // [Private] Members ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Internal data - IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. - IMGUI_API int CustomRectRegister(unsigned int id, int width, int height); - IMGUI_API void CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max); + int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList }; // Font runtime data and rendering // ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). struct ImFont { - struct Glyph - { - ImWchar Codepoint; - float XAdvance; - float X0, Y0, X1, Y1; - float U0, V0, U1, V1; // Texture coordinates - }; - // Members: Hot ~62/78 bytes float FontSize; // // Height of characters, set during loading (don't change after loading) float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() ImVec2 DisplayOffset; // = (0.f,1.f) // Offset font rendering by xx pixels - ImVector Glyphs; // // All glyphs. - ImVector IndexXAdvance; // // Sparse. Glyphs->XAdvance in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). + ImVector Glyphs; // // All glyphs. + ImVector IndexAdvanceX; // // Sparse. Glyphs->AdvanceX in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. - const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) - float FallbackXAdvance; // == FallbackGlyph->XAdvance + const ImFontGlyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + float FallbackAdvanceX; // == FallbackGlyph->AdvanceX ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() // Members: Cold ~18/26 bytes @@ -1508,9 +1527,9 @@ struct ImFont IMGUI_API ~ImFont(); IMGUI_API void Clear(); IMGUI_API void BuildLookupTable(); - IMGUI_API const Glyph* FindGlyph(ImWchar c) const; + IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const; IMGUI_API void SetFallbackChar(ImWchar c); - float GetCharAdvance(ImWchar c) const { return ((int)c < IndexXAdvance.Size) ? IndexXAdvance[(int)c] : FallbackXAdvance; } + float GetCharAdvance(ImWchar c) const { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } bool IsLoaded() const { return ContainerAtlas != NULL; } // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. @@ -1520,9 +1539,14 @@ struct ImFont IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; - // Private + // [Internal] IMGUI_API void GrowIndex(int new_size); + IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + typedef ImFontGlyph Glyph; // OBSOLETE 1.52+ +#endif }; #if defined(__clang__) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b0556d79e..90a39e70b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1937,7 +1937,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) { // Display all glyphs of the fonts in separate pages of 256 characters - const ImFont::Glyph* glyph_fallback = font->FallbackGlyph; // Forcefully/dodgily make FindGlyph() return NULL on fallback, which isn't the default behavior. + const ImFontGlyph* glyph_fallback = font->FallbackGlyph; // Forcefully/dodgily make FindGlyph() return NULL on fallback, which isn't the default behavior. font->FallbackGlyph = NULL; for (int base = 0; base < 0x10000; base += 256) { @@ -1954,7 +1954,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) { ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size.x + cell_spacing), base_pos.y + (n / 16) * (cell_size.y + cell_spacing)); ImVec2 cell_p2(cell_p1.x + cell_size.x, cell_p1.y + cell_size.y); - const ImFont::Glyph* glyph = font->FindGlyph((ImWchar)(base+n));; + const ImFontGlyph* glyph = font->FindGlyph((ImWchar)(base+n));; draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255,255,255,100) : IM_COL32(255,255,255,50)); font->RenderChar(draw_list, cell_size.x, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base+n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) @@ -1962,7 +1962,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::BeginTooltip(); ImGui::Text("Codepoint: U+%04X", base+n); ImGui::Separator(); - ImGui::Text("XAdvance+1: %.1f", glyph->XAdvance); + ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); ImGui::EndTooltip(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a28063975..aee9374d5 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1066,7 +1066,7 @@ ImFontConfig::ImFontConfig() // The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90; const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; -const int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0xF0000; +const unsigned int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0x80000000; const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" @@ -1101,11 +1101,14 @@ const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF ImFontAtlas::ImFontAtlas() { TexID = NULL; + TexDesiredWidth = 0; + TexGlyphPadding = 1; TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; - TexWidth = TexHeight = TexDesiredWidth = 0; - TexGlyphPadding = 1; + TexWidth = TexHeight = 0; TexUvWhitePixel = ImVec2(0, 0); + for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) + CustomRectIds[n] = -1; } ImFontAtlas::~ImFontAtlas() @@ -1122,7 +1125,7 @@ void ImFontAtlas::ClearInputData() ConfigData[i].FontData = NULL; } - // When clearing this we lose access to the font name and other information used to build the font. + // When clearing this we lose access to the font name and other information used to build the font. for (int i = 0; i < Fonts.Size; i++) if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) { @@ -1318,8 +1321,9 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed return font; } -int ImFontAtlas::CustomRectRegister(unsigned int id, int width, int height) +int ImFontAtlas::AddCustomRectRegular(unsigned int id, int width, int height) { + IM_ASSERT(id >= 0x10000); IM_ASSERT(width > 0 && width <= 0xFFFF); IM_ASSERT(height > 0 && height <= 0xFFFF); CustomRect r; @@ -1330,7 +1334,23 @@ int ImFontAtlas::CustomRectRegister(unsigned int id, int width, int height) return CustomRects.Size - 1; // Return index } -void ImFontAtlas::CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) +int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset) +{ + IM_ASSERT(font != NULL); + IM_ASSERT(width > 0 && width <= 0xFFFF); + IM_ASSERT(height > 0 && height <= 0xFFFF); + CustomRect r; + r.ID = id; + r.Width = (unsigned short)width; + r.Height = (unsigned short)height; + r.GlyphAdvanceX = advance_x; + r.GlyphOffset = offset; + r.Font = font; + CustomRects.push_back(r); + return CustomRects.Size - 1; // Return index +} + +void ImFontAtlas::CalcCustomRectUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) { IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed @@ -1535,26 +1555,9 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) stbtt_aligned_quad q; float dummy_x = 0.0f, dummy_y = 0.0f; stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); - - dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); - ImFont::Glyph& glyph = dst_font->Glyphs.back(); - glyph.Codepoint = (ImWchar)codepoint; - glyph.X0 = q.x0 + off_x; - glyph.Y0 = q.y0 + off_y; - glyph.X1 = q.x1 + off_x; - glyph.Y1 = q.y1 + off_y; - glyph.U0 = q.s0; - glyph.V0 = q.t0; - glyph.U1 = q.s1; - glyph.V1 = q.t1; - glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance - - if (cfg.PixelSnapH) - glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); - dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * atlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * atlas->TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round + dst_font->AddGlyph((ImWchar)codepoint, q.x0 + off_x, q.y0 + off_y, q.x1 + off_x, q.y1 + off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance); } } - cfg.DstFont->BuildLookupTable(); } // Cleanup temporaries @@ -1562,17 +1565,15 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) ImGui::MemFree(buf_ranges); ImGui::MemFree(tmp_array); - // Render into our custom data block - ImFontAtlasBuildRenderDefaultTexData(atlas); + ImFontAtlasBuildFinish(atlas); return true; } void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas) { - // FIXME-WIP: We should register in the constructor (but cannot because our static instances may not have allocator ready by the time they initialize). This needs to be fixed because we can expose CustomRects. - if (atlas->CustomRects.empty()) - atlas->CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); + if (atlas->CustomRectIds[0] < 0) + atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); } void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) @@ -1615,9 +1616,10 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaq } } -void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) +static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) { - ImFontAtlas::CustomRect& r = atlas->CustomRects[0]; + IM_ASSERT(atlas->CustomRectIds[0] >= 0); + ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->CustomRectIds[0]]; IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID); IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1); IM_ASSERT(r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); @@ -1665,6 +1667,29 @@ void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) } } +void ImFontAtlasBuildFinish(ImFontAtlas* atlas) +{ + // Render into our custom data block + ImFontAtlasBuildRenderDefaultTexData(atlas); + + // Register custom rectangle glyphs + for (int i = 0; i < atlas->CustomRects.Size; i++) + { + const ImFontAtlas::CustomRect& r = atlas->CustomRects[i]; + if (r.Font == NULL || r.ID > 0x10000) + continue; + + IM_ASSERT(r.Font->ContainerAtlas == atlas); + ImVec2 uv0, uv1; + atlas->CalcCustomRectUV(&r, &uv0, &uv1); + r.Font->AddGlyph((ImWchar)r.ID, r.GlyphOffset.x, r.GlyphOffset.y, r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height, uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX); + } + + // Build all fonts lookup tables + for (int i = 0; i < atlas->Fonts.Size; i++) + atlas->Fonts[i]->BuildLookupTable(); +} + // Retrieve list of range (2 int per range, values are inclusive) const ImWchar* ImFontAtlas::GetGlyphRangesDefault() { @@ -1858,10 +1883,10 @@ void ImFont::Clear() FontSize = 0.0f; DisplayOffset = ImVec2(0.0f, 1.0f); Glyphs.clear(); - IndexXAdvance.clear(); + IndexAdvanceX.clear(); IndexLookup.clear(); FallbackGlyph = NULL; - FallbackXAdvance = 0.0f; + FallbackAdvanceX = 0.0f; ConfigDataCount = 0; ConfigData = NULL; ContainerAtlas = NULL; @@ -1876,13 +1901,13 @@ void ImFont::BuildLookupTable() max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved - IndexXAdvance.clear(); + IndexAdvanceX.clear(); IndexLookup.clear(); GrowIndex(max_codepoint + 1); for (int i = 0; i < Glyphs.Size; i++) { int codepoint = (int)Glyphs[i].Codepoint; - IndexXAdvance[codepoint] = Glyphs[i].XAdvance; + IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; IndexLookup[codepoint] = (unsigned short)i; } @@ -1892,20 +1917,20 @@ void ImFont::BuildLookupTable() { if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times Glyphs.resize(Glyphs.Size + 1); - ImFont::Glyph& tab_glyph = Glyphs.back(); + ImFontGlyph& tab_glyph = Glyphs.back(); tab_glyph = *FindGlyph((unsigned short)' '); tab_glyph.Codepoint = '\t'; - tab_glyph.XAdvance *= 4; - IndexXAdvance[(int)tab_glyph.Codepoint] = (float)tab_glyph.XAdvance; + tab_glyph.AdvanceX *= 4; + IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); } FallbackGlyph = NULL; FallbackGlyph = FindGlyph(FallbackChar); - FallbackXAdvance = FallbackGlyph ? FallbackGlyph->XAdvance : 0.0f; + FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; for (int i = 0; i < max_codepoint + 1; i++) - if (IndexXAdvance[i] < 0.0f) - IndexXAdvance[i] = FallbackXAdvance; + if (IndexAdvanceX[i] < 0.0f) + IndexAdvanceX[i] = FallbackAdvanceX; } void ImFont::SetFallbackChar(ImWchar c) @@ -1916,13 +1941,35 @@ void ImFont::SetFallbackChar(ImWchar c) void ImFont::GrowIndex(int new_size) { - IM_ASSERT(IndexXAdvance.Size == IndexLookup.Size); + IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); if (new_size <= IndexLookup.Size) return; - IndexXAdvance.resize(new_size, -1.0f); + IndexAdvanceX.resize(new_size, -1.0f); IndexLookup.resize(new_size, (unsigned short)-1); } +void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x) +{ + Glyphs.resize(Glyphs.Size + 1); + ImFontGlyph& glyph = Glyphs.back(); + glyph.Codepoint = (ImWchar)codepoint; + glyph.X0 = x0; + glyph.Y0 = y0; + glyph.X1 = x1; + glyph.Y1 = y1; + glyph.U0 = u0; + glyph.V0 = v0; + glyph.U1 = u1; + glyph.V1 = v1; + glyph.AdvanceX = advance_x + ConfigData->GlyphExtraSpacing.x; // Bake spacing into AdvanceX + + if (ConfigData->PixelSnapH) + glyph.AdvanceX = (float)(int)(glyph.AdvanceX + 0.5f); + + // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round) + MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + 1.99f); +} + void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) { IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. @@ -1935,10 +1982,10 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) GrowIndex(dst + 1); IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; - IndexXAdvance[dst] = (src < index_size) ? IndexXAdvance.Data[src] : 1.0f; + IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; } -const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const +const ImFontGlyph* ImFont::FindGlyph(unsigned short c) const { if (c < IndexLookup.Size) { @@ -2003,7 +2050,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c } } - const float char_width = ((int)c < IndexXAdvance.Size ? IndexXAdvance[(int)c] : FallbackXAdvance); + const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX); if (ImCharIsSpace(c)) { if (inside_word) @@ -2120,7 +2167,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons continue; } - const float char_width = ((int)c < IndexXAdvance.Size ? IndexXAdvance[(int)c] : FallbackXAdvance) * scale; + const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX) * scale; if (line_width + char_width >= max_width) { s = prev_s; @@ -2146,7 +2193,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. return; - if (const Glyph* glyph = FindGlyph(c)) + if (const ImFontGlyph* glyph = FindGlyph(c)) { float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; pos.x = (float)(int)pos.x + DisplayOffset.x; @@ -2250,9 +2297,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col } float char_width = 0.0f; - if (const Glyph* glyph = FindGlyph((unsigned short)c)) + if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c)) { - char_width = glyph->XAdvance * scale; + char_width = glyph->AdvanceX * scale; // Arbitrarily assume that both space and tabs are empty glyphs as an optimization if (c != ' ' && c != '\t') diff --git a/imgui_internal.h b/imgui_internal.h index dd894538e..b2b6a14f0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -921,7 +921,7 @@ IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); -IMGUI_API void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);