using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using TerraFX.Interop.DirectX; namespace Auspex.Rendering.Direct3D; internal sealed class HighResFontAtlas : IDisposable { private struct ImFontConfigHead { public IntPtr FontData; public int FontDataSize; public byte FontDataOwnedByAtlas; private byte _pad1; private byte _pad2; private byte _pad3; public int FontNo; public float SizePixels; } private const float HighResFontSize = 64f; private IntPtr _atlas; private unsafe ID3D11Texture2D* _texture; private unsafe ID3D11ShaderResourceView* _srv; public ImFontPtr Font { get; } public unsafe IntPtr TextureSRV => (nint)_srv; public unsafe HighResFontAtlas(FrameRenderContext ctx) { _atlas = ImFontAtlas_ImFontAtlas(); try { IntPtr intPtr = ImFontConfig_ImFontConfig(); try { ((ImFontConfigHead*)intPtr)->SizePixels = 64f; IntPtr intPtr2 = ImFontAtlas_AddFontDefault(_atlas, intPtr); Font = Unsafe.As(ref intPtr2); } finally { ImFontConfig_destroy(intPtr); } if (!ImFontAtlas_Build(_atlas)) { throw new InvalidOperationException("Failed to build high-res font atlas"); } byte* pSysMem = default(byte*); int num = default(int); int height = default(int); int num2 = default(int); ImFontAtlas_GetTexDataAsRGBA32(_atlas, 0, &pSysMem, &num, &height, &num2); D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC = new D3D11_TEXTURE2D_DESC { Width = (uint)num, Height = (uint)height, MipLevels = 1u, ArraySize = 1u, Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM, SampleDesc = new DXGI_SAMPLE_DESC { Count = 1u, Quality = 0u }, Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT, BindFlags = 8u }; D3D11_SUBRESOURCE_DATA d3D11_SUBRESOURCE_DATA = new D3D11_SUBRESOURCE_DATA { pSysMem = pSysMem, SysMemPitch = (uint)(num * num2) }; ID3D11Texture2D* texture = default(ID3D11Texture2D*); Marshal.ThrowExceptionForHR(ctx.Device->CreateTexture2D(&d3D11_TEXTURE2D_DESC, &d3D11_SUBRESOURCE_DATA, &texture)); _texture = texture; ID3D11ShaderResourceView* srv = default(ID3D11ShaderResourceView*); Marshal.ThrowExceptionForHR(ctx.Device->CreateShaderResourceView((ID3D11Resource*)_texture, null, &srv)); _srv = srv; ImFontAtlas_ClearTexData(_atlas); } catch { Dispose(); throw; } } public unsafe void Dispose() { if (_srv != null) { _srv->Release(); _srv = null; } if (_texture != null) { _texture->Release(); _texture = null; } if (_atlas != (IntPtr)0) { ImFontAtlas_destroy(_atlas); _atlas = (IntPtr)0; } GC.SuppressFinalize(this); } [DllImport("cimgui")] private static extern IntPtr ImFontAtlas_ImFontAtlas(); [DllImport("cimgui")] private static extern void ImFontAtlas_destroy(IntPtr self); [DllImport("cimgui")] private static extern IntPtr ImFontAtlas_AddFontDefault(IntPtr self, IntPtr font_cfg); [DllImport("cimgui")] [return: MarshalAs(UnmanagedType.U1)] private static extern bool ImFontAtlas_Build(IntPtr self); [DllImport("cimgui")] private unsafe static extern void ImFontAtlas_GetTexDataAsRGBA32(IntPtr self, int texture_index, byte** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel); [DllImport("cimgui")] private static extern void ImFontAtlas_ClearTexData(IntPtr self); [DllImport("cimgui")] private static extern IntPtr ImFontConfig_ImFontConfig(); [DllImport("cimgui")] private static extern void ImFontConfig_destroy(IntPtr self); }