From fcd98ee2bb557c8048c91749e03b8defed593b32 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 29 Apr 2020 17:13:05 -0700 Subject: [PATCH] Add ability for plugins to add ImGui fonts at runtime, though we should discourage this as much as possible. Also sneak in an api-matching update to part of ImGuiScene, to make more things useable in SamplePlugin --- Dalamud/Interface/InterfaceManager.cs | 85 +++++++++++++++++++-------- Dalamud/Interface/UiBuilder.cs | 20 +++++++ lib/ImGuiScene | 2 +- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/Dalamud/Interface/InterfaceManager.cs b/Dalamud/Interface/InterfaceManager.cs index 3d66d24d8..5fde5a051 100644 --- a/Dalamud/Interface/InterfaceManager.cs +++ b/Dalamud/Interface/InterfaceManager.cs @@ -54,11 +54,15 @@ namespace Dalamud.Interface public ImGuiIOPtr LastImGuiIoPtr; + public Action OnBuildFonts; + private bool isRebuildingFonts = false; + /// /// This event gets called by a plugin UiBuilder when read /// public event RawDX11Scene.BuildUIDelegate OnDraw; + public InterfaceManager(Dalamud dalamud, SigScanner scanner) { this.dalamud = dalamud; @@ -200,7 +204,18 @@ namespace Dalamud.Interface return null; } - private unsafe IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags) + // Sets up a deferred invocation of font rebuilding, before the next render frame + public void RebuildFonts() + { + // don't invoke this multiple times per frame, in case multiple plugins call it + if (!this.isRebuildingFonts) + { + this.isRebuildingFonts = true; + this.scene.OnNewRenderFrame += RebuildFontsInternal; + } + } + + private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags) { if (this.scene == null) { @@ -209,30 +224,7 @@ namespace Dalamud.Interface this.scene.OnBuildUI += Display; this.scene.OnNewInputFrame += OnNewInputFrame; - ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); - fontConfig.MergeMode = true; - fontConfig.PixelSnapH = true; - - var fontPathJp = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "NotoSansCJKjp-Medium.otf"); - ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, ImGui.GetIO().Fonts.GetGlyphRangesJapanese()); - - var fontPathGame = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "gamesym.ttf"); - Log.Verbose(fontPathGame); - - var rangeHandle = GCHandle.Alloc(new ushort[] - { - 0xE020, - 0xE0DB, - 0 - }, GCHandleType.Pinned); - - - ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, rangeHandle.AddrOfPinnedObject()); - - ImGui.GetIO().Fonts.Build(); - - fontConfig.Destroy(); - rangeHandle.Free(); + SetupFonts(); ImGui.GetStyle().GrabRounding = 3f; ImGui.GetStyle().FrameRounding = 4f; @@ -268,6 +260,49 @@ namespace Dalamud.Interface return this.presentHook.Original(swapChain, syncInterval, presentFlags); } + private unsafe void SetupFonts() + { + ImGui.GetIO().Fonts.Clear(); + + ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); + fontConfig.MergeMode = true; + fontConfig.PixelSnapH = true; + + var fontPathJp = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "NotoSansCJKjp-Medium.otf"); + ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, ImGui.GetIO().Fonts.GetGlyphRangesJapanese()); + + var fontPathGame = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "gamesym.ttf"); + Log.Verbose(fontPathGame); + + var rangeHandle = GCHandle.Alloc(new ushort[] + { + 0xE020, + 0xE0DB, + 0 + }, GCHandleType.Pinned); + + + ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, rangeHandle.AddrOfPinnedObject()); + + OnBuildFonts?.Invoke(); + + ImGui.GetIO().Fonts.Build(); + + fontConfig.Destroy(); + rangeHandle.Free(); + } + + // This is intended to only be called as a handler attached to scene.OnNewRenderFrame + private void RebuildFontsInternal() + { + SetupFonts(); + + this.scene.OnNewRenderFrame -= RebuildFontsInternal; + this.scene.InvalidateFonts(); + + this.isRebuildingFonts = false; + } + private IntPtr ResizeBuffersDetour(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags) { Log.Verbose($"Calling resizebuffers {bufferCount} {width} {height} {newFormat} {swapChainFlags}"); diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index 31468e302..8b2d3f30a 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -70,6 +70,26 @@ namespace Dalamud.Interface public TextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) => this.interfaceManager.LoadImageRaw(imageData, width, height, numChannels); + /// + /// An event that is called any time ImGui fonts need to be rebuilt.
+ /// Any ImFontPtr objects that you store can be invalidated when fonts are rebuilt + /// (at any time), so you should both reload your custom fonts and restore those + /// pointers inside this handler. + ///
+ public Action OnBuildFonts + { + get { return this.interfaceManager.OnBuildFonts; } + set { this.interfaceManager.OnBuildFonts = value; } + } + + /// + /// Call this to queue a rebuild of the font atlas.
+ /// This will invoke any handlers and ensure that any loaded fonts are + /// ready to be used on the next UI frame. + ///
+ public void RebuildFonts() => + this.interfaceManager.RebuildFonts(); + /// /// Event that is fired when the plugin should open its configuration interface. /// diff --git a/lib/ImGuiScene b/lib/ImGuiScene index aaa037938..d5b9345dc 160000 --- a/lib/ImGuiScene +++ b/lib/ImGuiScene @@ -1 +1 @@ -Subproject commit aaa037938d6fe835a15542a3451d12108e3f83b6 +Subproject commit d5b9345dc1463d746b832843bd7c81b753d4e5b0