From 1e5c906ce843b70dc1c34f86338b5d77776bbf15 Mon Sep 17 00:00:00 2001 From: kizer Date: Mon, 18 Apr 2022 08:26:20 +0900 Subject: [PATCH] Set explicit fallback font size, and add fallback font notice window (#800) --- .../Internal/DalamudConfiguration.cs | 5 + .../Interface/Internal/DalamudInterface.cs | 10 +- .../Interface/Internal/InterfaceManager.cs | 196 +++++++++++------- .../Windows/FallbackFontNoticeWindow.cs | 95 +++++++++ .../Internal/Windows/SettingsWindow.cs | 16 +- 5 files changed, 242 insertions(+), 80 deletions(-) create mode 100644 Dalamud/Interface/Internal/Windows/FallbackFontNoticeWindow.cs 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."));