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

View file

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

View file

@ -71,7 +71,7 @@ internal sealed partial class TextureManager
using var istream = ManagedIStream.Create(stream, leaveStreamOpen);
var (specs, bytes) = await this.GetRawDataFromExistingTextureAsync(
var (specs, bytes) = await this.GetRawImageAsync(
wrap,
new()
{
@ -164,7 +164,7 @@ internal sealed partial class TextureManager
this.Wic.GetSupportedDecoderInfos();
/// <inheritdoc/>
IEnumerable<IBitmapCodecInfo> ITextureProvider.GetSupportedImageEncoderInfos() =>
IEnumerable<IBitmapCodecInfo> ITextureReadbackProvider.GetSupportedImageEncoderInfos() =>
this.Wic.GetSupportedEncoderInfos();
/// <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<ITextureSubstitutionProvider>]
#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));

View file

@ -6,8 +6,8 @@ using TerraFX.Interop.DirectX;
namespace Dalamud.Interface.Textures;
/// <summary>Describes how to modify an existing texture.</summary>
public record struct ExistingTextureModificationArgs()
/// <summary>Describes how to modify a texture.</summary>
public record struct TextureModificationArgs()
{
/// <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>
@ -52,10 +52,10 @@ public record struct ExistingTextureModificationArgs()
/// <summary>Gets the effective value of <see cref="Uv1"/>.</summary>
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>
/// <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>
internal bool IsCompleteSourceCopy(in D3D11_TEXTURE2D_DESC desc) =>
this.Uv0 == Vector2.Zero

View file

@ -41,7 +41,7 @@ public partial interface ITextureProvider
/// <remarks><para>This function may throw an exception.</para></remarks>
Task<IDalamudTextureWrap> CreateFromExistingTextureAsync(
IDalamudTextureWrap wrap,
ExistingTextureModificationArgs args = default,
TextureModificationArgs args = default,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
@ -151,17 +151,6 @@ public partial interface ITextureProvider
/// </remarks>
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>
/// <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>
@ -208,85 +197,6 @@ public partial interface ITextureProvider
/// <remarks><para>This function does not throw exceptions.</para></remarks>
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>
/// Determines whether the system supports the given DXGI format.
/// 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);
}