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();