From 28ff6d9f00ec3de494954021325f69d8b3ebdea9 Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Thu, 30 Jun 2022 23:16:17 +0900 Subject: [PATCH] wip --- Dalamud/EntryPoint.cs | 1 + .../Interface/GameFonts/GameFontManager.cs | 64 +++++++--- Dalamud/Interface/ImGuiHelpers.cs | 116 ++++++++++++++++-- .../Interface/Internal/InterfaceManager.cs | 14 +-- .../Internal/Windows/TitleScreenMenuWindow.cs | 2 - lib/ImGuiScene | 2 +- 6 files changed, 163 insertions(+), 36 deletions(-) diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index 029e8dd55..5fef315d5 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -10,6 +10,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Logging.Internal; using Dalamud.Support; using Dalamud.Utility; +using ImGuiNET; using Newtonsoft.Json; using PInvoke; using Serilog; diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs index ae44f4278..398785912 100644 --- a/Dalamud/Interface/GameFonts/GameFontManager.cs +++ b/Dalamud/Interface/GameFonts/GameFontManager.cs @@ -11,6 +11,7 @@ using Dalamud.Utility.Timing; using ImGuiNET; using Lumina.Data.Files; using Serilog; +using static Dalamud.Interface.ImGuiHelpers; namespace Dalamud.Interface.GameFonts { @@ -134,15 +135,18 @@ namespace Dalamud.Interface.GameFonts unsafe { var font = fontPtr.NativePtr; - for (int i = 0, i_ = font->IndexAdvanceX.Size; i < i_; ++i) - ((float*)font->IndexAdvanceX.Data)[i] /= fontScale; - font->FallbackAdvanceX /= fontScale; + for (int i = 0, i_ = font->IndexedHotData.Size; i < i_; ++i) + { + font->IndexedHotData.Ref(i).AdvanceX /= fontScale; + font->IndexedHotData.Ref(i).OccupiedWidth /= fontScale; + } + font->FontSize /= fontScale; font->Ascent /= fontScale; font->Descent /= fontScale; if (font->ConfigData != null) font->ConfigData->SizePixels /= fontScale; - var glyphs = (ImGuiHelpers.ImFontGlyphReal*)font->Glyphs.Data; + var glyphs = (ImFontGlyphReal*)font->Glyphs.Data; for (int i = 0, i_ = font->Glyphs.Size; i < i_; i++) { var glyph = &glyphs[i]; @@ -152,6 +156,11 @@ namespace Dalamud.Interface.GameFonts glyph->Y1 /= fontScale; glyph->AdvanceX /= fontScale; } + + for (int i = 0, i_ = font->KerningPairs.Size; i < i_; i++) + font->KerningPairs.Ref(i).AdvanceXAdjustment /= fontScale; + for (int i = 0, i_ = font->FrequentKerningPairs.Size; i < i_; i++) + font->FrequentKerningPairs.Ref(i) /= fontScale; } if (rebuildLookupTable) @@ -253,8 +262,11 @@ namespace Dalamud.Interface.GameFonts this.glyphRectIds.Clear(); this.fonts.Clear(); - foreach (var style in this.fontUseCounter.Keys) - this.EnsureFont(style); + lock (this.syncRoot) + { + foreach (var style in this.fontUseCounter.Keys) + this.EnsureFont(style); + } } /// @@ -279,10 +291,18 @@ namespace Dalamud.Interface.GameFonts { var interfaceManager = Service.Get(); var ioFonts = ImGui.GetIO().Fonts; - ioFonts.GetTexDataAsRGBA32(0, out byte* pixels8, out var width, out var height); - var pixels32 = (uint*)pixels8; var fontGamma = interfaceManager.FontGamma; + var pixels8s = new byte*[ioFonts.Textures.Size]; + var pixels32s = new uint*[ioFonts.Textures.Size]; + var widths = new int[ioFonts.Textures.Size]; + var heights = new int[ioFonts.Textures.Size]; + for (var i = 0; i < pixels8s.Length; i++) + { + ioFonts.GetTexDataAsRGBA32(i, out pixels8s[i], out widths[i], out heights[i]); + pixels32s[i] = (uint*)pixels8s[i]; + } + foreach (var (style, font) in this.fonts) { var fdt = this.fdts[(int)(this.isBuildingAsFallbackFontMode ? style.FamilyWithMinimumSize : style.FamilyAndSize)]; @@ -302,7 +322,10 @@ namespace Dalamud.Interface.GameFonts var glyph = font.FindGlyphNoFallback(fallbackCharCandidate); if ((IntPtr)glyph.NativePtr != IntPtr.Zero) { - font.SetFallbackChar(fallbackCharCandidate); + var ptr = font.NativePtr; + ptr->FallbackChar = fallbackCharCandidate; + ptr->FallbackGlyph = glyph.NativePtr; + ptr->FallbackHotData = (ImFontGlyphHotData*)ptr->IndexedHotData.Address(fallbackCharCandidate); break; } } @@ -323,7 +346,11 @@ namespace Dalamud.Interface.GameFonts foreach (var (c, (rectId, glyph)) in this.glyphRectIds[style]) { - var rc = ioFonts.GetCustomRectByIndex(rectId); + var rc = (ImFontAtlasCustomRectReal*)ioFonts.GetCustomRectByIndex(rectId).NativePtr; + var pixels8 = pixels8s[rc->TextureIndex]; + var pixels32 = pixels32s[rc->TextureIndex]; + var width = widths[rc->TextureIndex]; + var height = heights[rc->TextureIndex]; var sourceBuffer = this.texturePixels[glyph.TextureFileIndex]; var sourceBufferDelta = glyph.TextureChannelByteIndex; var widthAdjustment = style.CalculateBaseWidthAdjustment(fdt, glyph); @@ -334,7 +361,7 @@ namespace Dalamud.Interface.GameFonts for (var x = 0; x < glyph.BoundingWidth; x++) { var a = sourceBuffer[sourceBufferDelta + (4 * (((glyph.TextureOffsetY + y) * fdt.FontHeader.TextureWidth) + glyph.TextureOffsetX + x))]; - pixels32[((rc.Y + y) * width) + rc.X + x] = (uint)(a << 24) | 0xFFFFFFu; + pixels32[((rc->Y + y) * width) + rc->X + x] = (uint)(a << 24) | 0xFFFFFFu; } } } @@ -343,7 +370,7 @@ namespace Dalamud.Interface.GameFonts for (var y = 0; y < glyph.BoundingHeight; y++) { for (var x = 0; x < glyph.BoundingWidth + widthAdjustment; x++) - pixels32[((rc.Y + y) * width) + rc.X + x] = 0xFFFFFFu; + pixels32[((rc->Y + y) * width) + rc->X + x] = 0xFFFFFFu; } for (int xbold = 0, xbold_ = Math.Max(1, (int)Math.Ceiling(style.Weight + 1)); xbold < xbold_; xbold++) @@ -364,7 +391,7 @@ namespace Dalamud.Interface.GameFonts var a1 = sourceBuffer[sourceBufferDelta + (4 * sourcePixelIndex)]; var a2 = x == glyph.BoundingWidth - 1 ? 0 : sourceBuffer[sourceBufferDelta + (4 * (sourcePixelIndex + 1))]; var n = (a1 * xness) + (a2 * (1 - xness)); - var targetOffset = ((rc.Y + y) * width) + rc.X + x + xDeltaInt; + var targetOffset = ((rc->Y + y) * width) + rc->X + x + xDeltaInt; pixels8[(targetOffset * 4) + 3] = Math.Max(pixels8[(targetOffset * 4) + 3], (byte)(boldStrength * n)); } } @@ -374,9 +401,9 @@ namespace Dalamud.Interface.GameFonts if (Math.Abs(fontGamma - 1.4f) >= 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) - for (int y = rc.Y, y_ = rc.Y + rc.Height; y < y_; y++) + for (int y = rc->Y, y_ = rc->Y + rc->Height; y < y_; y++) { - for (int x = rc.X, x_ = rc.X + rc.Width; x < x_; x++) + for (int x = rc->X, x_ = rc->X + rc->Width; x < x_; x++) { var i = (((y * width) + x) * 4) + 3; pixels8[i] = (byte)(Math.Pow(pixels8[i] / 255.0f, 1.4f / fontGamma) * 255.0f); @@ -435,12 +462,15 @@ namespace Dalamud.Interface.GameFonts io.Fonts.AddCustomRectFontGlyph( font, c, - glyph.BoundingWidth + widthAdjustment + 1, - glyph.BoundingHeight + 1, + glyph.BoundingWidth + widthAdjustment, + glyph.BoundingHeight, glyph.AdvanceWidth, new Vector2(0, glyph.CurrentOffsetY)), glyph); } + + foreach (var kernPair in fdt.Distances) + font.AddKerningPair(kernPair.Left, kernPair.Right, kernPair.RightOffset); } } } diff --git a/Dalamud/Interface/ImGuiHelpers.cs b/Dalamud/Interface/ImGuiHelpers.cs index d949a06ea..324a45323 100644 --- a/Dalamud/Interface/ImGuiHelpers.cs +++ b/Dalamud/Interface/ImGuiHelpers.cs @@ -154,6 +154,7 @@ namespace Dalamud.Interface return; var scale = target.Value!.FontSize / source.Value!.FontSize; + var addedCodepoints = new HashSet(); unsafe { var glyphs = (ImFontGlyphReal*)source.Value!.Glyphs.Data; @@ -168,10 +169,11 @@ namespace Dalamud.Interface var prevGlyphPtr = (ImFontGlyphReal*)target.Value!.FindGlyphNoFallback((ushort)glyph->Codepoint).NativePtr; if ((IntPtr)prevGlyphPtr == IntPtr.Zero) { + addedCodepoints.Add(glyph->Codepoint); target.Value!.AddGlyph( target.Value!.ConfigData, (ushort)glyph->Codepoint, - 0, + glyph->TextureIndex, glyph->X0 * scale, ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent, glyph->X1 * scale, @@ -184,6 +186,7 @@ namespace Dalamud.Interface } else if (!missingOnly) { + addedCodepoints.Add(glyph->Codepoint); prevGlyphPtr->X0 = glyph->X0 * scale; prevGlyphPtr->Y0 = ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent; prevGlyphPtr->X1 = glyph->X1 * scale; @@ -195,6 +198,16 @@ namespace Dalamud.Interface prevGlyphPtr->AdvanceX = glyph->AdvanceX * scale; } } + + var kernPairs = source.Value!.KerningPairs; + for (int j = 0, k = kernPairs.Size; j < k; j++) + { + if (!addedCodepoints.Contains(kernPairs[j].Left)) + continue; + if (!addedCodepoints.Contains(kernPairs[j].Right)) + continue; + target.Value.AddKerningPair(kernPairs[j].Left, kernPairs[j].Right, kernPairs[j].AdvanceXAdjustment); + } } if (rebuildLookupTable) @@ -215,7 +228,7 @@ namespace Dalamud.Interface [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "ImGui internals")] public struct ImFontGlyphReal { - public uint ColoredVisibleCodepoint; + public uint ColoredVisibleTextureIndexCodepoint; public float AdvanceX; public float X0; public float Y0; @@ -226,23 +239,110 @@ namespace Dalamud.Interface public float U1; public float V1; + private const uint ColoredMask /*****/ = 0b_00000000_00000000_00000000_00000001u; + private const uint VisibleMask /*****/ = 0b_00000000_00000000_00000000_00000010u; + private const uint TextureMask /*****/ = 0b_00000000_00000000_00000111_11111100u; + private const uint CodepointMask /***/ = 0b_11111111_11111111_11111000_00000000u; + + private const int ColoredShift = 0; + private const int VisibleShift = 1; + private const int TextureShift = 2; + private const int CodepointShift = 11; + public bool Colored { - get => ((this.ColoredVisibleCodepoint >> 0) & 1) != 0; - set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFEu) | (value ? 1u : 0u); + get => (int)((this.ColoredVisibleTextureIndexCodepoint & ColoredMask) >> ColoredShift) != 0; + set => this.ColoredVisibleTextureIndexCodepoint = (this.ColoredVisibleTextureIndexCodepoint & ~ColoredMask) | (value ? 1u << ColoredShift : 0u); } public bool Visible { - get => ((this.ColoredVisibleCodepoint >> 1) & 1) != 0; - set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFDu) | (value ? 2u : 0u); + get => (int)((this.ColoredVisibleTextureIndexCodepoint & VisibleMask) >> VisibleShift) != 0; + set => this.ColoredVisibleTextureIndexCodepoint = (this.ColoredVisibleTextureIndexCodepoint & ~VisibleMask) | (value ? 1u << VisibleShift : 0u); + } + + public int TextureIndex + { + get => (int)(this.ColoredVisibleTextureIndexCodepoint & TextureMask) >> TextureShift; + set => this.ColoredVisibleTextureIndexCodepoint = (this.ColoredVisibleTextureIndexCodepoint & ~TextureMask) | ((uint)value << TextureShift); } public int Codepoint { - get => (int)(this.ColoredVisibleCodepoint >> 2); - set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 3u) | ((uint)value << 2); + get => (int)(this.ColoredVisibleTextureIndexCodepoint & CodepointMask) >> CodepointShift; + set => this.ColoredVisibleTextureIndexCodepoint = (this.ColoredVisibleTextureIndexCodepoint & ~CodepointMask) | ((uint)value << CodepointShift); } } + + /// + /// ImFontGlyphHotData the correct version. + /// + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "ImGui internals")] + public struct ImFontGlyphHotDataReal + { + public float AdvanceX; + public float OccupiedWidth; + public uint KerningPairInfo; + + private const uint UseBisectMask /***/ = 0b_00000000_00000000_00000000_00000001u; + private const uint OffsetMask /******/ = 0b_00000000_00001111_11111111_11111110u; + private const uint CountMask /*******/ = 0b_11111111_11110000_00000111_11111100u; + + private const int UseBisectShift = 0; + private const int OffsetShift = 1; + private const int CountShift = 20; + + public bool UseBisect + { + get => (int)((this.KerningPairInfo & UseBisectMask) >> UseBisectShift) != 0; + set => this.KerningPairInfo = (this.KerningPairInfo & ~UseBisectMask) | (value ? 1u << UseBisectShift : 0u); + } + + public bool Offset + { + get => (int)((this.KerningPairInfo & OffsetMask) >> OffsetShift) != 0; + set => this.KerningPairInfo = (this.KerningPairInfo & ~OffsetMask) | (value ? 1u << OffsetShift : 0u); + } + + public int Count + { + get => (int)(this.KerningPairInfo & CountMask) >> CountShift; + set => this.KerningPairInfo = (this.KerningPairInfo & ~CountMask) | ((uint)value << CountShift); + } + } + + /// + /// ImFontAtlasCustomRect the correct version. + /// + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "ImGui internals")] + public unsafe struct ImFontAtlasCustomRectReal + { + public ushort Width; + public ushort Height; + public ushort X; + public ushort Y; + public uint TextureIndexAndGlyphID; + public float GlyphAdvanceX; + public Vector2 GlyphOffset; + public ImFont* Font; + + private const uint TextureIndexMask /***/ = 0b_00000000_00000000_00000111_11111100u; + private const uint GlyphIDMask /********/ = 0b_11111111_11111111_11111000_00000000u; + + private const int TextureIndexShift = 2; + private const int GlyphIDShift = 11; + + public int TextureIndex + { + get => (int)(this.TextureIndexAndGlyphID & TextureIndexMask) >> TextureIndexShift; + set => this.TextureIndexAndGlyphID = (this.TextureIndexAndGlyphID & ~TextureIndexMask) | ((uint)value << TextureIndexShift); + } + + public int GlyphID + { + get => (int)(this.TextureIndexAndGlyphID & GlyphIDMask) >> GlyphIDShift; + set => this.TextureIndexAndGlyphID = (this.TextureIndexAndGlyphID & ~GlyphIDMask) | ((uint)value << GlyphIDShift); + } + }; } } diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 2869005da..924584b70 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -848,6 +848,12 @@ namespace Dalamud.Interface.Internal } } + for (int i = 0, i_ = ioFonts.ConfigData.Size; i < i_; i++) + { + var config = ioFonts.ConfigData[i]; + config.RasterizerGamma = config.RasterizerGamma * fontGamma; + } + Log.Verbose("[FONT] ImGui.IO.Build will be called."); ioFonts.Build(); gameFontManager.AfterIoFontsBuild(); @@ -881,14 +887,6 @@ namespace Dalamud.Interface.Internal if (!disableBigFonts) this.IsFallbackFontMode = false; - 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) - ioFonts.GetTexDataAsRGBA32(0, out byte* texPixels, out var texWidth, out var texHeight); - for (int i = 3, i_ = texWidth * texHeight * 4; i < i_; i += 4) - texPixels[i] = (byte)(Math.Pow(texPixels[i] / 255.0f, 1.0f / fontGamma) * 255.0f); - } - gameFontManager.AfterBuildFonts(); foreach (var (font, mod) in this.loadedFontInfo) diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index 628f52f2f..78581e624 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -142,7 +142,6 @@ namespace Dalamud.Interface.Internal.Windows } if (!ImGui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows | - ImGuiHoveredFlags.AllowWhenOverlapped | ImGuiHoveredFlags.AllowWhenBlockedByActiveItem)) { this.state = State.FadeOut; @@ -188,7 +187,6 @@ namespace Dalamud.Interface.Internal.Windows ImGui.PopStyleVar(); var isHover = ImGui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows | - ImGuiHoveredFlags.AllowWhenOverlapped | ImGuiHoveredFlags.AllowWhenBlockedByActiveItem); if (!isHover && this.fadeOutEasing!.IsDone) diff --git a/lib/ImGuiScene b/lib/ImGuiScene index 4a58aef66..cd6300cd7 160000 --- a/lib/ImGuiScene +++ b/lib/ImGuiScene @@ -1 +1 @@ -Subproject commit 4a58aef6683b12685ed064df286a6aa77fd04521 +Subproject commit cd6300cd7944b24643ed7381fbe5ae15efc10448