Browse Source

Fonts: make RasterizerDensity a dynamic field. (temporarily exposed as SetFontRasterizerDensity()).

# Conflicts:
#	imgui.cpp
#	imgui.h
ocornut 4 months ago
parent
commit
b32ef3c05d
5 changed files with 60 additions and 35 deletions
  1. 16 4
      imgui.cpp
  2. 6 4
      imgui.h
  3. 23 17
      imgui_draw.cpp
  4. 8 5
      imgui_internal.h
  5. 7 5
      misc/freetype/imgui_freetype.cpp

+ 16 - 4
imgui.cpp

@@ -3966,6 +3966,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     Font = NULL;
     FontBaked = NULL;
     FontSize = FontSizeBeforeScaling = FontScale = CurrentDpiScale = 0.0f;
+    FontRasterizerDensity = 1.0f;
     IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
     IO.Fonts->RefCount++;
     Time = 0.0f;
@@ -8656,14 +8657,25 @@ void ImGui::UpdateCurrentFontSize()
     // - We may support it better later and remove this rounding.
     final_size = GetRoundedFontSize(final_size);
     final_size = ImMax(1.0f, final_size);
-
+    if (g.Font != NULL)
+        g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity;
+    g.FontBaked = (g.Font != NULL) ? g.Font->GetFontBaked(final_size) : NULL;
     g.FontSize = final_size;
-    g.FontBaked = (g.Font != NULL) ? g.Font->GetFontBaked(g.FontSize) : NULL;
     g.FontScale = (g.Font != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
     g.DrawListSharedData.FontSize = g.FontSize;
     g.DrawListSharedData.FontScale = g.FontScale;
 }
 
+// FIXME-DPI: Not sure how to expose this. It may be automatically applied based on current viewport, if we had this information stored in viewport or monitor.
+void ImGui::SetFontRasterizerDensity(float rasterizer_density)
+{
+    ImGuiContext& g = *GImGui;
+    if (g.FontRasterizerDensity == rasterizer_density)
+        return;
+    g.FontRasterizerDensity = rasterizer_density;
+    UpdateCurrentFontSize();
+}
+
 void ImGui::PushFont(ImFont* font, float font_size)
 {
     ImGuiContext& g = *GImGui;
@@ -16701,7 +16713,7 @@ void ImGui::DebugNodeFont(ImFont* font)
         if (baked->ContainerFont != font)
             continue;
         PushID(baked_n);
-        if (TreeNode("Glyphs", "Baked at %.2fpx: %d glyphs%s", baked->Size, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : ""))
+        if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.1f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : ""))
         {
             if (SmallButton("Load all"))
                 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)
@@ -16714,7 +16726,7 @@ void ImGui::DebugNodeFont(ImFont* font)
             {
                 ImFontConfig* src = font->Sources[src_n];
                 int oversample_h, oversample_v;
-                ImFontAtlasBuildGetOversampleFactors(src, baked->Size, &oversample_h, &oversample_v);
+                ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);
                 BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
                     src_n, src->Name, src->OversampleH, oversample_h, src->OversampleV, oversample_v, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y);
             }

+ 6 - 4
imgui.h

@@ -3467,7 +3467,7 @@ struct ImFontConfig
     float           GlyphExtraAdvanceX;     // 0        // Extra spacing (in pixels) between glyphs. Please contact us if you are using this.
     unsigned int    FontBuilderFlags;       // 0        // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
     float           RasterizerMultiply;     // 1.0f     // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.
-    float           RasterizerDensity;      // 1.0f     // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered.
+    float           RasterizerDensity;      // 1.0f     // DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered.
     ImWchar         EllipsisChar;           // 0        // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
 
     // [Internal]
@@ -3709,6 +3709,7 @@ struct ImFontBaked
     ImVector<float>             IndexAdvanceX;      // 12-16 // out // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI).
     float                       FallbackAdvanceX;   // 4     // out // FindGlyph(FallbackChar)->AdvanceX
     float                       Size;               // 4     // in  // Height of characters/line, set during loading (doesn't change after loading)
+    float                       RasterizerDensity;  // 4     // in  // Density this is baked at
 
     // [Internal] Members: Hot ~28/36 bytes (for RenderText loop)
     ImVector<ImU16>             IndexLookup;        // 12-16 // out // Sparse. Index glyphs by Unicode code-point.
@@ -3753,9 +3754,10 @@ enum ImFontFlags_
 struct ImFont
 {
     // [Internal] Members: Hot ~12-20 bytes
-    ImFontBaked*                LastBaked;          // 4-8   // Cache last bound baked. DO NOT USE. Use GetFontBaked().
-    ImFontAtlas*                ContainerAtlas;     // 4-8   // What we has been loaded into
-    ImFontFlags                 Flags;              // 4     // Font flags
+    ImFontBaked*                LastBaked;          // 4-8   // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked().
+    ImFontAtlas*                ContainerAtlas;     // 4-8   // What we have been loaded into.
+    ImFontFlags                 Flags;              // 4     // Font flags.
+    float                       CurrentRasterizerDensity;    // Current rasterizer density. This is a varying state of the font.
 
     // [Internal] Members: Cold ~24-52 bytes
     // Conceptually Sources[] is the list of font sources merged to create this font.

+ 23 - 17
imgui_draw.cpp

@@ -3349,10 +3349,11 @@ void ImFontAtlasBuildMain(ImFontAtlas* atlas)
     atlas->TexIsBuilt = true;
 }
 
-void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, float size, int* out_oversample_h, int* out_oversample_v)
+void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v)
 {
     // Automatically disable horizontal oversampling over size 36
-    *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : ((size * src->RasterizerDensity > 36.0f) || src->PixelSnapH) ? 1 : 2;
+    const float raster_size = baked->Size * baked->RasterizerDensity * src->RasterizerDensity;
+    *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (raster_size > 36.0f || src->PixelSnapH) ? 1 : 2;
     *out_oversample_v = (src->OversampleV != 0) ? src->OversampleV : 1;
 }
 
@@ -3733,11 +3734,12 @@ void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBa
     baked->IndexAdvanceX[c] = baked->FallbackAdvanceX;
 }
 
-ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, ImGuiID baked_id)
+ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id)
 {
     IMGUI_DEBUG_LOG_FONT("[font] Created baked %.2fpx\n", font_size);
     ImFontBaked* baked = atlas->Builder->BakedPool.push_back(ImFontBaked());
     baked->Size = font_size;
+    baked->RasterizerDensity = font_rasterizer_density;
     baked->BakedId = baked_id;
     baked->ContainerFont = font;
     baked->LastUsedFrame = atlas->Builder->FrameCount;
@@ -3764,7 +3766,7 @@ ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_si
 }
 
 // FIXME-OPT: This is not a fast query. Adding a BakedCount field in Font might allow to take a shortcut for the most common case.
-ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size)
+ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density)
 {
     ImFontAtlasBuilder* builder = atlas->Builder;
     ImFontBaked* closest_larger_match = NULL;
@@ -3774,6 +3776,8 @@ ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, f
         ImFontBaked* baked = &builder->BakedPool[baked_n];
         if (baked->ContainerFont != font || baked->WantDestroy)
             continue;
+        if (baked->RasterizerDensity != font_rasterizer_density)
+            continue;
         if (baked->Size > font_size && (closest_larger_match == NULL || baked->Size < closest_larger_match->Size))
             closest_larger_match = baked;
         if (baked->Size < font_size && (closest_smaller_match == NULL || baked->Size > closest_smaller_match->Size))
@@ -4543,10 +4547,11 @@ static ImFontGlyph* ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas,
 
     // Fonts unit to pixels
     int oversample_h, oversample_v;
-    ImFontAtlasBuildGetOversampleFactors(src, baked->Size, &oversample_h, &oversample_v);
+    ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);
     const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size;
-    const float scale_for_raster_x = bd_font_data->ScaleFactor * baked->Size * src->RasterizerDensity * oversample_h;
-    const float scale_for_raster_y = bd_font_data->ScaleFactor * baked->Size * src->RasterizerDensity * oversample_v;
+    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
+    const float scale_for_raster_x = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_h;
+    const float scale_for_raster_y = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_v;
 
     // Obtain size and advance
     int x0, y0, x1, y1;
@@ -4601,8 +4606,8 @@ static ImFontGlyph* ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas,
             font_off_y = IM_ROUND(font_off_y);
         font_off_x += stbtt__oversample_shift(oversample_h);
         font_off_y += stbtt__oversample_shift(oversample_v) + IM_ROUND(baked->Ascent);
-        float recip_h = 1.0f / (oversample_h * src->RasterizerDensity);
-        float recip_v = 1.0f / (oversample_v * src->RasterizerDensity);
+        float recip_h = 1.0f / (oversample_h * rasterizer_density);
+        float recip_v = 1.0f / (oversample_v * rasterizer_density);
 
         // Register glyph
         // r->x r->y are coordinates inside texture (in pixels)
@@ -5172,11 +5177,12 @@ float ImFontBaked::GetCharAdvance(ImWchar c)
 }
 IM_MSVC_RUNTIME_CHECKS_RESTORE
 
-ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size)
+ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density)
 {
-    struct { ImGuiID FontId; float BakedSize; } hashed_data;
+    struct { ImGuiID FontId; float BakedSize; float RasterizerDensity; } hashed_data;
     hashed_data.FontId = font_id;
     hashed_data.BakedSize = baked_size;
+    hashed_data.RasterizerDensity = rasterizer_density;
     return ImHashData(&hashed_data, sizeof(hashed_data));
 }
 
@@ -5189,12 +5195,12 @@ ImFontBaked* ImFont::GetFontBaked(float size)
     // - ImGui::PushFontSize() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges)
     size = ImGui::GetRoundedFontSize(size);
 
-    if (baked && baked->Size == size)
+    if (baked && baked->Size == size && baked->RasterizerDensity == CurrentRasterizerDensity)
         return baked;
 
     ImFontAtlas* atlas = ContainerAtlas;
     ImFontAtlasBuilder* builder = atlas->Builder;
-    baked = ImFontAtlasBakedGetOrAdd(atlas, this, size);
+    baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, CurrentRasterizerDensity);
     if (baked == NULL)
         return NULL;
     baked->LastUsedFrame = builder->FrameCount;
@@ -5202,11 +5208,11 @@ ImFontBaked* ImFont::GetFontBaked(float size)
     return baked;
 }
 
-ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size)
+ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density)
 {
     // FIXME-NEWATLAS: Design for picking a nearest size based on some criteria?
     // FIXME-NEWATLAS: Altering font density won't work right away.
-    ImGuiID baked_id = ImFontAtlasBakedGetId(font->FontId, font_size);
+    ImGuiID baked_id = ImFontAtlasBakedGetId(font->FontId, font_size, font_rasterizer_density);
     ImFontAtlasBuilder* builder = atlas->Builder;
     ImFontBaked** p_baked_in_map = (ImFontBaked**)builder->BakedMap.GetVoidPtrRef(baked_id);
     ImFontBaked* baked = *p_baked_in_map;
@@ -5220,7 +5226,7 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
     // FIXME-OPT: This is not an optimal query.
     if ((font->Flags & ImFontFlags_LockBakedSizes) || atlas->Locked)
     {
-        baked = ImFontAtlasBakedGetClosestMatch(atlas, font, font_size);
+        baked = ImFontAtlasBakedGetClosestMatch(atlas, font, font_size, font_rasterizer_density);
         if (baked != NULL)
             return baked;
         if (atlas->Locked)
@@ -5231,7 +5237,7 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
     }
 
     // Create new
-    baked = ImFontAtlasBakedAdd(atlas, font, font_size, baked_id);
+    baked = ImFontAtlasBakedAdd(atlas, font, font_size, font_rasterizer_density, baked_id);
     *p_baked_in_map = baked; // To avoid 'builder->BakedMap.SetVoidPtr(baked_id, baked);' while we can.
     return baked;
 }

+ 8 - 5
imgui_internal.h

@@ -2142,6 +2142,7 @@ struct ImGuiContext
     float                   FontSize;                           // == FontSizeBeforeScaling * io.FontGlobalScale * font->Scale * g.CurrentWindow->FontWindowScale. Current text height.
     float                   FontSizeBeforeScaling;              // == value passed to PushFontSize()
     float                   FontScale;                          // == FontBaked->Size / Font->FontSize. Scale factor over baked size.
+    float                   FontRasterizerDensity;              // Current font density. Used by all calls to GetFontBaked().
     float                   CurrentDpiScale;                    // Current window/viewport DpiScale == CurrentViewport->DpiScale
     ImDrawListSharedData    DrawListSharedData;
     double                  Time;
@@ -3109,6 +3110,8 @@ namespace ImGui
 
     // Fonts, drawing
     IMGUI_API void          SetCurrentFont(ImFont* font, float font_size);
+    IMGUI_API void          SetFontRasterizerDensity(float rasterizer_density);
+    inline float            GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }
     IMGUI_API void          UpdateCurrentFontSize();
     inline float            GetRoundedFontSize(float size) { return IM_ROUND(size); }
     inline ImFont*          GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
@@ -3781,7 +3784,7 @@ IMGUI_API ImVec2i           ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas
 
 IMGUI_API void              ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
 IMGUI_API void              ImFontAtlasBuildPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy
-IMGUI_API void              ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, float size, int* out_oversample_h, int* out_oversample_v);
+IMGUI_API void              ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v);
 IMGUI_API void              ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames);
 
 IMGUI_API bool              ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src);
@@ -3791,10 +3794,10 @@ IMGUI_API bool              ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont
 IMGUI_API void              ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font);
 IMGUI_API void              ImFontAtlasFontDiscardOutputBakes(ImFontAtlas* atlas, ImFont* font);
 
-IMGUI_API ImGuiID           ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size);
-IMGUI_API ImFontBaked*      ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size);
-IMGUI_API ImFontBaked*      ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size);
-IMGUI_API ImFontBaked*      ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, ImGuiID baked_id);
+IMGUI_API ImGuiID           ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density);
+IMGUI_API ImFontBaked*      ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
+IMGUI_API ImFontBaked*      ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
+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 ImFontGlyph*      ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
 IMGUI_API void              ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);

+ 7 - 5
misc/freetype/imgui_freetype.cpp

@@ -438,10 +438,11 @@ bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImF
     // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
     // FT_Set_Pixel_Sizes() doesn't seem to get us the same result."
     // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)
+    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
     FT_Size_RequestRec req;
     req.type = (bd_font_data->UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
     req.width = 0;
-    req.height = (uint32_t)(size * 64 * src->RasterizerDensity);
+    req.height = (uint32_t)(size * 64 * rasterizer_density);
     req.horiResolution = 0;
     req.vertResolution = 0;
     FT_Request_Size(bd_font_data->FtFace, &req);
@@ -451,7 +452,7 @@ bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImF
     {
         // Read metrics
         FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics;
-        const float scale = 1.0f / src->RasterizerDensity;
+        const float scale = 1.0f / rasterizer_density;
         baked->Ascent     = (float)FT_CEIL(metrics.ascender) * scale;       // The pixel extents above the baseline in pixels (typically positive).
         baked->Descent    = (float)FT_CEIL(metrics.descender) * scale;      // The extents below the baseline in pixels (typically negative).
         //LineSpacing     = (float)FT_CEIL(metrics.height) * scale;         // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
@@ -503,12 +504,13 @@ ImFontGlyph* ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontCon
     const int w = (int)ft_bitmap->width;
     const int h = (int)ft_bitmap->rows;
     const bool is_visible = (w != 0 && h != 0);
+    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
 
     // Prepare glyph
     ImFontGlyph glyph_in = {};
     ImFontGlyph* glyph = &glyph_in;
     glyph->Codepoint = codepoint;
-    glyph->AdvanceX = (slot->advance.x / FT_SCALEFACTOR) / src->RasterizerDensity;
+    glyph->AdvanceX = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density;
 
     // Pack and retrieve position inside texture atlas
     if (is_visible)
@@ -534,8 +536,8 @@ ImFontGlyph* ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontCon
             font_off_x = IM_ROUND(font_off_x);
         if (src->PixelSnapV)
             font_off_y = IM_ROUND(font_off_y);
-        float recip_h = 1.0f / src->RasterizerDensity;
-        float recip_v = 1.0f / src->RasterizerDensity;
+        float recip_h = 1.0f / rasterizer_density;
+        float recip_v = 1.0f / rasterizer_density;
 
         // Register glyph
         float glyph_off_x = (float)face->glyph->bitmap_left;