diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index 222dccab6..530a472d6 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -149,6 +149,11 @@ namespace Dalamud.Configuration.Internal
///
public int FontResolutionLevel { get; set; } = 2;
+ ///
+ /// Gets or sets a value indicating whether to disable font fallback notice.
+ ///
+ public bool DisableFontFallbackNotice { get; set; } = false;
+
///
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
///
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index 9db0a3b5e..7ef743e1c 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -53,6 +53,7 @@ namespace Dalamud.Interface.Internal
private readonly SelfTestWindow selfTestWindow;
private readonly StyleEditorWindow styleEditorWindow;
private readonly TitleScreenMenuWindow titleScreenMenuWindow;
+ private readonly FallbackFontNoticeWindow fallbackFontNoticeWindow;
private readonly TextureWrap logoTexture;
private readonly TextureWrap tsmLogoTexture;
@@ -74,6 +75,7 @@ namespace Dalamud.Interface.Internal
public DalamudInterface()
{
var configuration = Service.Get();
+ var interfaceManager = Service.Get();
this.WindowSystem = new WindowSystem("DalamudCore");
@@ -91,6 +93,7 @@ namespace Dalamud.Interface.Internal
this.selfTestWindow = new SelfTestWindow() { IsOpen = false };
this.styleEditorWindow = new StyleEditorWindow() { IsOpen = false };
this.titleScreenMenuWindow = new TitleScreenMenuWindow() { IsOpen = false };
+ this.fallbackFontNoticeWindow = new FallbackFontNoticeWindow() { IsOpen = interfaceManager.IsFallbackFontMode && !configuration.DisableFontFallbackNotice };
this.WindowSystem.AddWindow(this.changelogWindow);
this.WindowSystem.AddWindow(this.colorDemoWindow);
@@ -106,10 +109,10 @@ namespace Dalamud.Interface.Internal
this.WindowSystem.AddWindow(this.selfTestWindow);
this.WindowSystem.AddWindow(this.styleEditorWindow);
this.WindowSystem.AddWindow(this.titleScreenMenuWindow);
+ this.WindowSystem.AddWindow(this.fallbackFontNoticeWindow);
ImGuiManagedAsserts.AssertsEnabled = configuration.AssertsEnabledAtStartup;
- var interfaceManager = Service.Get();
interfaceManager.Draw += this.OnDraw;
var dalamud = Service.Get();
@@ -207,6 +210,11 @@ namespace Dalamud.Interface.Internal
///
public void OpenDevMenu() => this.isImGuiDrawDevMenu = true;
+ ///
+ /// Opens the fallback font notice window.
+ ///
+ public void OpenFallbackFontNoticeWindow() => this.fallbackFontNoticeWindow.IsOpen = true;
+
///
/// Opens the .
///
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index cf87734a3..617ea14d8 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -47,6 +47,8 @@ namespace Dalamud.Interface.Internal
///
internal class InterfaceManager : IDisposable
{
+ private const float MinimumFallbackFontSizePt = 9.6f; // Game's minimum AXIS font size
+ private const float MinimumFallbackFontSizePx = MinimumFallbackFontSizePt * 4.0f / 3.0f;
private const float DefaultFontSizePt = 12.0f;
private const float DefaultFontSizePx = DefaultFontSizePt * 4.0f / 3.0f;
private const ushort Fallback1Codepoint = 0x3013; // Geta mark; FFXIV uses this to indicate that a glyph is missing.
@@ -69,6 +71,8 @@ namespace Dalamud.Interface.Internal
private bool lastWantCapture = false;
private bool isRebuildingFonts = false;
+ private bool isFallbackFontMode = false;
+
///
/// Initializes a new instance of the class.
///
@@ -146,6 +150,11 @@ namespace Dalamud.Interface.Internal
///
public event Action AfterBuildFonts;
+ ///
+ /// Gets or sets an action that is executed right after font fallback mode has been changed.
+ ///
+ public event Action OnFallbackFontModeChange;
+
///
/// Gets the default ImGui font.
///
@@ -195,6 +204,22 @@ namespace Dalamud.Interface.Internal
///
public bool IsReady => this.scene != null;
+ ///
+ /// Gets or sets a value indicating whether the font has been loaded in fallback mode.
+ ///
+ public bool IsFallbackFontMode
+ {
+ get => this.isFallbackFontMode;
+ internal set
+ {
+ if (value == this.isFallbackFontMode)
+ return;
+
+ this.isFallbackFontMode = value;
+ this.OnFallbackFontModeChange?.Invoke(value);
+ }
+ }
+
///
/// Gets or sets a value indicating whether to override configuration for UseAxis.
///
@@ -594,70 +619,11 @@ namespace Dalamud.Interface.Internal
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
}
- private unsafe class TargetFontModification : IDisposable
- {
- ///
- /// Initializes a new instance of the class.
- /// Constructs new target font modification information, assuming that AXIS fonts will not be applied.
- ///
- /// Name of the font to write to ImGui font information.
- /// Target font size in pixels, which will not be considered for further scaling.
- internal TargetFontModification(string name, float sizePx)
- {
- this.Name = name;
- this.Axis = AxisMode.Suppress;
- this.TargetSizePx = sizePx;
- this.Scale = 1;
- this.SourceAxis = null;
- }
-
- ///
- /// Initializes a new instance of the class.
- /// Constructs new target font modification information.
- ///
- /// Name of the font to write to ImGui font information.
- /// Whether and how to use AXIS fonts.
- /// Target font size in pixels, which will not be considered for further scaling.
- /// Font scale to be referred for loading AXIS font of appropriate size.
- internal TargetFontModification(string name, AxisMode axis, float sizePx, float scale)
- {
- this.Name = name;
- this.Axis = axis;
- this.TargetSizePx = sizePx;
- this.Scale = scale;
- this.SourceAxis = Service.Get().NewFontRef(new(GameFontFamily.Axis, sizePx * scale));
- }
-
- internal enum AxisMode
- {
- Suppress,
- GameGlyphsOnly,
- Overwrite,
- }
-
- internal string Name { get; private init; }
-
- internal AxisMode Axis { get; private init; }
-
- internal float TargetSizePx { get; private init; }
-
- internal float Scale { get; private init; }
-
- internal GameFontHandle? SourceAxis { get; private init; }
-
- internal bool SourceAxisAvailable => this.SourceAxis != null && this.SourceAxis.ImFont.NativePtr != null;
-
- public void Dispose()
- {
- this.SourceAxis?.Dispose();
- }
- }
-
///
/// Loads font for use in ImGui text functions.
///
- /// If non-zero, then glyphs will be loaded in smaller resolution to make all glyphs fit into given constraints.
- private unsafe void SetupFonts(int scaler = 0)
+ /// If set, then glyphs will be loaded in smaller resolution to make all glyphs fit into given constraints.
+ private unsafe void SetupFonts(bool disableBigFonts = false)
{
var gameFontManager = Service.Get();
var dalamud = Service.Get();
@@ -665,8 +631,6 @@ namespace Dalamud.Interface.Internal
var ioFonts = io.Fonts;
var maxTexDimension = 1 << (10 + Math.Max(0, Math.Min(4, this.FontResolutionLevel)));
- var disableBigFonts = scaler != 0;
- var fontLoadScale = disableBigFonts ? Math.Min(io.FontGlobalScale, 1.0f / scaler) : io.FontGlobalScale;
var fontGamma = this.FontGamma;
this.fontBuildSignal.Reset();
@@ -704,7 +668,8 @@ namespace Dalamud.Interface.Internal
"Default",
this.UseAxis ? TargetFontModification.AxisMode.Overwrite : TargetFontModification.AxisMode.GameGlyphsOnly,
this.UseAxis ? DefaultFontSizePx : DefaultFontSizePx + 1,
- fontLoadScale);
+ io.FontGlobalScale,
+ disableBigFonts);
Log.Verbose("[FONT] SetupFonts - Default corresponding AXIS size: {0}pt ({1}px)", fontInfo.SourceAxis.Style.BaseSizePt, fontInfo.SourceAxis.Style.BaseSizePx);
if (this.UseAxis)
{
@@ -721,7 +686,7 @@ namespace Dalamud.Interface.Internal
fontConfig.GlyphRanges = japaneseRangeHandle.AddrOfPinnedObject();
fontConfig.PixelSnapH = true;
- DefaultFont = ioFonts.AddFontFromFileTTF(fontPathJp, fontInfo.TargetSizePx * fontLoadScale, fontConfig);
+ DefaultFont = ioFonts.AddFontFromFileTTF(fontPathJp, disableBigFonts ? Math.Min(MinimumFallbackFontSizePx, fontInfo.TargetSizePx) : fontInfo.TargetSizePx * io.FontGlobalScale, fontConfig);
this.loadedFontInfo[DefaultFont] = fontInfo;
}
@@ -737,8 +702,8 @@ namespace Dalamud.Interface.Internal
fontConfig.GlyphRanges = iconRangeHandle.AddrOfPinnedObject();
fontConfig.PixelSnapH = true;
- IconFont = ioFonts.AddFontFromFileTTF(fontPathIcon, DefaultFontSizePx * fontLoadScale, fontConfig);
- this.loadedFontInfo[IconFont] = new("Icon", TargetFontModification.AxisMode.GameGlyphsOnly, DefaultFontSizePx, fontLoadScale);
+ IconFont = ioFonts.AddFontFromFileTTF(fontPathIcon, disableBigFonts ? Math.Min(MinimumFallbackFontSizePx, DefaultFontSizePx) : DefaultFontSizePx * io.FontGlobalScale, fontConfig);
+ this.loadedFontInfo[IconFont] = new("Icon", TargetFontModification.AxisMode.GameGlyphsOnly, DefaultFontSizePx, io.FontGlobalScale, disableBigFonts);
}
// Monospace font
@@ -750,8 +715,8 @@ namespace Dalamud.Interface.Internal
fontConfig.GlyphRanges = IntPtr.Zero;
fontConfig.PixelSnapH = true;
- MonoFont = ioFonts.AddFontFromFileTTF(fontPathMono, DefaultFontSizePx * fontLoadScale, fontConfig);
- this.loadedFontInfo[MonoFont] = new("Mono", TargetFontModification.AxisMode.GameGlyphsOnly, DefaultFontSizePx, fontLoadScale);
+ MonoFont = ioFonts.AddFontFromFileTTF(fontPathMono, disableBigFonts ? Math.Min(MinimumFallbackFontSizePx, DefaultFontSizePx) : DefaultFontSizePx * io.FontGlobalScale, fontConfig);
+ this.loadedFontInfo[MonoFont] = new("Mono", TargetFontModification.AxisMode.GameGlyphsOnly, DefaultFontSizePx, io.FontGlobalScale, disableBigFonts);
}
// Default font but in requested size for requested glyphs
@@ -803,7 +768,8 @@ namespace Dalamud.Interface.Internal
$"Requested({fontSize}px)",
this.UseAxis ? TargetFontModification.AxisMode.Overwrite : TargetFontModification.AxisMode.GameGlyphsOnly,
fontSize,
- fontLoadScale);
+ io.FontGlobalScale,
+ disableBigFonts);
if (this.UseAxis)
{
fontConfig.GlyphRanges = dummyRangeHandle.AddrOfPinnedObject();
@@ -821,7 +787,7 @@ namespace Dalamud.Interface.Internal
garbageList.Add(rangeHandle);
fontConfig.PixelSnapH = true;
- var sizedFont = ioFonts.AddFontFromFileTTF(fontPathJp, (disableBigFonts ? DefaultFontSizePx + 1 : fontSize) * fontLoadScale, fontConfig, rangeHandle.AddrOfPinnedObject());
+ var sizedFont = ioFonts.AddFontFromFileTTF(fontPathJp, disableBigFonts ? Math.Min(MinimumFallbackFontSizePx, fontSize) : fontSize * io.FontGlobalScale, fontConfig, rangeHandle.AddrOfPinnedObject());
this.loadedFontInfo[sizedFont] = fontInfo;
foreach (var request in requests)
request.FontInternal = sizedFont;
@@ -870,31 +836,45 @@ namespace Dalamud.Interface.Internal
{
// If a plugin has requested a font size that is bigger than current restrictions, load it scaled down.
// After loading glyphs onto font atlas, font information will be modified to make it look like the font of original size has been loaded.
- if (config.SizePixels > DefaultFontSizePx * fontLoadScale)
- config.SizePixels = DefaultFontSizePx * fontLoadScale;
+ if (config.SizePixels > MinimumFallbackFontSizePx)
+ config.SizePixels = MinimumFallbackFontSizePx;
}
else
{
- config.SizePixels = config.SizePixels * fontLoadScale;
+ config.SizePixels = config.SizePixels * io.FontGlobalScale;
}
}
+ Log.Verbose("[FONT] ImGui.IO.Build will be called.");
ioFonts.Build();
+ Log.Verbose("[FONT] ImGui.IO.Build OK!");
if (ioFonts.TexHeight > maxTexDimension)
{
- if (scaler < 4)
+ var possibilityForScaling = false;
+ foreach (var x in this.loadedFontInfo.Values)
{
- Log.Information("[FONT] Atlas size is {0}x{1} which is bigger than allowed {2}x{3}. Retrying with scale {4}.", ioFonts.TexWidth, ioFonts.TexHeight, maxTexDimension, maxTexDimension, scaler + 1);
- this.SetupFonts(scaler + 1);
+ if (x.TargetSizePx * x.Scale > MinimumFallbackFontSizePx)
+ {
+ possibilityForScaling = true;
+ break;
+ }
+ }
+
+ if (possibilityForScaling && !disableBigFonts)
+ {
+ Log.Information("[FONT] Atlas size is {0}x{1} which is bigger than allowed {2}x{3}. Retrying with minimized font sizes.", ioFonts.TexWidth, ioFonts.TexHeight, maxTexDimension, maxTexDimension);
+ this.SetupFonts(true);
return;
}
else
{
- Log.Warning("[FONT] Atlas size is {0}x{1} which is bigger than allowed {2}x{3}, but giving up trying to scale smaller.", ioFonts.TexWidth, ioFonts.TexHeight, maxTexDimension, maxTexDimension);
+ Log.Warning("[FONT] Atlas size is {0}x{1} which is bigger than allowed {2}x{3} even when font sizes are minimized up to {4}px. This may result in crash.", ioFonts.TexWidth, ioFonts.TexHeight, maxTexDimension, maxTexDimension, MinimumFallbackFontSizePx);
}
}
+ this.IsFallbackFontMode = disableBigFonts;
+
if (Math.Abs(fontGamma - 1.0f) >= 0.001)
{
// Gamma correction (stbtt/FreeType would output in linear space whereas most real world usages will apply 1.4 or 1.8 gamma; Windows/XIV prebaked uses 1.4)
@@ -1161,5 +1141,65 @@ namespace Dalamud.Interface.Internal
this.Manager.glyphRequests.Remove(this);
}
}
+
+ private unsafe class TargetFontModification : IDisposable
+ {
+ ///
+ /// Initializes a new instance of the class.
+ /// Constructs new target font modification information, assuming that AXIS fonts will not be applied.
+ ///
+ /// Name of the font to write to ImGui font information.
+ /// Target font size in pixels, which will not be considered for further scaling.
+ internal TargetFontModification(string name, float sizePx)
+ {
+ this.Name = name;
+ this.Axis = AxisMode.Suppress;
+ this.TargetSizePx = sizePx;
+ this.Scale = 1;
+ this.SourceAxis = null;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Constructs new target font modification information.
+ ///
+ /// Name of the font to write to ImGui font information.
+ /// Whether and how to use AXIS fonts.
+ /// Target font size in pixels, which will not be considered for further scaling.
+ /// Font scale to be referred for loading AXIS font of appropriate size.
+ /// Whether to enable loading big AXIS fonts.
+ internal TargetFontModification(string name, AxisMode axis, float sizePx, float globalFontScale, bool disableBigFonts)
+ {
+ this.Name = name;
+ this.Axis = axis;
+ this.TargetSizePx = sizePx;
+ this.Scale = disableBigFonts ? MinimumFallbackFontSizePx / sizePx : globalFontScale;
+ this.SourceAxis = Service.Get().NewFontRef(new(GameFontFamily.Axis, this.TargetSizePx * this.Scale));
+ }
+
+ internal enum AxisMode
+ {
+ Suppress,
+ GameGlyphsOnly,
+ Overwrite,
+ }
+
+ internal string Name { get; private init; }
+
+ internal AxisMode Axis { get; private init; }
+
+ internal float TargetSizePx { get; private init; }
+
+ internal float Scale { get; private init; }
+
+ internal GameFontHandle? SourceAxis { get; private init; }
+
+ internal bool SourceAxisAvailable => this.SourceAxis != null && this.SourceAxis.ImFont.NativePtr != null;
+
+ public void Dispose()
+ {
+ this.SourceAxis?.Dispose();
+ }
+ }
}
}
diff --git a/Dalamud/Interface/Internal/Windows/FallbackFontNoticeWindow.cs b/Dalamud/Interface/Internal/Windows/FallbackFontNoticeWindow.cs
new file mode 100644
index 000000000..c0a035d52
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/FallbackFontNoticeWindow.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Numerics;
+
+using CheapLoc;
+using Dalamud.Configuration.Internal;
+using Dalamud.Interface.Windowing;
+using ImGuiNET;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows
+{
+ ///
+ /// For major updates, an in-game Changelog window.
+ ///
+ internal sealed class FallbackFontNoticeWindow : Window, IDisposable
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FallbackFontNoticeWindow()
+ : base(Title, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus)
+ {
+ this.Namespace = "FallbackFontNoticeWindow";
+ this.RespectCloseHotkey = false;
+
+ this.Size = new Vector2(885, 463);
+ this.SizeCondition = ImGuiCond.Appearing;
+
+ var interfaceManager = Service.Get();
+ var dalamud = Service.Get();
+
+ Service.Get().OnFallbackFontModeChange += this.OnFallbackFontModeChange;
+ }
+
+ private static string Title => Loc.Localize("FallbackFontNoticeWindowTitle", "Fallback Font Mode Active");
+
+ ///
+ public override void Draw()
+ {
+ ImGui.Text(Title);
+ ImGuiHelpers.ScaledDummy(10);
+
+ ImGui.Text(Loc.Localize("FallbackFontNoticeWindowBody", "The text used by Dalamud and plugins has been made blurry in order to prevent possible crash."));
+ ImGuiHelpers.ScaledDummy(10);
+
+ ImGui.Text(Loc.Localize("FallbackFontNoticeWindowSolution1", "* You may attempt to increase the limits on text quality. This may result in a crash."));
+ ImGuiHelpers.ScaledDummy(10);
+ ImGui.SameLine();
+ if (ImGui.Button(Loc.Localize("FallbackFontNoticeWindowOpenDalamudSettings", "Open Dalamud Settings")))
+ Service.Get().OpenSettings();
+ ImGuiHelpers.ScaledDummy(10);
+ ImGui.SameLine();
+ ImGui.Text(string.Format(
+ Loc.Localize(
+ "FallbackFontNoticeWindowSolution1Instructions",
+ "In \"{0}\" tab, choose a better option for \"{1}\"."),
+ Loc.Localize("DalamudSettingsVisual", "Look & Feel"),
+ Loc.Localize("DalamudSettingsFontResolutionLevel", "Font resolution level")));
+
+ ImGuiHelpers.ScaledDummy(10);
+
+ ImGui.Text(Loc.Localize("FallbackFontNoticeWindowSolution2", "* You may disable custom fonts, or make fonts smaller, from individual plugin settings."));
+ ImGuiHelpers.ScaledDummy(10);
+ ImGui.SameLine();
+ if (ImGui.Button(Loc.Localize("FallbackFontNoticeWindowOpenDalamudPlugins", "Open Plugin Installer")))
+ Service.Get().OpenPluginInstaller();
+
+ ImGuiHelpers.ScaledDummy(10);
+
+ if (ImGui.Button(Loc.Localize("FallbackFontNoticeWindowDoNotShowAgain", "Do not show again")))
+ {
+ this.IsOpen = false;
+ Service.Get().DisableFontFallbackNotice = true;
+ Service.Get().Save();
+ }
+ }
+
+ ///
+ /// Dispose this window.
+ ///
+ public void Dispose()
+ {
+ Service.Get().OnFallbackFontModeChange -= this.OnFallbackFontModeChange;
+ }
+
+ private void OnFallbackFontModeChange(bool mode)
+ {
+ Log.Verbose("[{0}] OnFallbackFontModeChange called: {1} (disable={2})", this.Namespace, mode, Service.Get().DisableFontFallbackNotice);
+ if (!mode)
+ this.IsOpen = false;
+ else if (!Service.Get().DisableFontFallbackNotice)
+ this.IsOpen = true;
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
index 80d8d0a31..e79b8cc82 100644
--- a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
@@ -197,7 +197,9 @@ namespace Dalamud.Interface.Internal.Windows
var configuration = Service.Get();
var interfaceManager = Service.Get();
- var rebuildFont = interfaceManager.FontGamma != configuration.FontGammaLevel;
+ var rebuildFont = interfaceManager.FontGamma != configuration.FontGammaLevel
+ || interfaceManager.FontResolutionLevel != configuration.FontResolutionLevel
+ || interfaceManager.UseAxis != configuration.UseAxisFontsFromGame;
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
interfaceManager.FontGammaOverride = null;
@@ -396,6 +398,18 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.GetIO().Fonts.TexHeight));
ImGui.PopStyleColor();
+ if (Service.Get().DisableFontFallbackNotice)
+ {
+ ImGui.Text(Loc.Localize("DalamudSettingsFontResolutionLevelWarningDisabled", "Warning will not be displayed even when the limits are enforced and fonts become blurry."));
+ if (ImGui.Button(Loc.Localize("DalamudSettingsFontResolutionLevelWarningReset", "Show warnings") + "##DalamudSettingsFontResolutionLevelWarningReset"))
+ {
+ Service.Get().DisableFontFallbackNotice = false;
+ Service.Get().Save();
+ if (Service.Get().IsFallbackFontMode)
+ Service.Get().OpenFallbackFontNoticeWindow();
+ }
+ }
+
ImGuiHelpers.ScaledDummy(10);
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below."));