diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 5bafc4ff3..7e9c8eed0 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -26,14 +26,9 @@ using Dalamud.Utility.Timing;
using ImGuiNET;
using ImGuiScene;
-using Lumina.Data.Files;
-using Lumina.Data.Parsing.Tex.Buffers;
-
using PInvoke;
using Serilog;
using SharpDX;
-using SharpDX.Direct3D;
-using SharpDX.Direct3D11;
using SharpDX.DXGI;
// general dev notes, here because it's easiest
@@ -261,169 +256,6 @@ internal class InterfaceManager : IDisposable, IServiceType
}
}
-#nullable enable
-
- ///
- /// Load an image from disk.
- ///
- /// The filepath to load.
- /// A texture, ready to use in ImGui.
- public IDalamudTextureWrap? LoadImage(string filePath)
- {
- if (this.scene == null)
- throw new InvalidOperationException("Scene isn't ready.");
-
- try
- {
- var wrap = this.scene?.LoadImage(filePath);
- return wrap != null ? new DalamudTextureWrap(wrap) : null;
- }
- catch (Exception ex)
- {
- Log.Error(ex, $"Failed to load image from {filePath}");
- }
-
- return null;
- }
-
- ///
- /// Load an image from an array of bytes.
- ///
- /// The data to load.
- /// A texture, ready to use in ImGui.
- public IDalamudTextureWrap? LoadImage(byte[] imageData)
- {
- if (this.scene == null)
- throw new InvalidOperationException("Scene isn't ready.");
-
- try
- {
- var wrap = this.scene?.LoadImage(imageData);
- return wrap != null ? new DalamudTextureWrap(wrap) : null;
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Failed to load image from memory");
- }
-
- return null;
- }
-
- ///
- /// Load an image from an array of bytes.
- ///
- /// The data to load.
- /// The width in pixels.
- /// The height in pixels.
- /// The number of channels.
- /// A texture, ready to use in ImGui.
- public IDalamudTextureWrap? LoadImageRaw(byte[] imageData, int width, int height, int numChannels)
- {
- if (this.scene == null)
- throw new InvalidOperationException("Scene isn't ready.");
-
- try
- {
- var wrap = this.scene?.LoadImageRaw(imageData, width, height, numChannels);
- return wrap != null ? new DalamudTextureWrap(wrap) : null;
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Failed to load image from raw data");
- }
-
- return null;
- }
-
- ///
- /// Check whether the current D3D11 Device supports the given DXGI format.
- ///
- /// DXGI format to check.
- /// Whether it is supported.
- public bool SupportsDxgiFormat(Format dxgiFormat) => this.scene is null
- ? throw new InvalidOperationException("Scene isn't ready.")
- : this.scene.Device.CheckFormatSupport(dxgiFormat).HasFlag(FormatSupport.Texture2D);
-
- ///
- /// Load an image from a span of bytes of specified format.
- ///
- /// The data to load.
- /// The pitch(stride) in bytes.
- /// The width in pixels.
- /// The height in pixels.
- /// Format of the texture.
- /// A texture, ready to use in ImGui.
- public DalamudTextureWrap LoadImageFromDxgiFormat(ReadOnlySpan data, int pitch, int width, int height, Format dxgiFormat)
- {
- if (this.scene == null)
- throw new InvalidOperationException("Scene isn't ready.");
-
- ShaderResourceView resView;
- unsafe
- {
- fixed (void* pData = data)
- {
- var texDesc = new Texture2DDescription
- {
- Width = width,
- Height = height,
- MipLevels = 1,
- ArraySize = 1,
- Format = dxgiFormat,
- SampleDescription = new(1, 0),
- Usage = ResourceUsage.Immutable,
- BindFlags = BindFlags.ShaderResource,
- CpuAccessFlags = CpuAccessFlags.None,
- OptionFlags = ResourceOptionFlags.None,
- };
-
- using var texture = new Texture2D(this.Device, texDesc, new DataRectangle(new(pData), pitch));
- resView = new(this.Device, texture, new()
- {
- Format = texDesc.Format,
- Dimension = ShaderResourceViewDimension.Texture2D,
- Texture2D = { MipLevels = texDesc.MipLevels },
- });
- }
- }
-
- // no sampler for now because the ImGui implementation we copied doesn't allow for changing it
- return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height));
- }
-
-#nullable restore
-
- ///
- /// Get a texture handle for the specified Lumina TexFile.
- ///
- /// The texture to obtain a handle to.
- /// A texture wrap that can be used to render the texture.
- /// Thrown when the graphics system is not available yet. Relevant for plugins when LoadRequiredState is set to 0 or 1.
- /// Thrown when the given is not supported. Most likely is that the file is corrupt.
- public DalamudTextureWrap LoadImageFromTexFile(TexFile file)
- {
- if (!this.IsReady)
- throw new InvalidOperationException("Cannot create textures before scene is ready");
-
- var buffer = file.TextureBuffer;
- var bpp = 1 << (((int)file.Header.Format & (int)TexFile.TextureFormat.BppMask) >>
- (int)TexFile.TextureFormat.BppShift);
-
- var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false);
- if (conversion != TexFile.DxgiFormatConversion.NoConversion || !this.SupportsDxgiFormat((Format)dxgiFormat))
- {
- dxgiFormat = (int)Format.B8G8R8A8_UNorm;
- buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8);
- bpp = 32;
- }
-
- var pitch = buffer is BlockCompressionTextureBuffer
- ? Math.Max(1, (buffer.Width + 3) / 4) * 2 * bpp
- : ((buffer.Width * bpp) + 7) / 8;
-
- return this.LoadImageFromDxgiFormat(buffer.RawData, pitch, buffer.Width, buffer.Height, (Format)dxgiFormat);
- }
-
///
/// Sets up a deferred invocation of font rebuilding, before the next render frame.
///
diff --git a/Dalamud/Interface/Internal/SharableTextures/FileSystemSharableTexture.cs b/Dalamud/Interface/Internal/SharableTextures/FileSystemSharableTexture.cs
index bd867d6a3..fe1b16de8 100644
--- a/Dalamud/Interface/Internal/SharableTextures/FileSystemSharableTexture.cs
+++ b/Dalamud/Interface/Internal/SharableTextures/FileSystemSharableTexture.cs
@@ -1,3 +1,5 @@
+using System.Buffers;
+using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -64,11 +66,9 @@ internal sealed class FileSystemSharableTexture : SharableTexture
this.CreateTextureAsync,
this.LoadCancellationToken);
- private Task CreateTextureAsync(CancellationToken cancellationToken)
+ private async Task CreateTextureAsync(CancellationToken cancellationToken)
{
- var w = (IDalamudTextureWrap)Service.Get().LoadImage(this.path)
- ?? throw new("Failed to load image because of an unknown reason.");
- this.DisposeSuppressingWrap = new(w);
- return Task.FromResult(w);
+ var tm = await Service.GetAsync();
+ return tm.NoThrottleGetFromImage(await File.ReadAllBytesAsync(this.path, cancellationToken));
}
}
diff --git a/Dalamud/Interface/Internal/SharableTextures/GamePathSharableTexture.cs b/Dalamud/Interface/Internal/SharableTextures/GamePathSharableTexture.cs
index 82f2d1b48..d7c478187 100644
--- a/Dalamud/Interface/Internal/SharableTextures/GamePathSharableTexture.cs
+++ b/Dalamud/Interface/Internal/SharableTextures/GamePathSharableTexture.cs
@@ -70,10 +70,11 @@ internal sealed class GamePathSharableTexture : SharableTexture
private async Task CreateTextureAsync(CancellationToken cancellationToken)
{
var dm = await Service.GetAsync();
- var im = await Service.GetAsync();
- var file = dm.GetFile(this.path);
+ var tm = await Service.GetAsync();
+ if (dm.GetFile(this.path) is not { } file)
+ throw new FileNotFoundException();
cancellationToken.ThrowIfCancellationRequested();
- var t = (IDalamudTextureWrap)im.LoadImageFromTexFile(file ?? throw new FileNotFoundException());
+ var t = tm.NoThrottleGetFromTexFile(file);
this.DisposeSuppressingWrap = new(t);
return t;
}
diff --git a/Dalamud/Interface/Internal/TextureManager.cs b/Dalamud/Interface/Internal/TextureManager.cs
index 378697e88..a4edb4449 100644
--- a/Dalamud/Interface/Internal/TextureManager.cs
+++ b/Dalamud/Interface/Internal/TextureManager.cs
@@ -19,6 +19,11 @@ using Dalamud.Utility;
using Lumina.Data.Files;
+using SharpDX;
+using SharpDX.Direct3D;
+using SharpDX.Direct3D11;
+using SharpDX.DXGI;
+
namespace Dalamud.Interface.Internal;
// TODO API10: Remove keepAlive from public APIs
@@ -241,9 +246,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
CancellationToken cancellationToken = default) =>
this.textureLoadThrottler.CreateLoader(
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
- _ => Task.FromResult(
- this.interfaceManager.LoadImage(bytes.ToArray())
- ?? throw new("Failed to load image because of an unknown reason.")),
+ ct => Task.Run(() => this.NoThrottleGetFromImage(bytes.ToArray()), ct),
cancellationToken);
///
@@ -273,13 +276,46 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
///
public IDalamudTextureWrap GetFromRaw(
RawImageSpecification specs,
- ReadOnlySpan bytes) =>
- this.interfaceManager.LoadImageFromDxgiFormat(
- bytes,
- specs.Pitch,
- specs.Width,
- specs.Height,
- (SharpDX.DXGI.Format)specs.DxgiFormat);
+ ReadOnlySpan bytes)
+ {
+ if (this.interfaceManager.Scene is not { } scene)
+ {
+ _ = Service.Get();
+ scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
+ }
+
+ ShaderResourceView resView;
+ unsafe
+ {
+ fixed (void* pData = bytes)
+ {
+ var texDesc = new Texture2DDescription
+ {
+ Width = specs.Width,
+ Height = specs.Height,
+ MipLevels = 1,
+ ArraySize = 1,
+ Format = (Format)specs.DxgiFormat,
+ SampleDescription = new(1, 0),
+ Usage = ResourceUsage.Immutable,
+ BindFlags = BindFlags.ShaderResource,
+ CpuAccessFlags = CpuAccessFlags.None,
+ OptionFlags = ResourceOptionFlags.None,
+ };
+
+ using var texture = new Texture2D(scene.Device, texDesc, new DataRectangle(new(pData), specs.Pitch));
+ resView = new(scene.Device, texture, new()
+ {
+ Format = texDesc.Format,
+ Dimension = ShaderResourceViewDimension.Texture2D,
+ Texture2D = { MipLevels = texDesc.MipLevels },
+ });
+ }
+ }
+
+ // no sampler for now because the ImGui implementation we copied doesn't allow for changing it
+ return new DalamudTextureWrap(new D3DTextureWrap(resView, specs.Width, specs.Height));
+ }
///
public Task GetFromRawAsync(
@@ -325,12 +361,20 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
CancellationToken cancellationToken = default) =>
this.textureLoadThrottler.CreateLoader(
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
- _ => Task.FromResult(this.interfaceManager.LoadImageFromTexFile(file)),
+ ct => Task.Run(() => this.NoThrottleGetFromTexFile(file), ct),
cancellationToken);
-
+
///
- public bool SupportsDxgiFormat(int dxgiFormat) =>
- this.interfaceManager.SupportsDxgiFormat((SharpDX.DXGI.Format)dxgiFormat);
+ public bool SupportsDxgiFormat(int dxgiFormat)
+ {
+ if (this.interfaceManager.Scene is not { } scene)
+ {
+ _ = Service.Get();
+ scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
+ }
+
+ return scene.Device.CheckFormatSupport((Format)dxgiFormat).HasFlag(FormatSupport.Texture2D);
+ }
///
public bool TryGetIconPath(in GameIconLookup lookup, out string path)
@@ -443,6 +487,46 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
}
}
+ ///
+ /// Gets a texture from the given image. Skips the load throttler; intended to be used from implementation of
+ /// s.
+ ///
+ /// The data.
+ /// The loaded texture.
+ internal IDalamudTextureWrap NoThrottleGetFromImage(ReadOnlyMemory bytes)
+ {
+ if (this.interfaceManager.Scene is not { } scene)
+ {
+ _ = Service.Get();
+ scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
+ }
+
+ return new DalamudTextureWrap(
+ scene.LoadImage(bytes.ToArray())
+ ?? throw new("Failed to load image because of an unknown reason."));
+ }
+
+ ///
+ /// Gets a texture from the given . Skips the load throttler; intended to be used from
+ /// implementation of s.
+ ///
+ /// The data.
+ /// The loaded texture.
+ internal IDalamudTextureWrap NoThrottleGetFromTexFile(TexFile file)
+ {
+ var buffer = file.TextureBuffer;
+ var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false);
+ if (conversion != TexFile.DxgiFormatConversion.NoConversion || !this.SupportsDxgiFormat(dxgiFormat))
+ {
+ dxgiFormat = (int)Format.B8G8R8A8_UNorm;
+ buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8);
+ }
+
+ return this.GetFromRaw(
+ RawImageSpecification.From(buffer.Width, buffer.Height, dxgiFormat),
+ buffer.RawData);
+ }
+
private static string FormatIconPath(uint iconId, string? type, bool highResolution)
{
var format = highResolution ? HighResolutionIconFileFormat : IconFileFormat;
diff --git a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs
index 29adbb3e5..6ae45c962 100644
--- a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs
@@ -269,33 +269,17 @@ internal class PluginImageCache : IDisposable, IServiceType
if (bytes == null)
return null;
- var interfaceManager = (await Service.GetAsync()).Manager;
- var framework = await Service.GetAsync();
+ var textureManager = await Service.GetAsync();
IDalamudTextureWrap? image;
// FIXME(goat): This is a hack around this call failing randomly in certain situations. Might be related to not being called on the main thread.
try
{
- image = interfaceManager.LoadImage(bytes);
+ image = await textureManager.GetFromImageAsync(bytes);
}
catch (Exception ex)
{
- Log.Error(ex, "Access violation during load plugin {name} from {Loc} (Async Thread)", name, loc);
-
- try
- {
- image = await framework.RunOnFrameworkThread(() => interfaceManager.LoadImage(bytes));
- }
- catch (Exception ex2)
- {
- Log.Error(ex2, "Access violation during load plugin {name} from {Loc} (Framework Thread)", name, loc);
- return null;
- }
- }
-
- if (image == null)
- {
- Log.Error($"Could not load {name} for {manifest.InternalName} at {loc}");
+ Log.Error(ex, $"Could not load {name} for {manifest.InternalName} at {loc}");
return null;
}
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index 95c227662..29e76434b 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -69,8 +69,8 @@ internal class PluginInstallerWindow : Window, IDisposable
private string[] testerImagePaths = new string[5];
private string testerIconPath = string.Empty;
- private IDalamudTextureWrap?[]? testerImages;
- private IDalamudTextureWrap? testerIcon;
+ private Task?[]? testerImages;
+ private Task? testerIcon;
private bool testerError = false;
private bool testerUpdateAvailable = false;
@@ -1510,10 +1510,10 @@ internal class PluginInstallerWindow : Window, IDisposable
ImGui.SetCursorPos(startCursor);
- var hasIcon = this.testerIcon != null;
+ var hasIcon = this.testerIcon?.IsCompletedSuccessfully is true;
var iconTex = this.imageCache.DefaultIcon;
- if (hasIcon) iconTex = this.testerIcon;
+ if (hasIcon) iconTex = this.testerIcon.Result;
var iconSize = ImGuiHelpers.ScaledVector2(64, 64);
@@ -1607,10 +1607,24 @@ internal class PluginInstallerWindow : Window, IDisposable
for (var i = 0; i < this.testerImages.Length; i++)
{
var popupId = $"pluginTestingImage{i}";
- var image = this.testerImages[i];
- if (image == null)
+ var imageTask = this.testerImages[i];
+ if (imageTask == null)
continue;
+ if (!imageTask.IsCompleted)
+ {
+ ImGui.TextUnformatted("Loading...");
+ continue;
+ }
+
+ if (imageTask.Exception is not null)
+ {
+ ImGui.TextUnformatted(imageTask.Exception.ToString());
+ continue;
+ }
+
+ var image = imageTask.Result;
+
ImGui.PushStyleVar(ImGuiStyleVar.PopupBorderSize, 0);
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero);
@@ -1666,14 +1680,37 @@ internal class PluginInstallerWindow : Window, IDisposable
ImGuiHelpers.ScaledDummy(20);
- static void CheckImageSize(IDalamudTextureWrap? image, int maxWidth, int maxHeight, bool requireSquare)
+ static void CheckImageSize(Task? imageTask, int maxWidth, int maxHeight, bool requireSquare)
{
- if (image == null)
+ if (imageTask == null)
return;
- if (image.Width > maxWidth || image.Height > maxHeight)
- ImGui.TextColored(ImGuiColors.DalamudRed, $"Image is larger than the maximum allowed resolution ({image.Width}x{image.Height} > {maxWidth}x{maxHeight})");
- if (requireSquare && image.Width != image.Height)
- ImGui.TextColored(ImGuiColors.DalamudRed, $"Image must be square! Current size: {image.Width}x{image.Height}");
+
+ if (!imageTask.IsCompleted)
+ {
+ ImGui.Text("Loading...");
+ return;
+ }
+
+ ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
+
+ if (imageTask.Exception is { } exc)
+ {
+ ImGui.TextUnformatted(exc.ToString());
+ }
+ else
+ {
+ var image = imageTask.Result;
+ if (image.Width > maxWidth || image.Height > maxHeight)
+ {
+ ImGui.TextUnformatted(
+ $"Image is larger than the maximum allowed resolution ({image.Width}x{image.Height} > {maxWidth}x{maxHeight})");
+ }
+
+ if (requireSquare && image.Width != image.Height)
+ ImGui.TextUnformatted($"Image must be square! Current size: {image.Width}x{image.Height}");
+ }
+
+ ImGui.PopStyleColor();
}
ImGui.InputText("Icon Path", ref this.testerIconPath, 1000);
@@ -1695,7 +1732,7 @@ internal class PluginInstallerWindow : Window, IDisposable
if (this.testerImages?.Length > 4)
CheckImageSize(this.testerImages[4], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false);
- var im = Service.Get();
+ var tm = Service.Get();
if (ImGui.Button("Load"))
{
try
@@ -1708,23 +1745,18 @@ internal class PluginInstallerWindow : Window, IDisposable
if (!this.testerIconPath.IsNullOrEmpty())
{
- this.testerIcon = im.LoadImage(this.testerIconPath);
+ this.testerIcon = tm.GetFromFileAsync(this.testerIconPath);
}
- this.testerImages = new IDalamudTextureWrap[this.testerImagePaths.Length];
+ this.testerImages = new Task?[this.testerImagePaths.Length];
for (var i = 0; i < this.testerImagePaths.Length; i++)
{
if (this.testerImagePaths[i].IsNullOrEmpty())
continue;
- if (this.testerImages[i] != null)
- {
- this.testerImages[i].Dispose();
- this.testerImages[i] = null;
- }
-
- this.testerImages[i] = im.LoadImage(this.testerImagePaths[i]);
+ _ = this.testerImages[i]?.ToContentDisposedTask();
+ this.testerImages[i] = tm.GetFromFileAsync(this.testerImagePaths[i]);
}
}
catch (Exception ex)
diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
index 55af20329..0148e80dd 100644
--- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs
@@ -10,6 +10,7 @@ using Dalamud.Interface.FontIdentifier;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Utility;
+using Dalamud.Plugin.Services;
using Dalamud.Storage.Assets;
using Dalamud.Utility;
@@ -579,7 +580,7 @@ internal sealed partial class FontAtlasFactory
var buf = Array.Empty();
try
{
- var use4 = this.factory.InterfaceManager.SupportsDxgiFormat(Format.B4G4R4A4_UNorm);
+ var use4 = this.factory.TextureManager.SupportsDxgiFormat((int)Format.B4G4R4A4_UNorm);
var bpp = use4 ? 2 : 4;
var width = this.NewImAtlas.TexWidth;
var height = this.NewImAtlas.TexHeight;
@@ -591,12 +592,9 @@ internal sealed partial class FontAtlasFactory
}
else if (texture.TexPixelsRGBA32 is not null)
{
- var wrap = this.factory.InterfaceManager.LoadImageFromDxgiFormat(
- new(texture.TexPixelsRGBA32, width * height * 4),
- width * 4,
- width,
- height,
- use4 ? Format.B4G4R4A4_UNorm : Format.R8G8B8A8_UNorm);
+ var wrap = this.factory.TextureManager.GetFromRaw(
+ RawImageSpecification.Rgba32(width, height),
+ new(texture.TexPixelsRGBA32, width * height * 4));
this.data.AddExistingTexture(wrap);
texture.TexID = wrap.ImGuiHandle;
}
@@ -634,12 +632,13 @@ internal sealed partial class FontAtlasFactory
}
}
- var wrap = this.factory.InterfaceManager.LoadImageFromDxgiFormat(
- buf,
- width * bpp,
- width,
- height,
- use4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm);
+ var wrap = this.factory.TextureManager.GetFromRaw(
+ new(
+ width,
+ height,
+ width * bpp,
+ (int)(use4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm)),
+ buf);
this.data.AddExistingTexture(wrap);
texture.TexID = wrap.ImGuiHandle;
continue;
diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs
index 3e0fd1394..ffddcc272 100644
--- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs
@@ -46,11 +46,13 @@ internal sealed partial class FontAtlasFactory
DataManager dataManager,
Framework framework,
InterfaceManager interfaceManager,
- DalamudAssetManager dalamudAssetManager)
+ DalamudAssetManager dalamudAssetManager,
+ TextureManager textureManager)
{
this.Framework = framework;
this.InterfaceManager = interfaceManager;
this.dalamudAssetManager = dalamudAssetManager;
+ this.TextureManager = textureManager;
this.SceneTask = Service
.GetAsync()
.ContinueWith(r => r.Result.Manager.Scene);
@@ -144,6 +146,11 @@ internal sealed partial class FontAtlasFactory
///
public InterfaceManager InterfaceManager { get; }
+ ///
+ /// Gets the service instance of .
+ ///
+ public TextureManager TextureManager { get; }
+
///
/// Gets the async task for inside .
///
@@ -346,7 +353,7 @@ internal sealed partial class FontAtlasFactory
var numPixels = texFile.Header.Width * texFile.Header.Height;
_ = Service.Get();
- var targetIsB4G4R4A4 = this.InterfaceManager.SupportsDxgiFormat(Format.B4G4R4A4_UNorm);
+ var targetIsB4G4R4A4 = this.TextureManager.SupportsDxgiFormat((int)Format.B4G4R4A4_UNorm);
var bpp = targetIsB4G4R4A4 ? 2 : 4;
var buffer = ArrayPool.Shared.Rent(numPixels * bpp);
try
@@ -369,12 +376,13 @@ internal sealed partial class FontAtlasFactory
}
return this.scopedFinalizer.Add(
- this.InterfaceManager.LoadImageFromDxgiFormat(
- buffer,
- texFile.Header.Width * bpp,
- texFile.Header.Width,
- texFile.Header.Height,
- targetIsB4G4R4A4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm));
+ this.TextureManager.GetFromRaw(
+ new(
+ texFile.Header.Width,
+ texFile.Header.Height,
+ texFile.Header.Width * bpp,
+ (int)(targetIsB4G4R4A4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm)),
+ buffer));
}
finally
{
diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs
index ca0ecb71c..74a718507 100644
--- a/Dalamud/Interface/UiBuilder.cs
+++ b/Dalamud/Interface/UiBuilder.cs
@@ -379,6 +379,8 @@ public sealed class UiBuilder : IDisposable
private Task InterfaceManagerWithSceneAsync =>
Service.GetAsync().ContinueWith(task => task.Result.Manager);
+ private ITextureProvider TextureProvider => Service.Get();
+
///
/// Loads an image from the specified file.
///
@@ -386,9 +388,7 @@ public sealed class UiBuilder : IDisposable
/// A object wrapping the created image. Use inside ImGui.Image().
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
- public IDalamudTextureWrap LoadImage(string filePath)
- => this.InterfaceManagerWithScene?.LoadImage(filePath)
- ?? throw new InvalidOperationException("Load failed.");
+ public IDalamudTextureWrap LoadImage(string filePath) => this.TextureProvider.GetFromFileAsync(filePath).Result;
///
/// Loads an image from a byte stream, such as a png downloaded into memory.
@@ -397,9 +397,7 @@ public sealed class UiBuilder : IDisposable
/// A object wrapping the created image. Use inside ImGui.Image().
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
- public IDalamudTextureWrap LoadImage(byte[] imageData)
- => this.InterfaceManagerWithScene?.LoadImage(imageData)
- ?? throw new InvalidOperationException("Load failed.");
+ public IDalamudTextureWrap LoadImage(byte[] imageData) => this.TextureProvider.GetFromImageAsync(imageData).Result;
///
/// Loads an image from raw unformatted pixel data, with no type or header information. To load formatted data, use .
@@ -411,9 +409,12 @@ public sealed class UiBuilder : IDisposable
/// A object wrapping the created image. Use inside ImGui.Image().
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromRaw)} or {nameof(ITextureProvider.GetFromRawAsync)}.")]
- public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels)
- => this.InterfaceManagerWithScene?.LoadImageRaw(imageData, width, height, numChannels)
- ?? throw new InvalidOperationException("Load failed.");
+ public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) =>
+ numChannels switch
+ {
+ 4 => this.TextureProvider.GetFromRaw(RawImageSpecification.Rgba32(width, height), imageData),
+ _ => throw new NotSupportedException(),
+ };
///
/// Loads an ULD file that can load textures containing multiple icons in a single texture.
@@ -430,10 +431,7 @@ public sealed class UiBuilder : IDisposable
/// A object wrapping the created image. Use inside ImGui.Image().
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
- public Task LoadImageAsync(string filePath) => Task.Run(
- async () =>
- (await this.InterfaceManagerWithSceneAsync).LoadImage(filePath)
- ?? throw new InvalidOperationException("Load failed."));
+ public Task LoadImageAsync(string filePath) => this.TextureProvider.GetFromFileAsync(filePath);
///
/// Asynchronously loads an image from a byte stream, such as a png downloaded into memory, when it's possible to do so.
@@ -442,10 +440,8 @@ public sealed class UiBuilder : IDisposable
/// A object wrapping the created image. Use inside ImGui.Image().
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
- public Task LoadImageAsync(byte[] imageData) => Task.Run(
- async () =>
- (await this.InterfaceManagerWithSceneAsync).LoadImage(imageData)
- ?? throw new InvalidOperationException("Load failed."));
+ public Task LoadImageAsync(byte[] imageData) =>
+ this.TextureProvider.GetFromImageAsync(imageData);
///
/// Asynchronously loads an image from raw unformatted pixel data, with no type or header information, when it's possible to do so. To load formatted data, use .
@@ -457,10 +453,12 @@ public sealed class UiBuilder : IDisposable
/// A object wrapping the created image. Use inside ImGui.Image().
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromRawAsync)}.")]
- public Task LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) => Task.Run(
- async () =>
- (await this.InterfaceManagerWithSceneAsync).LoadImageRaw(imageData, width, height, numChannels)
- ?? throw new InvalidOperationException("Load failed."));
+ public Task LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) =>
+ numChannels switch
+ {
+ 4 => this.TextureProvider.GetFromRawAsync(RawImageSpecification.Rgba32(width, height), imageData),
+ _ => Task.FromException(new NotSupportedException()),
+ };
///
/// Waits for UI to become available for use.
diff --git a/Dalamud/Plugin/Services/RawImageSpecification.cs b/Dalamud/Plugin/Services/RawImageSpecification.cs
index 696b3d6b6..206ce578e 100644
--- a/Dalamud/Plugin/Services/RawImageSpecification.cs
+++ b/Dalamud/Plugin/Services/RawImageSpecification.cs
@@ -213,4 +213,14 @@ public record struct RawImageSpecification(int Width, int Height, int Pitch, int
/// The new instance.
public static RawImageSpecification Rgba32(int width, int height) =>
new(width, height, width * 4, (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM);
+
+ ///
+ /// Creates a new instance of record using the given resolution,
+ /// in A8 UNorm pixel format.
+ ///
+ /// The width.
+ /// The height.
+ /// The new instance.
+ public static RawImageSpecification A8(int width, int height) =>
+ new(width, height, width, (int)DXGI_FORMAT.DXGI_FORMAT_A8_UNORM);
}
diff --git a/Dalamud/Storage/Assets/DalamudAssetManager.cs b/Dalamud/Storage/Assets/DalamudAssetManager.cs
index 8abf42e5c..83f03e274 100644
--- a/Dalamud/Storage/Assets/DalamudAssetManager.cs
+++ b/Dalamud/Storage/Assets/DalamudAssetManager.cs
@@ -302,17 +302,17 @@ internal sealed class DalamudAssetManager : IServiceType, IDisposable, IDalamudA
var buf = Array.Empty();
try
{
- var im = (await Service.GetAsync()).Manager;
+ var tm = await Service.GetAsync();
await using var stream = await this.CreateStreamAsync(asset);
var length = checked((int)stream.Length);
buf = ArrayPool.Shared.Rent(length);
stream.ReadExactly(buf, 0, length);
var image = purpose switch
{
- DalamudAssetPurpose.TextureFromPng => im.LoadImage(buf),
+ DalamudAssetPurpose.TextureFromPng => await tm.GetFromImageAsync(buf),
DalamudAssetPurpose.TextureFromRaw =>
asset.GetAttribute() is { } raw
- ? im.LoadImageFromDxgiFormat(buf, raw.Pitch, raw.Width, raw.Height, raw.Format)
+ ? await tm.GetFromRawAsync(raw.Specification, buf)
: throw new InvalidOperationException(
"TextureFromRaw must accompany a DalamudAssetRawTextureAttribute."),
_ => null,
diff --git a/Dalamud/Storage/Assets/DalamudAssetRawTextureAttribute.cs b/Dalamud/Storage/Assets/DalamudAssetRawTextureAttribute.cs
index b79abb7d7..99253411b 100644
--- a/Dalamud/Storage/Assets/DalamudAssetRawTextureAttribute.cs
+++ b/Dalamud/Storage/Assets/DalamudAssetRawTextureAttribute.cs
@@ -1,4 +1,6 @@
-using SharpDX.DXGI;
+using Dalamud.Plugin.Services;
+
+using SharpDX.DXGI;
namespace Dalamud.Storage.Assets;
@@ -17,29 +19,11 @@ internal class DalamudAssetRawTextureAttribute : Attribute
/// The format.
public DalamudAssetRawTextureAttribute(int width, int pitch, int height, Format format)
{
- this.Width = width;
- this.Pitch = pitch;
- this.Height = height;
- this.Format = format;
+ this.Specification = new(width, height, pitch, (int)format);
}
///
- /// Gets the width.
+ /// Gets the specification.
///
- public int Width { get; }
-
- ///
- /// Gets the pitch.
- ///
- public int Pitch { get; }
-
- ///
- /// Gets the height.
- ///
- public int Height { get; }
-
- ///
- /// Gets the format.
- ///
- public Format Format { get; }
+ public RawImageSpecification Specification { get; }
}