mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-27 02:49:18 +01:00
Merge pull request #782 from Soreepeong/feature/nicer-fonts
This commit is contained in:
commit
b2107c6887
8 changed files with 574 additions and 186 deletions
|
|
@ -141,7 +141,12 @@ namespace Dalamud.Configuration.Internal
|
||||||
/// * ...TTF fonts loaded with stb or FreeType are in linear space.
|
/// * ...TTF fonts loaded with stb or FreeType are in linear space.
|
||||||
/// * ...the game's prebaked AXIS fonts are in gamma space with gamma value of 1.4.
|
/// * ...the game's prebaked AXIS fonts are in gamma space with gamma value of 1.4.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float FontGamma { get; set; } = 1.0f;
|
public float FontGamma { get; set; } = 1.4f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to allow big font atlas.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowBigFontAtlas { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
|
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
|
||||||
|
|
|
||||||
|
|
@ -117,18 +117,21 @@ namespace Dalamud.Interface.GameFonts
|
||||||
/// <param name="target">Target font.</param>
|
/// <param name="target">Target font.</param>
|
||||||
/// <param name="missingOnly">Whether to copy missing glyphs only.</param>
|
/// <param name="missingOnly">Whether to copy missing glyphs only.</param>
|
||||||
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||||
public static void CopyGlyphsAcrossFonts(ImFontPtr? source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable)
|
/// <param name="rangeLow">Low codepoint range to copy.</param>
|
||||||
|
/// <param name="rangeHigh">High codepoing range to copy.</param>
|
||||||
|
public static void CopyGlyphsAcrossFonts(ImFontPtr? source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable, int rangeLow = 32, int rangeHigh = 0xFFFE)
|
||||||
{
|
{
|
||||||
if (!source.HasValue || !target.HasValue)
|
if (!source.HasValue || !target.HasValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var scale = target.Value!.FontSize / source.Value!.FontSize;
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
var glyphs = (ImFontGlyphReal*)source.Value!.Glyphs.Data;
|
var glyphs = (ImFontGlyphReal*)source.Value!.Glyphs.Data;
|
||||||
for (int j = 0, j_ = source.Value!.Glyphs.Size; j < j_; j++)
|
for (int j = 0, j_ = source.Value!.Glyphs.Size; j < j_; j++)
|
||||||
{
|
{
|
||||||
var glyph = &glyphs[j];
|
var glyph = &glyphs[j];
|
||||||
if (glyph->Codepoint < 32 || glyph->Codepoint >= 0xFFFF)
|
if (glyph->Codepoint < rangeLow || glyph->Codepoint > rangeHigh)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var prevGlyphPtr = (ImFontGlyphReal*)target.Value!.FindGlyphNoFallback((ushort)glyph->Codepoint).NativePtr;
|
var prevGlyphPtr = (ImFontGlyphReal*)target.Value!.FindGlyphNoFallback((ushort)glyph->Codepoint).NativePtr;
|
||||||
|
|
@ -137,27 +140,27 @@ namespace Dalamud.Interface.GameFonts
|
||||||
target.Value!.AddGlyph(
|
target.Value!.AddGlyph(
|
||||||
target.Value!.ConfigData,
|
target.Value!.ConfigData,
|
||||||
(ushort)glyph->Codepoint,
|
(ushort)glyph->Codepoint,
|
||||||
glyph->X0,
|
glyph->X0 * scale,
|
||||||
glyph->Y0,
|
((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent,
|
||||||
glyph->X0 + ((glyph->X1 - glyph->X0) * target.Value!.FontSize / source.Value!.FontSize),
|
glyph->X1 * scale,
|
||||||
glyph->Y0 + ((glyph->Y1 - glyph->Y0) * target.Value!.FontSize / source.Value!.FontSize),
|
((glyph->Y1 - source.Value!.Ascent) * scale) + target.Value!.Ascent,
|
||||||
glyph->U0,
|
glyph->U0,
|
||||||
glyph->V0,
|
glyph->V0,
|
||||||
glyph->U1,
|
glyph->U1,
|
||||||
glyph->V1,
|
glyph->V1,
|
||||||
glyph->AdvanceX * target.Value!.FontSize / source.Value!.FontSize);
|
glyph->AdvanceX * scale);
|
||||||
}
|
}
|
||||||
else if (!missingOnly)
|
else if (!missingOnly)
|
||||||
{
|
{
|
||||||
prevGlyphPtr->X0 = glyph->X0;
|
prevGlyphPtr->X0 = glyph->X0 * scale;
|
||||||
prevGlyphPtr->Y0 = glyph->Y0;
|
prevGlyphPtr->Y0 = ((glyph->Y0 - source.Value!.Ascent) * scale) + target.Value!.Ascent;
|
||||||
prevGlyphPtr->X1 = glyph->X0 + ((glyph->X1 - glyph->X0) * target.Value!.FontSize / source.Value!.FontSize);
|
prevGlyphPtr->X1 = glyph->X1 * scale;
|
||||||
prevGlyphPtr->Y1 = glyph->Y0 + ((glyph->Y1 - glyph->Y0) * target.Value!.FontSize / source.Value!.FontSize);
|
prevGlyphPtr->Y1 = ((glyph->Y1 - source.Value!.Ascent) * scale) + target.Value!.Ascent;
|
||||||
prevGlyphPtr->U0 = glyph->U0;
|
prevGlyphPtr->U0 = glyph->U0;
|
||||||
prevGlyphPtr->V0 = glyph->V0;
|
prevGlyphPtr->V0 = glyph->V0;
|
||||||
prevGlyphPtr->U1 = glyph->U1;
|
prevGlyphPtr->U1 = glyph->U1;
|
||||||
prevGlyphPtr->V1 = glyph->V1;
|
prevGlyphPtr->V1 = glyph->V1;
|
||||||
prevGlyphPtr->AdvanceX = glyph->AdvanceX * target.Value!.FontSize / source.Value!.FontSize;
|
prevGlyphPtr->AdvanceX = glyph->AdvanceX * scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +169,39 @@ namespace Dalamud.Interface.GameFonts
|
||||||
target.Value!.BuildLookupTable();
|
target.Value!.BuildLookupTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unscales fonts after they have been rendered onto atlas.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fontPtr">Font to unscale.</param>
|
||||||
|
/// <param name="fontScale">Scale factor.</param>
|
||||||
|
/// <param name="rebuildLookupTable">Whether to call target.BuildLookupTable().</param>
|
||||||
|
public static void UnscaleFont(ImFontPtr fontPtr, float fontScale, bool rebuildLookupTable = true)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
font->FontSize /= fontScale;
|
||||||
|
font->Ascent /= fontScale;
|
||||||
|
font->Descent /= fontScale;
|
||||||
|
var glyphs = (ImFontGlyphReal*)font->Glyphs.Data;
|
||||||
|
for (int i = 0, i_ = font->Glyphs.Size; i < i_; i++)
|
||||||
|
{
|
||||||
|
var glyph = &glyphs[i];
|
||||||
|
glyph->X0 /= fontScale;
|
||||||
|
glyph->X1 /= fontScale;
|
||||||
|
glyph->Y0 /= fontScale;
|
||||||
|
glyph->Y1 /= fontScale;
|
||||||
|
glyph->AdvanceX /= fontScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rebuildLookupTable)
|
||||||
|
fontPtr.BuildLookupTable();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
@ -248,39 +284,48 @@ namespace Dalamud.Interface.GameFonts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BuildFonts()
|
public void BuildFonts()
|
||||||
{
|
{
|
||||||
var io = ImGui.GetIO();
|
unsafe
|
||||||
io.Fonts.TexDesiredWidth = 4096;
|
|
||||||
|
|
||||||
this.glyphRectIds.Clear();
|
|
||||||
this.fonts.Clear();
|
|
||||||
|
|
||||||
foreach (var style in this.fontUseCounter.Keys)
|
|
||||||
{
|
{
|
||||||
var rectIds = this.glyphRectIds[style] = new();
|
ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig();
|
||||||
|
fontConfig.OversampleH = 1;
|
||||||
|
fontConfig.OversampleV = 1;
|
||||||
|
fontConfig.PixelSnapH = true;
|
||||||
|
|
||||||
var fdt = this.fdts[(int)style.FamilyAndSize];
|
var io = ImGui.GetIO();
|
||||||
if (fdt == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var font = io.Fonts.AddFontDefault();
|
this.glyphRectIds.Clear();
|
||||||
this.fonts[style] = font;
|
this.fonts.Clear();
|
||||||
foreach (var glyph in fdt.Glyphs)
|
|
||||||
|
foreach (var style in this.fontUseCounter.Keys)
|
||||||
{
|
{
|
||||||
var c = glyph.Char;
|
var rectIds = this.glyphRectIds[style] = new();
|
||||||
if (c < 32 || c >= 0xFFFF)
|
|
||||||
|
var fdt = this.fdts[(int)style.FamilyAndSize];
|
||||||
|
if (fdt == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var widthAdjustment = style.CalculateWidthAdjustment(fdt, glyph);
|
var font = io.Fonts.AddFontDefault(fontConfig);
|
||||||
rectIds[c] = Tuple.Create(
|
this.fonts[style] = font;
|
||||||
io.Fonts.AddCustomRectFontGlyph(
|
foreach (var glyph in fdt.Glyphs)
|
||||||
font,
|
{
|
||||||
c,
|
var c = glyph.Char;
|
||||||
glyph.BoundingWidth + widthAdjustment + 1,
|
if (c < 32 || c >= 0xFFFF)
|
||||||
glyph.BoundingHeight + 1,
|
continue;
|
||||||
glyph.AdvanceWidth,
|
|
||||||
new Vector2(0, glyph.CurrentOffsetY)),
|
var widthAdjustment = style.CalculateWidthAdjustment(fdt, glyph);
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +343,7 @@ namespace Dalamud.Interface.GameFonts
|
||||||
{
|
{
|
||||||
var fdt = this.fdts[(int)style.FamilyAndSize];
|
var fdt = this.fdts[(int)style.FamilyAndSize];
|
||||||
var fontPtr = font.NativePtr;
|
var fontPtr = font.NativePtr;
|
||||||
fontPtr->ConfigData->SizePixels = fontPtr->FontSize = fdt.FontHeader.LineHeight;
|
fontPtr->ConfigData->SizePixels = fontPtr->FontSize = fdt.FontHeader.Size * 4 / 3;
|
||||||
fontPtr->Ascent = fdt.FontHeader.Ascent;
|
fontPtr->Ascent = fdt.FontHeader.Ascent;
|
||||||
fontPtr->Descent = fdt.FontHeader.Descent;
|
fontPtr->Descent = fdt.FontHeader.Descent;
|
||||||
fontPtr->EllipsisChar = '…';
|
fontPtr->EllipsisChar = '…';
|
||||||
|
|
@ -399,6 +444,9 @@ namespace Dalamud.Interface.GameFonts
|
||||||
{
|
{
|
||||||
lock (this.syncRoot)
|
lock (this.syncRoot)
|
||||||
{
|
{
|
||||||
|
if (!this.fontUseCounter.ContainsKey(style))
|
||||||
|
return;
|
||||||
|
|
||||||
if ((this.fontUseCounter[style] -= 1) == 0)
|
if ((this.fontUseCounter[style] -= 1) == 0)
|
||||||
this.fontUseCounter.Remove(style);
|
this.fontUseCounter.Remove(style);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,16 @@ namespace Dalamud.Interface.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class InterfaceManager : IDisposable
|
internal class InterfaceManager : IDisposable
|
||||||
{
|
{
|
||||||
|
private const float DefaultFontSizePt = 12.0f;
|
||||||
|
private const float DefaultFontSizePx = DefaultFontSizePt * 4.0f / 3.0f;
|
||||||
|
private const ushort Fallback1Codepoint = 0x3013; // Geta mark; FFXIV uses this to indicate that a glyph is missing.
|
||||||
|
private const ushort Fallback2Codepoint = '-'; // FFXIV uses dash if Geta mark is unavailable.
|
||||||
|
|
||||||
private readonly string rtssPath;
|
private readonly string rtssPath;
|
||||||
|
|
||||||
|
private readonly HashSet<SpecialGlyphRequest> glyphRequests = new();
|
||||||
|
private readonly List<GameFontHandle> axisFontHandles = new();
|
||||||
|
|
||||||
private readonly Hook<PresentDelegate> presentHook;
|
private readonly Hook<PresentDelegate> presentHook;
|
||||||
private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook;
|
private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook;
|
||||||
private readonly Hook<SetCursorDelegate> setCursorHook;
|
private readonly Hook<SetCursorDelegate> setCursorHook;
|
||||||
|
|
@ -57,8 +65,6 @@ namespace Dalamud.Interface.Internal
|
||||||
private readonly SwapChainVtableResolver address;
|
private readonly SwapChainVtableResolver address;
|
||||||
private RawDX11Scene? scene;
|
private RawDX11Scene? scene;
|
||||||
|
|
||||||
private GameFontHandle? axisFontHandle;
|
|
||||||
|
|
||||||
// can't access imgui IO before first present call
|
// can't access imgui IO before first present call
|
||||||
private bool lastWantCapture = false;
|
private bool lastWantCapture = false;
|
||||||
private bool isRebuildingFonts = false;
|
private bool isRebuildingFonts = false;
|
||||||
|
|
@ -189,6 +195,16 @@ namespace Dalamud.Interface.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsReady => this.scene != null;
|
public bool IsReady => this.scene != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to override configuration for UseAxis.
|
||||||
|
/// </summary>
|
||||||
|
public bool? UseAxisOverride { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether to use AXIS fonts.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseAxis => this.UseAxisOverride ?? Service<DalamudConfiguration>.Get().UseAxisFontsFromGame;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the overrided font gamma value, instead of using the value from configuration.
|
/// Gets or sets the overrided font gamma value, instead of using the value from configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -199,6 +215,16 @@ namespace Dalamud.Interface.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float FontGamma => Math.Max(0.1f, this.FontGammaOverride.GetValueOrDefault(Service<DalamudConfiguration>.Get().FontGamma));
|
public float FontGamma => Math.Max(0.1f, this.FontGammaOverride.GetValueOrDefault(Service<DalamudConfiguration>.Get().FontGamma));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to override configuration for AllowBigFontAtlas.
|
||||||
|
/// </summary>
|
||||||
|
public bool? AllowBigFontAtlasOverride { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether to allow big font atlas.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowBigFontAtlas => this.AllowBigFontAtlasOverride ?? Service<DalamudConfiguration>.Get().AllowBigFontAtlas;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable this module.
|
/// Enable this module.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -322,8 +348,6 @@ namespace Dalamud.Interface.Internal
|
||||||
if (!this.isRebuildingFonts)
|
if (!this.isRebuildingFonts)
|
||||||
{
|
{
|
||||||
Log.Verbose("[FONT] RebuildFonts() trigger");
|
Log.Verbose("[FONT] RebuildFonts() trigger");
|
||||||
this.SetAxisFonts();
|
|
||||||
|
|
||||||
this.isRebuildingFonts = true;
|
this.isRebuildingFonts = true;
|
||||||
this.scene.OnNewRenderFrame += this.RebuildFontsInternal;
|
this.scene.OnNewRenderFrame += this.RebuildFontsInternal;
|
||||||
}
|
}
|
||||||
|
|
@ -337,31 +361,75 @@ namespace Dalamud.Interface.Internal
|
||||||
this.fontBuildSignal.WaitOne();
|
this.fontBuildSignal.WaitOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests a default font of specified size to exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">Font size in pixels.</param>
|
||||||
|
/// <param name="ranges">Ranges of glyphs.</param>
|
||||||
|
/// <returns>Requets handle.</returns>
|
||||||
|
public SpecialGlyphRequest NewFontSizeRef(float size, List<Tuple<ushort, ushort>> ranges)
|
||||||
|
{
|
||||||
|
var allContained = false;
|
||||||
|
var fonts = ImGui.GetIO().Fonts.Fonts;
|
||||||
|
ImFontPtr foundFont = null;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
for (int i = 0, i_ = fonts.Size; i < i_; i++)
|
||||||
|
{
|
||||||
|
if (!this.glyphRequests.Any(x => x.FontInternal.NativePtr == fonts[i].NativePtr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
allContained = true;
|
||||||
|
foreach (var range in ranges)
|
||||||
|
{
|
||||||
|
if (!allContained)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (var j = range.Item1; j <= range.Item2 && allContained; j++)
|
||||||
|
allContained &= fonts[i].FindGlyphNoFallback(j).NativePtr != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allContained)
|
||||||
|
foundFont = fonts[i];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var req = new SpecialGlyphRequest(this, size, ranges);
|
||||||
|
req.FontInternal = foundFont;
|
||||||
|
|
||||||
|
if (!allContained)
|
||||||
|
this.RebuildFonts();
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests a default font of specified size to exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">Font size in pixels.</param>
|
||||||
|
/// <param name="text">Text to calculate glyph ranges from.</param>
|
||||||
|
/// <returns>Requets handle.</returns>
|
||||||
|
public SpecialGlyphRequest NewFontSizeRef(float size, string text)
|
||||||
|
{
|
||||||
|
List<Tuple<ushort, ushort>> ranges = new();
|
||||||
|
foreach (var c in new SortedSet<char>(text.ToHashSet()))
|
||||||
|
{
|
||||||
|
if (ranges.Any() && ranges[^1].Item2 + 1 == c)
|
||||||
|
ranges[^1] = Tuple.Create<ushort, ushort>(ranges[^1].Item1, c);
|
||||||
|
else
|
||||||
|
ranges.Add(Tuple.Create<ushort, ushort>(c, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.NewFontSizeRef(size, ranges);
|
||||||
|
}
|
||||||
|
|
||||||
private static void ShowFontError(string path)
|
private static void ShowFontError(string path)
|
||||||
{
|
{
|
||||||
Util.Fatal($"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}", "Error");
|
Util.Fatal($"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}", "Error");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetAxisFonts()
|
|
||||||
{
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
if (configuration.UseAxisFontsFromGame)
|
|
||||||
{
|
|
||||||
var currentFamilyAndSize = GameFontStyle.GetRecommendedFamilyAndSize(GameFontFamily.Axis, this.axisFontHandle?.Style.Size ?? 0f);
|
|
||||||
var expectedFamilyAndSize = GameFontStyle.GetRecommendedFamilyAndSize(GameFontFamily.Axis, 12 * ImGui.GetIO().FontGlobalScale);
|
|
||||||
if (currentFamilyAndSize == expectedFamilyAndSize)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.axisFontHandle?.Dispose();
|
|
||||||
this.axisFontHandle = Service<GameFontManager>.Get().NewFontRef(new(expectedFamilyAndSize));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.axisFontHandle?.Dispose();
|
|
||||||
this.axisFontHandle = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE(goat): When hooking ReShade DXGISwapChain::runtime_present, this is missing the syncInterval arg.
|
* NOTE(goat): When hooking ReShade DXGISwapChain::runtime_present, this is missing the syncInterval arg.
|
||||||
* Seems to work fine regardless, I guess, so whatever.
|
* Seems to work fine regardless, I guess, so whatever.
|
||||||
|
|
@ -423,8 +491,6 @@ namespace Dalamud.Interface.Internal
|
||||||
this.scene.OnBuildUI += this.Display;
|
this.scene.OnBuildUI += this.Display;
|
||||||
this.scene.OnNewInputFrame += this.OnNewInputFrame;
|
this.scene.OnNewInputFrame += this.OnNewInputFrame;
|
||||||
|
|
||||||
this.SetAxisFonts();
|
|
||||||
|
|
||||||
this.SetupFonts();
|
this.SetupFonts();
|
||||||
|
|
||||||
StyleModel.TransferOldModels();
|
StyleModel.TransferOldModels();
|
||||||
|
|
@ -530,105 +596,276 @@ namespace Dalamud.Interface.Internal
|
||||||
|
|
||||||
private unsafe void SetupFonts()
|
private unsafe void SetupFonts()
|
||||||
{
|
{
|
||||||
|
var gameFontManager = Service<GameFontManager>.Get();
|
||||||
var dalamud = Service<Dalamud>.Get();
|
var dalamud = Service<Dalamud>.Get();
|
||||||
var ioFonts = ImGui.GetIO().Fonts;
|
var io = ImGui.GetIO();
|
||||||
|
var ioFonts = io.Fonts;
|
||||||
|
var fontLoadScale = this.AllowBigFontAtlas ? io.FontGlobalScale : 1;
|
||||||
var fontGamma = this.FontGamma;
|
var fontGamma = this.FontGamma;
|
||||||
|
List<ImFontPtr> fontsToUnscale = new();
|
||||||
|
List<bool> fontsToOverwriteFromAxis = new();
|
||||||
|
List<float?> fontsToReassignSizes = new();
|
||||||
|
|
||||||
this.fontBuildSignal.Reset();
|
this.fontBuildSignal.Reset();
|
||||||
|
|
||||||
ioFonts.Clear();
|
ioFonts.Clear();
|
||||||
|
ioFonts.TexDesiredWidth = this.AllowBigFontAtlas ? 4096 : 2048;
|
||||||
|
|
||||||
ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig();
|
Log.Verbose("[FONT] SetupFonts - 1");
|
||||||
fontConfig.PixelSnapH = true;
|
|
||||||
|
|
||||||
var fontPathJp = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "NotoSansCJKjp-Medium.otf");
|
foreach (var v in this.axisFontHandles)
|
||||||
|
|
||||||
if (!File.Exists(fontPathJp))
|
|
||||||
ShowFontError(fontPathJp);
|
|
||||||
|
|
||||||
var japaneseRangeHandle = GCHandle.Alloc(GlyphRangesJapanese.GlyphRanges, GCHandleType.Pinned);
|
|
||||||
|
|
||||||
DefaultFont = ioFonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, japaneseRangeHandle.AddrOfPinnedObject());
|
|
||||||
|
|
||||||
var fontPathGame = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "gamesym.ttf");
|
|
||||||
|
|
||||||
if (!File.Exists(fontPathGame))
|
|
||||||
ShowFontError(fontPathGame);
|
|
||||||
|
|
||||||
var gameRangeHandle = GCHandle.Alloc(
|
|
||||||
new ushort[]
|
|
||||||
{
|
|
||||||
0xE020,
|
|
||||||
0xE0DB,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
GCHandleType.Pinned);
|
|
||||||
|
|
||||||
fontConfig.MergeMode = false;
|
|
||||||
ioFonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, gameRangeHandle.AddrOfPinnedObject());
|
|
||||||
fontConfig.MergeMode = true;
|
|
||||||
|
|
||||||
var fontPathIcon = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "FontAwesome5FreeSolid.otf");
|
|
||||||
|
|
||||||
if (!File.Exists(fontPathIcon))
|
|
||||||
ShowFontError(fontPathIcon);
|
|
||||||
|
|
||||||
var iconRangeHandle = GCHandle.Alloc(
|
|
||||||
new ushort[]
|
|
||||||
{
|
|
||||||
0xE000,
|
|
||||||
0xF8FF,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
GCHandleType.Pinned);
|
|
||||||
IconFont = ioFonts.AddFontFromFileTTF(fontPathIcon, 17.0f, null, iconRangeHandle.AddrOfPinnedObject());
|
|
||||||
|
|
||||||
var fontPathMono = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "Inconsolata-Regular.ttf");
|
|
||||||
|
|
||||||
if (!File.Exists(fontPathMono))
|
|
||||||
ShowFontError(fontPathMono);
|
|
||||||
|
|
||||||
MonoFont = ioFonts.AddFontFromFileTTF(fontPathMono, 16.0f);
|
|
||||||
|
|
||||||
var gameFontManager = Service<GameFontManager>.Get();
|
|
||||||
gameFontManager.BuildFonts();
|
|
||||||
|
|
||||||
Log.Verbose("[FONT] Invoke OnBuildFonts");
|
|
||||||
this.BuildFonts?.Invoke();
|
|
||||||
Log.Verbose("[FONT] OnBuildFonts OK!");
|
|
||||||
|
|
||||||
for (var i = 0; i < ImGui.GetIO().Fonts.Fonts.Size; i++)
|
|
||||||
{
|
{
|
||||||
Log.Verbose("{0} - {1}", i, ImGui.GetIO().Fonts.Fonts[i].GetDebugName());
|
if (v != null)
|
||||||
|
v.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
ioFonts.Build();
|
this.axisFontHandles.Clear();
|
||||||
|
|
||||||
if (Math.Abs(fontGamma - 1.0f) >= 0.001)
|
Log.Verbose("[FONT] SetupFonts - 2");
|
||||||
|
|
||||||
|
ImFontConfigPtr fontConfig = null;
|
||||||
|
List<GCHandle> garbageList = new();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// 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)
|
var dummyRangeHandle = GCHandle.Alloc(new ushort[] { '0', '0', 0 }, GCHandleType.Pinned);
|
||||||
ioFonts.GetTexDataAsRGBA32(out byte* texPixels, out var texWidth, out var texHeight);
|
garbageList.Add(dummyRangeHandle);
|
||||||
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);
|
fontConfig = ImGuiNative.ImFontConfig_ImFontConfig();
|
||||||
|
fontConfig.OversampleH = 1;
|
||||||
|
fontConfig.OversampleV = 1;
|
||||||
|
fontConfig.PixelSnapH = true;
|
||||||
|
|
||||||
|
var fontPathJp = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "NotoSansCJKjp-Medium.otf");
|
||||||
|
if (!File.Exists(fontPathJp))
|
||||||
|
ShowFontError(fontPathJp);
|
||||||
|
|
||||||
|
// Default font
|
||||||
|
Log.Verbose("[FONT] SetupFonts - Default font");
|
||||||
|
this.axisFontHandles.Add(gameFontManager.NewFontRef(this.AllowBigFontAtlas ? new(GameFontFamily.Axis, DefaultFontSizePt * fontLoadScale) : new(GameFontFamilyAndSize.Axis12)));
|
||||||
|
if (this.UseAxis)
|
||||||
|
{
|
||||||
|
fontConfig.GlyphRanges = dummyRangeHandle.AddrOfPinnedObject();
|
||||||
|
fontConfig.SizePixels = DefaultFontSizePx * fontLoadScale;
|
||||||
|
DefaultFont = ioFonts.AddFontDefault(fontConfig);
|
||||||
|
fontsToUnscale.Add(DefaultFont);
|
||||||
|
fontsToOverwriteFromAxis.Add(true);
|
||||||
|
fontsToReassignSizes.Add(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var japaneseRangeHandle = GCHandle.Alloc(GlyphRangesJapanese.GlyphRanges, GCHandleType.Pinned);
|
||||||
|
garbageList.Add(japaneseRangeHandle);
|
||||||
|
|
||||||
|
fontConfig.GlyphRanges = japaneseRangeHandle.AddrOfPinnedObject();
|
||||||
|
DefaultFont = ioFonts.AddFontFromFileTTF(fontPathJp, (DefaultFontSizePx + 1) * fontLoadScale, fontConfig);
|
||||||
|
fontsToUnscale.Add(DefaultFont);
|
||||||
|
fontsToOverwriteFromAxis.Add(false);
|
||||||
|
fontsToReassignSizes.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FontAwesome icon font
|
||||||
|
Log.Verbose("[FONT] SetupFonts - FontAwesome icon font");
|
||||||
|
{
|
||||||
|
var fontPathIcon = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "FontAwesome5FreeSolid.otf");
|
||||||
|
if (!File.Exists(fontPathIcon))
|
||||||
|
ShowFontError(fontPathIcon);
|
||||||
|
|
||||||
|
var iconRangeHandle = GCHandle.Alloc(new ushort[] { 0xE000, 0xF8FF, 0, }, GCHandleType.Pinned);
|
||||||
|
garbageList.Add(iconRangeHandle);
|
||||||
|
|
||||||
|
fontConfig.GlyphRanges = iconRangeHandle.AddrOfPinnedObject();
|
||||||
|
IconFont = ioFonts.AddFontFromFileTTF(fontPathIcon, DefaultFontSizePx * fontLoadScale, fontConfig);
|
||||||
|
fontsToUnscale.Add(IconFont);
|
||||||
|
this.axisFontHandles.Add(null);
|
||||||
|
fontsToOverwriteFromAxis.Add(false);
|
||||||
|
fontsToReassignSizes.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monospace font
|
||||||
|
Log.Verbose("[FONT] SetupFonts - Monospace font");
|
||||||
|
{
|
||||||
|
var fontPathMono = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "Inconsolata-Regular.ttf");
|
||||||
|
if (!File.Exists(fontPathMono))
|
||||||
|
ShowFontError(fontPathMono);
|
||||||
|
|
||||||
|
fontConfig.GlyphRanges = IntPtr.Zero;
|
||||||
|
MonoFont = ioFonts.AddFontFromFileTTF(fontPathMono, DefaultFontSizePx * fontLoadScale, fontConfig);
|
||||||
|
fontsToUnscale.Add(MonoFont);
|
||||||
|
this.axisFontHandles.Add(null);
|
||||||
|
fontsToOverwriteFromAxis.Add(false);
|
||||||
|
fontsToReassignSizes.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default font but in requested size for requested glyphs
|
||||||
|
Log.Verbose("[FONT] SetupFonts - Default font but in requested size for requested glyphs");
|
||||||
|
{
|
||||||
|
Dictionary<float, List<SpecialGlyphRequest>> extraFontRequests = new();
|
||||||
|
foreach (var extraFontRequest in this.glyphRequests)
|
||||||
|
{
|
||||||
|
if (!extraFontRequests.ContainsKey(extraFontRequest.Size))
|
||||||
|
extraFontRequests[extraFontRequest.Size] = new();
|
||||||
|
extraFontRequests[extraFontRequest.Size].Add(extraFontRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (fontSize, requests) in extraFontRequests)
|
||||||
|
{
|
||||||
|
List<Tuple<ushort, ushort>> codepointRanges = new();
|
||||||
|
codepointRanges.Add(Tuple.Create(Fallback1Codepoint, Fallback1Codepoint));
|
||||||
|
codepointRanges.Add(Tuple.Create(Fallback2Codepoint, Fallback2Codepoint));
|
||||||
|
|
||||||
|
// ImGui default ellipsis characters
|
||||||
|
codepointRanges.Add(Tuple.Create<ushort, ushort>(0x2026, 0x2026));
|
||||||
|
codepointRanges.Add(Tuple.Create<ushort, ushort>(0x0085, 0x0085));
|
||||||
|
|
||||||
|
foreach (var request in requests)
|
||||||
|
{
|
||||||
|
foreach (var range in request.CodepointRanges)
|
||||||
|
codepointRanges.Add(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
codepointRanges.Sort((x, y) => (x.Item1 == y.Item1 ? (x.Item2 < y.Item2 ? -1 : (x.Item2 == y.Item2 ? 0 : 1)) : (x.Item1 < y.Item1 ? -1 : 1)));
|
||||||
|
|
||||||
|
List<ushort> flattenedRanges = new();
|
||||||
|
foreach (var range in codepointRanges)
|
||||||
|
{
|
||||||
|
if (flattenedRanges.Any() && flattenedRanges[^1] >= range.Item1 - 1)
|
||||||
|
{
|
||||||
|
flattenedRanges[^1] = Math.Max(flattenedRanges[^1], range.Item2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flattenedRanges.Add(range.Item1);
|
||||||
|
flattenedRanges.Add(range.Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flattenedRanges.Add(0);
|
||||||
|
|
||||||
|
ImFontPtr sizedFont;
|
||||||
|
this.axisFontHandles.Add(gameFontManager.NewFontRef(this.AllowBigFontAtlas ? new(GameFontFamily.Axis, fontSize * 3 / 4 * fontLoadScale) : new(GameFontFamilyAndSize.Axis12)));
|
||||||
|
if (this.UseAxis)
|
||||||
|
{
|
||||||
|
fontConfig.GlyphRanges = dummyRangeHandle.AddrOfPinnedObject();
|
||||||
|
fontConfig.SizePixels = (this.AllowBigFontAtlas ? fontSize : DefaultFontSizePx) * fontLoadScale;
|
||||||
|
sizedFont = ioFonts.AddFontDefault(fontConfig);
|
||||||
|
fontsToUnscale.Add(sizedFont);
|
||||||
|
fontsToOverwriteFromAxis.Add(true);
|
||||||
|
fontsToReassignSizes.Add(this.AllowBigFontAtlas ? null : fontSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var rangeHandle = GCHandle.Alloc(flattenedRanges.ToArray(), GCHandleType.Pinned);
|
||||||
|
garbageList.Add(rangeHandle);
|
||||||
|
sizedFont = ioFonts.AddFontFromFileTTF(fontPathJp, (this.AllowBigFontAtlas ? fontSize : DefaultFontSizePx + 1) * fontLoadScale, fontConfig, rangeHandle.AddrOfPinnedObject());
|
||||||
|
fontsToUnscale.Add(sizedFont);
|
||||||
|
fontsToOverwriteFromAxis.Add(false);
|
||||||
|
fontsToReassignSizes.Add(this.AllowBigFontAtlas ? null : fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var request in requests)
|
||||||
|
request.FontInternal = sizedFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gameFontManager.BuildFonts();
|
||||||
|
|
||||||
|
Log.Verbose("[FONT] Invoke OnBuildFonts");
|
||||||
|
this.BuildFonts?.Invoke();
|
||||||
|
Log.Verbose("[FONT] OnBuildFonts OK!");
|
||||||
|
|
||||||
|
for (var i = 0; i < ImGui.GetIO().Fonts.Fonts.Size; i++)
|
||||||
|
{
|
||||||
|
Log.Verbose("{0} - {1}", i, ImGui.GetIO().Fonts.Fonts[i].GetDebugName());
|
||||||
|
}
|
||||||
|
|
||||||
|
ioFonts.Build();
|
||||||
|
|
||||||
|
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(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();
|
||||||
|
|
||||||
|
for (var i = 0; i < fontsToUnscale.Count; i++)
|
||||||
|
{
|
||||||
|
var font = fontsToUnscale[i];
|
||||||
|
var fontPtr = font.NativePtr;
|
||||||
|
var correspondingAxis = this.axisFontHandles[i];
|
||||||
|
var overwrite = fontsToOverwriteFromAxis[i];
|
||||||
|
var overwriteSize = fontsToReassignSizes[i];
|
||||||
|
|
||||||
|
GameFontManager.UnscaleFont(font, fontLoadScale, false);
|
||||||
|
|
||||||
|
if (correspondingAxis == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var scale = 1f;
|
||||||
|
|
||||||
|
if (overwrite)
|
||||||
|
{
|
||||||
|
var srcPtr = correspondingAxis.ImFont.NativePtr;
|
||||||
|
scale = fontPtr->ConfigData->SizePixels / srcPtr->ConfigData->SizePixels / fontLoadScale;
|
||||||
|
fontPtr->FontSize = srcPtr->FontSize * scale;
|
||||||
|
fontPtr->Ascent = srcPtr->Ascent * scale;
|
||||||
|
fontPtr->Descent = srcPtr->Descent * scale;
|
||||||
|
fontPtr->FallbackChar = srcPtr->FallbackChar;
|
||||||
|
fontPtr->EllipsisChar = srcPtr->EllipsisChar;
|
||||||
|
GameFontManager.CopyGlyphsAcrossFonts(correspondingAxis.ImFont, font, false, false);
|
||||||
|
|
||||||
|
scale = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overwriteSize != null)
|
||||||
|
scale *= overwriteSize.Value / fontPtr->ConfigData->SizePixels;
|
||||||
|
|
||||||
|
if (scale != 1f)
|
||||||
|
GameFontManager.UnscaleFont(font, 1 / scale, false);
|
||||||
|
|
||||||
|
Log.Verbose("[FONT] Font {0}: result size {1}", i, fontPtr->FontSize);
|
||||||
|
|
||||||
|
if (!this.UseAxis && fontPtr == DefaultFont.NativePtr)
|
||||||
|
{
|
||||||
|
fontPtr->FontSize -= 1;
|
||||||
|
GameFontManager.CopyGlyphsAcrossFonts(correspondingAxis.ImFont, font, true, false, 0xE020, 0xE0DB);
|
||||||
|
fontPtr->FontSize += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GameFontManager.CopyGlyphsAcrossFonts(correspondingAxis.ImFont, font, true, false, 0xE020, 0xE0DB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill missing glyphs in MonoFont from DefaultFont
|
||||||
|
GameFontManager.CopyGlyphsAcrossFonts(DefaultFont, MonoFont, true, false);
|
||||||
|
|
||||||
|
foreach (var font in fontsToUnscale)
|
||||||
|
{
|
||||||
|
font.FallbackChar = Fallback1Codepoint;
|
||||||
|
font.BuildLookupTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Verbose("[FONT] Invoke OnAfterBuildFonts");
|
||||||
|
this.AfterBuildFonts?.Invoke();
|
||||||
|
Log.Verbose("[FONT] OnAfterBuildFonts OK!");
|
||||||
|
|
||||||
|
Log.Verbose("[FONT] Fonts built!");
|
||||||
|
|
||||||
|
this.fontBuildSignal.Set();
|
||||||
|
|
||||||
|
this.FontsReady = true;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (fontConfig.NativePtr != null)
|
||||||
|
fontConfig.Destroy();
|
||||||
|
|
||||||
gameFontManager.AfterBuildFonts();
|
foreach (var garbage in garbageList)
|
||||||
GameFontManager.CopyGlyphsAcrossFonts(this.axisFontHandle?.ImFont, DefaultFont, false, true);
|
garbage.Free();
|
||||||
|
}
|
||||||
Log.Verbose("[FONT] Invoke OnAfterBuildFonts");
|
|
||||||
this.AfterBuildFonts?.Invoke();
|
|
||||||
Log.Verbose("[FONT] OnAfterBuildFonts OK!");
|
|
||||||
|
|
||||||
Log.Verbose("[FONT] Fonts built!");
|
|
||||||
|
|
||||||
this.fontBuildSignal.Set();
|
|
||||||
|
|
||||||
fontConfig.Destroy();
|
|
||||||
japaneseRangeHandle.Free();
|
|
||||||
gameRangeHandle.Free();
|
|
||||||
iconRangeHandle.Free();
|
|
||||||
|
|
||||||
this.FontsReady = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Disable()
|
private void Disable()
|
||||||
|
|
@ -765,5 +1002,65 @@ namespace Dalamud.Interface.Internal
|
||||||
|
|
||||||
Service<NotificationManager>.Get().Draw();
|
Service<NotificationManager>.Get().Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a glyph request.
|
||||||
|
/// </summary>
|
||||||
|
public class SpecialGlyphRequest : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SpecialGlyphRequest"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager">InterfaceManager to associate.</param>
|
||||||
|
/// <param name="size">Font size in pixels.</param>
|
||||||
|
/// <param name="ranges">Codepoint ranges.</param>
|
||||||
|
internal SpecialGlyphRequest(InterfaceManager manager, float size, List<Tuple<ushort, ushort>> ranges)
|
||||||
|
{
|
||||||
|
this.Manager = manager;
|
||||||
|
this.Size = size;
|
||||||
|
this.CodepointRanges = ranges;
|
||||||
|
this.Manager.glyphRequests.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the font of specified size, or DefaultFont if it's not ready yet.
|
||||||
|
/// </summary>
|
||||||
|
public ImFontPtr Font
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
return this.FontInternal.NativePtr == null ? DefaultFont : this.FontInternal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the associated ImFont.
|
||||||
|
/// </summary>
|
||||||
|
internal ImFontPtr FontInternal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets associated InterfaceManager.
|
||||||
|
/// </summary>
|
||||||
|
internal InterfaceManager Manager { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets font size.
|
||||||
|
/// </summary>
|
||||||
|
internal float Size { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets codepoint ranges.
|
||||||
|
/// </summary>
|
||||||
|
internal List<Tuple<ushort, ushort>> CodepointRanges { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.Manager.glyphRequests.Remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
internal class SettingsWindow : Window
|
internal class SettingsWindow : Window
|
||||||
{
|
{
|
||||||
private const float MinScale = 0.3f;
|
private const float MinScale = 0.3f;
|
||||||
private const float MaxScale = 2.0f;
|
private const float MaxScale = 3.0f;
|
||||||
|
|
||||||
private readonly string[] languages;
|
private readonly string[] languages;
|
||||||
private readonly string[] locLanguages;
|
private readonly string[] locLanguages;
|
||||||
|
|
@ -39,6 +39,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
private float globalUiScale;
|
private float globalUiScale;
|
||||||
private bool doUseAxisFontsFromGame;
|
private bool doUseAxisFontsFromGame;
|
||||||
|
private bool doAllowBigFontAtlas;
|
||||||
private float fontGamma;
|
private float fontGamma;
|
||||||
private bool doToggleUiHide;
|
private bool doToggleUiHide;
|
||||||
private bool doToggleUiHideDuringCutscenes;
|
private bool doToggleUiHideDuringCutscenes;
|
||||||
|
|
@ -96,6 +97,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
this.globalUiScale = configuration.GlobalUiScale;
|
this.globalUiScale = configuration.GlobalUiScale;
|
||||||
this.fontGamma = configuration.FontGamma;
|
this.fontGamma = configuration.FontGamma;
|
||||||
this.doUseAxisFontsFromGame = configuration.UseAxisFontsFromGame;
|
this.doUseAxisFontsFromGame = configuration.UseAxisFontsFromGame;
|
||||||
|
this.doAllowBigFontAtlas = configuration.AllowBigFontAtlas;
|
||||||
this.doToggleUiHide = configuration.ToggleUiHide;
|
this.doToggleUiHide = configuration.ToggleUiHide;
|
||||||
this.doToggleUiHideDuringCutscenes = configuration.ToggleUiHideDuringCutscenes;
|
this.doToggleUiHideDuringCutscenes = configuration.ToggleUiHideDuringCutscenes;
|
||||||
this.doToggleUiHideDuringGpose = configuration.ToggleUiHideDuringGpose;
|
this.doToggleUiHideDuringGpose = configuration.ToggleUiHideDuringGpose;
|
||||||
|
|
@ -188,6 +190,8 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
|
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
|
||||||
interfaceManager.FontGammaOverride = null;
|
interfaceManager.FontGammaOverride = null;
|
||||||
|
interfaceManager.AllowBigFontAtlasOverride = null;
|
||||||
|
interfaceManager.UseAxisOverride = null;
|
||||||
this.thirdRepoList = configuration.ThirdRepoList.Select(x => x.Clone()).ToList();
|
this.thirdRepoList = configuration.ThirdRepoList.Select(x => x.Clone()).ToList();
|
||||||
this.devPluginLocations = configuration.DevPluginLoadLocations.Select(x => x.Clone()).ToList();
|
this.devPluginLocations = configuration.DevPluginLoadLocations.Select(x => x.Clone()).ToList();
|
||||||
|
|
||||||
|
|
@ -325,9 +329,22 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below."));
|
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below."));
|
||||||
|
|
||||||
ImGui.Checkbox(Loc.Localize("DalamudSettingToggleAxisFonts", "Use AXIS fonts as default Dalamud font"), ref this.doUseAxisFontsFromGame);
|
if (ImGui.Checkbox(Loc.Localize("DalamudSettingToggleAxisFonts", "Use AXIS fonts as default Dalamud font"), ref this.doUseAxisFontsFromGame))
|
||||||
|
{
|
||||||
|
interfaceManager.UseAxisOverride = this.doUseAxisFontsFromGame;
|
||||||
|
interfaceManager.RebuildFonts();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiAxisFontsHint", "Use AXIS fonts (the game's main UI fonts) as default Dalamud font."));
|
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiAxisFontsHint", "Use AXIS fonts (the game's main UI fonts) as default Dalamud font."));
|
||||||
|
|
||||||
|
if (ImGui.Checkbox(Loc.Localize("DalamudSettingAllowBigFontAtlas", "Allow big font atlas"), ref this.doAllowBigFontAtlas))
|
||||||
|
{
|
||||||
|
interfaceManager.AllowBigFontAtlasOverride = this.doAllowBigFontAtlas;
|
||||||
|
interfaceManager.RebuildFonts();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudGrey, string.Format(Loc.Localize("DalamudSettingAllowBigFontAtlas", "Displays text crisply, but may crash if your GPU does not support it.\nCurrent size: {0}px * {1}px"), ImGui.GetIO().Fonts.TexWidth, ImGui.GetIO().Fonts.TexHeight));
|
||||||
|
|
||||||
ImGui.Checkbox(Loc.Localize("DalamudSettingToggleUiHide", "Hide plugin UI when the game UI is toggled off"), ref this.doToggleUiHide);
|
ImGui.Checkbox(Loc.Localize("DalamudSettingToggleUiHide", "Hide plugin UI when the game UI is toggled off"), ref this.doToggleUiHide);
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiHideHint", "Hide any open windows by plugins when toggling the game overlay."));
|
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleUiHideHint", "Hide any open windows by plugins when toggling the game overlay."));
|
||||||
|
|
||||||
|
|
@ -856,6 +873,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
configuration.Fools22Newer = this.doFools22;
|
configuration.Fools22Newer = this.doFools22;
|
||||||
|
|
||||||
configuration.UseAxisFontsFromGame = this.doUseAxisFontsFromGame;
|
configuration.UseAxisFontsFromGame = this.doUseAxisFontsFromGame;
|
||||||
|
configuration.AllowBigFontAtlas = this.doAllowBigFontAtlas;
|
||||||
configuration.FontGamma = this.fontGamma;
|
configuration.FontGamma = this.fontGamma;
|
||||||
|
|
||||||
// This is applied every frame in InterfaceManager::CheckViewportState()
|
// This is applied every frame in InterfaceManager::CheckViewportState()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
|
@ -20,17 +21,18 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TitleScreenMenuWindow : Window, IDisposable
|
internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
private const float TargetFontSize = 16.2f;
|
private const float TargetFontSizePt = 18f;
|
||||||
|
private const float TargetFontSizePx = TargetFontSizePt * 4 / 3;
|
||||||
|
|
||||||
private readonly TextureWrap shadeTexture;
|
private readonly TextureWrap shadeTexture;
|
||||||
|
|
||||||
private readonly Dictionary<Guid, InOutCubic> shadeEasings = new();
|
private readonly Dictionary<Guid, InOutCubic> shadeEasings = new();
|
||||||
private readonly Dictionary<Guid, InOutQuint> moveEasings = new();
|
private readonly Dictionary<Guid, InOutQuint> moveEasings = new();
|
||||||
private readonly Dictionary<Guid, InOutCubic> logoEasings = new();
|
private readonly Dictionary<Guid, InOutCubic> logoEasings = new();
|
||||||
|
private readonly Dictionary<string, InterfaceManager.SpecialGlyphRequest> specialGlyphRequests = new();
|
||||||
|
|
||||||
private InOutCubic? fadeOutEasing;
|
private InOutCubic? fadeOutEasing;
|
||||||
|
|
||||||
private GameFontHandle? axisFontHandle;
|
|
||||||
|
|
||||||
private State state = State.Hide;
|
private State state = State.Hide;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -71,19 +73,14 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void PreDraw()
|
public override void PreDraw()
|
||||||
{
|
{
|
||||||
this.SetAxisFonts();
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0));
|
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0));
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0));
|
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0));
|
||||||
if (this.axisFontHandle?.Available ?? false)
|
|
||||||
ImGui.PushFont(this.axisFontHandle.ImFont);
|
|
||||||
base.PreDraw();
|
base.PreDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void PostDraw()
|
public override void PostDraw()
|
||||||
{
|
{
|
||||||
if (this.axisFontHandle?.Available ?? false)
|
|
||||||
ImGui.PopFont();
|
|
||||||
ImGui.PopStyleVar(2);
|
ImGui.PopStyleVar(2);
|
||||||
base.PostDraw();
|
base.PostDraw();
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +96,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
ImGui.SetWindowFontScale(TargetFontSize / ImGui.GetFont().FontSize * 4 / 3);
|
var scale = ImGui.GetIO().FontGlobalScale;
|
||||||
|
|
||||||
var tsm = Service<TitleScreenMenu>.Get();
|
var tsm = Service<TitleScreenMenu>.Get();
|
||||||
|
|
||||||
|
|
@ -129,7 +126,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
moveEasing.Update();
|
moveEasing.Update();
|
||||||
|
|
||||||
var finalPos = (i + 1) * this.shadeTexture.Height;
|
var finalPos = (i + 1) * this.shadeTexture.Height * scale;
|
||||||
var pos = moveEasing.Value * finalPos;
|
var pos = moveEasing.Value * finalPos;
|
||||||
|
|
||||||
// FIXME(goat): Sometimes, easings can overshoot and bring things out of alignment.
|
// FIXME(goat): Sometimes, easings can overshoot and bring things out of alignment.
|
||||||
|
|
@ -180,7 +177,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
{
|
{
|
||||||
var entry = tsm.Entries[i];
|
var entry = tsm.Entries[i];
|
||||||
|
|
||||||
var finalPos = (i + 1) * this.shadeTexture.Height;
|
var finalPos = (i + 1) * this.shadeTexture.Height * scale;
|
||||||
|
|
||||||
this.DrawEntry(entry, i != 0, true, i == 0, false);
|
this.DrawEntry(entry, i != 0, true, i == 0, false);
|
||||||
|
|
||||||
|
|
@ -222,26 +219,36 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void SetAxisFonts()
|
var srcText = tsm.Entries.Select(e => e.Name).ToHashSet();
|
||||||
{
|
var keys = this.specialGlyphRequests.Keys.ToHashSet();
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
keys.RemoveWhere(x => srcText.Contains(x));
|
||||||
if (configuration.UseAxisFontsFromGame)
|
foreach (var key in keys)
|
||||||
{
|
{
|
||||||
if (this.axisFontHandle == null)
|
this.specialGlyphRequests[key].Dispose();
|
||||||
this.axisFontHandle = Service<GameFontManager>.Get().NewFontRef(new(GameFontFamily.Axis, TargetFontSize));
|
this.specialGlyphRequests.Remove(key);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.axisFontHandle?.Dispose();
|
|
||||||
this.axisFontHandle = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawEntry(
|
private bool DrawEntry(
|
||||||
TitleScreenMenu.TitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha)
|
TitleScreenMenu.TitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha)
|
||||||
{
|
{
|
||||||
|
InterfaceManager.SpecialGlyphRequest fontHandle;
|
||||||
|
if (this.specialGlyphRequests.TryGetValue(entry.Name, out fontHandle) && fontHandle.Size != TargetFontSizePx)
|
||||||
|
{
|
||||||
|
fontHandle.Dispose();
|
||||||
|
this.specialGlyphRequests.Remove(entry.Name);
|
||||||
|
fontHandle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontHandle == null)
|
||||||
|
this.specialGlyphRequests[entry.Name] = fontHandle = Service<InterfaceManager>.Get().NewFontSizeRef(TargetFontSizePx, entry.Name);
|
||||||
|
|
||||||
|
ImGui.PushFont(fontHandle.Font);
|
||||||
|
ImGui.SetWindowFontScale(TargetFontSizePx / fontHandle.Size);
|
||||||
|
|
||||||
|
var scale = ImGui.GetIO().FontGlobalScale;
|
||||||
|
|
||||||
if (!this.shadeEasings.TryGetValue(entry.Id, out var shadeEasing))
|
if (!this.shadeEasings.TryGetValue(entry.Id, out var shadeEasing))
|
||||||
{
|
{
|
||||||
shadeEasing = new InOutCubic(TimeSpan.FromMilliseconds(350));
|
shadeEasing = new InOutCubic(TimeSpan.FromMilliseconds(350));
|
||||||
|
|
@ -251,7 +258,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
var initialCursor = ImGui.GetCursorPos();
|
var initialCursor = ImGui.GetCursorPos();
|
||||||
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, (float)shadeEasing.Value);
|
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, (float)shadeEasing.Value);
|
||||||
ImGui.Image(this.shadeTexture.ImGuiHandle, new Vector2(this.shadeTexture.Width, this.shadeTexture.Height));
|
ImGui.Image(this.shadeTexture.ImGuiHandle, new Vector2(this.shadeTexture.Width * scale, this.shadeTexture.Height * scale));
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
|
|
||||||
var isHover = ImGui.IsItemHovered();
|
var isHover = ImGui.IsItemHovered();
|
||||||
|
|
@ -305,7 +312,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
|
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Image(entry.Texture.ImGuiHandle, new Vector2(TitleScreenMenu.TextureSize));
|
ImGui.Image(entry.Texture.ImGuiHandle, new Vector2(TitleScreenMenu.TextureSize * scale));
|
||||||
if (overrideAlpha || isFirst)
|
if (overrideAlpha || isFirst)
|
||||||
{
|
{
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
|
|
@ -319,23 +326,36 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
var textHeight = ImGui.GetTextLineHeightWithSpacing();
|
var textHeight = ImGui.GetTextLineHeightWithSpacing();
|
||||||
var cursor = ImGui.GetCursorPos();
|
var cursor = ImGui.GetCursorPos();
|
||||||
|
|
||||||
cursor.Y += (entry.Texture.Height / 2) - (textHeight / 2);
|
cursor.Y += (entry.Texture.Height * scale / 2) - (textHeight / 2);
|
||||||
ImGui.SetCursorPos(cursor);
|
|
||||||
|
|
||||||
if (overrideAlpha)
|
if (overrideAlpha)
|
||||||
{
|
{
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, showText ? (float)logoEasing.Value : 0f);
|
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, showText ? (float)logoEasing.Value : 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop shadow
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, 0xFF000000);
|
||||||
|
for (int i = 0, i_ = (int)Math.Ceiling(1 * scale); i < i_; i++)
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPos(new Vector2(cursor.X, cursor.Y + i));
|
||||||
|
ImGui.Text(entry.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
|
||||||
|
ImGui.SetCursorPos(cursor);
|
||||||
ImGui.Text(entry.Name);
|
ImGui.Text(entry.Name);
|
||||||
|
|
||||||
if (overrideAlpha)
|
if (overrideAlpha)
|
||||||
{
|
{
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialCursor.Y += entry.Texture.Height;
|
initialCursor.Y += entry.Texture.Height * scale;
|
||||||
ImGui.SetCursorPos(initialCursor);
|
ImGui.SetCursorPos(initialCursor);
|
||||||
|
|
||||||
|
ImGui.PopFont();
|
||||||
|
|
||||||
return isHover;
|
return isHover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@ int CoreCLR::load_hostfxr(const struct get_hostfxr_parameters* parameters)
|
||||||
&& m_hostfxr_close_fptr ? 0 : -1;
|
&& m_hostfxr_close_fptr ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreCLR::load_runtime(const std::wstring& runtime_config_path)
|
int CoreCLR::load_runtime(const std::wstring& runtime_config_path)
|
||||||
{
|
{
|
||||||
return CoreCLR::load_runtime(runtime_config_path, nullptr);
|
return CoreCLR::load_runtime(runtime_config_path, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreCLR::load_runtime(const std::wstring& runtime_config_path, const struct hostfxr_initialize_parameters* parameters)
|
int CoreCLR::load_runtime(const std::wstring& runtime_config_path, const struct hostfxr_initialize_parameters* parameters)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ class CoreCLR {
|
||||||
int load_hostfxr();
|
int load_hostfxr();
|
||||||
int load_hostfxr(const get_hostfxr_parameters* parameters);
|
int load_hostfxr(const get_hostfxr_parameters* parameters);
|
||||||
|
|
||||||
bool load_runtime(const std::wstring& runtime_config_path);
|
int load_runtime(const std::wstring& runtime_config_path);
|
||||||
bool load_runtime(
|
int load_runtime(
|
||||||
const std::wstring& runtime_config_path,
|
const std::wstring& runtime_config_path,
|
||||||
const struct hostfxr_initialize_parameters* parameters);
|
const struct hostfxr_initialize_parameters* parameters);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ int InitializeClrAndGetEntryPoint(
|
||||||
printf("Loading hostfxr... ");
|
printf("Loading hostfxr... ");
|
||||||
if ((result = g_clr->load_hostfxr(&init_parameters)) != 0)
|
if ((result = g_clr->load_hostfxr(&init_parameters)) != 0)
|
||||||
{
|
{
|
||||||
printf("\nError: Failed to load the `hostfxr` library (err=%d)\n", result);
|
printf("\nError: Failed to load the `hostfxr` library (err=0x%08x)\n", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
printf("Done!\n");
|
printf("Done!\n");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue