diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontAtlas.cs b/Dalamud/Interface/ManagedFontAtlas/IFontAtlas.cs index a79ab099d..2feac8849 100644 --- a/Dalamud/Interface/ManagedFontAtlas/IFontAtlas.cs +++ b/Dalamud/Interface/ManagedFontAtlas/IFontAtlas.cs @@ -85,6 +85,10 @@ public interface IFontAtlas : IDisposable /// Creates a new from game's built-in fonts. /// Font to use. /// Handle to a font that may or may not be ready yet. + /// When called during , + /// , , and alike. Move the font handle + /// creating code outside those handlers, and only initialize them once. Call + /// on a previous font handle if you're replacing one. /// This function does not throw. will be populated instead, if /// the build procedure has failed. can be used regardless of the state of the font /// handle. @@ -93,6 +97,13 @@ public interface IFontAtlas : IDisposable /// Creates a new IFontHandle using your own callbacks. /// Callback for . /// Handle to a font that may or may not be ready yet. + /// When called during , + /// , , and alike. Move the font handle + /// creating code outside those handlers, and only initialize them once. Call + /// on a previous font handle if you're replacing one. + /// Consider calling to + /// support glyphs that are not supplied by the game by default; this mostly affects Chinese and Korean language + /// users. /// /// Consider calling to /// support glyphs that are not supplied by the game by default; this mostly affects Chinese and Korean language diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs index b3d330075..3c175ae3c 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs @@ -35,6 +35,9 @@ internal sealed partial class FontAtlasFactory /// public const string EllipsisCodepoints = "\u2026\u0085"; + /// Marker for tasks on whether it's being called inside a font build cycle. + public static readonly AsyncLocal IsBuildInProgressForTask = new(); + /// /// If set, disables concurrent font build operation. /// @@ -427,11 +430,28 @@ internal sealed partial class FontAtlasFactory } /// - public IFontHandle NewGameFontHandle(GameFontStyle style) => this.gameFontHandleManager.NewFontHandle(style); + public IFontHandle NewGameFontHandle(GameFontStyle style) + { + if (IsBuildInProgressForTask.Value) + { + throw new InvalidOperationException( + $"{nameof(this.NewGameFontHandle)} may not be called during {nameof(this.BuildStepChange)}, the callback of {nameof(this.NewDelegateFontHandle)}, {nameof(UiBuilder.BuildFonts)} or {nameof(UiBuilder.AfterBuildFonts)}."); + } + + return this.gameFontHandleManager.NewFontHandle(style); + } /// - public IFontHandle NewDelegateFontHandle(FontAtlasBuildStepDelegate buildStepDelegate) => - this.delegateFontHandleManager.NewFontHandle(buildStepDelegate); + public IFontHandle NewDelegateFontHandle(FontAtlasBuildStepDelegate buildStepDelegate) + { + if (IsBuildInProgressForTask.Value) + { + throw new InvalidOperationException( + $"{nameof(this.NewDelegateFontHandle)} may not be called during {nameof(this.BuildStepChange)} or the callback of {nameof(this.NewDelegateFontHandle)}, {nameof(UiBuilder.BuildFonts)} or {nameof(UiBuilder.AfterBuildFonts)}."); + } + + return this.delegateFontHandleManager.NewFontHandle(buildStepDelegate); + } /// public void BuildFontsOnNextFrame() @@ -630,6 +650,8 @@ internal sealed partial class FontAtlasFactory FontAtlasBuiltData? res = null; nint atlasPtr = 0; BuildToolkit? toolkit = null; + + IsBuildInProgressForTask.Value = true; try { res = new(this, scale); @@ -754,6 +776,7 @@ internal sealed partial class FontAtlasFactory // ReSharper disable once ConstantConditionalAccessQualifier toolkit?.Dispose(); this.buildQueued = false; + IsBuildInProgressForTask.Value = false; } unsafe bool ValidateMergeFontReferences(ImFontPtr replacementDstFont) diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index 03132a530..2c2ca9725 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -516,9 +516,16 @@ public sealed class UiBuilder : IDisposable /// Handle to the game font which may or may not be available for use yet. [Obsolete($"Use {nameof(this.FontAtlas)}.{nameof(IFontAtlas.NewGameFontHandle)} instead.", false)] [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] - public GameFontHandle GetGameFontHandle(GameFontStyle style) => new( - (GamePrebakedFontHandle)this.FontAtlas.NewGameFontHandle(style), - Service.Get()); + public GameFontHandle GetGameFontHandle(GameFontStyle style) + { + var prevValue = FontAtlasFactory.IsBuildInProgressForTask.Value; + FontAtlasFactory.IsBuildInProgressForTask.Value = false; + var v = new GameFontHandle( + (GamePrebakedFontHandle)this.FontAtlas.NewGameFontHandle(style), + Service.Get()); + FontAtlasFactory.IsBuildInProgressForTask.Value = prevValue; + return v; + } /// /// Call this to queue a rebuild of the font atlas.
diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs index f02effe1d..639b0315d 100644 --- a/Dalamud/Interface/Utility/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -493,12 +493,13 @@ public static class ImGuiHelpers /// The range array that can be used for . public static ushort[] CreateImGuiRangesFrom(IEnumerable ranges) => ranges - .Where(x => x.FirstCodePoint <= ushort.MaxValue) + .Select(x => (First: Math.Max(x.FirstCodePoint, 1), Last: x.FirstCodePoint + x.Length)) + .Where(x => x.First <= ushort.MaxValue && x.First <= x.Last) .SelectMany( x => new[] { - (ushort)Math.Min(x.FirstCodePoint, ushort.MaxValue), - (ushort)Math.Min(x.FirstCodePoint + x.Length, ushort.MaxValue), + (ushort)Math.Min(x.First, ushort.MaxValue), + (ushort)Math.Min(x.Last, ushort.MaxValue), }) .Append((ushort)0) .ToArray();