Add FontAwesome fixed width (#1737)

* Add FA-FW

* remove braces (ask of kizer)

* reuse iconfont glyphs

* use FAFS for initial font

---------

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon 2024-04-14 23:19:27 +02:00 committed by GitHub
parent b8802f0609
commit e97d95dba8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 11 deletions

View file

@ -25,10 +25,15 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Utility; using Dalamud.Utility;
using Dalamud.Utility.Timing; using Dalamud.Utility.Timing;
using ImGuiNET; using ImGuiNET;
using ImGuiScene; using ImGuiScene;
using PInvoke; using PInvoke;
using Serilog; using Serilog;
using SharpDX; using SharpDX;
using SharpDX.Direct3D; using SharpDX.Direct3D;
using SharpDX.Direct3D11; using SharpDX.Direct3D11;
@ -131,6 +136,13 @@ internal class InterfaceManager : IInternalDisposableService
public static ImFontPtr IconFont => public static ImFontPtr IconFont =>
WhenFontsReady().IconFontHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault); WhenFontsReady().IconFontHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
/// <summary>
/// Gets an included FontAwesome icon font with fixed width.
/// <strong>Accessing this static property outside of the main thread is dangerous and not supported.</strong>
/// </summary>
public static ImFontPtr IconFontFixedWidth =>
WhenFontsReady().IconFontFixedWidthHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault);
/// <summary> /// <summary>
/// Gets an included monospaced font.<br /> /// Gets an included monospaced font.<br />
/// <strong>Accessing this static property outside of the main thread is dangerous and not supported.</strong> /// <strong>Accessing this static property outside of the main thread is dangerous and not supported.</strong>
@ -148,6 +160,11 @@ internal class InterfaceManager : IInternalDisposableService
/// </summary> /// </summary>
public FontHandle? IconFontHandle { get; private set; } public FontHandle? IconFontHandle { get; private set; }
/// <summary>
/// Gets the icon font handle with fixed width.
/// </summary>
public FontHandle? IconFontFixedWidthHandle { get; private set; }
/// <summary> /// <summary>
/// Gets the mono font handle. /// Gets the mono font handle.
/// </summary> /// </summary>
@ -402,7 +419,7 @@ internal class InterfaceManager : IInternalDisposableService
}); });
} }
} }
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it // no sampler for now because the ImGui implementation we copied doesn't allow for changing it
return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height)); return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height));
} }
@ -498,7 +515,7 @@ internal class InterfaceManager : IInternalDisposableService
atlas.BuildTask.GetAwaiter().GetResult(); atlas.BuildTask.GetAwaiter().GetResult();
return im; return im;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RenderImGui(RawDX11Scene scene) private static void RenderImGui(RawDX11Scene scene)
{ {
@ -732,6 +749,13 @@ internal class InterfaceManager : IInternalDisposableService
GlyphMinAdvanceX = DefaultFontSizePx, GlyphMinAdvanceX = DefaultFontSizePx,
GlyphMaxAdvanceX = DefaultFontSizePx, GlyphMaxAdvanceX = DefaultFontSizePx,
}))); })));
this.IconFontFixedWidthHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(tk => tk.AddDalamudAssetFont(
DalamudAsset.FontAwesomeFreeSolid,
new()
{
GlyphRanges = new ushort[] { 0x20 },
})));
this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
e => e.OnPreBuild( e => e.OnPreBuild(
tk => tk.AddDalamudAssetFont( tk => tk.AddDalamudAssetFont(
@ -748,6 +772,13 @@ internal class InterfaceManager : IInternalDisposableService
tk.GetFont(this.DefaultFontHandle), tk.GetFont(this.DefaultFontHandle),
tk.GetFont(this.MonoFontHandle), tk.GetFont(this.MonoFontHandle),
missingOnly: true); missingOnly: true);
// Fill missing glyphs in IconFontFixedWidth with IconFont and fit ratio
tk.CopyGlyphsAcrossFonts(
tk.GetFont(this.IconFontHandle),
tk.GetFont(this.IconFontFixedWidthHandle),
missingOnly: true);
tk.FitRatio(tk.GetFont(this.IconFontFixedWidthHandle));
}); });
this.DefaultFontHandle.ImFontChanged += (_, font) => this.DefaultFontHandle.ImFontChanged += (_, font) =>
{ {

View file

@ -1,4 +1,4 @@
using Dalamud.Interface.Internal; using Dalamud.Interface.Internal;
using Dalamud.Utility; using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
@ -27,6 +27,13 @@ public interface IFontAtlasBuildToolkitPostBuild : IFontAtlasBuildToolkit
/// <returns>The texture index.</returns> /// <returns>The texture index.</returns>
int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError); int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError);
/// <summary>
/// Fits a font to a fixed 1:1 ratio adjusting glyph positions horizontally and vertically to fit within font size boundaries.
/// </summary>
/// <param name="font">The font to fit.</param>
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
void FitRatio(ImFontPtr font, bool rebuildLookupTable = true);
/// <summary> /// <summary>
/// Copies glyphs across fonts, in a safer way.<br /> /// Copies glyphs across fonts, in a safer way.<br />
/// If the font does not belong to the current atlas, this function is a no-op. /// If the font does not belong to the current atlas, this function is a no-op.

View file

@ -1,4 +1,4 @@
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -166,7 +166,7 @@ internal sealed partial class FontAtlasFactory
/// <inheritdoc/> /// <inheritdoc/>
public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) => public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) =>
this.data.AddNewTexture(textureWrap, disposeOnError); this.data.AddNewTexture(textureWrap, disposeOnError);
/// <inheritdoc/> /// <inheritdoc/>
public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action); public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action);
@ -391,12 +391,10 @@ internal sealed partial class FontAtlasFactory
}); });
case DalamudAsset.LodestoneGameSymbol when !this.factory.HasGameSymbolsFontFile: case DalamudAsset.LodestoneGameSymbol when !this.factory.HasGameSymbolsFontFile:
{
return this.AddGameGlyphs( return this.AddGameGlyphs(
new(GameFontFamily.Axis, fontConfig.SizePx), new(GameFontFamily.Axis, fontConfig.SizePx),
fontConfig.GlyphRanges, fontConfig.GlyphRanges,
fontConfig.MergeFont); fontConfig.MergeFont);
}
default: default:
return this.factory.AddFont( return this.factory.AddFont(
@ -858,5 +856,30 @@ internal sealed partial class FontAtlasFactory
} }
} }
} }
/// <inheritdoc/>
public void FitRatio(ImFontPtr font, bool rebuildLookupTable = true)
{
var nsize = font.FontSize;
var glyphs = font.GlyphsWrapped();
foreach (ref var glyph in glyphs.DataSpan)
{
var ratio = 1f;
if (glyph.X1 - glyph.X0 > nsize)
ratio = Math.Max(ratio, (glyph.X1 - glyph.X0) / nsize);
if (glyph.Y1 - glyph.Y0 > nsize)
ratio = Math.Max(ratio, (glyph.Y1 - glyph.Y0) / nsize);
var w = MathF.Round((glyph.X1 - glyph.X0) / ratio, MidpointRounding.ToZero);
var h = MathF.Round((glyph.Y1 - glyph.Y0) / ratio, MidpointRounding.AwayFromZero);
glyph.X0 = MathF.Round((nsize - w) / 2f, MidpointRounding.ToZero);
glyph.Y0 = MathF.Round((nsize - h) / 2f, MidpointRounding.AwayFromZero);
glyph.X1 = glyph.X0 + w;
glyph.Y1 = glyph.Y0 + h;
glyph.AdvanceX = nsize;
}
if (rebuildLookupTable)
this.BuildLookupTable(font);
}
} }
} }

View file

@ -21,9 +21,13 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility; using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using ImGuiScene; using ImGuiScene;
using Serilog; using Serilog;
using SharpDX.Direct3D11; using SharpDX.Direct3D11;
namespace Dalamud.Interface; namespace Dalamud.Interface;
@ -53,6 +57,7 @@ public sealed class UiBuilder : IDisposable
private IFontHandle? defaultFontHandle; private IFontHandle? defaultFontHandle;
private IFontHandle? iconFontHandle; private IFontHandle? iconFontHandle;
private IFontHandle? monoFontHandle; private IFontHandle? monoFontHandle;
private IFontHandle? iconFontFixedWidthHandle;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it. /// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it.
@ -106,7 +111,7 @@ public sealed class UiBuilder : IDisposable
/// Event that is fired when the plugin should open its configuration interface. /// Event that is fired when the plugin should open its configuration interface.
/// </summary> /// </summary>
public event Action OpenConfigUi; public event Action OpenConfigUi;
/// <summary> /// <summary>
/// Event that is fired when the plugin should open its main interface. /// Event that is fired when the plugin should open its main interface.
/// </summary> /// </summary>
@ -251,6 +256,16 @@ public sealed class UiBuilder : IDisposable
this.InterfaceManagerWithScene?.IconFontHandle this.InterfaceManagerWithScene?.IconFontHandle
?? throw new InvalidOperationException("Scene is not yet ready."))); ?? throw new InvalidOperationException("Scene is not yet ready.")));
/// <summary>
/// Gets the default Dalamud icon font based on FontAwesome 5 free solid with a fixed width and vertically centered glyphs.
/// </summary>
public IFontHandle IconFontFixedWidthHandle =>
this.iconFontFixedWidthHandle ??=
this.scopedFinalizer.Add(
new FontHandleWrapper(
this.InterfaceManagerWithScene?.IconFontFixedWidthHandle
?? throw new InvalidOperationException("Scene is not yet ready.")));
/// <summary> /// <summary>
/// Gets the default Dalamud monospaced font based on Inconsolata Regular. /// Gets the default Dalamud monospaced font based on Inconsolata Regular.
/// </summary> /// </summary>
@ -266,7 +281,7 @@ public sealed class UiBuilder : IDisposable
/// new() { SizePx = UiBuilder.DefaultFontSizePx }))); /// new() { SizePx = UiBuilder.DefaultFontSizePx })));
/// </code> /// </code>
/// </remarks> /// </remarks>
public IFontHandle MonoFontHandle => public IFontHandle MonoFontHandle =>
this.monoFontHandle ??= this.monoFontHandle ??=
this.scopedFinalizer.Add( this.scopedFinalizer.Add(
new FontHandleWrapper( new FontHandleWrapper(
@ -630,7 +645,7 @@ public sealed class UiBuilder : IDisposable
{ {
this.OpenConfigUi?.InvokeSafely(); this.OpenConfigUi?.InvokeSafely();
} }
/// <summary> /// <summary>
/// Open the registered configuration UI, if it exists. /// Open the registered configuration UI, if it exists.
/// </summary> /// </summary>
@ -838,5 +853,5 @@ public sealed class UiBuilder : IDisposable
private void WrappedOnImFontChanged(IFontHandle obj, ILockedImFont lockedFont) => private void WrappedOnImFontChanged(IFontHandle obj, ILockedImFont lockedFont) =>
this.ImFontChanged?.Invoke(obj, lockedFont); this.ImFontChanged?.Invoke(obj, lockedFont);
} }
} }