This commit is contained in:
Soreepeong 2024-05-21 09:27:51 +09:00
parent 8c7771bf7d
commit 20717cce3d
13 changed files with 213 additions and 199 deletions

View file

@ -1,7 +1,6 @@
using System.IO;
using System.Numerics;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Textures.Internal;
namespace Dalamud.Interface.ImGuiNotification.Internal.NotificationIcon;
@ -9,26 +8,26 @@ namespace Dalamud.Interface.ImGuiNotification.Internal.NotificationIcon;
/// <remarks>If there was no texture loaded for any reason, the plugin icon will be displayed instead.</remarks>
internal class FilePathNotificationIcon : INotificationIcon
{
private readonly FileInfo fileInfo;
private readonly string filePath;
/// <summary>Initializes a new instance of the <see cref="FilePathNotificationIcon"/> class.</summary>
/// <param name="filePath">The path to a .tex file inside the game resources.</param>
public FilePathNotificationIcon(string filePath) => this.fileInfo = new(filePath);
public FilePathNotificationIcon(string filePath) => this.filePath = new(filePath);
/// <inheritdoc/>
public bool DrawIcon(Vector2 minCoord, Vector2 maxCoord, Vector4 color) =>
NotificationUtilities.DrawIconFrom(
minCoord,
maxCoord,
Service<TextureManager>.Get().GetTextureFromFile(this.fileInfo));
Service<TextureManager>.Get().Shared.GetFromFile(this.filePath).GetWrapOrDefault());
/// <inheritdoc/>
public override bool Equals(object? obj) =>
obj is FilePathNotificationIcon r && r.fileInfo.FullName == this.fileInfo.FullName;
obj is FilePathNotificationIcon r && r.filePath == this.filePath;
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.GetType().GetHashCode(), this.fileInfo.FullName);
public override int GetHashCode() => HashCode.Combine(this.GetType().GetHashCode(), this.filePath);
/// <inheritdoc/>
public override string ToString() => $"{nameof(FilePathNotificationIcon)}({this.fileInfo.FullName})";
public override string ToString() => $"{nameof(FilePathNotificationIcon)}({this.filePath})";
}

View file

@ -1,7 +1,6 @@
using System.Numerics;
using Dalamud.Interface.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Interface.Textures.Internal;
namespace Dalamud.Interface.ImGuiNotification.Internal.NotificationIcon;
@ -13,7 +12,6 @@ internal class GamePathNotificationIcon : INotificationIcon
/// <summary>Initializes a new instance of the <see cref="GamePathNotificationIcon"/> class.</summary>
/// <param name="gamePath">The path to a .tex file inside the game resources.</param>
/// <remarks>Use <see cref="ITextureProvider.GetIconPath"/> to get the game path from icon IDs.</remarks>
public GamePathNotificationIcon(string gamePath) => this.gamePath = gamePath;
/// <inheritdoc/>
@ -21,7 +19,7 @@ internal class GamePathNotificationIcon : INotificationIcon
NotificationUtilities.DrawIconFrom(
minCoord,
maxCoord,
Service<TextureManager>.Get().GetTextureFromGame(this.gamePath));
Service<TextureManager>.Get().Shared.GetFromGame(this.gamePath).GetWrapOrDefault());
/// <inheritdoc/>
public override bool Equals(object? obj) => obj is GamePathNotificationIcon r && r.gamePath == this.gamePath;

View file

@ -19,8 +19,9 @@ using Dalamud.Hooking;
using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Internal.Windows;
using Dalamud.Interface.Internal.Windows.Data;
using Dalamud.Interface.Internal.Windows.PluginInstaller;

View file

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Dalamud.Game.Text;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Windowing;
using Dalamud.Storage.Assets;
using Dalamud.Utility;
@ -230,12 +231,14 @@ internal class ImGuiWidget : IDataWindowWidget
break;
case 7:
n.SetIconTexture(
DisposeLoggingTextureWrap.Wrap(tm.GetTextureFromGame(this.notificationTemplate.IconText)),
DisposeLoggingTextureWrap.Wrap(
tm.Shared.GetFromGame(this.notificationTemplate.IconText).GetWrapOrDefault()),
this.notificationTemplate.LeaveTexturesOpen);
break;
case 8:
n.SetIconTexture(
DisposeLoggingTextureWrap.Wrap(tm.GetTextureFromFile(new(this.notificationTemplate.IconText))),
DisposeLoggingTextureWrap.Wrap(
tm.Shared.GetFromFile(this.notificationTemplate.IconText).GetWrapOrDefault()),
this.notificationTemplate.LeaveTexturesOpen);
break;
}
@ -306,7 +309,8 @@ internal class ImGuiWidget : IDataWindowWidget
foreach (var n in this.notifications)
{
var i = (uint)Random.Shared.NextInt64(0, 200000);
n.IconTexture = DisposeLoggingTextureWrap.Wrap(Service<TextureManager>.Get().GetIcon(i));
n.IconTexture = DisposeLoggingTextureWrap.Wrap(
Service<TextureManager>.Get().Shared.GetFromGameIcon(new(i)).GetWrapOrDefault());
}
}
}

View file

@ -8,7 +8,8 @@ using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Textures.Internal.SharedImmediateTextures;
using Dalamud.Interface.Utility;
@ -90,8 +91,6 @@ internal class TexWidget : IDataWindowWidget
/// <inheritdoc/>
public bool Ready { get; set; }
private ITextureProvider TextureManagerForApi9 => this.textureManager!;
/// <inheritdoc/>
public void Load()
{
@ -634,17 +633,6 @@ internal class TexWidget : IDataWindowWidget
ImGui.InputText("Icon ID", ref this.iconId, 32);
ImGui.Checkbox("HQ Item", ref this.hq);
ImGui.Checkbox("Hi-Res", ref this.hiRes);
#pragma warning disable CS0618 // Type or member is obsolete
if (ImGui.Button("Load Icon (API9)"))
{
var flags = ITextureProvider.IconFlags.None;
if (this.hq)
flags |= ITextureProvider.IconFlags.ItemHighQuality;
if (this.hiRes)
flags |= ITextureProvider.IconFlags.HiRes;
this.addedTextures.Add(new(Api9: this.TextureManagerForApi9.GetIcon(uint.Parse(this.iconId), flags)));
}
#pragma warning restore CS0618 // Type or member is obsolete
ImGui.SameLine();
if (ImGui.Button("Load Icon (Async)"))
@ -668,11 +656,6 @@ internal class TexWidget : IDataWindowWidget
{
ImGui.InputText("Tex Path", ref this.inputTexPath, 255);
#pragma warning disable CS0618 // Type or member is obsolete
if (ImGui.Button("Load Tex (API9)"))
this.addedTextures.Add(new(Api9: this.TextureManagerForApi9.GetTextureFromGame(this.inputTexPath)));
#pragma warning restore CS0618 // Type or member is obsolete
ImGui.SameLine();
if (ImGui.Button("Load Tex (Async)"))
this.addedTextures.Add(new(Api10: this.textureManager.Shared.GetFromGame(this.inputTexPath).RentAsync()));
@ -688,11 +671,6 @@ internal class TexWidget : IDataWindowWidget
{
ImGui.InputText("File Path", ref this.inputFilePath, 255);
#pragma warning disable CS0618 // Type or member is obsolete
if (ImGui.Button("Load File (API9)"))
this.addedTextures.Add(new(Api9: this.TextureManagerForApi9.GetTextureFromFile(new(this.inputFilePath))));
#pragma warning restore CS0618 // Type or member is obsolete
ImGui.SameLine();
if (ImGui.Button("Load File (Async)"))
this.addedTextures.Add(new(Api10: this.textureManager.Shared.GetFromFile(this.inputFilePath).RentAsync()));
@ -929,7 +907,6 @@ internal class TexWidget : IDataWindowWidget
private record TextureEntry(
IDalamudTextureWrap? SharedResource = null,
IDalamudTextureWrap? Api9 = null,
Task<IDalamudTextureWrap>? Api10 = null,
GameIconLookup? Api10ImmGameIcon = null,
string? Api10ImmGamePath = null,
@ -943,7 +920,6 @@ internal class TexWidget : IDataWindowWidget
public void Dispose()
{
this.SharedResource?.Dispose();
this.Api9?.Dispose();
_ = this.Api10?.ToContentDisposedTask();
}
@ -951,8 +927,6 @@ internal class TexWidget : IDataWindowWidget
{
if (this.SharedResource is not null)
return "Unknown error";
if (this.Api9 is not null)
return "Unknown error";
if (this.Api10 is not null)
{
return !this.Api10.IsCompleted
@ -975,8 +949,6 @@ internal class TexWidget : IDataWindowWidget
{
if (this.SharedResource is not null)
return this.SharedResource;
if (this.Api9 is not null)
return this.Api9;
if (this.Api10 is not null)
return this.Api10.IsCompletedSuccessfully ? this.Api10.Result : null;
if (this.Api10ImmGameIcon is not null)
@ -1014,8 +986,6 @@ internal class TexWidget : IDataWindowWidget
{
if (this.SharedResource is not null)
return $"{nameof(this.SharedResource)}: {this.SharedResource}";
if (this.Api9 is not null)
return $"{nameof(this.Api9)}: {this.Api9}";
if (this.Api10 is { IsCompletedSuccessfully: true })
return $"{nameof(this.Api10)}: {this.Api10.Result}";
if (this.Api10 is not null)

View file

@ -17,6 +17,7 @@ using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;

View file

@ -7,6 +7,7 @@ using Dalamud.Interface.Textures.TextureWraps.Internal;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using Dalamud.Utility.TerraFxCom;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@ -62,18 +63,11 @@ internal sealed partial class TextureManager
unsafe
{
var srvDesc = new D3D11_SHADER_RESOURCE_VIEW_DESC(
using var srv = this.device.CreateShaderResourceView(
tex,
D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D);
using var srv = default(ComPtr<ID3D11ShaderResourceView>);
this.device.Get()->CreateShaderResourceView(
(ID3D11Resource*)tex.Get(),
&srvDesc,
srv.GetAddressOf())
.ThrowOnError();
new(tex.Get(), D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D));
var desc = default(D3D11_TEXTURE2D_DESC);
tex.Get()->GetDesc(&desc);
var desc = tex.GetDesc();
var outWrap = new UnknownTextureWrap(
(IUnknown*)srv.Get(),
@ -126,15 +120,10 @@ internal sealed partial class TextureManager
TextureModificationArgs args = default,
CancellationToken cancellationToken = default)
{
using var tex2D = wrapAux.NewTexRef();
if (!args.IsCompleteSourceCopy(wrapAux.Desc))
{
using var tmp = await this.NoThrottleCreateFromExistingTextureAsync(wrapAux, args);
unsafe
{
tex2D.Swap(&tmp);
}
}
using var tex2D =
args.IsCompleteSourceCopy(wrapAux.Desc)
? wrapAux.NewTexRef()
: await this.NoThrottleCreateFromExistingTextureAsync(wrapAux, args);
cancellationToken.ThrowIfCancellationRequested();
@ -147,32 +136,29 @@ internal sealed partial class TextureManager
{
cancellationToken.ThrowIfCancellationRequested();
D3D11_TEXTURE2D_DESC desc;
tex2D.Get()->GetDesc(&desc);
var desc = tex2D.GetDesc();
using var device = default(ComPtr<ID3D11Device>);
tex2D.Get()->GetDevice(device.GetAddressOf());
using var context = default(ComPtr<ID3D11DeviceContext>);
device.Get()->GetImmediateContext(context.GetAddressOf());
using var tmpTex = default(ComPtr<ID3D11Texture2D>);
if ((desc.CPUAccessFlags & (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ) == 0)
{
var tmpTexDesc = desc with
{
MipLevels = 1,
ArraySize = 1,
SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_STAGING,
BindFlags = 0u,
CPUAccessFlags = (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ,
MiscFlags = 0u,
};
device.Get()->CreateTexture2D(&tmpTexDesc, null, tmpTex.GetAddressOf()).ThrowOnError();
context.Get()->CopyResource((ID3D11Resource*)tmpTex.Get(), (ID3D11Resource*)tex2D.Get());
cancellationToken.ThrowIfCancellationRequested();
}
using var tmpTex =
(desc.CPUAccessFlags & (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ) == 0
? device.CreateTexture2D(
desc with
{
MipLevels = 1,
ArraySize = 1,
SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_STAGING,
BindFlags = 0u,
CPUAccessFlags = (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ,
MiscFlags = 0u,
},
tex2D)
: default;
cancellationToken.ThrowIfCancellationRequested();
var mapWhat = (ID3D11Resource*)(tmpTex.IsEmpty() ? tex2D.Get() : tmpTex.Get());
@ -205,25 +191,22 @@ internal sealed partial class TextureManager
if (args.NewHeight == 0)
args = args with { NewHeight = (int)MathF.Round((args.Uv1Effective.Y - args.Uv0.Y) * wrapAux.Desc.Height) };
using var tex2DCopyTemp = default(ComPtr<ID3D11Texture2D>);
unsafe
{
var tex2DCopyTempDesc = new D3D11_TEXTURE2D_DESC
{
Width = (uint)args.NewWidth,
Height = (uint)args.NewHeight,
MipLevels = 1,
ArraySize = 1,
Format = args.Format,
SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = (uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE |
D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET),
CPUAccessFlags = 0u,
MiscFlags = 0u,
};
this.device.Get()->CreateTexture2D(&tex2DCopyTempDesc, null, tex2DCopyTemp.GetAddressOf()).ThrowOnError();
}
using var tex2DCopyTemp =
this.device.CreateTexture2D(
new()
{
Width = (uint)args.NewWidth,
Height = (uint)args.NewHeight,
MipLevels = 1,
ArraySize = 1,
Format = args.Format,
SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = (uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE |
D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET),
CPUAccessFlags = 0u,
MiscFlags = 0u,
});
await this.RunDuringPresent(() => DrawSourceTextureToTarget(wrapAux, args, this.SimpleDrawer, tex2DCopyTemp));

View file

@ -12,6 +12,7 @@ using Dalamud.Interface.Textures.TextureWraps.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using Dalamud.Utility.TerraFxCom;
using Lumina.Data;
using Lumina.Data.Files;
@ -24,8 +25,7 @@ namespace Dalamud.Interface.Textures.Internal;
/// <summary>Service responsible for loading and disposing ImGui texture wraps.</summary>
[ServiceManager.EarlyLoadedService]
internal sealed partial class TextureManager
: IServiceType,
IDisposable,
: IInternalDisposableService,
ITextureProvider,
ITextureSubstitutionProvider,
ITextureReadbackProvider
@ -101,7 +101,7 @@ internal sealed partial class TextureManager
}
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
if (this.disposing)
return;
@ -221,6 +221,53 @@ internal sealed partial class TextureManager
static T? ForceNullable<T>(T s) => s;
}
/// <inheritdoc/>
public unsafe IDalamudTextureWrap CreateEmpty(
RawImageSpecification specs,
bool cpuRead,
bool cpuWrite,
string? debugName = null)
{
if (cpuRead && cpuWrite)
throw new ArgumentException("cpuRead and cpuWrite cannot be set at the same time.");
var cpuaf = default(D3D11_CPU_ACCESS_FLAG);
if (cpuRead)
cpuaf |= D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ;
if (cpuWrite)
cpuaf |= D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE;
D3D11_USAGE usage;
if (cpuRead)
usage = D3D11_USAGE.D3D11_USAGE_STAGING;
else if (cpuWrite)
usage = D3D11_USAGE.D3D11_USAGE_DYNAMIC;
else
usage = D3D11_USAGE.D3D11_USAGE_DEFAULT;
using var texture = this.device.CreateTexture2D(
new()
{
Width = (uint)specs.Width,
Height = (uint)specs.Height,
MipLevels = 1,
ArraySize = 1,
Format = specs.Format,
SampleDesc = new(1, 0),
Usage = usage,
BindFlags = (uint)D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE,
CPUAccessFlags = (uint)cpuaf,
MiscFlags = 0,
});
using var view = this.device.CreateShaderResourceView(
texture,
new(texture, D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D));
var wrap = new UnknownTextureWrap((IUnknown*)view.Get(), specs.Width, specs.Height, true);
this.BlameSetName(wrap, debugName ?? $"{nameof(this.CreateEmpty)}({specs})");
return wrap;
}
/// <inheritdoc/>
bool ITextureProvider.IsDxgiFormatSupported(int dxgiFormat) =>
this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat);

View file

@ -28,9 +28,8 @@ namespace Dalamud.Interface.Textures.Internal;
[ResolveVia<ITextureSubstitutionProvider>]
[ResolveVia<ITextureReadbackProvider>]
#pragma warning restore SA1015
internal sealed partial class TextureManagerPluginScoped
: IServiceType,
IDisposable,
internal sealed class TextureManagerPluginScoped
: IInternalDisposableService,
ITextureProvider,
ITextureSubstitutionProvider,
ITextureReadbackProvider
@ -114,7 +113,7 @@ internal sealed partial class TextureManagerPluginScoped
}
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
if (Interlocked.Exchange(ref this.managerTaskNullable, null) is not { } task)
return;
@ -134,6 +133,19 @@ internal sealed partial class TextureManagerPluginScoped
: $"{nameof(TextureManagerPluginScoped)}({this.plugin.Name})";
}
/// <inheritdoc/>
public IDalamudTextureWrap CreateEmpty(
RawImageSpecification specs,
bool cpuRead,
bool cpuWrite,
string? debugName = null)
{
var manager = this.ManagerOrThrow;
var textureWrap = manager.CreateEmpty(specs, cpuRead, cpuWrite, debugName);
manager.Blame(textureWrap, this.plugin);
return textureWrap;
}
/// <inheritdoc/>
public async Task<IDalamudTextureWrap> CreateFromExistingTextureAsync(
IDalamudTextureWrap wrap,

View file

@ -31,6 +31,13 @@ public record struct RawImageSpecification
this.DxgiFormat = dxgiFormat;
}
/// <summary>Initializes a new instance of the <see cref="RawImageSpecification"/> class.</summary>
/// <param name="desc">The source texture description.</param>
internal RawImageSpecification(in D3D11_TEXTURE2D_DESC desc)
: this((int)desc.Width, (int)desc.Height, (int)desc.Format)
{
}
/// <summary>Initializes a new instance of the <see cref="RawImageSpecification"/> class.</summary>
/// <param name="desc">The source texture description.</param>
/// <param name="pitch">The pitch of the raw image in bytes.</param>

View file

@ -12,16 +12,11 @@ using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using ImGuiNET;
using ImGuiScene;
using Serilog;
using SharpDX.Direct3D11;
@ -46,9 +41,6 @@ public sealed class UiBuilder : IDisposable
private readonly DisposeSafety.ScopedFinalizer scopedFinalizer = new();
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
private readonly TextureManagerPluginScoped scopedTextureProvider;
private bool hasErrorWindow = false;
private bool lastFrameUiHideState = false;
@ -78,8 +70,6 @@ public sealed class UiBuilder : IDisposable
this.interfaceManager.ResizeBuffers += this.OnResizeBuffers;
this.scopedFinalizer.Add(() => this.interfaceManager.ResizeBuffers -= this.OnResizeBuffers);
this.scopedFinalizer.Add(this.scopedTextureProvider = new(plugin));
this.FontAtlas =
this.scopedFinalizer
.Add(
@ -350,43 +340,6 @@ public sealed class UiBuilder : IDisposable
private Task<InterfaceManager> InterfaceManagerWithSceneAsync =>
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync().ContinueWith(task => task.Result.Manager);
/// <summary>
/// Loads an image from the specified file.
/// </summary>
/// <param name="filePath">The full filepath to the image.</param>
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromFile)}.")]
public IDalamudTextureWrap LoadImage(string filePath) =>
this.scopedTextureProvider.GetFromFile(filePath).RentAsync().Result;
/// <summary>
/// Loads an image from a byte stream, such as a png downloaded into memory.
/// </summary>
/// <param name="imageData">A byte array containing the raw image data.</param>
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.CreateFromImageAsync)}.")]
public IDalamudTextureWrap LoadImage(byte[] imageData) =>
this.scopedTextureProvider.CreateFromImageAsync(imageData).Result;
/// <summary>
/// Loads an image from raw unformatted pixel data, with no type or header information. To load formatted data, use <see cref="LoadImage(byte[])"/>.
/// </summary>
/// <param name="imageData">A byte array containing the raw pixel data.</param>
/// <param name="width">The width of the image contained in <paramref name="imageData"/>.</param>
/// <param name="height">The height of the image contained in <paramref name="imageData"/>.</param>
/// <param name="numChannels">The number of channels (bytes per pixel) of the image contained in <paramref name="imageData"/>. This should usually be 4.</param>
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.CreateFromRaw)} or {nameof(ITextureProvider.CreateFromRawAsync)}.")]
public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) =>
numChannels switch
{
4 => this.scopedTextureProvider.CreateFromRaw(RawImageSpecification.Rgba32(width, height), imageData),
_ => throw new NotSupportedException(),
};
/// <summary>
/// Loads an ULD file that can load textures containing multiple icons in a single texture.
/// </summary>
@ -395,43 +348,6 @@ public sealed class UiBuilder : IDisposable
public UldWrapper LoadUld(string uldPath)
=> new(this, uldPath);
/// <summary>
/// Asynchronously loads an image from the specified file, when it's possible to do so.
/// </summary>
/// <param name="filePath">The full filepath to the image.</param>
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.GetFromFile)}.")]
public Task<IDalamudTextureWrap> LoadImageAsync(string filePath) =>
this.scopedTextureProvider.GetFromFile(filePath).RentAsync();
/// <summary>
/// Asynchronously loads an image from a byte stream, such as a png downloaded into memory, when it's possible to do so.
/// </summary>
/// <param name="imageData">A byte array containing the raw image data.</param>
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.CreateFromImageAsync)}.")]
public Task<IDalamudTextureWrap> LoadImageAsync(byte[] imageData) =>
this.scopedTextureProvider.CreateFromImageAsync(imageData);
/// <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[])"/>.
/// </summary>
/// <param name="imageData">A byte array containing the raw pixel data.</param>
/// <param name="width">The width of the image contained in <paramref name="imageData"/>.</param>
/// <param name="height">The height of the image contained in <paramref name="imageData"/>.</param>
/// <param name="numChannels">The number of channels (bytes per pixel) of the image contained in <paramref name="imageData"/>. This should usually be 4.</param>
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
[Obsolete($"Use {nameof(ITextureProvider.CreateFromRawAsync)}.")]
public Task<IDalamudTextureWrap> LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) =>
numChannels switch
{
4 => this.scopedTextureProvider.CreateFromRawAsync(RawImageSpecification.Rgba32(width, height), imageData),
_ => Task.FromException<IDalamudTextureWrap>(new NotSupportedException()),
};
/// <summary>
/// Waits for UI to become available for use.
/// </summary>

View file

@ -5,7 +5,6 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Windows.Data.Widgets;
using Dalamud.Interface.Textures;
@ -32,8 +31,20 @@ namespace Dalamud.Plugin.Services;
/// <see cref="TexWidget"/>.
/// </para>
/// </remarks>
public partial interface ITextureProvider
public interface ITextureProvider
{
/// <summary>Creates an empty texture.</summary>
/// <param name="specs">Texture specifications.</param>
/// <param name="cpuRead">Whether to support reading from CPU, while disabling reading from GPU.</param>
/// <param name="cpuWrite">Whether to support writing from CPU, while disabling writing from GPU.</param>
/// <param name="debugName">Name for debug display purposes.</param>
/// <returns>A new empty texture.</returns>
IDalamudTextureWrap CreateEmpty(
RawImageSpecification specs,
bool cpuRead,
bool cpuWrite,
string? debugName = null);
/// <summary>Creates a texture from the given existing texture, cropping and converting pixel format as needed.
/// </summary>
/// <param name="wrap">The source texture wrap. The passed value may be disposed once this function returns,

View file

@ -0,0 +1,65 @@
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Dalamud.Utility.TerraFxCom;
/// <summary>Extension methods for D3D11 TerraFX objects.</summary>
internal static class TerraFxD3D11Extensions
{
/// <summary>Creates a 2D texture with the given descriptor.</summary>
/// <param name="device">Device to copy from and to.</param>
/// <param name="desc">Resource descriptor.</param>
/// <param name="copyFrom">Optional initial data for the texture.</param>
/// <returns>New copied texture.</returns>
public static unsafe ComPtr<ID3D11Texture2D> CreateTexture2D(
this ComPtr<ID3D11Device> device,
D3D11_TEXTURE2D_DESC desc,
ComPtr<ID3D11Texture2D> copyFrom = default)
{
using var tmpTex = default(ComPtr<ID3D11Texture2D>);
device.Get()->CreateTexture2D(&desc, null, tmpTex.GetAddressOf()).ThrowOnError();
if (!copyFrom.IsEmpty())
{
using var context = default(ComPtr<ID3D11DeviceContext>);
device.Get()->GetImmediateContext(context.GetAddressOf());
context.Get()->CopyResource((ID3D11Resource*)tmpTex.Get(), (ID3D11Resource*)copyFrom.Get());
}
return new(tmpTex);
}
/// <summary>Creates a shader resource view for a resource.</summary>
/// <param name="device">Device to create the resource view into.</param>
/// <param name="resource">Resource to create a view on.</param>
/// <param name="desc">Resource view descriptor.</param>
/// <typeparam name="T">Type of the resource.</typeparam>
/// <returns>New shader resource view.</returns>
public static unsafe ComPtr<ID3D11ShaderResourceView> CreateShaderResourceView<T>(
this ComPtr<ID3D11Device> device,
ComPtr<T> resource,
in D3D11_SHADER_RESOURCE_VIEW_DESC desc)
where T : unmanaged, ID3D11Resource.Interface
{
fixed (D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc = &desc)
{
var srv = default(ComPtr<ID3D11ShaderResourceView>);
device.Get()->CreateShaderResourceView(
(ID3D11Resource*)resource.Get(),
pDesc,
srv.GetAddressOf())
.ThrowOnError();
return srv;
}
}
/// <summary>Gets the descriptor for a <see cref="ID3D11Texture2D"/>.</summary>
/// <param name="texture">Texture.</param>
/// <returns>Texture descriptor.</returns>
public static unsafe D3D11_TEXTURE2D_DESC GetDesc(this ComPtr<ID3D11Texture2D> texture)
{
var desc = default(D3D11_TEXTURE2D_DESC);
texture.Get()->GetDesc(&desc);
return desc;
}
}