mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Implement feature to use game resource fonts
This commit is contained in:
parent
b7c47b4c97
commit
f3588dfe23
9 changed files with 1137 additions and 2 deletions
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Style;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
|
@ -128,6 +129,11 @@ namespace Dalamud.Configuration.Internal
|
|||
/// </summary>
|
||||
public float GlobalUiScale { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the game font to use for Dalamud UI.
|
||||
/// </summary>
|
||||
public GameFont DefaultFontFromGame { get; set; } = GameFont.Undefined;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using Dalamud.Game.Network.Internal;
|
|||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking.Internal;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
|
|
@ -191,6 +192,9 @@ namespace Dalamud
|
|||
Service<InterfaceManager>.Set().Enable();
|
||||
Log.Information("[T2] IM OK!");
|
||||
|
||||
Service<GameFontManager>.Set();
|
||||
Log.Information("[T2] GFM OK!");
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
Service<SeStringManager>.Set();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
|
|
|||
428
Dalamud/Interface/GameFonts/FdtReader.cs
Normal file
428
Dalamud/Interface/GameFonts/FdtReader.cs
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Interface.GameFonts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a game font file.
|
||||
/// </summary>
|
||||
public class FdtReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FdtReader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">Content of a FDT file.</param>
|
||||
public FdtReader(byte[] data)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = data)
|
||||
{
|
||||
this.FileHeader = *(FdtHeader*)ptr;
|
||||
this.FontHeader = *(FontTableHeader*)(ptr + this.FileHeader.FontTableHeaderOffset);
|
||||
this.KerningHeader = *(KerningTableHeader*)(ptr + this.FileHeader.KerningTableHeaderOffset);
|
||||
|
||||
var glyphs = (FontTableEntry*)(ptr + this.FileHeader.FontTableHeaderOffset + Marshal.SizeOf(this.FontHeader));
|
||||
for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
|
||||
this.Glyphs.Add(glyphs[i]);
|
||||
|
||||
var kerns = (KerningTableEntry*)(ptr + this.FileHeader.KerningTableHeaderOffset + Marshal.SizeOf(this.KerningHeader));
|
||||
for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
|
||||
this.Distances.Add(kerns[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header of this file.
|
||||
/// </summary>
|
||||
public FdtHeader FileHeader { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font header of this file.
|
||||
/// </summary>
|
||||
public FontTableHeader FontHeader { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kerning table header of this file.
|
||||
/// </summary>
|
||||
public KerningTableHeader KerningHeader { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the glyphs defined in this file.
|
||||
/// </summary>
|
||||
public List<FontTableEntry> Glyphs { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the kerning entries defined in this file.
|
||||
/// </summary>
|
||||
public List<KerningTableEntry> Distances { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Finds glyph definition for corresponding codepoint.
|
||||
/// </summary>
|
||||
/// <param name="codepoint">Unicode codepoint (UTF-32 value).</param>
|
||||
/// <returns>Corresponding FontTableEntry, or null if not found.</returns>
|
||||
public FontTableEntry? FindGlyph(int codepoint)
|
||||
{
|
||||
var i = this.Glyphs.BinarySearch(new FontTableEntry { CharUtf8 = CodePointToUtf8int32(codepoint) });
|
||||
if (i < 0 || i == this.Glyphs.Count)
|
||||
return null;
|
||||
return this.Glyphs[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns glyph definition for corresponding codepoint.
|
||||
/// </summary>
|
||||
/// <param name="codepoint">Unicode codepoint (UTF-32 value).</param>
|
||||
/// <returns>Corresponding FontTableEntry, or that of a fallback character.</returns>
|
||||
public FontTableEntry GetGlyph(int codepoint)
|
||||
{
|
||||
return (this.FindGlyph(codepoint)
|
||||
?? this.FindGlyph('〓')
|
||||
?? this.FindGlyph('?')
|
||||
?? this.FindGlyph('='))!.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns distance adjustment between two adjacent characters.
|
||||
/// </summary>
|
||||
/// <param name="codepoint1">Left character.</param>
|
||||
/// <param name="codepoint2">Right character.</param>
|
||||
/// <returns>Supposed distance adjustment between given characters.</returns>
|
||||
public int GetDistance(int codepoint1, int codepoint2)
|
||||
{
|
||||
var i = this.Distances.BinarySearch(new KerningTableEntry { LeftUtf8 = CodePointToUtf8int32(codepoint1), RightUtf8 = CodePointToUtf8int32(codepoint2) });
|
||||
if (i < 0 || i == this.Distances.Count)
|
||||
return 0;
|
||||
return this.Distances[i].RightOffset;
|
||||
}
|
||||
|
||||
private static int CodePointToUtf8int32(int codepoint)
|
||||
{
|
||||
if (codepoint <= 0x7F)
|
||||
{
|
||||
return codepoint;
|
||||
}
|
||||
else if (codepoint <= 0x7FF)
|
||||
{
|
||||
return ((0xC0 | (codepoint >> 6)) << 8)
|
||||
| ((0x80 | ((codepoint >> 0) & 0x3F)) << 0);
|
||||
}
|
||||
else if (codepoint <= 0xFFFF)
|
||||
{
|
||||
return ((0xE0 | (codepoint >> 12)) << 16)
|
||||
| ((0x80 | ((codepoint >> 6) & 0x3F)) << 8)
|
||||
| ((0x80 | ((codepoint >> 0) & 0x3F)) << 0);
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF)
|
||||
{
|
||||
return ((0xF0 | (codepoint >> 18)) << 24)
|
||||
| ((0x80 | ((codepoint >> 12) & 0x3F)) << 16)
|
||||
| ((0x80 | ((codepoint >> 6) & 0x3F)) << 8)
|
||||
| ((0x80 | ((codepoint >> 0) & 0x3F)) << 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xFFFE;
|
||||
}
|
||||
}
|
||||
|
||||
private static int Utf8Uint32ToCodePoint(int n)
|
||||
{
|
||||
if ((n & 0xFFFFFF80) == 0)
|
||||
{
|
||||
return n & 0x7F;
|
||||
}
|
||||
else if ((n & 0xFFFFE0C0) == 0xC080)
|
||||
{
|
||||
return
|
||||
(((n >> 0x08) & 0x1F) << 6) |
|
||||
(((n >> 0x00) & 0x3F) << 0);
|
||||
}
|
||||
else if ((n & 0xF0C0C0) == 0xE08080)
|
||||
{
|
||||
return
|
||||
(((n >> 0x10) & 0x0F) << 12) |
|
||||
(((n >> 0x08) & 0x3F) << 6) |
|
||||
(((n >> 0x00) & 0x3F) << 0);
|
||||
}
|
||||
else if ((n & 0xF8C0C0C0) == 0xF0808080)
|
||||
{
|
||||
return
|
||||
(((n >> 0x18) & 0x07) << 18) |
|
||||
(((n >> 0x10) & 0x3F) << 12) |
|
||||
(((n >> 0x08) & 0x3F) << 6) |
|
||||
(((n >> 0x00) & 0x3F) << 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xFFFF; // Guaranteed non-unicode
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Header of game font file format.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct FdtHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature: "fcsv".
|
||||
/// </summary>
|
||||
public fixed byte Signature[8];
|
||||
|
||||
/// <summary>
|
||||
/// Offset to FontTableHeader.
|
||||
/// </summary>
|
||||
public int FontTableHeaderOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset to KerningTableHeader.
|
||||
/// </summary>
|
||||
public int KerningTableHeaderOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Unused/unknown.
|
||||
/// </summary>
|
||||
public fixed byte Padding[0x10];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Header of glyph table.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct FontTableHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature: "fthd".
|
||||
/// </summary>
|
||||
public fixed byte Signature[4];
|
||||
|
||||
/// <summary>
|
||||
/// Number of glyphs defined in this file.
|
||||
/// </summary>
|
||||
public int FontTableEntryCount;
|
||||
|
||||
/// <summary>
|
||||
/// Number of kerning informations defined in this file.
|
||||
/// </summary>
|
||||
public int KerningTableEntryCount;
|
||||
|
||||
/// <summary>
|
||||
/// Unused/unknown.
|
||||
/// </summary>
|
||||
public fixed byte Padding[0x04];
|
||||
|
||||
/// <summary>
|
||||
/// Width of backing texture.
|
||||
/// </summary>
|
||||
public ushort TextureWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Height of backing texture.
|
||||
/// </summary>
|
||||
public ushort TextureHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the font defined from this file, in points unit.
|
||||
/// </summary>
|
||||
public float Size;
|
||||
|
||||
/// <summary>
|
||||
/// Line height of the font defined forom this file, in pixels unit.
|
||||
/// </summary>
|
||||
public int LineHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Ascent of the font defined from this file, in pixels unit.
|
||||
/// </summary>
|
||||
public int Ascent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets descent of the font defined from this file, in pixels unit.
|
||||
/// </summary>
|
||||
public int Descent => this.LineHeight - this.Ascent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Glyph table entry.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct FontTableEntry : IComparable<FontTableEntry>
|
||||
{
|
||||
/// <summary>
|
||||
/// Mapping of texture channel index to byte index.
|
||||
/// </summary>
|
||||
public static readonly int[] TextureChannelOrder = { 2, 1, 0, 3 };
|
||||
|
||||
/// <summary>
|
||||
/// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian.
|
||||
/// </summary>
|
||||
public int CharUtf8;
|
||||
|
||||
/// <summary>
|
||||
/// Integer representation of a Shift_JIS character in reverse order, read in little endian.
|
||||
/// </summary>
|
||||
public ushort CharSjis;
|
||||
|
||||
/// <summary>
|
||||
/// Index of backing texture.
|
||||
/// </summary>
|
||||
public ushort TextureIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal offset of glyph image in the backing texture.
|
||||
/// </summary>
|
||||
public ushort TextureOffsetX;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical offset of glyph image in the backing texture.
|
||||
/// </summary>
|
||||
public ushort TextureOffsetY;
|
||||
|
||||
/// <summary>
|
||||
/// Bounding width of this glyph.
|
||||
/// </summary>
|
||||
public byte BoundingWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Bounding height of this glyph.
|
||||
/// </summary>
|
||||
public byte BoundingHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Distance adjustment for drawing next character.
|
||||
/// </summary>
|
||||
public sbyte NextOffsetX;
|
||||
|
||||
/// <summary>
|
||||
/// Distance adjustment for drawing current character.
|
||||
/// </summary>
|
||||
public sbyte CurrentOffsetY;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the file among all the backing texture files.
|
||||
/// </summary>
|
||||
public int TextureFileIndex => this.TextureIndex / 4;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the channel index in the backing texture file.
|
||||
/// </summary>
|
||||
public int TextureChannelIndex => this.TextureIndex % 4;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the byte index in a multichannel pixel corresponding to the channel.
|
||||
/// </summary>
|
||||
public int TextureChannelByteIndex => TextureChannelOrder[this.TextureChannelIndex];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the advance width of this character.
|
||||
/// </summary>
|
||||
public int AdvanceWidth => this.BoundingWidth + this.NextOffsetX;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode codepoint of the character for this entry in int type.
|
||||
/// </summary>
|
||||
public int CharInt => Utf8Uint32ToCodePoint(this.CharUtf8);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode codepoint of the character for this entry in char type.
|
||||
/// </summary>
|
||||
public char Char => (char)Utf8Uint32ToCodePoint(this.CharUtf8);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(FontTableEntry other)
|
||||
{
|
||||
return this.CharUtf8 - other.CharUtf8;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Header of kerning table.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct KerningTableHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature: "knhd".
|
||||
/// </summary>
|
||||
public fixed byte Signature[4];
|
||||
|
||||
/// <summary>
|
||||
/// Number of kerning entries in this table.
|
||||
/// </summary>
|
||||
public int Count;
|
||||
|
||||
/// <summary>
|
||||
/// Unused/unknown.
|
||||
/// </summary>
|
||||
public fixed byte Padding[0x08];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kerning table entry.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct KerningTableEntry : IComparable<KerningTableEntry>
|
||||
{
|
||||
/// <summary>
|
||||
/// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian, for the left character.
|
||||
/// </summary>
|
||||
public int LeftUtf8;
|
||||
|
||||
/// <summary>
|
||||
/// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian, for the right character.
|
||||
/// </summary>
|
||||
public int RightUtf8;
|
||||
|
||||
/// <summary>
|
||||
/// Integer representation of a Shift_JIS character in reverse order, read in little endian, for the left character.
|
||||
/// </summary>
|
||||
public ushort LeftSjis;
|
||||
|
||||
/// <summary>
|
||||
/// Integer representation of a Shift_JIS character in reverse order, read in little endian, for the right character.
|
||||
/// </summary>
|
||||
public ushort RightSjis;
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal offset adjustment for the right character.
|
||||
/// </summary>
|
||||
public int RightOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode codepoint of the character for this entry in int type.
|
||||
/// </summary>
|
||||
public int LeftInt => Utf8Uint32ToCodePoint(this.LeftUtf8);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode codepoint of the character for this entry in char type.
|
||||
/// </summary>
|
||||
public char Left => (char)Utf8Uint32ToCodePoint(this.LeftUtf8);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode codepoint of the character for this entry in int type.
|
||||
/// </summary>
|
||||
public int RightInt => Utf8Uint32ToCodePoint(this.RightUtf8);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode codepoint of the character for this entry in char type.
|
||||
/// </summary>
|
||||
public char Right => (char)Utf8Uint32ToCodePoint(this.RightUtf8);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(KerningTableEntry other)
|
||||
{
|
||||
if (this.LeftUtf8 == other.LeftUtf8)
|
||||
return this.RightUtf8 - other.RightUtf8;
|
||||
else
|
||||
return this.LeftUtf8 - other.LeftUtf8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Dalamud/Interface/GameFonts/GameFont.cs
Normal file
174
Dalamud/Interface/GameFonts/GameFont.cs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
namespace Dalamud.Interface.GameFonts
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum of available game fonts.
|
||||
/// </summary>
|
||||
public enum GameFont : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder meaning unused.
|
||||
/// </summary>
|
||||
Undefined,
|
||||
|
||||
/// <summary>
|
||||
/// AXIS (9.6pt)
|
||||
///
|
||||
/// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
|
||||
/// </summary>
|
||||
Axis96,
|
||||
|
||||
/// <summary>
|
||||
/// AXIS (12pt)
|
||||
///
|
||||
/// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
|
||||
/// </summary>
|
||||
Axis12,
|
||||
|
||||
/// <summary>
|
||||
/// AXIS (14pt)
|
||||
///
|
||||
/// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
|
||||
/// </summary>
|
||||
Axis14,
|
||||
|
||||
/// <summary>
|
||||
/// AXIS (18pt)
|
||||
///
|
||||
/// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
|
||||
/// </summary>
|
||||
Axis18,
|
||||
|
||||
/// <summary>
|
||||
/// AXIS (36pt)
|
||||
///
|
||||
/// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
|
||||
/// </summary>
|
||||
Axis36,
|
||||
|
||||
/// <summary>
|
||||
/// Jupiter (16pt)
|
||||
///
|
||||
/// Serif font. Contains mostly ASCII range. Used in game for job names.
|
||||
/// </summary>
|
||||
Jupiter16,
|
||||
|
||||
/// <summary>
|
||||
/// Jupiter (20pt)
|
||||
///
|
||||
/// Serif font. Contains mostly ASCII range. Used in game for job names.
|
||||
/// </summary>
|
||||
Jupiter20,
|
||||
|
||||
/// <summary>
|
||||
/// Jupiter (23pt)
|
||||
///
|
||||
/// Serif font. Contains mostly ASCII range. Used in game for job names.
|
||||
/// </summary>
|
||||
Jupiter23,
|
||||
|
||||
/// <summary>
|
||||
/// Jupiter (45pt)
|
||||
///
|
||||
/// Serif font. Contains mostly numbers. Used in game for flying texts.
|
||||
/// </summary>
|
||||
Jupiter45,
|
||||
|
||||
/// <summary>
|
||||
/// Jupiter (46pt)
|
||||
///
|
||||
/// Serif font. Contains mostly ASCII range. Used in game for job names.
|
||||
/// </summary>
|
||||
Jupiter46,
|
||||
|
||||
/// <summary>
|
||||
/// Jupiter (90pt)
|
||||
///
|
||||
/// Serif font. Contains mostly numbers. Used in game for flying texts.
|
||||
/// </summary>
|
||||
Jupiter90,
|
||||
|
||||
/// <summary>
|
||||
/// Meidinger (16pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly numbers. Used in game for HP/MP/IL stuff.
|
||||
/// </summary>
|
||||
Meidinger16,
|
||||
|
||||
/// <summary>
|
||||
/// Meidinger (20pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly numbers. Used in game for HP/MP/IL stuff.
|
||||
/// </summary>
|
||||
Meidinger20,
|
||||
|
||||
/// <summary>
|
||||
/// Meidinger (40pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly numbers. Used in game for HP/MP/IL stuff.
|
||||
/// </summary>
|
||||
Meidinger40,
|
||||
|
||||
/// <summary>
|
||||
/// MiedingerMid (10pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly ASCII range.
|
||||
/// </summary>
|
||||
MiedingerMid10,
|
||||
|
||||
/// <summary>
|
||||
/// MiedingerMid (12pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly ASCII range.
|
||||
/// </summary>
|
||||
MiedingerMid12,
|
||||
|
||||
/// <summary>
|
||||
/// MiedingerMid (14pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly ASCII range.
|
||||
/// </summary>
|
||||
MiedingerMid14,
|
||||
|
||||
/// <summary>
|
||||
/// MiedingerMid (18pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly ASCII range.
|
||||
/// </summary>
|
||||
MiedingerMid18,
|
||||
|
||||
/// <summary>
|
||||
/// MiedingerMid (36pt)
|
||||
///
|
||||
/// Horizontally wide. Contains mostly ASCII range.
|
||||
/// </summary>
|
||||
MiedingerMid36,
|
||||
|
||||
/// <summary>
|
||||
/// TrumpGothic (18.4pt)
|
||||
///
|
||||
/// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
|
||||
/// </summary>
|
||||
TrumpGothic184,
|
||||
|
||||
/// <summary>
|
||||
/// TrumpGothic (23pt)
|
||||
///
|
||||
/// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
|
||||
/// </summary>
|
||||
TrumpGothic23,
|
||||
|
||||
/// <summary>
|
||||
/// TrumpGothic (34pt)
|
||||
///
|
||||
/// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
|
||||
/// </summary>
|
||||
TrumpGothic34,
|
||||
|
||||
/// <summary>
|
||||
/// TrumpGothic (688pt)
|
||||
///
|
||||
/// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
|
||||
/// </summary>
|
||||
TrumpGothic68,
|
||||
}
|
||||
}
|
||||
35
Dalamud/Interface/GameFonts/GameFontHandle.cs
Normal file
35
Dalamud/Interface/GameFonts/GameFontHandle.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.GameFonts
|
||||
{
|
||||
/// <summary>
|
||||
/// Prepare and keep game font loaded for use in OnDraw.
|
||||
/// </summary>
|
||||
public class GameFontHandle : IDisposable
|
||||
{
|
||||
private readonly GameFontManager manager;
|
||||
private readonly GameFont font;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GameFontHandle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="manager">GameFontManager instance.</param>
|
||||
/// <param name="font">Font to use.</param>
|
||||
internal GameFontHandle(GameFontManager manager, GameFont font)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font.
|
||||
/// </summary>
|
||||
/// <returns>Corresponding font or null.</returns>
|
||||
public ImFontPtr? Get() => this.manager.GetFont(this.font);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose() => this.manager.DecreaseFontRef(this.font);
|
||||
}
|
||||
}
|
||||
407
Dalamud/Interface/GameFonts/GameFontManager.cs
Normal file
407
Dalamud/Interface/GameFonts/GameFontManager.cs
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Interface.Internal;
|
||||
using ImGuiNET;
|
||||
using Lumina.Data.Files;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Interface.GameFonts
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads game font for use in ImGui.
|
||||
/// </summary>
|
||||
internal class GameFontManager : IDisposable
|
||||
{
|
||||
private static readonly string[] FontNames =
|
||||
{
|
||||
null,
|
||||
"AXIS_96", "AXIS_12", "AXIS_14", "AXIS_18", "AXIS_36",
|
||||
"Jupiter_16", "Jupiter_20", "Jupiter_23", "Jupiter_45", "Jupiter_46", "Jupiter_90",
|
||||
"Meidinger_16", "Meidinger_20", "Meidinger_40",
|
||||
"MiedingerMid_10", "MiedingerMid_12", "MiedingerMid_14", "MiedingerMid_18", "MiedingerMid_36",
|
||||
"TrumpGothic_184", "TrumpGothic_23", "TrumpGothic_34", "TrumpGothic_68",
|
||||
};
|
||||
|
||||
private readonly object syncRoot = new();
|
||||
|
||||
private readonly InterfaceManager interfaceManager;
|
||||
|
||||
private readonly FdtReader?[] fdts;
|
||||
private readonly List<byte[]> texturePixels;
|
||||
private readonly ImFontPtr?[] fonts = new ImFontPtr?[FontNames.Length];
|
||||
|
||||
private readonly int[] fontUseCounter = new int[FontNames.Length];
|
||||
private readonly List<Dictionary<char, Tuple<int, FdtReader.FontTableEntry>>> glyphRectIds = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GameFontManager"/> class.
|
||||
/// </summary>
|
||||
public GameFontManager()
|
||||
{
|
||||
var dataManager = Service<DataManager>.Get();
|
||||
|
||||
this.fdts = FontNames.Select(fontName =>
|
||||
{
|
||||
var file = fontName == null ? null : dataManager.GetFile($"common/font/{fontName}.fdt");
|
||||
return file == null ? null : new FdtReader(file!.Data);
|
||||
}).ToArray();
|
||||
this.texturePixels = Enumerable.Range(1, 1 + this.fdts.Where(x => x != null).Select(x => x.Glyphs.Select(x => x.TextureFileIndex).Max()).Max()).Select(x => dataManager.GameData.GetFile<TexFile>($"common/font/font{x}.tex").ImageData).ToList();
|
||||
|
||||
this.interfaceManager = Service<InterfaceManager>.Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describe font into a string.
|
||||
/// </summary>
|
||||
/// <param name="font">Font to describe.</param>
|
||||
/// <returns>A string in a form of "FontName (NNNpt)".</returns>
|
||||
public static string DescribeFont(GameFont font)
|
||||
{
|
||||
return font switch
|
||||
{
|
||||
GameFont.Undefined => "-",
|
||||
GameFont.Axis96 => "AXIS (9.6pt)",
|
||||
GameFont.Axis12 => "AXIS (12pt)",
|
||||
GameFont.Axis14 => "AXIS (14pt)",
|
||||
GameFont.Axis18 => "AXIS (18pt)",
|
||||
GameFont.Axis36 => "AXIS (36pt)",
|
||||
GameFont.Jupiter16 => "Jupiter (16pt)",
|
||||
GameFont.Jupiter20 => "Jupiter (20pt)",
|
||||
GameFont.Jupiter23 => "Jupiter (23pt)",
|
||||
GameFont.Jupiter45 => "Jupiter Numeric (45pt)",
|
||||
GameFont.Jupiter46 => "Jupiter (46pt)",
|
||||
GameFont.Jupiter90 => "Jupiter Numeric (90pt)",
|
||||
GameFont.Meidinger16 => "Meidinger Numeric (16pt)",
|
||||
GameFont.Meidinger20 => "Meidinger Numeric (20pt)",
|
||||
GameFont.Meidinger40 => "Meidinger Numeric (40pt)",
|
||||
GameFont.MiedingerMid10 => "MiedingerMid (10pt)",
|
||||
GameFont.MiedingerMid12 => "MiedingerMid (12pt)",
|
||||
GameFont.MiedingerMid14 => "MiedingerMid (14pt)",
|
||||
GameFont.MiedingerMid18 => "MiedingerMid (18pt)",
|
||||
GameFont.MiedingerMid36 => "MiedingerMid (36pt)",
|
||||
GameFont.TrumpGothic184 => "Trump Gothic (18.4pt)",
|
||||
GameFont.TrumpGothic23 => "Trump Gothic (23pt)",
|
||||
GameFont.TrumpGothic34 => "Trump Gothic (34pt)",
|
||||
GameFont.TrumpGothic68 => "Trump Gothic (68pt)",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(font), font, "Invalid argument"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a font should be able to display most of stuff.
|
||||
/// </summary>
|
||||
/// <param name="font">Font to check.</param>
|
||||
/// <returns>True if it can.</returns>
|
||||
public static bool IsGenericPurposeFont(GameFont font)
|
||||
{
|
||||
return font switch
|
||||
{
|
||||
GameFont.Axis96 => true,
|
||||
GameFont.Axis12 => true,
|
||||
GameFont.Axis14 => true,
|
||||
GameFont.Axis18 => true,
|
||||
GameFont.Axis36 => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills missing glyphs in target font from source font, if both are not null.
|
||||
/// </summary>
|
||||
/// <param name="source">Source font.</param>
|
||||
/// <param name="target">Target font.</param>
|
||||
/// <param name="missingOnly">Whether to copy missing glyphs only.</param>
|
||||
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||
public static void CopyGlyphsAcrossFonts(ImFontPtr? source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable)
|
||||
{
|
||||
if (!source.HasValue || !target.HasValue)
|
||||
return;
|
||||
|
||||
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 < 32 || glyph->Codepoint >= 0xFFFF)
|
||||
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,
|
||||
glyph->Y0,
|
||||
glyph->X0 + ((glyph->X1 - glyph->X0) * target.Value!.FontSize / source.Value!.FontSize),
|
||||
glyph->Y0 + ((glyph->Y1 - glyph->Y0) * target.Value!.FontSize / source.Value!.FontSize),
|
||||
glyph->U0,
|
||||
glyph->V0,
|
||||
glyph->U1,
|
||||
glyph->V1,
|
||||
glyph->AdvanceX * target.Value!.FontSize / source.Value!.FontSize);
|
||||
}
|
||||
else if (!missingOnly)
|
||||
{
|
||||
prevGlyphPtr->X0 = glyph->X0;
|
||||
prevGlyphPtr->Y0 = glyph->Y0;
|
||||
prevGlyphPtr->X1 = glyph->X0 + ((glyph->X1 - glyph->X0) * target.Value!.FontSize / source.Value!.FontSize);
|
||||
prevGlyphPtr->Y1 = glyph->Y0 + ((glyph->Y1 - glyph->Y0) * target.Value!.FontSize / source.Value!.FontSize);
|
||||
prevGlyphPtr->U0 = glyph->U0;
|
||||
prevGlyphPtr->V0 = glyph->V0;
|
||||
prevGlyphPtr->U1 = glyph->U1;
|
||||
prevGlyphPtr->V1 = glyph->V1;
|
||||
prevGlyphPtr->AdvanceX = glyph->AdvanceX * target.Value!.FontSize / source.Value!.FontSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rebuildLookupTable)
|
||||
target.Value!.BuildLookupTable();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GameFontHandle, and increases internal font reference counter, and if it's first time use, then the font will be loaded on next font building process.
|
||||
/// </summary>
|
||||
/// <param name="gameFont">Font to use.</param>
|
||||
/// <returns>Handle to game font that may or may not be ready yet.</returns>
|
||||
public GameFontHandle NewFontRef(GameFont gameFont)
|
||||
{
|
||||
var fontIndex = (int)gameFont;
|
||||
var needRebuild = false;
|
||||
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
var prev = this.fontUseCounter[fontIndex] == 0;
|
||||
this.fontUseCounter[fontIndex] += 1;
|
||||
needRebuild = prev != (this.fontUseCounter[fontIndex] == 0);
|
||||
}
|
||||
|
||||
if (needRebuild)
|
||||
this.interfaceManager.RebuildFonts();
|
||||
|
||||
return new(this, gameFont);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font.
|
||||
/// </summary>
|
||||
/// <param name="gameFont">Font to get.</param>
|
||||
/// <returns>Corresponding font or null.</returns>
|
||||
public ImFontPtr? GetFont(GameFont gameFont) => this.fonts[(int)gameFont];
|
||||
|
||||
/// <summary>
|
||||
/// Fills missing glyphs in target font from source font, if both are not null.
|
||||
/// </summary>
|
||||
/// <param name="source">Source font.</param>
|
||||
/// <param name="target">Target font.</param>
|
||||
/// <param name="missingOnly">Whether to copy missing glyphs only.</param>
|
||||
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||
public void CopyGlyphsAcrossFonts(ImFontPtr? source, GameFont target, bool missingOnly, bool rebuildLookupTable)
|
||||
{
|
||||
GameFontManager.CopyGlyphsAcrossFonts(source, this.fonts[(int)target], missingOnly, rebuildLookupTable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills missing glyphs in target font from source font, if both are not null.
|
||||
/// </summary>
|
||||
/// <param name="source">Source font.</param>
|
||||
/// <param name="target">Target font.</param>
|
||||
/// <param name="missingOnly">Whether to copy missing glyphs only.</param>
|
||||
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||
public void CopyGlyphsAcrossFonts(GameFont source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable)
|
||||
{
|
||||
GameFontManager.CopyGlyphsAcrossFonts(this.fonts[(int)source], target, missingOnly, rebuildLookupTable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills missing glyphs in target font from source font, if both are not null.
|
||||
/// </summary>
|
||||
/// <param name="source">Source font.</param>
|
||||
/// <param name="target">Target font.</param>
|
||||
/// <param name="missingOnly">Whether to copy missing glyphs only.</param>
|
||||
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||
public void CopyGlyphsAcrossFonts(GameFont source, GameFont target, bool missingOnly, bool rebuildLookupTable)
|
||||
{
|
||||
GameFontManager.CopyGlyphsAcrossFonts(this.fonts[(int)source], this.fonts[(int)target], missingOnly, rebuildLookupTable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build fonts before plugins do something more. To be called from InterfaceManager.
|
||||
/// </summary>
|
||||
public void BuildFonts()
|
||||
{
|
||||
this.glyphRectIds.Clear();
|
||||
var io = ImGui.GetIO();
|
||||
io.Fonts.TexDesiredWidth = 4096;
|
||||
|
||||
for (var i = 0; i < FontNames.Length; i++)
|
||||
{
|
||||
this.fonts[i] = null;
|
||||
this.glyphRectIds.Add(new());
|
||||
|
||||
var fdt = this.fdts[i];
|
||||
if (this.fontUseCounter[i] == 0 || fdt == null)
|
||||
continue;
|
||||
|
||||
Log.Information($"GameFontManager BuildFont: {FontNames[i]}");
|
||||
|
||||
var font = io.Fonts.AddFontDefault();
|
||||
this.fonts[i] = font;
|
||||
foreach (var glyph in fdt.Glyphs)
|
||||
{
|
||||
var c = glyph.Char;
|
||||
if (c < 32 || c >= 0xFFFF)
|
||||
continue;
|
||||
|
||||
this.glyphRectIds[i][c] = Tuple.Create(io.Fonts.AddCustomRectFontGlyph(font, c, glyph.BoundingWidth + 1, glyph.BoundingHeight + 1, glyph.BoundingWidth + glyph.NextOffsetX, new Vector2(0, glyph.CurrentOffsetY)), glyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post-build fonts before plugins do something more. To be called from InterfaceManager.
|
||||
/// </summary>
|
||||
public unsafe void AfterBuildFonts()
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
io.Fonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height);
|
||||
var pixels32 = (uint*)pixels8;
|
||||
|
||||
for (var i = 0; i < this.fonts.Length; i++)
|
||||
{
|
||||
if (!this.fonts[i].HasValue)
|
||||
continue;
|
||||
|
||||
var font = this.fonts[i]!.Value;
|
||||
var fdt = this.fdts[i];
|
||||
var fontPtr = font.NativePtr;
|
||||
fontPtr->ConfigData->SizePixels = fontPtr->FontSize = fdt.FontHeader.LineHeight;
|
||||
fontPtr->Ascent = fdt.FontHeader.Ascent;
|
||||
fontPtr->Descent = fdt.FontHeader.Descent;
|
||||
fontPtr->EllipsisChar = '…';
|
||||
foreach (var fallbackCharCandidate in "〓?!")
|
||||
{
|
||||
var glyph = font.FindGlyphNoFallback(fallbackCharCandidate);
|
||||
if ((IntPtr)glyph.NativePtr != IntPtr.Zero)
|
||||
{
|
||||
font.SetFallbackChar(fallbackCharCandidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fixed (char* c = FontNames[i])
|
||||
{
|
||||
for (var j = 0; j < 40; j++)
|
||||
fontPtr->ConfigData->Name[j] = 0;
|
||||
Encoding.UTF8.GetBytes(c, FontNames[i].Length, fontPtr->ConfigData->Name, 40);
|
||||
}
|
||||
|
||||
foreach (var (c, (rectId, glyph)) in this.glyphRectIds[i])
|
||||
{
|
||||
var rc = io.Fonts.GetCustomRectByIndex(rectId);
|
||||
var sourceBuffer = this.texturePixels[glyph.TextureFileIndex];
|
||||
var sourceBufferDelta = glyph.TextureChannelByteIndex;
|
||||
for (var y = 0; y < glyph.BoundingHeight; y++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis96, true, false);
|
||||
this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis12, true, false);
|
||||
this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis14, true, false);
|
||||
this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis18, true, false);
|
||||
this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis36, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.Jupiter16, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter20, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter23, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter45, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter46, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter90, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.Meidinger16, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Meidinger20, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Meidinger40, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis96, GameFont.MiedingerMid10, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis12, GameFont.MiedingerMid12, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis14, GameFont.MiedingerMid14, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.MiedingerMid18, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.MiedingerMid36, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.TrumpGothic184, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.TrumpGothic23, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.TrumpGothic34, true, false);
|
||||
this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.TrumpGothic68, true, false);
|
||||
|
||||
foreach (var font in this.fonts)
|
||||
font?.BuildLookupTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrease font reference counter and release if nobody is using it.
|
||||
/// </summary>
|
||||
/// <param name="gameFont">Font to release.</param>
|
||||
internal void DecreaseFontRef(GameFont gameFont)
|
||||
{
|
||||
var fontIndex = (int)gameFont;
|
||||
var needRebuild = false;
|
||||
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
var prev = this.fontUseCounter[fontIndex] == 0;
|
||||
this.fontUseCounter[fontIndex] -= 1;
|
||||
needRebuild = prev != (this.fontUseCounter[fontIndex] == 0);
|
||||
}
|
||||
|
||||
if (needRebuild)
|
||||
this.interfaceManager.RebuildFonts();
|
||||
}
|
||||
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ using Dalamud.Game.Gui.Internal;
|
|||
using Dalamud.Game.Internal.DXGI;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Hooking.Internal;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Internal.Windows.StyleEditor;
|
||||
|
|
@ -56,6 +57,9 @@ namespace Dalamud.Interface.Internal
|
|||
private readonly SwapChainVtableResolver address;
|
||||
private RawDX11Scene? scene;
|
||||
|
||||
private GameFont overwriteDefaultFontFromGameFont = GameFont.Undefined;
|
||||
private GameFontHandle? overwriteDefaultFontFromGameFontHandle;
|
||||
|
||||
// can't access imgui IO before first present call
|
||||
private bool lastWantCapture = false;
|
||||
private bool isRebuildingFonts = false;
|
||||
|
|
@ -128,10 +132,15 @@ namespace Dalamud.Interface.Internal
|
|||
public event Action ResizeBuffers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that is executed when fonts are rebuilt.
|
||||
/// Gets or sets an action that is executed right before fonts are rebuilt.
|
||||
/// </summary>
|
||||
public event Action BuildFonts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that is executed right after fonts are rebuilt.
|
||||
/// </summary>
|
||||
public event Action AfterBuildFonts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default ImGui font.
|
||||
/// </summary>
|
||||
|
|
@ -304,6 +313,16 @@ namespace Dalamud.Interface.Internal
|
|||
if (!this.isRebuildingFonts)
|
||||
{
|
||||
Log.Verbose("[FONT] RebuildFonts() trigger");
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
if (this.overwriteDefaultFontFromGameFont != configuration.DefaultFontFromGame)
|
||||
{
|
||||
this.overwriteDefaultFontFromGameFont = configuration.DefaultFontFromGame;
|
||||
this.overwriteDefaultFontFromGameFontHandle?.Dispose();
|
||||
if (configuration.DefaultFontFromGame == GameFont.Undefined)
|
||||
this.overwriteDefaultFontFromGameFontHandle = null;
|
||||
else
|
||||
this.overwriteDefaultFontFromGameFontHandle = Service<GameFontManager>.Get().NewFontRef(configuration.DefaultFontFromGame);
|
||||
}
|
||||
|
||||
this.isRebuildingFonts = true;
|
||||
this.scene.OnNewRenderFrame += this.RebuildFontsInternal;
|
||||
|
|
@ -384,6 +403,16 @@ namespace Dalamud.Interface.Internal
|
|||
this.scene.OnBuildUI += this.Display;
|
||||
this.scene.OnNewInputFrame += this.OnNewInputFrame;
|
||||
|
||||
if (this.overwriteDefaultFontFromGameFont != configuration.DefaultFontFromGame)
|
||||
{
|
||||
this.overwriteDefaultFontFromGameFont = configuration.DefaultFontFromGame;
|
||||
this.overwriteDefaultFontFromGameFontHandle?.Dispose();
|
||||
if (configuration.DefaultFontFromGame == GameFont.Undefined)
|
||||
this.overwriteDefaultFontFromGameFontHandle = null;
|
||||
else
|
||||
this.overwriteDefaultFontFromGameFontHandle = Service<GameFontManager>.Get().NewFontRef(configuration.DefaultFontFromGame);
|
||||
}
|
||||
|
||||
this.SetupFonts();
|
||||
|
||||
StyleModel.TransferOldModels();
|
||||
|
|
@ -496,7 +525,6 @@ namespace Dalamud.Interface.Internal
|
|||
ImGui.GetIO().Fonts.Clear();
|
||||
|
||||
ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig();
|
||||
fontConfig.MergeMode = true;
|
||||
fontConfig.PixelSnapH = true;
|
||||
|
||||
var fontPathJp = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "NotoSansCJKjp-Medium.otf");
|
||||
|
|
@ -522,7 +550,9 @@ namespace Dalamud.Interface.Internal
|
|||
},
|
||||
GCHandleType.Pinned);
|
||||
|
||||
fontConfig.MergeMode = false;
|
||||
ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, gameRangeHandle.AddrOfPinnedObject());
|
||||
fontConfig.MergeMode = true;
|
||||
|
||||
var fontPathIcon = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "FontAwesome5FreeSolid.otf");
|
||||
|
||||
|
|
@ -546,6 +576,9 @@ namespace Dalamud.Interface.Internal
|
|||
|
||||
MonoFont = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathMono, 16.0f);
|
||||
|
||||
var gameFontManager = Service<GameFontManager>.Get();
|
||||
gameFontManager.BuildFonts();
|
||||
|
||||
Log.Verbose("[FONT] Invoke OnBuildFonts");
|
||||
this.BuildFonts?.Invoke();
|
||||
Log.Verbose("[FONT] OnBuildFonts OK!");
|
||||
|
|
@ -557,6 +590,13 @@ namespace Dalamud.Interface.Internal
|
|||
|
||||
ImGui.GetIO().Fonts.Build();
|
||||
|
||||
gameFontManager.AfterBuildFonts();
|
||||
GameFontManager.CopyGlyphsAcrossFonts(this.overwriteDefaultFontFromGameFontHandle?.Get(), DefaultFont, false, true);
|
||||
|
||||
Log.Verbose("[FONT] Invoke OnAfterBuildFonts");
|
||||
this.AfterBuildFonts?.Invoke();
|
||||
Log.Verbose("[FONT] OnAfterBuildFonts OK!");
|
||||
|
||||
Log.Verbose("[FONT] Fonts built!");
|
||||
|
||||
this.fontBuildSignal.Set();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Dalamud.Game.Gui.Dtr;
|
|||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -27,6 +28,8 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
private const float MinScale = 0.3f;
|
||||
private const float MaxScale = 2.0f;
|
||||
|
||||
private readonly List<GameFont> validFontChoices;
|
||||
private readonly string[] validFontNames;
|
||||
private readonly string[] languages;
|
||||
private readonly string[] locLanguages;
|
||||
private int langIndex;
|
||||
|
|
@ -66,6 +69,8 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
private bool doButtonsSystemMenu;
|
||||
private bool disableRmtFiltering;
|
||||
|
||||
private int validFontIndex;
|
||||
|
||||
#region Experimental
|
||||
|
||||
private bool doPluginTest;
|
||||
|
|
@ -111,6 +116,10 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
this.doButtonsSystemMenu = configuration.DoButtonsSystemMenu;
|
||||
this.disableRmtFiltering = configuration.DisableRmtFiltering;
|
||||
|
||||
this.validFontChoices = Enum.GetValues<GameFont>().Where(x => x == GameFont.Undefined || GameFontManager.IsGenericPurposeFont(x)).ToList();
|
||||
this.validFontNames = this.validFontChoices.Select(x => GameFontManager.DescribeFont(x)).ToArray();
|
||||
this.validFontIndex = Math.Max(0, this.validFontChoices.IndexOf(configuration.DefaultFontFromGame));
|
||||
|
||||
this.languages = Localization.ApplicableLangCodes.Prepend("en").ToArray();
|
||||
try
|
||||
{
|
||||
|
|
@ -286,6 +295,11 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
|
||||
ImGuiHelpers.ScaledDummy(10, 16);
|
||||
|
||||
ImGui.Text(Loc.Localize("DalamudSettingsGlobalFont", "Global Font"));
|
||||
ImGui.Combo("##DalamudSettingsGlobalFontDrag", ref this.validFontIndex, this.validFontNames, this.validFontNames.Length);
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10, 16);
|
||||
|
||||
if (ImGui.Button(Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor")))
|
||||
{
|
||||
Service<DalamudInterface>.Get().OpenStyleEditor();
|
||||
|
|
@ -800,6 +814,8 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
configuration.IsFocusManagementEnabled = this.doFocus;
|
||||
configuration.ShowTsm = this.doTsm;
|
||||
|
||||
configuration.DefaultFontFromGame = this.validFontChoices[this.validFontIndex];
|
||||
|
||||
// This is applied every frame in InterfaceManager::CheckViewportState()
|
||||
configuration.IsDisableViewport = !this.doViewport;
|
||||
|
||||
|
|
@ -842,6 +858,8 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
|
||||
configuration.Save();
|
||||
|
||||
Service<InterfaceManager>.Get().RebuildFonts();
|
||||
|
||||
_ = Service<PluginManager>.Get().ReloadPluginMastersAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Diagnostics;
|
|||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
|
|
@ -39,6 +40,7 @@ namespace Dalamud.Interface
|
|||
var interfaceManager = Service<InterfaceManager>.Get();
|
||||
interfaceManager.Draw += this.OnDraw;
|
||||
interfaceManager.BuildFonts += this.OnBuildFonts;
|
||||
interfaceManager.AfterBuildFonts += this.OnAfterBuildFonts;
|
||||
interfaceManager.ResizeBuffers += this.OnResizeBuffers;
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +69,15 @@ namespace Dalamud.Interface
|
|||
/// </summary>
|
||||
public event Action BuildFonts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that is called any time right after ImGui fonts are rebuilt.<br/>
|
||||
/// Any ImFontPtr objects that you store <strong>can be invalidated</strong> when fonts are rebuilt
|
||||
/// (at any time), so you should both reload your custom fonts and restore those
|
||||
/// pointers inside this handler.<br/>
|
||||
/// <strong>PLEASE remove this handler inside Dispose, or when you no longer need your fonts!</strong>
|
||||
/// </summary>
|
||||
public event Action AfterBuildFonts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default Dalamud font based on Noto Sans CJK Medium in 17pt - supporting all game languages and icons.
|
||||
/// </summary>
|
||||
|
|
@ -201,6 +212,13 @@ namespace Dalamud.Interface
|
|||
public TextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels)
|
||||
=> Service<InterfaceManager>.Get().LoadImageRaw(imageData, width, height, numChannels);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a game font.
|
||||
/// </summary>
|
||||
/// <param name="gameFont">Font to get.</param>
|
||||
/// <returns>Handle to the game font which may or may not be available for use yet.</returns>
|
||||
public GameFontHandle GetGameFontHandle(GameFont gameFont) => Service<GameFontManager>.Get().NewFontRef(gameFont);
|
||||
|
||||
/// <summary>
|
||||
/// Call this to queue a rebuild of the font atlas.<br/>
|
||||
/// This will invoke any <see cref="OnBuildFonts"/> handlers and ensure that any loaded fonts are
|
||||
|
|
@ -320,6 +338,11 @@ namespace Dalamud.Interface
|
|||
this.BuildFonts?.Invoke();
|
||||
}
|
||||
|
||||
private void OnAfterBuildFonts()
|
||||
{
|
||||
this.AfterBuildFonts?.Invoke();
|
||||
}
|
||||
|
||||
private void OnResizeBuffers()
|
||||
{
|
||||
this.ResizeBuffers?.Invoke();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue