mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-02 13:53:40 +01:00
Move all texture loading functionalities from IM to TM
This commit is contained in:
parent
248c7911a0
commit
71b84bcf40
12 changed files with 230 additions and 298 deletions
|
|
@ -26,14 +26,9 @@ using Dalamud.Utility.Timing;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
|
|
||||||
using Lumina.Data.Files;
|
|
||||||
using Lumina.Data.Parsing.Tex.Buffers;
|
|
||||||
|
|
||||||
using PInvoke;
|
using PInvoke;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using SharpDX;
|
using SharpDX;
|
||||||
using SharpDX.Direct3D;
|
|
||||||
using SharpDX.Direct3D11;
|
|
||||||
using SharpDX.DXGI;
|
using SharpDX.DXGI;
|
||||||
|
|
||||||
// general dev notes, here because it's easiest
|
// general dev notes, here because it's easiest
|
||||||
|
|
@ -261,169 +256,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#nullable enable
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load an image from disk.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filePath">The filepath to load.</param>
|
|
||||||
/// <returns>A texture, ready to use in ImGui.</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load an image from an array of bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="imageData">The data to load.</param>
|
|
||||||
/// <returns>A texture, ready to use in ImGui.</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load an image from an array of bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="imageData">The data to load.</param>
|
|
||||||
/// <param name="width">The width in pixels.</param>
|
|
||||||
/// <param name="height">The height in pixels.</param>
|
|
||||||
/// <param name="numChannels">The number of channels.</param>
|
|
||||||
/// <returns>A texture, ready to use in ImGui.</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check whether the current D3D11 Device supports the given DXGI format.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dxgiFormat">DXGI format to check.</param>
|
|
||||||
/// <returns>Whether it is supported.</returns>
|
|
||||||
public bool SupportsDxgiFormat(Format dxgiFormat) => this.scene is null
|
|
||||||
? throw new InvalidOperationException("Scene isn't ready.")
|
|
||||||
: this.scene.Device.CheckFormatSupport(dxgiFormat).HasFlag(FormatSupport.Texture2D);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load an image from a span of bytes of specified format.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The data to load.</param>
|
|
||||||
/// <param name="pitch">The pitch(stride) in bytes.</param>
|
|
||||||
/// <param name="width">The width in pixels.</param>
|
|
||||||
/// <param name="height">The height in pixels.</param>
|
|
||||||
/// <param name="dxgiFormat">Format of the texture.</param>
|
|
||||||
/// <returns>A texture, ready to use in ImGui.</returns>
|
|
||||||
public DalamudTextureWrap LoadImageFromDxgiFormat(ReadOnlySpan<byte> 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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a texture handle for the specified Lumina TexFile.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">The texture to obtain a handle to.</param>
|
|
||||||
/// <returns>A texture wrap that can be used to render the texture.</returns>
|
|
||||||
/// <exception cref="InvalidOperationException">Thrown when the graphics system is not available yet. Relevant for plugins when LoadRequiredState is set to 0 or 1.</exception>
|
|
||||||
/// <exception cref="NotSupportedException">Thrown when the given <see cref="TexFile"/> is not supported. Most likely is that the file is corrupt.</exception>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets up a deferred invocation of font rebuilding, before the next render frame.
|
/// Sets up a deferred invocation of font rebuilding, before the next render frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Buffers;
|
||||||
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -64,11 +66,9 @@ internal sealed class FileSystemSharableTexture : SharableTexture
|
||||||
this.CreateTextureAsync,
|
this.CreateTextureAsync,
|
||||||
this.LoadCancellationToken);
|
this.LoadCancellationToken);
|
||||||
|
|
||||||
private Task<IDalamudTextureWrap> CreateTextureAsync(CancellationToken cancellationToken)
|
private async Task<IDalamudTextureWrap> CreateTextureAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var w = (IDalamudTextureWrap)Service<InterfaceManager>.Get().LoadImage(this.path)
|
var tm = await Service<TextureManager>.GetAsync();
|
||||||
?? throw new("Failed to load image because of an unknown reason.");
|
return tm.NoThrottleGetFromImage(await File.ReadAllBytesAsync(this.path, cancellationToken));
|
||||||
this.DisposeSuppressingWrap = new(w);
|
|
||||||
return Task.FromResult(w);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,11 @@ internal sealed class GamePathSharableTexture : SharableTexture
|
||||||
private async Task<IDalamudTextureWrap> CreateTextureAsync(CancellationToken cancellationToken)
|
private async Task<IDalamudTextureWrap> CreateTextureAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var dm = await Service<DataManager>.GetAsync();
|
var dm = await Service<DataManager>.GetAsync();
|
||||||
var im = await Service<InterfaceManager>.GetAsync();
|
var tm = await Service<TextureManager>.GetAsync();
|
||||||
var file = dm.GetFile<TexFile>(this.path);
|
if (dm.GetFile<TexFile>(this.path) is not { } file)
|
||||||
|
throw new FileNotFoundException();
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var t = (IDalamudTextureWrap)im.LoadImageFromTexFile(file ?? throw new FileNotFoundException());
|
var t = tm.NoThrottleGetFromTexFile(file);
|
||||||
this.DisposeSuppressingWrap = new(t);
|
this.DisposeSuppressingWrap = new(t);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ using Dalamud.Utility;
|
||||||
|
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
|
|
||||||
|
using SharpDX;
|
||||||
|
using SharpDX.Direct3D;
|
||||||
|
using SharpDX.Direct3D11;
|
||||||
|
using SharpDX.DXGI;
|
||||||
|
|
||||||
namespace Dalamud.Interface.Internal;
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
// TODO API10: Remove keepAlive from public APIs
|
// TODO API10: Remove keepAlive from public APIs
|
||||||
|
|
@ -241,9 +246,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
this.textureLoadThrottler.CreateLoader(
|
this.textureLoadThrottler.CreateLoader(
|
||||||
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
|
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
|
||||||
_ => Task.FromResult(
|
ct => Task.Run(() => this.NoThrottleGetFromImage(bytes.ToArray()), ct),
|
||||||
this.interfaceManager.LoadImage(bytes.ToArray())
|
|
||||||
?? throw new("Failed to load image because of an unknown reason.")),
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -273,13 +276,46 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDalamudTextureWrap GetFromRaw(
|
public IDalamudTextureWrap GetFromRaw(
|
||||||
RawImageSpecification specs,
|
RawImageSpecification specs,
|
||||||
ReadOnlySpan<byte> bytes) =>
|
ReadOnlySpan<byte> bytes)
|
||||||
this.interfaceManager.LoadImageFromDxgiFormat(
|
{
|
||||||
bytes,
|
if (this.interfaceManager.Scene is not { } scene)
|
||||||
specs.Pitch,
|
{
|
||||||
specs.Width,
|
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
|
||||||
specs.Height,
|
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
|
||||||
(SharpDX.DXGI.Format)specs.DxgiFormat);
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task<IDalamudTextureWrap> GetFromRawAsync(
|
public Task<IDalamudTextureWrap> GetFromRawAsync(
|
||||||
|
|
@ -325,12 +361,20 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
this.textureLoadThrottler.CreateLoader(
|
this.textureLoadThrottler.CreateLoader(
|
||||||
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
|
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
|
||||||
_ => Task.FromResult<IDalamudTextureWrap>(this.interfaceManager.LoadImageFromTexFile(file)),
|
ct => Task.Run(() => this.NoThrottleGetFromTexFile(file), ct),
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool SupportsDxgiFormat(int dxgiFormat) =>
|
public bool SupportsDxgiFormat(int dxgiFormat)
|
||||||
this.interfaceManager.SupportsDxgiFormat((SharpDX.DXGI.Format)dxgiFormat);
|
{
|
||||||
|
if (this.interfaceManager.Scene is not { } scene)
|
||||||
|
{
|
||||||
|
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
|
||||||
|
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return scene.Device.CheckFormatSupport((Format)dxgiFormat).HasFlag(FormatSupport.Texture2D);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool TryGetIconPath(in GameIconLookup lookup, out string path)
|
public bool TryGetIconPath(in GameIconLookup lookup, out string path)
|
||||||
|
|
@ -443,6 +487,46 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a texture from the given image. Skips the load throttler; intended to be used from implementation of
|
||||||
|
/// <see cref="SharableTexture"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytes">The data.</param>
|
||||||
|
/// <returns>The loaded texture.</returns>
|
||||||
|
internal IDalamudTextureWrap NoThrottleGetFromImage(ReadOnlyMemory<byte> bytes)
|
||||||
|
{
|
||||||
|
if (this.interfaceManager.Scene is not { } scene)
|
||||||
|
{
|
||||||
|
_ = Service<InterfaceManager.InterfaceManagerWithScene>.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."));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used from
|
||||||
|
/// implementation of <see cref="SharableTexture"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file">The data.</param>
|
||||||
|
/// <returns>The loaded texture.</returns>
|
||||||
|
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)
|
private static string FormatIconPath(uint iconId, string? type, bool highResolution)
|
||||||
{
|
{
|
||||||
var format = highResolution ? HighResolutionIconFileFormat : IconFileFormat;
|
var format = highResolution ? HighResolutionIconFileFormat : IconFileFormat;
|
||||||
|
|
|
||||||
|
|
@ -269,33 +269,17 @@ internal class PluginImageCache : IDisposable, IServiceType
|
||||||
if (bytes == null)
|
if (bytes == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var interfaceManager = (await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync()).Manager;
|
var textureManager = await Service<TextureManager>.GetAsync();
|
||||||
var framework = await Service<Framework>.GetAsync();
|
|
||||||
|
|
||||||
IDalamudTextureWrap? image;
|
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.
|
// 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
|
try
|
||||||
{
|
{
|
||||||
image = interfaceManager.LoadImage(bytes);
|
image = await textureManager.GetFromImageAsync(bytes);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Access violation during load plugin {name} from {Loc} (Async Thread)", name, loc);
|
Log.Error(ex, $"Could not load {name} for {manifest.InternalName} at {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}");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
private string[] testerImagePaths = new string[5];
|
private string[] testerImagePaths = new string[5];
|
||||||
private string testerIconPath = string.Empty;
|
private string testerIconPath = string.Empty;
|
||||||
|
|
||||||
private IDalamudTextureWrap?[]? testerImages;
|
private Task<IDalamudTextureWrap>?[]? testerImages;
|
||||||
private IDalamudTextureWrap? testerIcon;
|
private Task<IDalamudTextureWrap>? testerIcon;
|
||||||
|
|
||||||
private bool testerError = false;
|
private bool testerError = false;
|
||||||
private bool testerUpdateAvailable = false;
|
private bool testerUpdateAvailable = false;
|
||||||
|
|
@ -1510,10 +1510,10 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
ImGui.SetCursorPos(startCursor);
|
ImGui.SetCursorPos(startCursor);
|
||||||
|
|
||||||
var hasIcon = this.testerIcon != null;
|
var hasIcon = this.testerIcon?.IsCompletedSuccessfully is true;
|
||||||
|
|
||||||
var iconTex = this.imageCache.DefaultIcon;
|
var iconTex = this.imageCache.DefaultIcon;
|
||||||
if (hasIcon) iconTex = this.testerIcon;
|
if (hasIcon) iconTex = this.testerIcon.Result;
|
||||||
|
|
||||||
var iconSize = ImGuiHelpers.ScaledVector2(64, 64);
|
var iconSize = ImGuiHelpers.ScaledVector2(64, 64);
|
||||||
|
|
||||||
|
|
@ -1607,10 +1607,24 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
for (var i = 0; i < this.testerImages.Length; i++)
|
for (var i = 0; i < this.testerImages.Length; i++)
|
||||||
{
|
{
|
||||||
var popupId = $"pluginTestingImage{i}";
|
var popupId = $"pluginTestingImage{i}";
|
||||||
var image = this.testerImages[i];
|
var imageTask = this.testerImages[i];
|
||||||
if (image == null)
|
if (imageTask == null)
|
||||||
continue;
|
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.PopupBorderSize, 0);
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
|
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
||||||
|
|
@ -1666,14 +1680,37 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(20);
|
ImGuiHelpers.ScaledDummy(20);
|
||||||
|
|
||||||
static void CheckImageSize(IDalamudTextureWrap? image, int maxWidth, int maxHeight, bool requireSquare)
|
static void CheckImageSize(Task<IDalamudTextureWrap>? imageTask, int maxWidth, int maxHeight, bool requireSquare)
|
||||||
{
|
{
|
||||||
if (image == null)
|
if (imageTask == null)
|
||||||
return;
|
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 (!imageTask.IsCompleted)
|
||||||
if (requireSquare && image.Width != image.Height)
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, $"Image must be square! Current size: {image.Width}x{image.Height}");
|
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);
|
ImGui.InputText("Icon Path", ref this.testerIconPath, 1000);
|
||||||
|
|
@ -1695,7 +1732,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (this.testerImages?.Length > 4)
|
if (this.testerImages?.Length > 4)
|
||||||
CheckImageSize(this.testerImages[4], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false);
|
CheckImageSize(this.testerImages[4], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false);
|
||||||
|
|
||||||
var im = Service<InterfaceManager>.Get();
|
var tm = Service<TextureManager>.Get();
|
||||||
if (ImGui.Button("Load"))
|
if (ImGui.Button("Load"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -1708,23 +1745,18 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
|
|
||||||
if (!this.testerIconPath.IsNullOrEmpty())
|
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<IDalamudTextureWrap>?[this.testerImagePaths.Length];
|
||||||
|
|
||||||
for (var i = 0; i < this.testerImagePaths.Length; i++)
|
for (var i = 0; i < this.testerImagePaths.Length; i++)
|
||||||
{
|
{
|
||||||
if (this.testerImagePaths[i].IsNullOrEmpty())
|
if (this.testerImagePaths[i].IsNullOrEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (this.testerImages[i] != null)
|
_ = this.testerImages[i]?.ToContentDisposedTask();
|
||||||
{
|
this.testerImages[i] = tm.GetFromFileAsync(this.testerImagePaths[i]);
|
||||||
this.testerImages[i].Dispose();
|
|
||||||
this.testerImages[i] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.testerImages[i] = im.LoadImage(this.testerImagePaths[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using Dalamud.Interface.FontIdentifier;
|
||||||
using Dalamud.Interface.GameFonts;
|
using Dalamud.Interface.GameFonts;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Storage.Assets;
|
using Dalamud.Storage.Assets;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
|
@ -579,7 +580,7 @@ internal sealed partial class FontAtlasFactory
|
||||||
var buf = Array.Empty<byte>();
|
var buf = Array.Empty<byte>();
|
||||||
try
|
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 bpp = use4 ? 2 : 4;
|
||||||
var width = this.NewImAtlas.TexWidth;
|
var width = this.NewImAtlas.TexWidth;
|
||||||
var height = this.NewImAtlas.TexHeight;
|
var height = this.NewImAtlas.TexHeight;
|
||||||
|
|
@ -591,12 +592,9 @@ internal sealed partial class FontAtlasFactory
|
||||||
}
|
}
|
||||||
else if (texture.TexPixelsRGBA32 is not null)
|
else if (texture.TexPixelsRGBA32 is not null)
|
||||||
{
|
{
|
||||||
var wrap = this.factory.InterfaceManager.LoadImageFromDxgiFormat(
|
var wrap = this.factory.TextureManager.GetFromRaw(
|
||||||
new(texture.TexPixelsRGBA32, width * height * 4),
|
RawImageSpecification.Rgba32(width, height),
|
||||||
width * 4,
|
new(texture.TexPixelsRGBA32, width * height * 4));
|
||||||
width,
|
|
||||||
height,
|
|
||||||
use4 ? Format.B4G4R4A4_UNorm : Format.R8G8B8A8_UNorm);
|
|
||||||
this.data.AddExistingTexture(wrap);
|
this.data.AddExistingTexture(wrap);
|
||||||
texture.TexID = wrap.ImGuiHandle;
|
texture.TexID = wrap.ImGuiHandle;
|
||||||
}
|
}
|
||||||
|
|
@ -634,12 +632,13 @@ internal sealed partial class FontAtlasFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrap = this.factory.InterfaceManager.LoadImageFromDxgiFormat(
|
var wrap = this.factory.TextureManager.GetFromRaw(
|
||||||
buf,
|
new(
|
||||||
width * bpp,
|
width,
|
||||||
width,
|
height,
|
||||||
height,
|
width * bpp,
|
||||||
use4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm);
|
(int)(use4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm)),
|
||||||
|
buf);
|
||||||
this.data.AddExistingTexture(wrap);
|
this.data.AddExistingTexture(wrap);
|
||||||
texture.TexID = wrap.ImGuiHandle;
|
texture.TexID = wrap.ImGuiHandle;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,13 @@ internal sealed partial class FontAtlasFactory
|
||||||
DataManager dataManager,
|
DataManager dataManager,
|
||||||
Framework framework,
|
Framework framework,
|
||||||
InterfaceManager interfaceManager,
|
InterfaceManager interfaceManager,
|
||||||
DalamudAssetManager dalamudAssetManager)
|
DalamudAssetManager dalamudAssetManager,
|
||||||
|
TextureManager textureManager)
|
||||||
{
|
{
|
||||||
this.Framework = framework;
|
this.Framework = framework;
|
||||||
this.InterfaceManager = interfaceManager;
|
this.InterfaceManager = interfaceManager;
|
||||||
this.dalamudAssetManager = dalamudAssetManager;
|
this.dalamudAssetManager = dalamudAssetManager;
|
||||||
|
this.TextureManager = textureManager;
|
||||||
this.SceneTask = Service<InterfaceManager.InterfaceManagerWithScene>
|
this.SceneTask = Service<InterfaceManager.InterfaceManagerWithScene>
|
||||||
.GetAsync()
|
.GetAsync()
|
||||||
.ContinueWith(r => r.Result.Manager.Scene);
|
.ContinueWith(r => r.Result.Manager.Scene);
|
||||||
|
|
@ -144,6 +146,11 @@ internal sealed partial class FontAtlasFactory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public InterfaceManager InterfaceManager { get; }
|
public InterfaceManager InterfaceManager { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the service instance of <see cref="TextureManager"/>.
|
||||||
|
/// </summary>
|
||||||
|
public TextureManager TextureManager { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the async task for <see cref="RawDX11Scene"/> inside <see cref="InterfaceManager"/>.
|
/// Gets the async task for <see cref="RawDX11Scene"/> inside <see cref="InterfaceManager"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -346,7 +353,7 @@ internal sealed partial class FontAtlasFactory
|
||||||
var numPixels = texFile.Header.Width * texFile.Header.Height;
|
var numPixels = texFile.Header.Width * texFile.Header.Height;
|
||||||
|
|
||||||
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
|
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
|
||||||
var targetIsB4G4R4A4 = this.InterfaceManager.SupportsDxgiFormat(Format.B4G4R4A4_UNorm);
|
var targetIsB4G4R4A4 = this.TextureManager.SupportsDxgiFormat((int)Format.B4G4R4A4_UNorm);
|
||||||
var bpp = targetIsB4G4R4A4 ? 2 : 4;
|
var bpp = targetIsB4G4R4A4 ? 2 : 4;
|
||||||
var buffer = ArrayPool<byte>.Shared.Rent(numPixels * bpp);
|
var buffer = ArrayPool<byte>.Shared.Rent(numPixels * bpp);
|
||||||
try
|
try
|
||||||
|
|
@ -369,12 +376,13 @@ internal sealed partial class FontAtlasFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.scopedFinalizer.Add(
|
return this.scopedFinalizer.Add(
|
||||||
this.InterfaceManager.LoadImageFromDxgiFormat(
|
this.TextureManager.GetFromRaw(
|
||||||
buffer,
|
new(
|
||||||
texFile.Header.Width * bpp,
|
texFile.Header.Width,
|
||||||
texFile.Header.Width,
|
texFile.Header.Height,
|
||||||
texFile.Header.Height,
|
texFile.Header.Width * bpp,
|
||||||
targetIsB4G4R4A4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm));
|
(int)(targetIsB4G4R4A4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm)),
|
||||||
|
buffer));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,8 @@ public sealed class UiBuilder : IDisposable
|
||||||
private Task<InterfaceManager> InterfaceManagerWithSceneAsync =>
|
private Task<InterfaceManager> InterfaceManagerWithSceneAsync =>
|
||||||
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync().ContinueWith(task => task.Result.Manager);
|
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync().ContinueWith(task => task.Result.Manager);
|
||||||
|
|
||||||
|
private ITextureProvider TextureProvider => Service<TextureManager>.Get();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an image from the specified file.
|
/// Loads an image from the specified file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -386,9 +388,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
|
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
|
||||||
public IDalamudTextureWrap LoadImage(string filePath)
|
public IDalamudTextureWrap LoadImage(string filePath) => this.TextureProvider.GetFromFileAsync(filePath).Result;
|
||||||
=> this.InterfaceManagerWithScene?.LoadImage(filePath)
|
|
||||||
?? throw new InvalidOperationException("Load failed.");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an image from a byte stream, such as a png downloaded into memory.
|
/// Loads an image from a byte stream, such as a png downloaded into memory.
|
||||||
|
|
@ -397,9 +397,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
|
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
|
||||||
public IDalamudTextureWrap LoadImage(byte[] imageData)
|
public IDalamudTextureWrap LoadImage(byte[] imageData) => this.TextureProvider.GetFromImageAsync(imageData).Result;
|
||||||
=> this.InterfaceManagerWithScene?.LoadImage(imageData)
|
|
||||||
?? throw new InvalidOperationException("Load failed.");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an image from raw unformatted pixel data, with no type or header information. To load formatted data, use <see cref="LoadImage(byte[])"/>.
|
/// Loads an image from raw unformatted pixel data, with no type or header information. To load formatted data, use <see cref="LoadImage(byte[])"/>.
|
||||||
|
|
@ -411,9 +409,12 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromRaw)} or {nameof(ITextureProvider.GetFromRawAsync)}.")]
|
[Obsolete($"Use {nameof(ITextureProvider.GetFromRaw)} or {nameof(ITextureProvider.GetFromRawAsync)}.")]
|
||||||
public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels)
|
public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) =>
|
||||||
=> this.InterfaceManagerWithScene?.LoadImageRaw(imageData, width, height, numChannels)
|
numChannels switch
|
||||||
?? throw new InvalidOperationException("Load failed.");
|
{
|
||||||
|
4 => this.TextureProvider.GetFromRaw(RawImageSpecification.Rgba32(width, height), imageData),
|
||||||
|
_ => throw new NotSupportedException(),
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an ULD file that can load textures containing multiple icons in a single texture.
|
/// Loads an ULD file that can load textures containing multiple icons in a single texture.
|
||||||
|
|
@ -430,10 +431,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
|
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
|
||||||
public Task<IDalamudTextureWrap> LoadImageAsync(string filePath) => Task.Run(
|
public Task<IDalamudTextureWrap> LoadImageAsync(string filePath) => this.TextureProvider.GetFromFileAsync(filePath);
|
||||||
async () =>
|
|
||||||
(await this.InterfaceManagerWithSceneAsync).LoadImage(filePath)
|
|
||||||
?? throw new InvalidOperationException("Load failed."));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asynchronously loads an image from a byte stream, such as a png downloaded into memory, when it's possible to do so.
|
/// 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
|
||||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
|
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
|
||||||
public Task<IDalamudTextureWrap> LoadImageAsync(byte[] imageData) => Task.Run(
|
public Task<IDalamudTextureWrap> LoadImageAsync(byte[] imageData) =>
|
||||||
async () =>
|
this.TextureProvider.GetFromImageAsync(imageData);
|
||||||
(await this.InterfaceManagerWithSceneAsync).LoadImage(imageData)
|
|
||||||
?? throw new InvalidOperationException("Load failed."));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 <see cref="LoadImage(byte[])"/>.
|
/// 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 <see cref="LoadImage(byte[])"/>.
|
||||||
|
|
@ -457,10 +453,12 @@ public sealed class UiBuilder : IDisposable
|
||||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromRawAsync)}.")]
|
[Obsolete($"Use {nameof(ITextureProvider.GetFromRawAsync)}.")]
|
||||||
public Task<IDalamudTextureWrap> LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) => Task.Run(
|
public Task<IDalamudTextureWrap> LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) =>
|
||||||
async () =>
|
numChannels switch
|
||||||
(await this.InterfaceManagerWithSceneAsync).LoadImageRaw(imageData, width, height, numChannels)
|
{
|
||||||
?? throw new InvalidOperationException("Load failed."));
|
4 => this.TextureProvider.GetFromRawAsync(RawImageSpecification.Rgba32(width, height), imageData),
|
||||||
|
_ => Task.FromException<IDalamudTextureWrap>(new NotSupportedException()),
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits for UI to become available for use.
|
/// Waits for UI to become available for use.
|
||||||
|
|
|
||||||
|
|
@ -213,4 +213,14 @@ public record struct RawImageSpecification(int Width, int Height, int Pitch, int
|
||||||
/// <returns>The new instance.</returns>
|
/// <returns>The new instance.</returns>
|
||||||
public static RawImageSpecification Rgba32(int width, int height) =>
|
public static RawImageSpecification Rgba32(int width, int height) =>
|
||||||
new(width, height, width * 4, (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM);
|
new(width, height, width * 4, (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="RawImageSpecification"/> record using the given resolution,
|
||||||
|
/// in A8 UNorm pixel format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">The width.</param>
|
||||||
|
/// <param name="height">The height.</param>
|
||||||
|
/// <returns>The new instance.</returns>
|
||||||
|
public static RawImageSpecification A8(int width, int height) =>
|
||||||
|
new(width, height, width, (int)DXGI_FORMAT.DXGI_FORMAT_A8_UNORM);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -302,17 +302,17 @@ internal sealed class DalamudAssetManager : IServiceType, IDisposable, IDalamudA
|
||||||
var buf = Array.Empty<byte>();
|
var buf = Array.Empty<byte>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var im = (await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync()).Manager;
|
var tm = await Service<TextureManager>.GetAsync();
|
||||||
await using var stream = await this.CreateStreamAsync(asset);
|
await using var stream = await this.CreateStreamAsync(asset);
|
||||||
var length = checked((int)stream.Length);
|
var length = checked((int)stream.Length);
|
||||||
buf = ArrayPool<byte>.Shared.Rent(length);
|
buf = ArrayPool<byte>.Shared.Rent(length);
|
||||||
stream.ReadExactly(buf, 0, length);
|
stream.ReadExactly(buf, 0, length);
|
||||||
var image = purpose switch
|
var image = purpose switch
|
||||||
{
|
{
|
||||||
DalamudAssetPurpose.TextureFromPng => im.LoadImage(buf),
|
DalamudAssetPurpose.TextureFromPng => await tm.GetFromImageAsync(buf),
|
||||||
DalamudAssetPurpose.TextureFromRaw =>
|
DalamudAssetPurpose.TextureFromRaw =>
|
||||||
asset.GetAttribute<DalamudAssetRawTextureAttribute>() is { } raw
|
asset.GetAttribute<DalamudAssetRawTextureAttribute>() is { } raw
|
||||||
? im.LoadImageFromDxgiFormat(buf, raw.Pitch, raw.Width, raw.Height, raw.Format)
|
? await tm.GetFromRawAsync(raw.Specification, buf)
|
||||||
: throw new InvalidOperationException(
|
: throw new InvalidOperationException(
|
||||||
"TextureFromRaw must accompany a DalamudAssetRawTextureAttribute."),
|
"TextureFromRaw must accompany a DalamudAssetRawTextureAttribute."),
|
||||||
_ => null,
|
_ => null,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using SharpDX.DXGI;
|
using Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
using SharpDX.DXGI;
|
||||||
|
|
||||||
namespace Dalamud.Storage.Assets;
|
namespace Dalamud.Storage.Assets;
|
||||||
|
|
||||||
|
|
@ -17,29 +19,11 @@ internal class DalamudAssetRawTextureAttribute : Attribute
|
||||||
/// <param name="format">The format.</param>
|
/// <param name="format">The format.</param>
|
||||||
public DalamudAssetRawTextureAttribute(int width, int pitch, int height, Format format)
|
public DalamudAssetRawTextureAttribute(int width, int pitch, int height, Format format)
|
||||||
{
|
{
|
||||||
this.Width = width;
|
this.Specification = new(width, height, pitch, (int)format);
|
||||||
this.Pitch = pitch;
|
|
||||||
this.Height = height;
|
|
||||||
this.Format = format;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the width.
|
/// Gets the specification.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Width { get; }
|
public RawImageSpecification Specification { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the pitch.
|
|
||||||
/// </summary>
|
|
||||||
public int Pitch { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the height.
|
|
||||||
/// </summary>
|
|
||||||
public int Height { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the format.
|
|
||||||
/// </summary>
|
|
||||||
public Format Format { get; }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue