From 99f6b305c1e7d6b08c70f18971b8865af2a1b725 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Feb 2025 17:46:59 +0100 Subject: [PATCH] Fonts: Baked system, v12: support GlyphOffset / GlyphMinAdvanceX / GlyphMaxAdvanceX by scaling from ref value. Overwriting cfg->PixelSnapH = true; in imgui_freetype is weird. --- imgui.cpp | 2 +- imgui.h | 9 +++++---- imgui_draw.cpp | 14 +++++++++++--- misc/freetype/imgui_freetype.cpp | 12 ++++++++++-- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fb26c967b..a7cc6a81a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8602,7 +8602,7 @@ void ImGui::SetCurrentFont(ImFont* font, float font_size) { ImGuiContext& g = *GImGui; g.Font = font; - //g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.FontBaked->FontSize * g.Font->Scale); + //g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.FontBaked->Size * g.Font->Scale); g.FontSize = font_size;// g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; if (font != NULL) { diff --git a/imgui.h b/imgui.h index 935f2b3b7..24706e062 100644 --- a/imgui.h +++ b/imgui.h @@ -3424,15 +3424,16 @@ struct ImFontConfig bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). 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. bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. 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. + bool PixelSnapV; // true // Align Scaled GlyphOffset.y to pixel boundaries. int FontNo; // 0 // Index of font within TTF/OTF file int OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. int OversampleV; // 0 (1) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis. float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - //ImVec2 GlyphExtraSpacing; // 0, 0 // (REMOVED IN 1.91.9: use GlyphExtraAdvanceX) - ImVec2 GlyphOffset; // 0, 0 // [FIXME-BAKED] Offset all glyphs from this font input. + //ImVec2 GlyphExtraSpacing; // 0, 0 // (REMOVED AT IT SEEMS LARGELY OBSOLETE. PLEASE REPORT IF YOU WERE USING THIS). Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now. + ImVec2 GlyphOffset; // 0, 0 // Offset (in pixels) all glyphs from this font input. Absolute value for default size, other sizes will scale this value. const ImWchar* GlyphRanges; // NULL // THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). - float GlyphMinAdvanceX; // 0 // [FIXME-BAKED] Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font - float GlyphMaxAdvanceX; // FLT_MAX // [FIXME-BAKED] Maximum AdvanceX for glyphs + float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font. Absolute value for default size, other sizes will scale this value. + float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs 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. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index b537132fb..921241185 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -4351,8 +4351,15 @@ static bool ImGui_ImplStbTrueType_FontBakedAddGlyph(ImFontAtlas* atlas, ImFontBa if (oversample_v > 1) stbtt__v_prefilter(bitmap_pixels, r->w, r->h, r->w, oversample_v); - float font_off_x = src->GlyphOffset.x + stbtt__oversample_shift(oversample_h); - float font_off_y = src->GlyphOffset.y + stbtt__oversample_shift(oversample_v) + IM_ROUND(baked->Ascent); + const float offsets_scale = baked->Size / baked->ContainerFont->Sources[0].SizePixels; + float font_off_x = (src->GlyphOffset.x * offsets_scale); + float font_off_y = (src->GlyphOffset.y * offsets_scale); + if (src->PixelSnapH) // Snap scaled offset. This is to mitigate backward compatibility issues for GlyphOffset, but a better design would be welcome. + font_off_x = IM_ROUND(font_off_x); + if (src->PixelSnapV) + 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); @@ -4800,7 +4807,8 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked if (src != NULL) { // Clamp & recenter if needed - float advance_x = ImClamp(glyph.AdvanceX, src->GlyphMinAdvanceX, src->GlyphMaxAdvanceX); + const float offsets_scale = baked->Size / baked->ContainerFont->Sources[0].SizePixels; + float advance_x = ImClamp(glyph.AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale); if (advance_x != glyph.AdvanceX) { float char_off_x = src->PixelSnapH ? ImTrunc((advance_x - glyph.AdvanceX) * 0.5f) : (advance_x - glyph.AdvanceX) * 0.5f; diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 022ec2b2c..326132b41 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -204,6 +204,9 @@ namespace if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting) LoadFlags |= FT_LOAD_NO_HINTING; + else + src->PixelSnapH = true; // FIXME: A bit weird to do this this way. + if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint) LoadFlags |= FT_LOAD_NO_AUTOHINT; if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint) @@ -559,8 +562,13 @@ bool ImGui_ImplFreeType_FontBakedAddGlyph(ImFontAtlas* atlas, ImFontBaked* baked uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data; bd_font_data->BlitGlyph(ft_bitmap, temp_buffer, w); - float font_off_x = src->GlyphOffset.x; - float font_off_y = src->GlyphOffset.y + IM_ROUND(baked->Ascent); + const float offsets_scale = baked->Size / baked->ContainerFont->Sources[0].SizePixels; + float font_off_x = (src->GlyphOffset.x * offsets_scale); + float font_off_y = (src->GlyphOffset.y * offsets_scale) + baked->Ascent; + if (src->PixelSnapH) // Snap scaled offset. This is to mitigate backward compatibility issues for GlyphOffset, but a better design would be welcome. + 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;