From d68b3a1845575a0608197922c5fcaf782c9b26f6 Mon Sep 17 00:00:00 2001 From: kizer Date: Thu, 12 May 2022 17:46:50 +0900 Subject: [PATCH] Expose CopyGlyphsAcrossFonts (#824) --- .../Interface/GameFonts/GameFontManager.cs | 199 ++++++------------ .../Interface/Internal/InterfaceManager.cs | 13 +- .../Internal/Windows/SettingsWindow.cs | 3 +- Dalamud/Utility/Util.cs | 94 +++++++++ 4 files changed, 166 insertions(+), 143 deletions(-) diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs index ae45b7226..fec324a73 100644 --- a/Dalamud/Interface/GameFonts/GameFontManager.cs +++ b/Dalamud/Interface/GameFonts/GameFontManager.cs @@ -7,6 +7,7 @@ using System.Text; using Dalamud.Data; using Dalamud.Interface.Internal; +using Dalamud.Utility; using ImGuiNET; using Lumina.Data.Files; using Serilog; @@ -38,6 +39,9 @@ namespace Dalamud.Interface.GameFonts private readonly Dictionary fontUseCounter = new(); private readonly Dictionary>> glyphRectIds = new(); + private bool isBetweenBuildFontsAndAfterBuildFonts = false; + private bool isBuildingAsFallbackFontMode = false; + /// /// Initializes a new instance of the class. /// @@ -110,65 +114,6 @@ namespace Dalamud.Interface.GameFonts }; } - /// - /// Fills missing glyphs in target font from source font, if both are not null. - /// - /// Source font. - /// Target font. - /// Whether to copy missing glyphs only. - /// Whether to call target.BuildLookupTable(). - /// Low codepoint range to copy. - /// High codepoing range to copy. - public static void CopyGlyphsAcrossFonts(ImFontPtr? source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable, int rangeLow = 32, int rangeHigh = 0xFFFE) - { - if (!source.HasValue || !target.HasValue) - return; - - var scale = target.Value!.FontSize / source.Value!.FontSize; - unsafe - { - var glyphs = (ImFontGlyphReal*)source.Value!.Glyphs.Data; - for (int j = 0, j_ = source.Value!.Glyphs.Size; j < j_; j++) - { - var glyph = &glyphs[j]; - if (glyph->Codepoint < rangeLow || glyph->Codepoint > rangeHigh) - continue; - - var prevGlyphPtr = (ImFontGlyphReal*)target.Value!.FindGlyphNoFallback((ushort)glyph->Codepoint).NativePtr; - if ((IntPtr)prevGlyphPtr == IntPtr.Zero) - { - target.Value!.AddGlyph( - target.Value!.ConfigData, - (ushort)glyph->Codepoint, - glyph->X0 * scale, - ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent, - glyph->X1 * scale, - ((glyph->Y1 - source.Value!.Ascent) * scale) + target.Value!.Ascent, - glyph->U0, - glyph->V0, - glyph->U1, - glyph->V1, - glyph->AdvanceX * scale); - } - else if (!missingOnly) - { - prevGlyphPtr->X0 = glyph->X0 * scale; - prevGlyphPtr->Y0 = ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent; - prevGlyphPtr->X1 = glyph->X1 * scale; - prevGlyphPtr->Y1 = ((glyph->Y1 - source.Value!.Ascent) * scale) + target.Value!.Ascent; - prevGlyphPtr->U0 = glyph->U0; - prevGlyphPtr->V0 = glyph->V0; - prevGlyphPtr->U1 = glyph->U1; - prevGlyphPtr->V1 = glyph->V1; - prevGlyphPtr->AdvanceX = glyph->AdvanceX * scale; - } - } - } - - if (rebuildLookupTable) - target.Value!.BuildLookupTable(); - } - /// /// Unscales fonts after they have been rendered onto atlas. /// @@ -191,7 +136,7 @@ namespace Dalamud.Interface.GameFonts font->Descent /= fontScale; if (font->ConfigData != null) font->ConfigData->SizePixels /= fontScale; - var glyphs = (ImFontGlyphReal*)font->Glyphs.Data; + var glyphs = (Util.ImFontGlyphReal*)font->Glyphs.Data; for (int i = 0, i_ = font->Glyphs.Size; i < i_; i++) { var glyph = &glyphs[i]; @@ -223,15 +168,22 @@ namespace Dalamud.Interface.GameFonts lock (this.syncRoot) { - var prevValue = this.fontUseCounter.GetValueOrDefault(style, 0); - var newValue = this.fontUseCounter[style] = prevValue + 1; - needRebuild = (prevValue == 0) != (newValue == 0) && !this.fonts.ContainsKey(style); + this.fontUseCounter[style] = this.fontUseCounter.GetValueOrDefault(style, 0) + 1; } + needRebuild = !this.fonts.ContainsKey(style); if (needRebuild) { - Log.Information("[GameFontManager] Calling RebuildFonts because {0} has been requested.", style.ToString()); - this.interfaceManager.RebuildFonts(); + if (Service.Get().IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndAfterBuildFonts) + { + Log.Information("[GameFontManager] NewFontRef: Building {0} right now, as it is called while BuildFonts is already in progress yet atlas build has not been called yet.", style.ToString()); + this.EnsureFont(style); + } + else + { + Log.Information("[GameFontManager] NewFontRef: Calling RebuildFonts because {0} has been requested.", style.ToString()); + this.interfaceManager.RebuildFonts(); + } } return new(this, style); @@ -260,7 +212,7 @@ namespace Dalamud.Interface.GameFonts /// Whether to call target.BuildLookupTable(). public void CopyGlyphsAcrossFonts(ImFontPtr? source, GameFontStyle target, bool missingOnly, bool rebuildLookupTable) { - GameFontManager.CopyGlyphsAcrossFonts(source, this.fonts[target], missingOnly, rebuildLookupTable); + Util.CopyGlyphsAcrossFonts(source, this.fonts[target], missingOnly, rebuildLookupTable); } /// @@ -272,7 +224,7 @@ namespace Dalamud.Interface.GameFonts /// Whether to call target.BuildLookupTable(). public void CopyGlyphsAcrossFonts(GameFontStyle source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable) { - GameFontManager.CopyGlyphsAcrossFonts(this.fonts[source], target, missingOnly, rebuildLookupTable); + Util.CopyGlyphsAcrossFonts(this.fonts[source], target, missingOnly, rebuildLookupTable); } /// @@ -284,7 +236,7 @@ namespace Dalamud.Interface.GameFonts /// Whether to call target.BuildLookupTable(). public void CopyGlyphsAcrossFonts(GameFontStyle source, GameFontStyle target, bool missingOnly, bool rebuildLookupTable) { - GameFontManager.CopyGlyphsAcrossFonts(this.fonts[source], this.fonts[target], missingOnly, rebuildLookupTable); + Util.CopyGlyphsAcrossFonts(this.fonts[source], this.fonts[target], missingOnly, rebuildLookupTable); } /// @@ -293,57 +245,20 @@ namespace Dalamud.Interface.GameFonts /// Whether to load fonts in minimum sizes. public void BuildFonts(bool forceMinSize) { - unsafe - { - ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); - fontConfig.OversampleH = 1; - fontConfig.OversampleV = 1; - fontConfig.PixelSnapH = false; + this.isBuildingAsFallbackFontMode = forceMinSize; + this.isBetweenBuildFontsAndAfterBuildFonts = true; - var io = ImGui.GetIO(); + this.glyphRectIds.Clear(); + this.fonts.Clear(); - this.glyphRectIds.Clear(); - this.fonts.Clear(); - - foreach (var style in this.fontUseCounter.Keys) - { - var rectIds = this.glyphRectIds[style] = new(); - - var fdt = this.fdts[(int)(forceMinSize ? style.FamilyWithMinimumSize : style.FamilyAndSize)]; - if (fdt == null) - continue; - - var font = io.Fonts.AddFontDefault(fontConfig); - - this.fonts[style] = font; - foreach (var glyph in fdt.Glyphs) - { - var c = glyph.Char; - if (c < 32 || c >= 0xFFFF) - continue; - - var widthAdjustment = style.CalculateBaseWidthAdjustment(fdt, glyph); - rectIds[c] = Tuple.Create( - io.Fonts.AddCustomRectFontGlyph( - font, - c, - glyph.BoundingWidth + widthAdjustment + 1, - glyph.BoundingHeight + 1, - glyph.AdvanceWidth, - new Vector2(0, glyph.CurrentOffsetY)), - glyph); - } - } - - fontConfig.Destroy(); - } + foreach (var style in this.fontUseCounter.Keys) + this.EnsureFont(style); } /// /// Post-build fonts before plugins do something more. To be called from InterfaceManager. /// - /// Whether to load fonts in minimum sizes. - public unsafe void AfterBuildFonts(bool forceMinSize) + public unsafe void AfterBuildFonts() { var ioFonts = ImGui.GetIO().Fonts; ioFonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height); @@ -352,7 +267,7 @@ namespace Dalamud.Interface.GameFonts foreach (var (style, font) in this.fonts) { - var fdt = this.fdts[(int)(forceMinSize ? style.FamilyWithMinimumSize : style.FamilyAndSize)]; + var fdt = this.fdts[(int)(this.isBuildingAsFallbackFontMode ? style.FamilyWithMinimumSize : style.FamilyAndSize)]; var scale = style.SizePt / fdt.FontHeader.Size; var fontPtr = font.NativePtr; fontPtr->FontSize = fdt.FontHeader.Size * 4 / 3; @@ -449,10 +364,12 @@ namespace Dalamud.Interface.GameFonts } } - CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, font, true, false); + Util.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, font, true, false); UnscaleFont(font, 1 / scale, false); font.BuildLookupTable(); } + + this.isBetweenBuildFontsAndAfterBuildFonts = false; } /// @@ -471,35 +388,41 @@ namespace Dalamud.Interface.GameFonts } } - private struct ImFontGlyphReal + private unsafe void EnsureFont(GameFontStyle style) { - public uint ColoredVisibleCodepoint; - public float AdvanceX; - public float X0; - public float Y0; - public float X1; - public float Y1; - public float U0; - public float V0; - public float U1; - public float V1; + var rectIds = this.glyphRectIds[style] = new(); - public bool Colored - { - get => ((this.ColoredVisibleCodepoint >> 0) & 1) != 0; - set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFEu) | (value ? 1u : 0u); - } + var fdt = this.fdts[(int)(this.isBuildingAsFallbackFontMode ? style.FamilyWithMinimumSize : style.FamilyAndSize)]; + if (fdt == null) + return; - public bool Visible - { - get => ((this.ColoredVisibleCodepoint >> 1) & 1) != 0; - set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFDu) | (value ? 2u : 0u); - } + ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); + fontConfig.OversampleH = 1; + fontConfig.OversampleV = 1; + fontConfig.PixelSnapH = false; - public int Codepoint + var io = ImGui.GetIO(); + var font = io.Fonts.AddFontDefault(fontConfig); + + fontConfig.Destroy(); + + this.fonts[style] = font; + foreach (var glyph in fdt.Glyphs) { - get => (int)(this.ColoredVisibleCodepoint >> 2); - set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 3u) | ((uint)this.Codepoint << 2); + var c = glyph.Char; + if (c < 32 || c >= 0xFFFF) + continue; + + var widthAdjustment = style.CalculateBaseWidthAdjustment(fdt, glyph); + rectIds[c] = Tuple.Create( + io.Fonts.AddCustomRectFontGlyph( + font, + c, + glyph.BoundingWidth + widthAdjustment + 1, + glyph.BoundingHeight + 1, + glyph.AdvanceWidth, + new Vector2(0, glyph.CurrentOffsetY)), + glyph); } } } diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index c2ee00fdc..1d31e4df9 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -248,6 +248,11 @@ namespace Dalamud.Interface.Internal /// public int FontResolutionLevel => this.FontResolutionLevelOverride ?? Service.Get().FontResolutionLevel; + /// + /// Gets a value indicating whether we're building fonts but haven't generated atlas yet. + /// + public bool IsBuildingFontsBeforeAtlasBuild => this.isRebuildingFonts && !this.fontBuildSignal.WaitOne(0); + /// /// Enable this module. /// @@ -900,7 +905,7 @@ namespace Dalamud.Interface.Internal texPixels[i] = (byte)(Math.Pow(texPixels[i] / 255.0f, 1.0f / fontGamma) * 255.0f); } - gameFontManager.AfterBuildFonts(disableBigFonts); + gameFontManager.AfterBuildFonts(); foreach (var (font, mod) in this.loadedFontInfo) { @@ -929,14 +934,14 @@ namespace Dalamud.Interface.Internal font.Descent = mod.SourceAxis.ImFont.Descent; font.FallbackChar = mod.SourceAxis.ImFont.FallbackChar; font.EllipsisChar = mod.SourceAxis.ImFont.EllipsisChar; - GameFontManager.CopyGlyphsAcrossFonts(mod.SourceAxis.ImFont, font, false, false); + Util.CopyGlyphsAcrossFonts(mod.SourceAxis.ImFont, font, false, false); } else if (mod.Axis == TargetFontModification.AxisMode.GameGlyphsOnly) { Log.Verbose("[FONT] {0}: Overwrite game specific glyphs from AXIS of size {1}px", mod.Name, mod.SourceAxis.ImFont.FontSize, font.FontSize); if (!this.UseAxis && font.NativePtr == DefaultFont.NativePtr) mod.SourceAxis.ImFont.FontSize -= 1; - GameFontManager.CopyGlyphsAcrossFonts(mod.SourceAxis.ImFont, font, true, false, 0xE020, 0xE0DB); + Util.CopyGlyphsAcrossFonts(mod.SourceAxis.ImFont, font, true, false, 0xE020, 0xE0DB); if (!this.UseAxis && font.NativePtr == DefaultFont.NativePtr) mod.SourceAxis.ImFont.FontSize += 1; } @@ -946,7 +951,7 @@ namespace Dalamud.Interface.Internal } // Fill missing glyphs in MonoFont from DefaultFont - GameFontManager.CopyGlyphsAcrossFonts(DefaultFont, MonoFont, true, false); + Util.CopyGlyphsAcrossFonts(DefaultFont, MonoFont, true, false); for (int i = 0, i_ = ioFonts.Fonts.Size; i < i_; i++) { diff --git a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs index 1a34ccd23..72f699840 100644 --- a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs @@ -176,7 +176,8 @@ namespace Dalamud.Interface.Internal.Windows var configuration = Service.Get(); var interfaceManager = Service.Get(); - var rebuildFont = interfaceManager.FontGamma != configuration.FontGammaLevel + var rebuildFont = ImGui.GetIO().FontGlobalScale != configuration.GlobalUiScale + || interfaceManager.FontGamma != configuration.FontGammaLevel || interfaceManager.FontResolutionLevel != configuration.FontResolutionLevel || interfaceManager.UseAxis != configuration.UseAxisFontsFromGame; diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 9c02efe2c..4eb178854 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -526,6 +526,65 @@ namespace Dalamud.Utility Process.Start(process); } + /// + /// Fills missing glyphs in target font from source font, if both are not null. + /// + /// Source font. + /// Target font. + /// Whether to copy missing glyphs only. + /// Whether to call target.BuildLookupTable(). + /// Low codepoint range to copy. + /// High codepoing range to copy. + public static void CopyGlyphsAcrossFonts(ImFontPtr? source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable, int rangeLow = 32, int rangeHigh = 0xFFFE) + { + if (!source.HasValue || !target.HasValue) + return; + + var scale = target.Value!.FontSize / source.Value!.FontSize; + unsafe + { + var glyphs = (ImFontGlyphReal*)source.Value!.Glyphs.Data; + for (int j = 0, j_ = source.Value!.Glyphs.Size; j < j_; j++) + { + var glyph = &glyphs[j]; + if (glyph->Codepoint < rangeLow || glyph->Codepoint > rangeHigh) + continue; + + var prevGlyphPtr = (ImFontGlyphReal*)target.Value!.FindGlyphNoFallback((ushort)glyph->Codepoint).NativePtr; + if ((IntPtr)prevGlyphPtr == IntPtr.Zero) + { + target.Value!.AddGlyph( + target.Value!.ConfigData, + (ushort)glyph->Codepoint, + glyph->X0 * scale, + ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent, + glyph->X1 * scale, + ((glyph->Y1 - source.Value!.Ascent) * scale) + target.Value!.Ascent, + glyph->U0, + glyph->V0, + glyph->U1, + glyph->V1, + glyph->AdvanceX * scale); + } + else if (!missingOnly) + { + prevGlyphPtr->X0 = glyph->X0 * scale; + prevGlyphPtr->Y0 = ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent; + prevGlyphPtr->X1 = glyph->X1 * scale; + prevGlyphPtr->Y1 = ((glyph->Y1 - source.Value!.Ascent) * scale) + target.Value!.Ascent; + prevGlyphPtr->U0 = glyph->U0; + prevGlyphPtr->V0 = glyph->V0; + prevGlyphPtr->U1 = glyph->U1; + prevGlyphPtr->V1 = glyph->V1; + prevGlyphPtr->AdvanceX = glyph->AdvanceX * scale; + } + } + } + + if (rebuildLookupTable) + target.Value!.BuildLookupTable(); + } + /// /// Dispose this object. /// @@ -590,5 +649,40 @@ namespace Dalamud.Utility } } } + + /// + /// ImFontGlyph the correct version. + /// + public struct ImFontGlyphReal + { + public uint ColoredVisibleCodepoint; + public float AdvanceX; + public float X0; + public float Y0; + public float X1; + public float Y1; + public float U0; + public float V0; + public float U1; + public float V1; + + public bool Colored + { + get => ((this.ColoredVisibleCodepoint >> 0) & 1) != 0; + set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFEu) | (value ? 1u : 0u); + } + + public bool Visible + { + get => ((this.ColoredVisibleCodepoint >> 1) & 1) != 0; + set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFDu) | (value ? 2u : 0u); + } + + public int Codepoint + { + get => (int)(this.ColoredVisibleCodepoint >> 2); + set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 3u) | ((uint)this.Codepoint << 2); + } + } } }