Move save get/save functions to ITextureReadbackProvider

This commit is contained in:
Soreepeong 2024-03-03 21:00:37 +09:00
parent fa5e708a43
commit 5ad8edbc04
7 changed files with 135 additions and 120 deletions

View file

@ -51,7 +51,7 @@ internal class TexWidget : IDataWindowWidget
private Vector2 inputTexScale = Vector2.Zero; private Vector2 inputTexScale = Vector2.Zero;
private TextureManager textureManager = null!; private TextureManager textureManager = null!;
private FileDialogManager fileDialogManager = null!; private FileDialogManager fileDialogManager = null!;
private ExistingTextureModificationArgs existingTextureModificationArgs; private TextureModificationArgs textureModificationArgs;
private ImGuiViewportTextureArgs viewportTextureArgs; private ImGuiViewportTextureArgs viewportTextureArgs;
private int viewportIndexInt; private int viewportIndexInt;
@ -88,7 +88,7 @@ internal class TexWidget : IDataWindowWidget
this.supportedRenderTargetFormatNames = null; this.supportedRenderTargetFormatNames = null;
this.renderTargetChoiceInt = 0; this.renderTargetChoiceInt = 0;
this.fileDialogManager = new(); this.fileDialogManager = new();
this.existingTextureModificationArgs = new() this.textureModificationArgs = new()
{ {
Uv0 = new(0.25f), Uv0 = new(0.25f),
Uv1 = new(0.75f), Uv1 = new(0.75f),
@ -231,7 +231,7 @@ internal class TexWidget : IDataWindowWidget
return; return;
var texTask = this.textureManager.CreateFromExistingTextureAsync( var texTask = this.textureManager.CreateFromExistingTextureAsync(
source.CreateWrapSharingLowLevelResource(), source.CreateWrapSharingLowLevelResource(),
this.existingTextureModificationArgs with this.textureModificationArgs with
{ {
Format = supportedFormats[this.renderTargetChoiceInt], Format = supportedFormats[this.renderTargetChoiceInt],
}); });
@ -626,9 +626,9 @@ internal class TexWidget : IDataWindowWidget
private void DrawExistingTextureModificationArgs() private void DrawExistingTextureModificationArgs()
{ {
var b = this.existingTextureModificationArgs.MakeOpaque; var b = this.textureModificationArgs.MakeOpaque;
if (ImGui.Checkbox(nameof(this.existingTextureModificationArgs.MakeOpaque), ref b)) if (ImGui.Checkbox(nameof(this.textureModificationArgs.MakeOpaque), ref b))
this.existingTextureModificationArgs.MakeOpaque = b; this.textureModificationArgs.MakeOpaque = b;
if (this.supportedRenderTargetFormats is null) if (this.supportedRenderTargetFormats is null)
{ {
@ -642,29 +642,29 @@ internal class TexWidget : IDataWindowWidget
this.supportedRenderTargetFormatNames ??= this.supportedRenderTargetFormats.Select(Enum.GetName).ToArray(); this.supportedRenderTargetFormatNames ??= this.supportedRenderTargetFormats.Select(Enum.GetName).ToArray();
ImGui.Combo( ImGui.Combo(
nameof(this.existingTextureModificationArgs.DxgiFormat), nameof(this.textureModificationArgs.DxgiFormat),
ref this.renderTargetChoiceInt, ref this.renderTargetChoiceInt,
this.supportedRenderTargetFormatNames, this.supportedRenderTargetFormatNames,
this.supportedRenderTargetFormatNames.Length); this.supportedRenderTargetFormatNames.Length);
Span<int> wh = stackalloc int[2]; Span<int> wh = stackalloc int[2];
wh[0] = this.existingTextureModificationArgs.NewWidth; wh[0] = this.textureModificationArgs.NewWidth;
wh[1] = this.existingTextureModificationArgs.NewHeight; wh[1] = this.textureModificationArgs.NewHeight;
if (ImGui.InputInt2( if (ImGui.InputInt2(
$"{nameof(this.existingTextureModificationArgs.NewWidth)}/{nameof(this.existingTextureModificationArgs.NewHeight)}", $"{nameof(this.textureModificationArgs.NewWidth)}/{nameof(this.textureModificationArgs.NewHeight)}",
ref wh[0])) ref wh[0]))
{ {
this.existingTextureModificationArgs.NewWidth = wh[0]; this.textureModificationArgs.NewWidth = wh[0];
this.existingTextureModificationArgs.NewHeight = wh[1]; this.textureModificationArgs.NewHeight = wh[1];
} }
var vec2 = this.existingTextureModificationArgs.Uv0; var vec2 = this.textureModificationArgs.Uv0;
if (ImGui.InputFloat2(nameof(this.existingTextureModificationArgs.Uv0), ref vec2)) if (ImGui.InputFloat2(nameof(this.textureModificationArgs.Uv0), ref vec2))
this.existingTextureModificationArgs.Uv0 = vec2; this.textureModificationArgs.Uv0 = vec2;
vec2 = this.existingTextureModificationArgs.Uv1; vec2 = this.textureModificationArgs.Uv1;
if (ImGui.InputFloat2(nameof(this.existingTextureModificationArgs.Uv1), ref vec2)) if (ImGui.InputFloat2(nameof(this.textureModificationArgs.Uv1), ref vec2))
this.existingTextureModificationArgs.Uv1 = vec2; this.textureModificationArgs.Uv1 = vec2;
ImGuiHelpers.ScaledDummy(10); ImGuiHelpers.ScaledDummy(10);
} }

View file

@ -45,7 +45,7 @@ internal sealed partial class TextureManager
/// <inheritdoc/> /// <inheritdoc/>
public Task<IDalamudTextureWrap> CreateFromExistingTextureAsync( public Task<IDalamudTextureWrap> CreateFromExistingTextureAsync(
IDalamudTextureWrap wrap, IDalamudTextureWrap wrap,
ExistingTextureModificationArgs args = default, TextureModificationArgs args = default,
bool leaveWrapOpen = false, bool leaveWrapOpen = false,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
@ -94,9 +94,9 @@ internal sealed partial class TextureManager
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<(RawImageSpecification Specification, byte[] RawData)> GetRawDataFromExistingTextureAsync( public async Task<(RawImageSpecification Specification, byte[] RawData)> GetRawImageAsync(
IDalamudTextureWrap wrap, IDalamudTextureWrap wrap,
ExistingTextureModificationArgs args = default, TextureModificationArgs args = default,
bool leaveWrapOpen = false, bool leaveWrapOpen = false,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
@ -197,7 +197,7 @@ internal sealed partial class TextureManager
private async Task<ComPtr<ID3D11Texture2D>> NoThrottleCreateFromExistingTextureAsync( private async Task<ComPtr<ID3D11Texture2D>> NoThrottleCreateFromExistingTextureAsync(
IDalamudTextureWrap wrap, IDalamudTextureWrap wrap,
ExistingTextureModificationArgs args) TextureModificationArgs args)
{ {
args.ThrowOnInvalidValues(); args.ThrowOnInvalidValues();

View file

@ -71,7 +71,7 @@ internal sealed partial class TextureManager
using var istream = ManagedIStream.Create(stream, leaveStreamOpen); using var istream = ManagedIStream.Create(stream, leaveStreamOpen);
var (specs, bytes) = await this.GetRawDataFromExistingTextureAsync( var (specs, bytes) = await this.GetRawImageAsync(
wrap, wrap,
new() new()
{ {
@ -164,7 +164,7 @@ internal sealed partial class TextureManager
this.Wic.GetSupportedDecoderInfos(); this.Wic.GetSupportedDecoderInfos();
/// <inheritdoc/> /// <inheritdoc/>
IEnumerable<IBitmapCodecInfo> ITextureProvider.GetSupportedImageEncoderInfos() => IEnumerable<IBitmapCodecInfo> ITextureReadbackProvider.GetSupportedImageEncoderInfos() =>
this.Wic.GetSupportedEncoderInfos(); this.Wic.GetSupportedEncoderInfos();
/// <summary>Creates a texture from the given bytes of an image file. Skips the load throttler; intended to be used /// <summary>Creates a texture from the given bytes of an image file. Skips the load throttler; intended to be used

View file

@ -29,7 +29,8 @@ namespace Dalamud.Interface.Textures.Internal;
[ResolveVia<ITextureProvider>] [ResolveVia<ITextureProvider>]
[ResolveVia<ITextureSubstitutionProvider>] [ResolveVia<ITextureSubstitutionProvider>]
#pragma warning restore SA1015 #pragma warning restore SA1015
internal sealed partial class TextureManager : IServiceType, IDisposable, ITextureProvider, ITextureSubstitutionProvider internal sealed partial class TextureManager
: IServiceType, IDisposable, ITextureProvider, ITextureSubstitutionProvider, ITextureReadbackProvider
{ {
private static readonly ModuleLog Log = new(nameof(TextureManager)); private static readonly ModuleLog Log = new(nameof(TextureManager));

View file

@ -6,8 +6,8 @@ using TerraFX.Interop.DirectX;
namespace Dalamud.Interface.Textures; namespace Dalamud.Interface.Textures;
/// <summary>Describes how to modify an existing texture.</summary> /// <summary>Describes how to modify a texture.</summary>
public record struct ExistingTextureModificationArgs() public record struct TextureModificationArgs()
{ {
/// <summary>Gets or sets a value indicating whether to make the texture opaque.</summary> /// <summary>Gets or sets a value indicating whether to make the texture opaque.</summary>
/// <remarks>If <c>true</c>, then the alpha channel values will be filled with 1.0.</remarks> /// <remarks>If <c>true</c>, then the alpha channel values will be filled with 1.0.</remarks>
@ -52,10 +52,10 @@ public record struct ExistingTextureModificationArgs()
/// <summary>Gets the effective value of <see cref="Uv1"/>.</summary> /// <summary>Gets the effective value of <see cref="Uv1"/>.</summary>
internal Vector2 Uv1Effective => this.Uv1 == Vector2.Zero ? Vector2.One : this.Uv1; internal Vector2 Uv1Effective => this.Uv1 == Vector2.Zero ? Vector2.One : this.Uv1;
/// <summary>Test if this instance of <see cref="ExistingTextureModificationArgs"/> does not instruct to change the /// <summary>Test if this instance of <see cref="TextureModificationArgs"/> does not instruct to change the
/// underlying data of a texture.</summary> /// underlying data of a texture.</summary>
/// <param name="desc">The texture description to test against.</param> /// <param name="desc">The texture description to test against.</param>
/// <returns><c>true</c> if this instance of <see cref="ExistingTextureModificationArgs"/> does not instruct to /// <returns><c>true</c> if this instance of <see cref="TextureModificationArgs"/> does not instruct to
/// change the underlying data of a texture.</returns> /// change the underlying data of a texture.</returns>
internal bool IsCompleteSourceCopy(in D3D11_TEXTURE2D_DESC desc) => internal bool IsCompleteSourceCopy(in D3D11_TEXTURE2D_DESC desc) =>
this.Uv0 == Vector2.Zero this.Uv0 == Vector2.Zero

View file

@ -41,7 +41,7 @@ public partial interface ITextureProvider
/// <remarks><para>This function may throw an exception.</para></remarks> /// <remarks><para>This function may throw an exception.</para></remarks>
Task<IDalamudTextureWrap> CreateFromExistingTextureAsync( Task<IDalamudTextureWrap> CreateFromExistingTextureAsync(
IDalamudTextureWrap wrap, IDalamudTextureWrap wrap,
ExistingTextureModificationArgs args = default, TextureModificationArgs args = default,
bool leaveWrapOpen = false, bool leaveWrapOpen = false,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
@ -151,17 +151,6 @@ public partial interface ITextureProvider
/// </remarks> /// </remarks>
IEnumerable<IBitmapCodecInfo> GetSupportedImageDecoderInfos(); IEnumerable<IBitmapCodecInfo> GetSupportedImageDecoderInfos();
/// <summary>Gets the supported bitmap encoders.</summary>
/// <returns>The supported bitmap encoders.</returns>
/// <remarks>
/// The following function supports the files of the container types pointed by yielded values.
/// <ul>
/// <li><see cref="SaveToStreamAsync"/></li>
/// </ul>
/// <para>This function may throw an exception.</para>
/// </remarks>
IEnumerable<IBitmapCodecInfo> GetSupportedImageEncoderInfos();
/// <summary>Gets a shared texture corresponding to the given game resource icon specifier.</summary> /// <summary>Gets a shared texture corresponding to the given game resource icon specifier.</summary>
/// <param name="lookup">A game icon specifier.</param> /// <param name="lookup">A game icon specifier.</param>
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns> /// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
@ -208,85 +197,6 @@ public partial interface ITextureProvider
/// <remarks><para>This function does not throw exceptions.</para></remarks> /// <remarks><para>This function does not throw exceptions.</para></remarks>
bool TryGetIconPath(in GameIconLookup lookup, [NotNullWhen(true)] out string? path); bool TryGetIconPath(in GameIconLookup lookup, [NotNullWhen(true)] out string? path);
/// <summary>Gets the raw data of a texture wrap.</summary>
/// <param name="wrap">The source texture wrap.</param>
/// <param name="args">The texture modification arguments.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The raw data and its specifications.</returns>
/// <remarks>
/// <para>The length of the returned <c>RawData</c> may not match
/// <see cref="RawImageSpecification.Height"/> * <see cref="RawImageSpecification.Pitch"/>.</para>
/// <para>This function may throw an exception.</para>
/// </remarks>
Task<(RawImageSpecification Specification, byte[] RawData)> GetRawDataFromExistingTextureAsync(
IDalamudTextureWrap wrap,
ExistingTextureModificationArgs args = default,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Saves a texture wrap to a stream in an image file format.</summary>
/// <param name="wrap">The texture wrap to save.</param>
/// <param name="containerGuid">The container GUID, obtained from <see cref="GetSupportedImageEncoderInfos"/>.</param>
/// <param name="stream">The stream to save to.</param>
/// <param name="props">Properties to pass to the encoder. See remarks for valid values.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="leaveStreamOpen">Whether to leave <paramref name="stream"/> open when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the save process.</returns>
/// <remarks>
/// <para><paramref name="wrap"/> must not be disposed until the task finishes.</para>
/// <para>See the following webpages for the valid values for <paramref name="props"/> per
/// <paramref name="containerGuid"/>.</para>
/// <ul>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/native-wic-codecs">
/// WIC Codecs from Microsoft</a></li>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/-wic-creating-encoder#encoder-options">
/// Image Encoding Overview: Encoder options</a></li>
/// </ul>
/// <para>This function may throw an exception.</para>
/// </remarks>
Task SaveToStreamAsync(
IDalamudTextureWrap wrap,
Guid containerGuid,
Stream stream,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
bool leaveStreamOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Saves a texture wrap to a file as an image file.</summary>
/// <param name="wrap">The texture wrap to save.</param>
/// <param name="containerGuid">The container GUID, obtained from <see cref="GetSupportedImageEncoderInfos"/>.</param>
/// <param name="path">The target file path. The target file will be overwritten if it exist.</param>
/// <param name="props">Properties to pass to the encoder. See remarks for valid values.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the save process.</returns>
/// <remarks>
/// <para><paramref name="wrap"/> must not be disposed until the task finishes.</para>
/// <para>See the following webpages for the valid values for <paramref name="props"/> per
/// <paramref name="containerGuid"/>.</para>
/// <ul>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/native-wic-codecs">
/// WIC Codecs from Microsoft</a></li>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/-wic-creating-encoder#encoder-options">
/// Image Encoding Overview: Encoder options</a></li>
/// </ul>
/// <para>This function may throw an exception.</para>
/// </remarks>
Task SaveToFileAsync(
IDalamudTextureWrap wrap,
Guid containerGuid,
string path,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Determines whether the system supports the given DXGI format. /// Determines whether the system supports the given DXGI format.
/// For use with <see cref="RawImageSpecification.DxgiFormat"/>. /// For use with <see cref="RawImageSpecification.DxgiFormat"/>.

View file

@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Textures;
namespace Dalamud.Plugin.Services;
/// <summary>Service that grants you to read instances of <see cref="IDalamudTextureWrap"/>.</summary>
public interface ITextureReadbackProvider
{
/// <summary>Gets the raw data of a texture wrap.</summary>
/// <param name="wrap">The source texture wrap.</param>
/// <param name="args">The texture modification arguments.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The raw data and its specifications.</returns>
/// <remarks>
/// <para>The length of the returned <c>RawData</c> may not match
/// <see cref="RawImageSpecification.Height"/> * <see cref="RawImageSpecification.Pitch"/>.</para>
/// <para>This function may throw an exception.</para>
/// </remarks>
Task<(RawImageSpecification Specification, byte[] RawData)> GetRawImageAsync(
IDalamudTextureWrap wrap,
TextureModificationArgs args = default,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Gets the supported bitmap encoders.</summary>
/// <returns>The supported bitmap encoders.</returns>
/// <remarks>
/// The following functions support the files of the container types pointed by yielded values.
/// <ul>
/// <li><see cref="SaveToStreamAsync"/></li>
/// <li><see cref="SaveToFileAsync"/></li>
/// </ul>
/// <para>This function may throw an exception.</para>
/// </remarks>
IEnumerable<IBitmapCodecInfo> GetSupportedImageEncoderInfos();
/// <summary>Saves a texture wrap to a stream in an image file format.</summary>
/// <param name="wrap">The texture wrap to save.</param>
/// <param name="containerGuid">The container GUID, obtained from <see cref="GetSupportedImageEncoderInfos"/>.</param>
/// <param name="stream">The stream to save to.</param>
/// <param name="props">Properties to pass to the encoder. See remarks for valid values.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="leaveStreamOpen">Whether to leave <paramref name="stream"/> open when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the save process.</returns>
/// <remarks>
/// <para><paramref name="wrap"/> must not be disposed until the task finishes.</para>
/// <para>See the following webpages for the valid values for <paramref name="props"/> per
/// <paramref name="containerGuid"/>.</para>
/// <ul>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/native-wic-codecs">
/// WIC Codecs from Microsoft</a></li>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/-wic-creating-encoder#encoder-options">
/// Image Encoding Overview: Encoder options</a></li>
/// </ul>
/// <para>This function may throw an exception.</para>
/// </remarks>
Task SaveToStreamAsync(
IDalamudTextureWrap wrap,
Guid containerGuid,
Stream stream,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
bool leaveStreamOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Saves a texture wrap to a file as an image file.</summary>
/// <param name="wrap">The texture wrap to save.</param>
/// <param name="containerGuid">The container GUID, obtained from <see cref="GetSupportedImageEncoderInfos"/>.</param>
/// <param name="path">The target file path. The target file will be overwritten if it exist.</param>
/// <param name="props">Properties to pass to the encoder. See remarks for valid values.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the save process.</returns>
/// <remarks>
/// <para><paramref name="wrap"/> must not be disposed until the task finishes.</para>
/// <para>See the following webpages for the valid values for <paramref name="props"/> per
/// <paramref name="containerGuid"/>.</para>
/// <ul>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/native-wic-codecs">
/// WIC Codecs from Microsoft</a></li>
/// <li><a href="https://learn.microsoft.com/en-us/windows/win32/wic/-wic-creating-encoder#encoder-options">
/// Image Encoding Overview: Encoder options</a></li>
/// </ul>
/// <para>This function may throw an exception.</para>
/// </remarks>
Task SaveToFileAsync(
IDalamudTextureWrap wrap,
Guid containerGuid,
string path,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
}