diff --git a/Dalamud/Interface/FontIdentifier/IFontSpec.cs b/Dalamud/Interface/FontIdentifier/IFontSpec.cs
index e4d931605..4d0719d4e 100644
--- a/Dalamud/Interface/FontIdentifier/IFontSpec.cs
+++ b/Dalamud/Interface/FontIdentifier/IFontSpec.cs
@@ -31,6 +31,8 @@ public interface IFontSpec
/// The atlas to bind this font handle to.
/// Optional callback to be called after creating the font handle.
/// The new font handle.
+ /// will be set when is invoked.
+ ///
IFontHandle CreateFontHandle(IFontAtlas atlas, FontAtlasBuildStepDelegate? callback = null);
///
diff --git a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs
index 0604b22ea..946215b85 100644
--- a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs
+++ b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs
@@ -109,7 +109,9 @@ public record SingleFontSpec : IFontSpec
tk.RegisterPostBuild(
() =>
{
- var roundUnit = tk.IsGlobalScaleIgnored(font) ? 1 : 1 / tk.Scale;
+ // Multiplication by scale will be done with global scale, outside of this handling.
+ var scale = tk.GetFontScaleMode(font) == FontScaleMode.UndoGlobalScale ? 1 / tk.Scale : 1;
+ var roundUnit = tk.GetFontScaleMode(font) == FontScaleMode.SkipHandling ? 1 : 1 / tk.Scale;
var newAscent = MathF.Round((font.Ascent * this.LineHeight) / roundUnit) * roundUnit;
var newFontSize = MathF.Round((font.FontSize * this.LineHeight) / roundUnit) * roundUnit;
var shiftDown = MathF.Round((newFontSize - font.FontSize) / 2f / roundUnit) * roundUnit;
@@ -129,13 +131,10 @@ public record SingleFontSpec : IFontSpec
}
}
- // `/ roundUnit` = `* scale`
- var dax = MathF.Round(this.LetterSpacing / roundUnit / roundUnit) * roundUnit;
- var dxy0 = this.GlyphOffset / roundUnit;
-
+ var dax = MathF.Round((this.LetterSpacing * scale) / roundUnit) * roundUnit;
+ var dxy0 = this.GlyphOffset * scale;
dxy0 /= roundUnit;
- dxy0.X = MathF.Round(dxy0.X);
- dxy0.Y = MathF.Round(dxy0.Y);
+ dxy0 = new(MathF.Round(dxy0.X), MathF.Round(dxy0.Y));
dxy0 *= roundUnit;
dxy0.Y += shiftDown;
diff --git a/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs b/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs
index 410bf7d18..ca75e5ce0 100644
--- a/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs
+++ b/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs
@@ -342,9 +342,7 @@ public sealed class SingleFontChooserDialog : IDisposable
{
this.fontHandle ??= this.selectedFont.CreateFontHandle(
this.atlas,
- tk =>
- tk.OnPreBuild(e => e.IgnoreGlobalScale(e.Font))
- .OnPostBuild(e => e.Font.AdjustGlyphMetrics(1f / e.Scale)));
+ tk => tk.OnPreBuild(e => e.SetFontScaleMode(e.Font, FontScaleMode.UndoGlobalScale)));
}
else
{
@@ -837,7 +835,7 @@ public sealed class SingleFontChooserDialog : IDisposable
var changed = false;
if (!ImGui.BeginTable("##advancedOptions", 4))
- return changed;
+ return false;
var labelWidth = ImGui.CalcTextSize("Letter Spacing:").X;
labelWidth = Math.Max(labelWidth, ImGui.CalcTextSize("Offset:").X);
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs
index 84682e7c2..8bb999557 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs
@@ -25,12 +25,20 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
///
internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
{
+ private static readonly string[] FontScaleModes =
+ {
+ nameof(FontScaleMode.Default),
+ nameof(FontScaleMode.SkipHandling),
+ nameof(FontScaleMode.UndoGlobalScale),
+ };
+
private ImVectorWrapper testStringBuffer;
private IFontAtlas? privateAtlas;
private SingleFontSpec fontSpec = new() { FontId = DalamudDefaultFontAndFamilyId.Instance };
private IFontHandle? fontDialogHandle;
private IReadOnlyDictionary Handle)[]>? fontHandles;
- private bool useGlobalScale;
+ private bool atlasScaleMode = true;
+ private int fontScaleMode = (int)FontScaleMode.UndoGlobalScale;
private bool useWordWrap;
private bool useItalic;
private bool useBold;
@@ -52,12 +60,14 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
public unsafe void Draw()
{
ImGui.AlignTextToFramePadding();
- fixed (byte* labelPtr = "Global Scale"u8)
+ if (ImGui.Combo("Global Scale per Font", ref this.fontScaleMode, FontScaleModes, FontScaleModes.Length))
+ this.ClearAtlas();
+ fixed (byte* labelPtr = "Global Scale for Atlas"u8)
{
- var v = (byte)(this.useGlobalScale ? 1 : 0);
+ var v = (byte)(this.atlasScaleMode ? 1 : 0);
if (ImGuiNative.igCheckbox(labelPtr, &v) != 0)
{
- this.useGlobalScale = v != 0;
+ this.atlasScaleMode = v != 0;
this.ClearAtlas();
}
}
@@ -124,7 +134,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
$"{nameof(GamePrebakedFontsTestWidget)}:EditorFont",
FontAtlasAutoRebuildMode.Async));
fcd.SelectedFont = this.fontSpec;
- fcd.IgnorePreviewGlobalScale = !this.useGlobalScale;
+ fcd.IgnorePreviewGlobalScale = !this.atlasScaleMode;
Service.Get().Draw += fcd.Draw;
fcd.ResultTask.ContinueWith(
r => Service.Get().RunOnFrameworkThread(
@@ -148,12 +158,14 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
Service.Get().CreateFontAtlas(
nameof(GamePrebakedFontsTestWidget),
FontAtlasAutoRebuildMode.Async,
- this.useGlobalScale);
- this.fontDialogHandle ??= this.fontSpec.CreateFontHandle(this.privateAtlas);
+ this.atlasScaleMode);
+ this.fontDialogHandle ??= this.fontSpec.CreateFontHandle(
+ this.privateAtlas,
+ e => e.OnPreBuild(tk => tk.SetFontScaleMode(tk.Font, (FontScaleMode)this.fontScaleMode)));
fixed (byte* labelPtr = "Test Input"u8)
{
- if (!this.useGlobalScale)
+ if (!this.atlasScaleMode)
ImGuiNative.igSetWindowFontScale(1 / ImGuiHelpers.GlobalScale);
using (this.fontDialogHandle.Push())
{
@@ -180,7 +192,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
}
}
- if (!this.useGlobalScale)
+ if (!this.atlasScaleMode)
ImGuiNative.igSetWindowFontScale(1);
}
@@ -192,17 +204,29 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
.ToImmutableDictionary(
x => x.Key,
x => x.Select(
- y => (y, new Lazy(
- () => this.useMinimumBuild
- ? this.privateAtlas.NewDelegateFontHandle(
- e =>
- e.OnPreBuild(
- tk => tk.AddGameGlyphs(
- y,
- Encoding.UTF8.GetString(
- this.testStringBuffer.DataSpan).ToGlyphRange(),
- default)))
- : this.privateAtlas.NewGameFontHandle(y))))
+ y =>
+ {
+ var range = Encoding.UTF8.GetString(this.testStringBuffer.DataSpan).ToGlyphRange();
+
+ Lazy l;
+ if (this.useMinimumBuild
+ || (this.atlasScaleMode && this.fontScaleMode != (int)FontScaleMode.Default))
+ {
+ l = new(
+ () => this.privateAtlas!.NewDelegateFontHandle(
+ e =>
+ e.OnPreBuild(
+ tk => tk.SetFontScaleMode(
+ tk.AddGameGlyphs(y, range, default),
+ (FontScaleMode)this.fontScaleMode))));
+ }
+ else
+ {
+ l = new(() => this.privateAtlas!.NewGameFontHandle(y));
+ }
+
+ return (y, l);
+ })
.ToArray());
var offsetX = ImGui.CalcTextSize("99.9pt").X + (ImGui.GetStyle().FramePadding.X * 2);
@@ -230,7 +254,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
}
else
{
- if (!this.useGlobalScale)
+ if (!this.atlasScaleMode)
ImGuiNative.igSetWindowFontScale(1 / ImGuiHelpers.GlobalScale);
if (counter++ % 2 == 0)
{
@@ -251,8 +275,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
}
finally
{
- ImGuiNative.igPopTextWrapPos();
ImGuiNative.igSetWindowFontScale(1);
+ ImGuiNative.igPopTextWrapPos();
}
}
}
diff --git a/Dalamud/Interface/ManagedFontAtlas/FontScaleMode.cs b/Dalamud/Interface/ManagedFontAtlas/FontScaleMode.cs
new file mode 100644
index 000000000..b30d5c26c
--- /dev/null
+++ b/Dalamud/Interface/ManagedFontAtlas/FontScaleMode.cs
@@ -0,0 +1,33 @@
+using Dalamud.Interface.Utility;
+
+using ImGuiNET;
+
+namespace Dalamud.Interface.ManagedFontAtlas;
+
+///
+/// Specifies how should global font scale affect a font.
+///
+public enum FontScaleMode
+{
+ ///
+ /// Do the default handling. Dalamud will load the sufficienty large font that will accomodate the global scale,
+ /// and stretch the loaded glyphs so that they look pixel-perfect after applying global scale on drawing.
+ /// Note that bitmap fonts and game fonts will always look blurry if they're not in their original sizes.
+ ///
+ Default,
+
+ ///
+ /// Do nothing with the font. Dalamud will load the font with the size that is exactly as specified.
+ /// On drawing, the font will look blurry due to stretching.
+ /// Intended for use with custom scale handling.
+ ///
+ SkipHandling,
+
+ ///
+ /// Stretch the glyphs of the loaded font by the inverse of the global scale.
+ /// On drawing, the font will always render exactly as the requested size without blurring, as long as
+ /// and do not affect the scale any
+ /// further. Note that bitmap fonts and game fonts will always look blurry if they're not in their original sizes.
+ ///
+ UndoGlobalScale,
+}
diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs
index d824eca52..827187063 100644
--- a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs
@@ -1,4 +1,5 @@
using Dalamud.Interface.Internal;
+using Dalamud.Utility;
using ImGuiNET;
@@ -10,12 +11,13 @@ namespace Dalamud.Interface.ManagedFontAtlas;
///
public interface IFontAtlasBuildToolkitPostBuild : IFontAtlasBuildToolkit
{
- ///
- /// Gets whether global scaling is ignored for the given font.
- ///
- /// The font.
- /// True if ignored.
- bool IsGlobalScaleIgnored(ImFontPtr fontPtr);
+ ///
+ [Obsolete($"Use {nameof(this.GetFontScaleMode)}")]
+ [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
+ bool IsGlobalScaleIgnored(ImFontPtr fontPtr) => this.GetFontScaleMode(fontPtr) == FontScaleMode.UndoGlobalScale;
+
+ ///
+ FontScaleMode GetFontScaleMode(ImFontPtr fontPtr);
///
/// Stores a texture to be managed with the atlas.
diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPreBuild.cs b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPreBuild.cs
index 9ab480374..9b80d27ff 100644
--- a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPreBuild.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPreBuild.cs
@@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using Dalamud.Interface.FontIdentifier;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Utility;
+using Dalamud.Utility;
using ImGuiNET;
@@ -45,14 +46,37 @@ public interface IFontAtlasBuildToolkitPreBuild : IFontAtlasBuildToolkit
///
/// The font.
/// Same with .
- ImFontPtr IgnoreGlobalScale(ImFontPtr fontPtr);
+ [Obsolete(
+ $"Use {nameof(this.SetFontScaleMode)} with {nameof(FontScaleMode)}.{nameof(FontScaleMode.UndoGlobalScale)}")]
+ [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
+ ImFontPtr IgnoreGlobalScale(ImFontPtr fontPtr) => this.SetFontScaleMode(fontPtr, FontScaleMode.UndoGlobalScale);
///
/// Gets whether global scaling is ignored for the given font.
///
/// The font.
/// True if ignored.
- bool IsGlobalScaleIgnored(ImFontPtr fontPtr);
+ [Obsolete($"Use {nameof(this.GetFontScaleMode)}")]
+ [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
+ bool IsGlobalScaleIgnored(ImFontPtr fontPtr) => this.GetFontScaleMode(fontPtr) == FontScaleMode.UndoGlobalScale;
+
+ ///
+ /// Sets the scaling mode for the given font.
+ ///
+ /// The font, returned from and alike.
+ /// Note that property is not guaranteed to be automatically updated upon
+ /// calling font adding functions. Pass the return value from font adding functions, not
+ /// property.
+ /// The scaling mode.
+ /// .
+ ImFontPtr SetFontScaleMode(ImFontPtr fontPtr, FontScaleMode mode);
+
+ ///
+ /// Gets the scaling mode for the given font.
+ ///
+ /// The font.
+ /// The scaling mode.
+ FontScaleMode GetFontScaleMode(ImFontPtr fontPtr);
///
/// Registers a function to be run after build.
diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
index a57e6d036..55af20329 100644
--- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
@@ -83,9 +83,9 @@ internal sealed partial class FontAtlasFactory
public ImVectorWrapper Fonts => this.data.Fonts;
///
- /// Gets the list of fonts to ignore global scale.
+ /// Gets the font scale modes.
///
- public List GlobalScaleExclusions { get; } = new();
+ private Dictionary FontScaleModes { get; } = new();
///
public void Dispose() => this.disposeAfterBuild.Dispose();
@@ -151,15 +151,15 @@ internal sealed partial class FontAtlasFactory
}
///
- public ImFontPtr IgnoreGlobalScale(ImFontPtr fontPtr)
+ public ImFontPtr SetFontScaleMode(ImFontPtr fontPtr, FontScaleMode scaleMode)
{
- this.GlobalScaleExclusions.Add(fontPtr);
+ this.FontScaleModes[fontPtr] = scaleMode;
return fontPtr;
}
- ///
- public bool IsGlobalScaleIgnored(ImFontPtr fontPtr) =>
- this.GlobalScaleExclusions.Contains(fontPtr);
+ ///
+ public FontScaleMode GetFontScaleMode(ImFontPtr fontPtr) =>
+ this.FontScaleModes.GetValueOrDefault(fontPtr, FontScaleMode.Default);
///
public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) =>
@@ -496,17 +496,17 @@ internal sealed partial class FontAtlasFactory
var configData = this.data.ConfigData;
foreach (ref var config in configData.DataSpan)
{
- if (this.GlobalScaleExclusions.Contains(new(config.DstFont)))
+ if (this.GetFontScaleMode(config.DstFont) != FontScaleMode.Default)
continue;
config.SizePixels *= this.Scale;
config.GlyphMaxAdvanceX *= this.Scale;
- if (float.IsInfinity(config.GlyphMaxAdvanceX))
+ if (float.IsInfinity(config.GlyphMaxAdvanceX) || float.IsNaN(config.GlyphMaxAdvanceX))
config.GlyphMaxAdvanceX = config.GlyphMaxAdvanceX > 0 ? float.MaxValue : -float.MaxValue;
config.GlyphMinAdvanceX *= this.Scale;
- if (float.IsInfinity(config.GlyphMinAdvanceX))
+ if (float.IsInfinity(config.GlyphMinAdvanceX) || float.IsNaN(config.GlyphMinAdvanceX))
config.GlyphMinAdvanceX = config.GlyphMinAdvanceX > 0 ? float.MaxValue : -float.MaxValue;
config.GlyphOffset *= this.Scale;
@@ -536,7 +536,7 @@ internal sealed partial class FontAtlasFactory
var scale = this.Scale;
foreach (ref var font in this.Fonts.DataSpan)
{
- if (!this.GlobalScaleExclusions.Contains(font))
+ if (this.GetFontScaleMode(font) != FontScaleMode.SkipHandling)
font.AdjustGlyphMetrics(1 / scale, 1 / scale);
foreach (var c in FallbackCodepoints)
diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs
index b6c9817aa..1101e7119 100644
--- a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs
@@ -345,17 +345,36 @@ internal class GamePrebakedFontHandle : FontHandle
{
foreach (var (font, style, ranges) in this.attachments)
{
- var effectiveStyle =
- toolkitPreBuild.IsGlobalScaleIgnored(font)
- ? style.Scale(1 / toolkitPreBuild.Scale)
- : style;
if (!this.fonts.TryGetValue(style, out var plan))
{
- plan = new(
- effectiveStyle,
- toolkitPreBuild.Scale,
- this.handleManager.GameFontTextureProvider,
- this.CreateTemplateFont(toolkitPreBuild, style.SizePx));
+ switch (toolkitPreBuild.GetFontScaleMode(font))
+ {
+ case FontScaleMode.Default:
+ default:
+ plan = new(
+ style,
+ toolkitPreBuild.Scale,
+ this.handleManager.GameFontTextureProvider,
+ this.CreateTemplateFont(toolkitPreBuild, style.SizePx));
+ break;
+
+ case FontScaleMode.SkipHandling:
+ plan = new(
+ style,
+ 1f,
+ this.handleManager.GameFontTextureProvider,
+ this.CreateTemplateFont(toolkitPreBuild, style.SizePx));
+ break;
+
+ case FontScaleMode.UndoGlobalScale:
+ plan = new(
+ style.Scale(1 / toolkitPreBuild.Scale),
+ toolkitPreBuild.Scale,
+ this.handleManager.GameFontTextureProvider,
+ this.CreateTemplateFont(toolkitPreBuild, style.SizePx));
+ break;
+ }
+
this.fonts[style] = plan;
}
@@ -620,15 +639,14 @@ internal class GamePrebakedFontHandle : FontHandle
public unsafe void CopyGlyphsToRanges(IFontAtlasBuildToolkitPostBuild toolkitPostBuild)
{
var scale = this.Style.SizePt / this.Fdt.FontHeader.Size;
- var atlasScale = toolkitPostBuild.Scale;
- var round = 1 / atlasScale;
foreach (var (font, rangeBits) in this.Ranges)
{
if (font.NativePtr == this.FullRangeFont.NativePtr)
continue;
- var noGlobalScale = toolkitPostBuild.IsGlobalScaleIgnored(font);
+ var fontScaleMode = toolkitPostBuild.GetFontScaleMode(font);
+ var round = fontScaleMode == FontScaleMode.SkipHandling ? 1 : 1 / toolkitPostBuild.Scale;
var lookup = font.IndexLookupWrapped();
var glyphs = font.GlyphsWrapped();
@@ -649,7 +667,7 @@ internal class GamePrebakedFontHandle : FontHandle
ref var g = ref glyphs[glyphIndex];
g = sourceGlyph;
- if (noGlobalScale)
+ if (fontScaleMode == FontScaleMode.SkipHandling)
{
g.XY *= scale;
g.AdvanceX *= scale;
@@ -673,7 +691,7 @@ internal class GamePrebakedFontHandle : FontHandle
continue;
if (!rangeBits[leftInt] || !rangeBits[rightInt])
continue;
- if (noGlobalScale)
+ if (fontScaleMode == FontScaleMode.SkipHandling)
{
font.AddKerningPair((ushort)leftInt, (ushort)rightInt, k.RightOffset * scale);
}