Set explicit fallback font size, and add fallback font notice window (#800)

This commit is contained in:
kizer 2022-04-18 08:26:20 +09:00 committed by GitHub
parent 2becb12855
commit 1e5c906ce8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 242 additions and 80 deletions

View file

@ -149,6 +149,11 @@ namespace Dalamud.Configuration.Internal
/// </summary>
public int FontResolutionLevel { get; set; } = 2;
/// <summary>
/// Gets or sets a value indicating whether to disable font fallback notice.
/// </summary>
public bool DisableFontFallbackNotice { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
/// </summary>

View file

@ -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<DalamudConfiguration>.Get();
var interfaceManager = Service<InterfaceManager>.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<InterfaceManager>.Get();
interfaceManager.Draw += this.OnDraw;
var dalamud = Service<Dalamud>.Get();
@ -207,6 +210,11 @@ namespace Dalamud.Interface.Internal
/// </summary>
public void OpenDevMenu() => this.isImGuiDrawDevMenu = true;
/// <summary>
/// Opens the fallback font notice window.
/// </summary>
public void OpenFallbackFontNoticeWindow() => this.fallbackFontNoticeWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="GamepadModeNotifierWindow"/>.
/// </summary>

View file

@ -47,6 +47,8 @@ namespace Dalamud.Interface.Internal
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="InterfaceManager"/> class.
/// </summary>
@ -146,6 +150,11 @@ namespace Dalamud.Interface.Internal
/// </summary>
public event Action AfterBuildFonts;
/// <summary>
/// Gets or sets an action that is executed right after font fallback mode has been changed.
/// </summary>
public event Action<bool> OnFallbackFontModeChange;
/// <summary>
/// Gets the default ImGui font.
/// </summary>
@ -195,6 +204,22 @@ namespace Dalamud.Interface.Internal
/// </summary>
public bool IsReady => this.scene != null;
/// <summary>
/// Gets or sets a value indicating whether the font has been loaded in fallback mode.
/// </summary>
public bool IsFallbackFontMode
{
get => this.isFallbackFontMode;
internal set
{
if (value == this.isFallbackFontMode)
return;
this.isFallbackFontMode = value;
this.OnFallbackFontModeChange?.Invoke(value);
}
}
/// <summary>
/// Gets or sets a value indicating whether to override configuration for UseAxis.
/// </summary>
@ -594,70 +619,11 @@ namespace Dalamud.Interface.Internal
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
}
private unsafe class TargetFontModification : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="TargetFontModification"/> class.
/// Constructs new target font modification information, assuming that AXIS fonts will not be applied.
/// </summary>
/// <param name="name">Name of the font to write to ImGui font information.</param>
/// <param name="sizePx">Target font size in pixels, which will not be considered for further scaling.</param>
internal TargetFontModification(string name, float sizePx)
{
this.Name = name;
this.Axis = AxisMode.Suppress;
this.TargetSizePx = sizePx;
this.Scale = 1;
this.SourceAxis = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="TargetFontModification"/> class.
/// Constructs new target font modification information.
/// </summary>
/// <param name="name">Name of the font to write to ImGui font information.</param>
/// <param name="axis">Whether and how to use AXIS fonts.</param>
/// <param name="sizePx">Target font size in pixels, which will not be considered for further scaling.</param>
/// <param name="scale">Font scale to be referred for loading AXIS font of appropriate size.</param>
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<GameFontManager>.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();
}
}
/// <summary>
/// Loads font for use in ImGui text functions.
/// </summary>
/// <param name="scaler">If non-zero, then glyphs will be loaded in smaller resolution to make all glyphs fit into given constraints.</param>
private unsafe void SetupFonts(int scaler = 0)
/// <param name="disableBigFonts">If set, then glyphs will be loaded in smaller resolution to make all glyphs fit into given constraints.</param>
private unsafe void SetupFonts(bool disableBigFonts = false)
{
var gameFontManager = Service<GameFontManager>.Get();
var dalamud = Service<Dalamud>.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
{
/// <summary>
/// Initializes a new instance of the <see cref="TargetFontModification"/> class.
/// Constructs new target font modification information, assuming that AXIS fonts will not be applied.
/// </summary>
/// <param name="name">Name of the font to write to ImGui font information.</param>
/// <param name="sizePx">Target font size in pixels, which will not be considered for further scaling.</param>
internal TargetFontModification(string name, float sizePx)
{
this.Name = name;
this.Axis = AxisMode.Suppress;
this.TargetSizePx = sizePx;
this.Scale = 1;
this.SourceAxis = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="TargetFontModification"/> class.
/// Constructs new target font modification information.
/// </summary>
/// <param name="name">Name of the font to write to ImGui font information.</param>
/// <param name="axis">Whether and how to use AXIS fonts.</param>
/// <param name="sizePx">Target font size in pixels, which will not be considered for further scaling.</param>
/// <param name="globalFontScale">Font scale to be referred for loading AXIS font of appropriate size.</param>
/// <param name="disableBigFonts">Whether to enable loading big AXIS fonts.</param>
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<GameFontManager>.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();
}
}
}
}

View file

@ -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
{
/// <summary>
/// For major updates, an in-game Changelog window.
/// </summary>
internal sealed class FallbackFontNoticeWindow : Window, IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="FallbackFontNoticeWindow"/> class.
/// </summary>
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<InterfaceManager>.Get();
var dalamud = Service<Dalamud>.Get();
Service<InterfaceManager>.Get().OnFallbackFontModeChange += this.OnFallbackFontModeChange;
}
private static string Title => Loc.Localize("FallbackFontNoticeWindowTitle", "Fallback Font Mode Active");
/// <inheritdoc/>
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<DalamudInterface>.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<DalamudInterface>.Get().OpenPluginInstaller();
ImGuiHelpers.ScaledDummy(10);
if (ImGui.Button(Loc.Localize("FallbackFontNoticeWindowDoNotShowAgain", "Do not show again")))
{
this.IsOpen = false;
Service<DalamudConfiguration>.Get().DisableFontFallbackNotice = true;
Service<DalamudConfiguration>.Get().Save();
}
}
/// <summary>
/// Dispose this window.
/// </summary>
public void Dispose()
{
Service<InterfaceManager>.Get().OnFallbackFontModeChange -= this.OnFallbackFontModeChange;
}
private void OnFallbackFontModeChange(bool mode)
{
Log.Verbose("[{0}] OnFallbackFontModeChange called: {1} (disable={2})", this.Namespace, mode, Service<DalamudConfiguration>.Get().DisableFontFallbackNotice);
if (!mode)
this.IsOpen = false;
else if (!Service<DalamudConfiguration>.Get().DisableFontFallbackNotice)
this.IsOpen = true;
}
}
}

View file

@ -197,7 +197,9 @@ namespace Dalamud.Interface.Internal.Windows
var configuration = Service<DalamudConfiguration>.Get();
var interfaceManager = Service<InterfaceManager>.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<DalamudConfiguration>.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<DalamudConfiguration>.Get().DisableFontFallbackNotice = false;
Service<DalamudConfiguration>.Get().Save();
if (Service<InterfaceManager>.Get().IsFallbackFontMode)
Service<DalamudInterface>.Get().OpenFallbackFontNoticeWindow();
}
}
ImGuiHelpers.ScaledDummy(10);
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below."));