2
0
Эх сурвалжийг харах

Fonts: for large size fonts, layout/size calculation only load glyphs metrics. Actual glyphs are renderer+packed when used by drawing functions. (#8758, #8465)

(Breaking) breaks signature of ImFontLoader::FontBakedLoadGlyph, sorry.
ocornut 2 сар өмнө
parent
commit
fd75bdccb0

+ 2 - 0
docs/CHANGELOG.txt

@@ -45,6 +45,8 @@ Other changes:
 
 
 - Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font
 - Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font
   loader at runtime without using internal API. (#8752, #8465)
   loader at runtime without using internal API. (#8752, #8465)
+- Fonts: for large size fonts, layout/size calculation only load glyphs metrics.
+  Actual glyphs are renderer+packed when used by drawing functions. (#8758, #8465)
 - Fonts: set a maximum font size of 512.0f at ImGui:: API level to reduce
 - Fonts: set a maximum font size of 512.0f at ImGui:: API level to reduce
   edge cases (e.g. out of memory errors). ImDrawList:: API doesn't have the
   edge cases (e.g. out of memory errors). ImDrawList:: API doesn't have the
   constraint. (#8758)
   constraint. (#8758)

+ 73 - 17
imgui_draw.cpp

@@ -2560,6 +2560,7 @@ void ImTextureData::DestroyPixels()
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 // - ImFontBaked_BuildGrowIndex()
 // - ImFontBaked_BuildGrowIndex()
 // - ImFontBaked_BuildLoadGlyph()
 // - ImFontBaked_BuildLoadGlyph()
+// - ImFontBaked_BuildLoadGlyphAdvanceX()
 // - ImFontAtlasDebugLogTextureRequests()
 // - ImFontAtlasDebugLogTextureRequests()
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 // - ImFontAtlasGetFontLoaderForStbTruetype()
 // - ImFontAtlasGetFontLoaderForStbTruetype()
@@ -4409,7 +4410,7 @@ static void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font,
         *c = (ImWchar)font->RemapPairs.GetInt((ImGuiID)*c, (int)*c);
         *c = (ImWchar)font->RemapPairs.GetInt((ImGuiID)*c, (int)*c);
 }
 }
 
 
-static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint)
+static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x)
 {
 {
     ImFont* font = baked->ContainerFont;
     ImFont* font = baked->ContainerFont;
     ImFontAtlas* atlas = font->ContainerAtlas;
     ImFontAtlas* atlas = font->ContainerAtlas;
@@ -4442,13 +4443,25 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep
         const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
         const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
         if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint))
         if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint))
         {
         {
-            ImFontGlyph glyph_buf;
-            if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf))
+            if (only_load_advance_x == NULL)
             {
             {
-                // FIXME: Add hooks for e.g. #7962
-                glyph_buf.Codepoint = src_codepoint;
-                glyph_buf.SourceIdx = src_n;
-                return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf);
+                ImFontGlyph glyph_buf;
+                if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL))
+                {
+                    // FIXME: Add hooks for e.g. #7962
+                    glyph_buf.Codepoint = src_codepoint;
+                    glyph_buf.SourceIdx = src_n;
+                    return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf);
+                }
+            }
+            else
+            {
+                // Special mode but only loading glyphs metrics. Will rasterize and pack later.
+                if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x))
+                {
+                    ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x);
+                    return NULL;
+                }
             }
             }
         }
         }
         loader_user_data_p += loader->FontBakedSrcLoaderDataSize;
         loader_user_data_p += loader->FontBakedSrcLoaderDataSize;
@@ -4468,12 +4481,27 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep
     return NULL;
     return NULL;
 }
 }
 
 
+static float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint)
+{
+    if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE)
+    {
+        // First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API.
+        float only_advance_x = 0.0f;
+        ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, &only_advance_x);
+        return glyph ? glyph->AdvanceX : only_advance_x;
+    }
+    else
+    {
+        ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, NULL);
+        return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX;
+    }
+}
+
 // The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b
 // The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b
 IM_MSVC_RUNTIME_CHECKS_OFF
 IM_MSVC_RUNTIME_CHECKS_OFF
 static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint)
 static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint)
 {
 {
-    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint);
-    return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX;
+    return ImFontBaked_BuildLoadGlyphAdvanceX(baked, (ImWchar)codepoint);
 }
 }
 IM_MSVC_RUNTIME_CHECKS_RESTORE
 IM_MSVC_RUNTIME_CHECKS_RESTORE
 
 
@@ -4594,7 +4622,7 @@ static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig
     return true;
     return true;
 }
 }
 
 
-static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph)
+static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)
 {
 {
     // Search for first font which has the glyph
     // Search for first font which has the glyph
     ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
     ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
@@ -4616,7 +4644,14 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC
     int advance, lsb;
     int advance, lsb;
     stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1);
     stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1);
     stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb);
     stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb);
-    const bool is_visible = (x0 != x1 && y0 != y1);
+
+    // Load metrics only mode
+    if (out_advance_x != NULL)
+    {
+        IM_ASSERT(out_glyph == NULL);
+        *out_advance_x = advance * scale_for_layout;
+        return true;
+    }
 
 
     // Prepare glyph
     // Prepare glyph
     out_glyph->Codepoint = codepoint;
     out_glyph->Codepoint = codepoint;
@@ -4624,6 +4659,7 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC
 
 
     // Pack and retrieve position inside texture atlas
     // Pack and retrieve position inside texture atlas
     // (generally based on stbtt_PackFontRangesRenderIntoRects)
     // (generally based on stbtt_PackFontRangesRenderIntoRects)
+    const bool is_visible = (x0 != x1 && y0 != y1);
     if (is_visible)
     if (is_visible)
     {
     {
         const int w = (x1 - x0 + oversample_h - 1);
         const int w = (x1 - x0 + oversample_h - 1);
@@ -5124,6 +5160,29 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked
     return glyph;
     return glyph;
 }
 }
 
 
+// FIXME: Code is duplicated with code above.
+void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x)
+{
+    IM_UNUSED(atlas);
+    if (src != NULL)
+    {
+        // Clamp & recenter if needed
+        const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
+        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
+        advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
+
+        // Snap to pixel
+        if (src->PixelSnapH)
+            advance_x = IM_ROUND(advance_x);
+
+        // Bake spacing
+        advance_x += src->GlyphExtraAdvanceX;
+    }
+
+    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
+    baked->IndexAdvanceX[codepoint] = advance_x;
+}
+
 // Copy to texture, post-process and queue update for backend
 // Copy to texture, post-process and queue update for backend
 void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch)
 void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch)
 {
 {
@@ -5151,7 +5210,7 @@ ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c)
         if (i != IM_FONTGLYPH_INDEX_UNUSED)
         if (i != IM_FONTGLYPH_INDEX_UNUSED)
             return &Glyphs.Data[i];
             return &Glyphs.Data[i];
     }
     }
-    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c);
+    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);
     return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex];
     return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex];
 }
 }
 
 
@@ -5167,7 +5226,7 @@ ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c)
             return &Glyphs.Data[i];
             return &Glyphs.Data[i];
     }
     }
     LockLoadingFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
     LockLoadingFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
-    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c);
+    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);
     LockLoadingFallback = false;
     LockLoadingFallback = false;
     return glyph;
     return glyph;
 }
 }
@@ -5210,10 +5269,7 @@ float ImFontBaked::GetCharAdvance(ImWchar c)
         if (x >= 0.0f)
         if (x >= 0.0f)
             return x;
             return x;
     }
     }
-
-    // Same as BuildLoadGlyphGetAdvanceOrFallback():
-    const ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c);
-    return glyph ? glyph->AdvanceX : FallbackAdvanceX;
+    return ImFontBaked_BuildLoadGlyphAdvanceX(this, c);
 }
 }
 IM_MSVC_RUNTIME_CHECKS_RESTORE
 IM_MSVC_RUNTIME_CHECKS_RESTORE
 
 

+ 4 - 2
imgui_internal.h

@@ -3691,7 +3691,7 @@ struct ImFontLoader
     bool            (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);
     bool            (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);
     bool            (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
     bool            (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
     void            (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
     void            (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
-    bool            (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph);
+    bool            (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x);
 
 
     // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
     // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
     // FIXME: At this point the two other types of buffers may be managed by core to be consistent?
     // FIXME: At this point the two other types of buffers may be managed by core to be consistent?
@@ -3711,7 +3711,8 @@ typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are
 // [SECTION] ImFontAtlas internal API
 // [SECTION] ImFontAtlas internal API
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
-#define IMGUI_FONT_SIZE_MAX     (512.0f)
+#define IMGUI_FONT_SIZE_MAX                                     (512.0f)
+#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE      (128.0f)
 
 
 // Helpers: ImTextureRef ==/!= operators provided as convenience
 // Helpers: ImTextureRef ==/!= operators provided as convenience
 // (note that _TexID and _TexData are never set simultaneously)
 // (note that _TexID and _TexData are never set simultaneously)
@@ -3829,6 +3830,7 @@ IMGUI_API ImFontBaked*      ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas,
 IMGUI_API ImFontBaked*      ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id);
 IMGUI_API ImFontBaked*      ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id);
 IMGUI_API void              ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);
 IMGUI_API void              ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);
 IMGUI_API ImFontGlyph*      ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
 IMGUI_API ImFontGlyph*      ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
+IMGUI_API void              ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x);
 IMGUI_API void              ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);
 IMGUI_API void              ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);
 IMGUI_API void              ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);
 IMGUI_API void              ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);
 
 

+ 14 - 4
misc/freetype/imgui_freetype.cpp

@@ -475,7 +475,7 @@ void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src,
     bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE()
     bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE()
 }
 }
 
 
-bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph)
+bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)
 {
 {
     ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
     ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
     uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint);
     uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint);
@@ -494,9 +494,20 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src
     if (metrics == nullptr)
     if (metrics == nullptr)
         return false;
         return false;
 
 
-    // Render glyph into a bitmap (currently held by FreeType)
     FT_Face face = bd_font_data->FtFace;
     FT_Face face = bd_font_data->FtFace;
     FT_GlyphSlot slot = face->glyph;
     FT_GlyphSlot slot = face->glyph;
+    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
+
+    // Load metrics only mode
+    const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density;
+    if (out_advance_x != NULL)
+    {
+        IM_ASSERT(out_glyph == NULL);
+        *out_advance_x = advance_x;
+        return true;
+    }
+
+    // Render glyph into a bitmap (currently held by FreeType)
     FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL;
     FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL;
     FT_Error error = FT_Render_Glyph(slot, render_mode);
     FT_Error error = FT_Render_Glyph(slot, render_mode);
     const FT_Bitmap* ft_bitmap = &slot->bitmap;
     const FT_Bitmap* ft_bitmap = &slot->bitmap;
@@ -506,11 +517,10 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src
     const int w = (int)ft_bitmap->width;
     const int w = (int)ft_bitmap->width;
     const int h = (int)ft_bitmap->rows;
     const int h = (int)ft_bitmap->rows;
     const bool is_visible = (w != 0 && h != 0);
     const bool is_visible = (w != 0 && h != 0);
-    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
 
 
     // Prepare glyph
     // Prepare glyph
     out_glyph->Codepoint = codepoint;
     out_glyph->Codepoint = codepoint;
-    out_glyph->AdvanceX = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density;
+    out_glyph->AdvanceX = advance_x;
 
 
     // Pack and retrieve position inside texture atlas
     // Pack and retrieve position inside texture atlas
     if (is_visible)
     if (is_visible)