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

# Conflicts:
#	imgui.cpp
#	imgui.h
This commit is contained in:
ocornut
2025-04-30 21:29:09 +02:00
parent 8140a9d8a6
commit b32ef3c05d
5 changed files with 60 additions and 35 deletions

View File

@@ -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);
}

10
imgui.h
View File

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

View File

@@ -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;
}

View File

@@ -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);

View File

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