diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index 7930f5c79..6350da4aa 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -134,6 +134,15 @@ namespace Dalamud.Configuration.Internal /// public bool UseAxisFontsFromGame { get; set; } = false; + /// + /// Gets or sets the gamma value to apply for Dalamud fonts. Effects text thickness. + /// + /// Before gamma is applied... + /// * ...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. + /// + public float FontGamma { get; set; } = 1.0f; + /// /// Gets or sets a value indicating whether or not plugin UI should be hidden. /// diff --git a/Dalamud/Interface/GameFonts/GameFontHandle.cs b/Dalamud/Interface/GameFonts/GameFontHandle.cs index a50941883..e2fa1d941 100644 --- a/Dalamud/Interface/GameFonts/GameFontHandle.cs +++ b/Dalamud/Interface/GameFonts/GameFontHandle.cs @@ -1,5 +1,6 @@ using System; using System.Numerics; + using ImGuiNET; namespace Dalamud.Interface.GameFonts @@ -31,7 +32,16 @@ namespace Dalamud.Interface.GameFonts /// /// Gets a value indicating whether this font is ready for use. /// - public bool Available => this.manager.GetFont(this.fontStyle) != null; + public bool Available + { + get + { + unsafe + { + return this.manager.GetFont(this.fontStyle).GetValueOrDefault(null).NativePtr != null; + } + } + } /// /// Gets the font. @@ -68,9 +78,13 @@ namespace Dalamud.Interface.GameFonts } else { - this.LayoutBuilder(text) - .Build() - .Draw(ImGui.GetWindowDrawList(), ImGui.GetWindowPos() + ImGui.GetCursorPos(), ImGui.GetColorU32(ImGuiCol.Text)); + var pos = ImGui.GetWindowPos() + ImGui.GetCursorPos(); + pos.X -= ImGui.GetScrollX(); + pos.Y -= ImGui.GetScrollY(); + + var layout = this.LayoutBuilder(text).Build(); + layout.Draw(ImGui.GetWindowDrawList(), pos, ImGui.GetColorU32(ImGuiCol.Text)); + ImGui.Dummy(new Vector2(layout.Width, layout.Height)); } } diff --git a/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs b/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs index dc2d5f380..482ef22e2 100644 --- a/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs +++ b/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs @@ -73,7 +73,6 @@ namespace Dalamud.Interface.GameFonts /// Color. public void Draw(ImDrawListPtr drawListPtr, Vector2 pos, uint col) { - ImGui.Dummy(new Vector2(this.Width, this.Height)); foreach (var element in this.Elements) { if (element.IsControl) diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs index 44772bc48..ba9253a9b 100644 --- a/Dalamud/Interface/GameFonts/GameFontManager.cs +++ b/Dalamud/Interface/GameFonts/GameFontManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Numerics; using System.Text; +using Dalamud.Configuration.Internal; using Dalamud.Data; using Dalamud.Interface.Internal; using ImGuiNET; @@ -288,9 +289,10 @@ namespace Dalamud.Interface.GameFonts /// public unsafe void AfterBuildFonts() { - var io = ImGui.GetIO(); - io.Fonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height); + var ioFonts = ImGui.GetIO().Fonts; + ioFonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height); var pixels32 = (uint*)pixels8; + var fontGamma = this.interfaceManager.FontGamma; foreach (var (style, font) in this.fonts) { @@ -319,7 +321,7 @@ namespace Dalamud.Interface.GameFonts foreach (var (c, (rectId, glyph)) in this.glyphRectIds[style]) { - var rc = io.Fonts.GetCustomRectByIndex(rectId); + var rc = ioFonts.GetCustomRectByIndex(rectId); var sourceBuffer = this.texturePixels[glyph.TextureFileIndex]; var sourceBufferDelta = glyph.TextureChannelByteIndex; var widthAdjustment = style.CalculateWidthAdjustment(fdt, glyph); @@ -366,6 +368,19 @@ namespace Dalamud.Interface.GameFonts } } } + + if (Math.Abs(fontGamma - 1.4f) >= 0.001) + { + // Gamma correction (stbtt/FreeType would output in linear space whereas most real world usages will apply 1.4 or 1.8 gamma; Windows/XIV prebaked uses 1.4) + for (int y = rc.Y, y_ = rc.Y + rc.Height; y < y_; y++) + { + for (int x = rc.X, x_ = rc.X + rc.Width; x < x_; x++) + { + var i = (((y * width) + x) * 4) + 3; + pixels8[i] = (byte)(Math.Pow(pixels8[i] / 255.0f, 1.4f / fontGamma) * 255.0f); + } + } + } } } diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 58f18bf70..0c6d99efa 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -189,6 +189,16 @@ namespace Dalamud.Interface.Internal /// public bool IsReady => this.scene != null; + /// + /// Gets or sets the overrided font gamma value, instead of using the value from configuration. + /// + public float? FontGammaOverride { get; set; } = null; + + /// + /// Gets the font gamma value to use. + /// + public float FontGamma => Math.Max(0.1f, this.FontGammaOverride.GetValueOrDefault(Service.Get().FontGamma)); + /// /// Enable this module. /// @@ -521,10 +531,12 @@ namespace Dalamud.Interface.Internal private unsafe void SetupFonts() { var dalamud = Service.Get(); + var ioFonts = ImGui.GetIO().Fonts; + var fontGamma = this.FontGamma; this.fontBuildSignal.Reset(); - ImGui.GetIO().Fonts.Clear(); + ioFonts.Clear(); ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); fontConfig.PixelSnapH = true; @@ -536,7 +548,7 @@ namespace Dalamud.Interface.Internal var japaneseRangeHandle = GCHandle.Alloc(GlyphRangesJapanese.GlyphRanges, GCHandleType.Pinned); - DefaultFont = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, japaneseRangeHandle.AddrOfPinnedObject()); + DefaultFont = ioFonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, japaneseRangeHandle.AddrOfPinnedObject()); var fontPathGame = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "gamesym.ttf"); @@ -553,7 +565,7 @@ namespace Dalamud.Interface.Internal GCHandleType.Pinned); fontConfig.MergeMode = false; - ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, gameRangeHandle.AddrOfPinnedObject()); + ioFonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, gameRangeHandle.AddrOfPinnedObject()); fontConfig.MergeMode = true; var fontPathIcon = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "FontAwesome5FreeSolid.otf"); @@ -569,14 +581,14 @@ namespace Dalamud.Interface.Internal 0, }, GCHandleType.Pinned); - IconFont = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathIcon, 17.0f, null, iconRangeHandle.AddrOfPinnedObject()); + 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 = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathMono, 16.0f); + MonoFont = ioFonts.AddFontFromFileTTF(fontPathMono, 16.0f); var gameFontManager = Service.Get(); gameFontManager.BuildFonts(); @@ -590,7 +602,15 @@ namespace Dalamud.Interface.Internal Log.Verbose("{0} - {1}", i, ImGui.GetIO().Fonts.Fonts[i].GetDebugName()); } - ImGui.GetIO().Fonts.Build(); + 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(); GameFontManager.CopyGlyphsAcrossFonts(this.axisFontHandle?.ImFont, DefaultFont, false, true); diff --git a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs index 99ed6fef1..c8870123a 100644 --- a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs @@ -39,6 +39,7 @@ namespace Dalamud.Interface.Internal.Windows private float globalUiScale; private bool doUseAxisFontsFromGame; + private float fontGamma; private bool doToggleUiHide; private bool doToggleUiHideDuringCutscenes; private bool doToggleUiHideDuringGpose; @@ -91,6 +92,7 @@ namespace Dalamud.Interface.Internal.Windows this.doCfChatMessage = configuration.DutyFinderChatMessage; this.globalUiScale = configuration.GlobalUiScale; + this.fontGamma = configuration.FontGamma; this.doUseAxisFontsFromGame = configuration.UseAxisFontsFromGame; this.doToggleUiHide = configuration.ToggleUiHide; this.doToggleUiHideDuringCutscenes = configuration.ToggleUiHideDuringCutscenes; @@ -176,13 +178,20 @@ namespace Dalamud.Interface.Internal.Windows public override void OnClose() { var configuration = Service.Get(); + var interfaceManager = Service.Get(); + + var rebuildFont = interfaceManager.FontGamma != configuration.FontGamma; ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale; + interfaceManager.FontGammaOverride = null; this.thirdRepoList = configuration.ThirdRepoList.Select(x => x.Clone()).ToList(); this.devPluginLocations = configuration.DevPluginLoadLocations.Select(x => x.Clone()).ToList(); configuration.DtrOrder = this.dtrOrder; configuration.DtrIgnore = this.dtrIgnore; + + if (rebuildFont) + interfaceManager.RebuildFonts(); } /// @@ -272,21 +281,23 @@ namespace Dalamud.Interface.Internal.Windows private void DrawLookAndFeelTab() { + var interfaceManager = Service.Get(); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3); ImGui.Text(Loc.Localize("DalamudSettingsGlobalUiScale", "Global UI Scale")); ImGui.SameLine(); ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 3); - if (ImGui.Button("Reset")) + if (ImGui.Button(Loc.Localize("DalamudSettingsIndividualConfigResetToDefaultValue", "Reset") + "##DalamudSettingsGlobalUiScaleReset")) { this.globalUiScale = 1.0f; ImGui.GetIO().FontGlobalScale = this.globalUiScale; - Service.Get().RebuildFonts(); + interfaceManager.RebuildFonts(); } if (ImGui.DragFloat("##DalamudSettingsGlobalUiScaleDrag", ref this.globalUiScale, 0.005f, MinScale, MaxScale, "%.2f")) { ImGui.GetIO().FontGlobalScale = this.globalUiScale; - Service.Get().RebuildFonts(); + interfaceManager.RebuildFonts(); } ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsGlobalUiScaleHint", "Scale all XIVLauncher UI elements - useful for 4K displays.")); @@ -332,6 +343,28 @@ namespace Dalamud.Interface.Internal.Windows ImGui.Checkbox(Loc.Localize("DalamudSettingToggleTsm", "Show title screen menu"), ref this.doTsm); ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingToggleTsmHint", "This will allow you to access certain Dalamud and Plugin functionality from the title screen.")); + + ImGuiHelpers.ScaledDummy(10, 16); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3); + ImGui.Text(Loc.Localize("DalamudSettingsFontGamma", "Font Gamma")); + ImGui.SameLine(); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 3); + if (ImGui.Button(Loc.Localize("DalamudSettingsIndividualConfigResetToDefaultValue", "Reset") + "##DalamudSettingsFontGammaReset")) + { + this.fontGamma = 1.4f; + interfaceManager.FontGammaOverride = this.fontGamma; + interfaceManager.RebuildFonts(); + } + + if (ImGui.DragFloat("##DalamudSettingsFontGammaDrag", ref this.fontGamma, 0.005f, MinScale, MaxScale, "%.2f")) + { + interfaceManager.FontGammaOverride = this.fontGamma; + interfaceManager.RebuildFonts(); + } + + ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsFontGammaHint", "Changes the thickness of text.")); + + ImGuiHelpers.ScaledDummy(10, 16); } private void DrawServerInfoBarTab() @@ -811,6 +844,7 @@ namespace Dalamud.Interface.Internal.Windows configuration.ShowTsm = this.doTsm; configuration.UseAxisFontsFromGame = this.doUseAxisFontsFromGame; + configuration.FontGamma = this.fontGamma; // This is applied every frame in InterfaceManager::CheckViewportState() configuration.IsDisableViewport = !this.doViewport;