Add a few texture manipulation tools.

This commit is contained in:
Exter-N 2023-08-25 06:28:09 +02:00
parent 3530e139d1
commit 42b874413d
4 changed files with 381 additions and 109 deletions

@ -1 +1 @@
Subproject commit 863d08bd83381bb7fe4a8d5c514f0ba55379336f
Subproject commit c8394607addd29cb7f8ae3257f635a4486c40a63

View file

@ -6,23 +6,110 @@ using ImGuiNET;
using OtterGui.Raii;
using OtterGui;
using SixLabors.ImageSharp.PixelFormats;
using Dalamud.Interface;
using Penumbra.UI;
namespace Penumbra.Import.Textures;
public partial class CombinedTexture
{
private enum CombineOp
{
LeftCopy = -3,
RightCopy = -2,
Invalid = -1,
Over = 0,
Under = 1,
LeftMultiply = 2,
RightMultiply = 3,
CopyChannels = 4,
}
[Flags]
private enum Channels
{
Red = 1,
Green = 2,
Blue = 4,
Alpha = 8,
}
private Matrix4x4 _multiplierLeft = Matrix4x4.Identity;
private Vector4 _constantLeft = Vector4.Zero;
private Matrix4x4 _multiplierRight = Matrix4x4.Identity;
private bool _invertLeft = false;
private bool _invertRight = false;
private Vector4 _constantRight = Vector4.Zero;
private int _offsetX = 0;
private int _offsetY = 0;
private int _offsetY = 0;
private CombineOp _combineOp = CombineOp.Over;
private Channels _copyChannels = Channels.Red | Channels.Green | Channels.Blue | Channels.Alpha;
private static readonly IReadOnlyList<string> CombineOpLabels = new string[]
{
"Overlay over Input",
"Input over Overlay",
"Ignore Overlay",
"Replace Input",
"Copy Channels",
};
private static readonly IReadOnlyList<string> CombineOpTooltips = new string[]
{
"Standard composition.\nApply the overlay over the input.",
"Standard composition, reversed.\nApply the input over the overlay.",
"Use only the input, and ignore the overlay.",
"Completely replace the input with the overlay.",
"Replace some input channels with those from the overlay.\nUseful for Multi maps.",
};
private const float OneThird = 1.0f / 3.0f;
private const float RWeight = 0.2126f;
private const float GWeight = 0.7152f;
private const float BWeight = 0.0722f;
private static readonly IReadOnlyList<(string Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms = new (string, Matrix4x4, Vector4)[]
{
("No Transform (Identity)", Matrix4x4.Identity, Vector4.Zero),
("Grayscale (Average)", new Matrix4x4(OneThird, OneThird, OneThird, 0.0f, OneThird, OneThird, OneThird, 0.0f, OneThird, OneThird, OneThird, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), Vector4.Zero),
("Grayscale (Weighted)", new Matrix4x4(RWeight, RWeight, RWeight, 0.0f, GWeight, GWeight, GWeight, 0.0f, BWeight, BWeight, BWeight, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), Vector4.Zero),
("Grayscale (Average) to Alpha", new Matrix4x4(OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.Zero),
("Grayscale (Weighted) to Alpha", new Matrix4x4(RWeight, RWeight, RWeight, RWeight, GWeight, GWeight, GWeight, GWeight, BWeight, BWeight, BWeight, BWeight, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.Zero),
("Extract Red", new Matrix4x4(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW),
("Extract Green", new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW),
("Extract Blue", new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW),
("Extract Alpha", new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vector4.UnitW),
};
private CombineOp GetActualCombineOp()
{
var combineOp = (_left.IsLoaded, _right.IsLoaded) switch
{
(true, true) => _combineOp,
(true, false) => CombineOp.LeftMultiply,
(false, true) => CombineOp.RightMultiply,
(false, false) => CombineOp.Invalid,
};
if (combineOp == CombineOp.CopyChannels)
{
if (_copyChannels == 0)
combineOp = CombineOp.LeftMultiply;
else if (_copyChannels == (Channels.Red | Channels.Green | Channels.Blue | Channels.Alpha))
combineOp = CombineOp.RightMultiply;
}
return combineOp switch
{
CombineOp.LeftMultiply => (_multiplierLeft.IsIdentity && _constantLeft == Vector4.Zero) ? CombineOp.LeftCopy : CombineOp.LeftMultiply,
CombineOp.RightMultiply => (_multiplierRight.IsIdentity && _constantRight == Vector4.Zero) ? CombineOp.RightCopy : CombineOp.RightMultiply,
_ => combineOp,
};
}
private Vector4 DataLeft( int offset )
=> CappedVector( _left.RgbaPixels, offset, _multiplierLeft, _invertLeft );
=> CappedVector( _left.RgbaPixels, offset, _multiplierLeft, _constantLeft );
private Vector4 DataRight( int offset )
=> CappedVector( _right.RgbaPixels, offset, _multiplierRight, _invertRight );
=> CappedVector( _right.RgbaPixels, offset, _multiplierRight, _constantRight );
private Vector4 DataRight( int x, int y )
{
@ -34,7 +121,7 @@ public partial class CombinedTexture
}
var offset = ( y * _right.TextureWrap!.Width + x ) * 4;
return CappedVector( _right.RgbaPixels, offset, _multiplierRight, _invertRight );
return CappedVector( _right.RgbaPixels, offset, _multiplierRight, _constantRight );
}
private void AddPixelsMultiplied( int y, ParallelLoopState _ )
@ -55,6 +142,43 @@ public partial class CombinedTexture
}
}
private void ReverseAddPixelsMultiplied( int y, ParallelLoopState _ )
{
for( var x = 0; x < _left.TextureWrap!.Width; ++x )
{
var offset = ( _left.TextureWrap!.Width * y + x ) * 4;
var left = DataLeft( offset );
var right = DataRight( x, y );
var alpha = left.W + right.W * ( 1 - left.W );
var rgba = alpha == 0
? new Rgba32()
: new Rgba32( ( ( left * left.W + right * right.W * ( 1 - left.W ) ) / alpha ) with { W = alpha } );
_centerStorage.RgbaPixels[ offset ] = rgba.R;
_centerStorage.RgbaPixels[ offset + 1 ] = rgba.G;
_centerStorage.RgbaPixels[ offset + 2 ] = rgba.B;
_centerStorage.RgbaPixels[ offset + 3 ] = rgba.A;
}
}
private void ChannelMergePixelsMultiplied( int y, ParallelLoopState _ )
{
var channels = _copyChannels;
for( var x = 0; x < _left.TextureWrap!.Width; ++x )
{
var offset = ( _left.TextureWrap!.Width * y + x ) * 4;
var left = DataLeft( offset );
var right = DataRight( x, y );
var rgba = new Rgba32( ( channels & Channels.Red ) != 0 ? right.X : left.X,
( channels & Channels.Green ) != 0 ? right.Y : left.Y,
( channels & Channels.Blue ) != 0 ? right.Z : left.Z,
( channels & Channels.Alpha ) != 0 ? right.W : left.W );
_centerStorage.RgbaPixels[ offset ] = rgba.R;
_centerStorage.RgbaPixels[ offset + 1 ] = rgba.G;
_centerStorage.RgbaPixels[ offset + 2 ] = rgba.B;
_centerStorage.RgbaPixels[ offset + 3 ] = rgba.A;
}
}
private void MultiplyPixelsLeft( int y, ParallelLoopState _ )
{
for( var x = 0; x < _left.TextureWrap!.Width; ++x )
@ -74,8 +198,8 @@ public partial class CombinedTexture
for( var x = 0; x < _right.TextureWrap!.Width; ++x )
{
var offset = ( _right.TextureWrap!.Width * y + x ) * 4;
var left = DataRight( offset );
var rgba = new Rgba32( left );
var right = DataRight( offset );
var rgba = new Rgba32( right );
_centerStorage.RgbaPixels[ offset ] = rgba.R;
_centerStorage.RgbaPixels[ offset + 1 ] = rgba.G;
_centerStorage.RgbaPixels[ offset + 2 ] = rgba.B;
@ -86,23 +210,25 @@ public partial class CombinedTexture
private (int Width, int Height) CombineImage()
{
var (width, height) = _left.IsLoaded
var combineOp = GetActualCombineOp();
var (width, height) = combineOp is not CombineOp.Invalid or CombineOp.RightCopy or CombineOp.RightMultiply
? ( _left.TextureWrap!.Width, _left.TextureWrap!.Height )
: ( _right.TextureWrap!.Width, _right.TextureWrap!.Height );
_centerStorage.RgbaPixels = new byte[width * height * 4];
_centerStorage.Type = TextureType.Bitmap;
if( _left.IsLoaded )
Parallel.For( 0, height, combineOp switch
{
Parallel.For( 0, height, _right.IsLoaded ? AddPixelsMultiplied : MultiplyPixelsLeft );
}
else
{
Parallel.For( 0, height, MultiplyPixelsRight );
}
CombineOp.Over => AddPixelsMultiplied,
CombineOp.Under => ReverseAddPixelsMultiplied,
CombineOp.LeftMultiply => MultiplyPixelsLeft,
CombineOp.RightMultiply => MultiplyPixelsRight,
CombineOp.CopyChannels => ChannelMergePixelsMultiplied,
_ => throw new InvalidOperationException( $"Cannot combine images with operation {combineOp}" ),
} );
return ( width, height );
}
private static Vector4 CappedVector( IReadOnlyList< byte > bytes, int offset, Matrix4x4 transform, bool invert )
private static Vector4 CappedVector( IReadOnlyList< byte > bytes, int offset, Matrix4x4 transform, Vector4 constant )
{
if( bytes.Count == 0 )
{
@ -110,11 +236,7 @@ public partial class CombinedTexture
}
var rgba = new Rgba32( bytes[ offset ], bytes[ offset + 1 ], bytes[ offset + 2 ], bytes[ offset + 3 ] );
var transformed = Vector4.Transform( rgba.ToVector4(), transform );
if( invert )
{
transformed = new Vector4( 1 - transformed.X, 1 - transformed.Y, 1 - transformed.Z, transformed.W );
}
var transformed = Vector4.Transform( rgba.ToVector4(), transform ) + constant;
transformed.X = Math.Clamp( transformed.X, 0, 1 );
transformed.Y = Math.Clamp( transformed.Y, 0, 1 );
@ -138,8 +260,8 @@ public partial class CombinedTexture
public void DrawMatrixInputLeft( float width )
{
var ret = DrawMatrixInput( ref _multiplierLeft, width );
ret |= ImGui.Checkbox( "Invert Colors##Left", ref _invertLeft );
var ret = DrawMatrixInput( ref _multiplierLeft, ref _constantLeft, width );
ret |= DrawMatrixTools( ref _multiplierLeft, ref _constantLeft );
if( ret )
{
Update();
@ -148,23 +270,56 @@ public partial class CombinedTexture
public void DrawMatrixInputRight( float width )
{
var ret = DrawMatrixInput( ref _multiplierRight, width );
ret |= ImGui.Checkbox( "Invert Colors##Right", ref _invertRight );
ImGui.SameLine();
ImGui.SetNextItemWidth( 75 );
var ret = DrawMatrixInput( ref _multiplierRight, ref _constantRight, width );
ret |= DrawMatrixTools( ref _multiplierRight, ref _constantRight );
ImGui.SetNextItemWidth( 75.0f * UiHelpers.Scale );
ImGui.DragInt( "##XOffset", ref _offsetX, 0.5f );
ret |= ImGui.IsItemDeactivatedAfterEdit();
ImGui.SameLine();
ImGui.SetNextItemWidth( 75 );
ImGui.SetNextItemWidth( 75.0f * UiHelpers.Scale );
ImGui.DragInt( "Offsets##YOffset", ref _offsetY, 0.5f );
ret |= ImGui.IsItemDeactivatedAfterEdit();
ImGui.SetNextItemWidth( 200.0f * UiHelpers.Scale );
using( var c = ImRaii.Combo( "Combine Operation", CombineOpLabels[ (int)_combineOp ] ) )
{
if( c )
{
foreach( var op in Enum.GetValues<CombineOp>() )
{
if ( (int)op < 0 ) // Negative codes are for internal use only.
continue;
if( ImGui.Selectable( CombineOpLabels[ (int)op ], op == _combineOp ) )
{
_combineOp = op;
ret = true;
}
ImGuiUtil.SelectableHelpMarker( CombineOpTooltips[ (int)op ] );
}
}
}
using( var dis = ImRaii.Disabled( _combineOp != CombineOp.CopyChannels ))
{
ImGui.TextUnformatted( "Copy" );
foreach( var channel in Enum.GetValues<Channels>() )
{
ImGui.SameLine();
var copy = ( _copyChannels & channel ) != 0;
if( ImGui.Checkbox( channel.ToString(), ref copy ) )
{
_copyChannels = copy ? ( _copyChannels | channel ) : ( _copyChannels & ~channel );
ret = true;
}
}
}
if( ret )
{
Update();
}
}
private static bool DrawMatrixInput( ref Matrix4x4 multiplier, float width )
private static bool DrawMatrixInput( ref Matrix4x4 multiplier, ref Vector4 constant, float width )
{
using var table = ImRaii.Table( string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit );
if( !table )
@ -217,6 +372,110 @@ public partial class CombinedTexture
changes |= DragFloat( "##AB", inputWidth, ref multiplier.M43 );
changes |= DragFloat( "##AA", inputWidth, ref multiplier.M44 );
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.Text( "1 " );
changes |= DragFloat( "##1R", inputWidth, ref constant.X );
changes |= DragFloat( "##1G", inputWidth, ref constant.Y );
changes |= DragFloat( "##1B", inputWidth, ref constant.Z );
changes |= DragFloat( "##1A", inputWidth, ref constant.W );
return changes;
}
private static bool DrawMatrixTools( ref Matrix4x4 multiplier, ref Vector4 constant )
{
var changes = false;
using( var combo = ImRaii.Combo( "Presets", string.Empty, ImGuiComboFlags.NoPreview ) )
{
if( combo )
{
foreach( var (label, preMultiplier, preConstant) in PredefinedColorTransforms )
{
if( ImGui.Selectable( label, multiplier == preMultiplier && constant == preConstant ) )
{
multiplier = preMultiplier;
constant = preConstant;
changes = true;
}
}
}
}
ImGui.SameLine();
ImGui.Dummy( ImGuiHelpers.ScaledVector2( 20, 0 ) );
ImGui.SameLine();
ImGui.TextUnformatted( "Invert" );
ImGui.SameLine();
if( ImGui.Button( "Colors" ) )
{
InvertRed( ref multiplier, ref constant );
InvertGreen( ref multiplier, ref constant );
InvertBlue( ref multiplier, ref constant );
changes = true;
}
ImGui.SameLine();
if( ImGui.Button( "R" ) )
{
InvertRed( ref multiplier, ref constant );
changes = true;
}
ImGui.SameLine();
if( ImGui.Button( "G" ) )
{
InvertGreen( ref multiplier, ref constant );
changes = true;
}
ImGui.SameLine();
if( ImGui.Button( "B" ) )
{
InvertBlue( ref multiplier, ref constant );
changes = true;
}
ImGui.SameLine();
if( ImGui.Button( "A" ) )
{
InvertAlpha( ref multiplier, ref constant );
changes = true;
}
return changes;
}
private static void InvertRed( ref Matrix4x4 multiplier, ref Vector4 constant )
{
multiplier.M11 = -multiplier.M11;
multiplier.M21 = -multiplier.M21;
multiplier.M31 = -multiplier.M31;
multiplier.M41 = -multiplier.M41;
constant.X = 1.0f - constant.X;
}
private static void InvertGreen( ref Matrix4x4 multiplier, ref Vector4 constant )
{
multiplier.M12 = -multiplier.M12;
multiplier.M22 = -multiplier.M22;
multiplier.M32 = -multiplier.M32;
multiplier.M42 = -multiplier.M42;
constant.Y = 1.0f - constant.Y;
}
private static void InvertBlue( ref Matrix4x4 multiplier, ref Vector4 constant )
{
multiplier.M13 = -multiplier.M13;
multiplier.M23 = -multiplier.M23;
multiplier.M33 = -multiplier.M33;
multiplier.M43 = -multiplier.M43;
constant.Z = 1.0f - constant.Z;
}
private static void InvertAlpha( ref Matrix4x4 multiplier, ref Vector4 constant )
{
multiplier.M14 = -multiplier.M14;
multiplier.M24 = -multiplier.M24;
multiplier.M34 = -multiplier.M34;
multiplier.M44 = -multiplier.M44;
constant.W = 1.0f - constant.W;
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Numerics;
using System.Threading.Tasks;
@ -68,6 +69,38 @@ public partial class CombinedTexture : IDisposable
_current.TextureWrap!.Height);
}
public void SaveAs(TextureType? texType, TextureManager textures, string path, TextureSaveType type, bool mipMaps)
{
TextureType finalTexType;
if (texType.HasValue)
finalTexType = texType.Value;
else
{
finalTexType = Path.GetExtension(path).ToLowerInvariant() switch
{
".tex" => TextureType.Tex,
".dds" => TextureType.Dds,
".png" => TextureType.Png,
_ => TextureType.Unknown,
};
}
switch (finalTexType)
{
case TextureType.Tex:
SaveAsTex(textures, path, type, mipMaps);
break;
case TextureType.Dds:
SaveAsDds(textures, path, type, mipMaps);
break;
case TextureType.Png:
SaveAsPng(textures, path);
break;
default:
throw new ArgumentException($"Cannot save texture as TextureType {finalTexType} with extension {Path.GetExtension(path).ToLowerInvariant()}");
}
}
public void SaveAsTex(TextureManager textures, string path, TextureSaveType type, bool mipMaps)
=> SaveAs(textures, path, type, mipMaps, true);
@ -97,36 +130,22 @@ public partial class CombinedTexture : IDisposable
public void Update()
{
Clean();
if (_left.IsLoaded)
switch (GetActualCombineOp())
{
if (_right.IsLoaded)
{
_current = _centerStorage;
_mode = Mode.Custom;
}
else if (!_invertLeft && _multiplierLeft.IsIdentity)
{
case CombineOp.Invalid:
break;
case CombineOp.LeftCopy:
_mode = Mode.LeftCopy;
_current = _left;
}
else
{
_current = _centerStorage;
_mode = Mode.Custom;
}
}
else if (_right.IsLoaded)
{
if (!_invertRight && _multiplierRight.IsIdentity)
{
_current = _right;
break;
case CombineOp.RightCopy:
_mode = Mode.RightCopy;
}
else
{
_current = _centerStorage;
_current = _right;
break;
default:
_mode = Mode.Custom;
}
_current = _centerStorage;
break;
}
}

View file

@ -90,7 +90,7 @@ public partial class ModEditWindow
if (ImGui.Selectable(newText, idx == _currentSaveAs))
_currentSaveAs = idx;
ImGuiUtil.HoverTooltip(newDesc);
ImGuiUtil.SelectableHelpMarker(newDesc);
}
}
@ -114,73 +114,65 @@ public partial class ModEditWindow
SaveAsCombo();
ImGui.SameLine();
MipMapInput();
if (ImGui.Button("Save as TEX", -Vector2.UnitX))
var canSaveInPlace = Path.IsPathRooted(_left.Path) && _left.Type is TextureType.Tex or TextureType.Dds or TextureType.Png;
var buttonSize2 = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0);
if (ImGuiUtil.DrawDisabledButton("Save in place", buttonSize2,
"This saves the texture in place. This is not revertible.",
!canSaveInPlace || _center.IsLeftCopy && _currentSaveAs == (int)CombinedTexture.TextureSaveType.AsIs))
{
var fileName = Path.GetFileNameWithoutExtension(_left.Path.Length > 0 ? _left.Path : _right.Path);
_fileDialog.OpenSavePicker("Save Texture as TEX...", ".tex", fileName, ".tex", (a, b) =>
{
if (a)
_center.SaveAsTex(_textures, b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
}, _mod!.ModPath.FullName, _forceTextureStartPath);
_forceTextureStartPath = false;
_center.SaveAs(_left.Type, _textures, _left.Path, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
AddReloadTask(_left.Path, false);
}
if (ImGui.Button("Save as DDS", -Vector2.UnitX))
ImGui.SameLine();
if (ImGui.Button("Save as TEX, DDS or PNG", buttonSize2))
{
var fileName = Path.GetFileNameWithoutExtension(_right.Path.Length > 0 ? _right.Path : _left.Path);
_fileDialog.OpenSavePicker("Save Texture as DDS...", ".dds", fileName, ".dds", (a, b) =>
var fileName = Path.GetFileNameWithoutExtension(_left.Path.Length > 0 ? _left.Path : _right.Path);
_fileDialog.OpenSavePicker("Save Texture as TEX, DDS or PNG...", "Textures{.png,.dds,.tex},.tex,.dds,.png", fileName, ".tex", (a, b) =>
{
if (a)
_center.SaveAsDds(_textures, b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
{
_center.SaveAs(null, _textures, b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
if (b == _left.Path)
AddReloadTask(_left.Path, false);
else if (b == _right.Path)
AddReloadTask(_right.Path, true);
}
}, _mod!.ModPath.FullName, _forceTextureStartPath);
_forceTextureStartPath = false;
}
ImGui.NewLine();
if (ImGui.Button("Save as PNG", -Vector2.UnitX))
var canConvertInPlace = canSaveInPlace && _left.Type is TextureType.Tex && _center.IsLeftCopy;
var buttonSize3 = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X * 2) / 3, 0);
if (ImGuiUtil.DrawDisabledButton("Convert to BC7", buttonSize3,
"This converts the texture to BC7 format in place. This is not revertible.",
!canConvertInPlace || _left.Format is DXGIFormat.BC7Typeless or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB))
{
var fileName = Path.GetFileNameWithoutExtension(_right.Path.Length > 0 ? _right.Path : _left.Path);
_fileDialog.OpenSavePicker("Save Texture as PNG...", ".png", fileName, ".png", (a, b) =>
{
if (a)
_center.SaveAsPng(_textures, b);
}, _mod!.ModPath.FullName, _forceTextureStartPath);
_forceTextureStartPath = false;
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC7, _left.MipMaps > 1);
AddReloadTask(_left.Path, false);
}
if (_left.Type is TextureType.Tex && _center.IsLeftCopy)
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Convert to BC3", buttonSize3,
"This converts the texture to BC3 format in place. This is not revertible.",
!canConvertInPlace || _left.Format is DXGIFormat.BC3Typeless or DXGIFormat.BC3UNorm or DXGIFormat.BC3UNormSRGB))
{
var buttonSize = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X * 2) / 3, 0);
if (ImGuiUtil.DrawDisabledButton("Convert to BC7", buttonSize,
"This converts the texture to BC7 format in place. This is not revertible.",
_left.Format is DXGIFormat.BC7Typeless or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB))
{
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC7, _left.MipMaps > 1);
AddReloadTask(_left.Path);
}
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Convert to BC3", buttonSize,
"This converts the texture to BC3 format in place. This is not revertible.",
_left.Format is DXGIFormat.BC3Typeless or DXGIFormat.BC3UNorm or DXGIFormat.BC3UNormSRGB))
{
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC3, _left.MipMaps > 1);
AddReloadTask(_left.Path);
}
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Convert to RGBA", buttonSize,
"This converts the texture to RGBA format in place. This is not revertible.",
_left.Format is DXGIFormat.B8G8R8A8UNorm or DXGIFormat.B8G8R8A8Typeless or DXGIFormat.B8G8R8A8UNormSRGB))
{
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.Bitmap, _left.MipMaps > 1);
AddReloadTask(_left.Path);
}
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC3, _left.MipMaps > 1);
AddReloadTask(_left.Path, false);
}
else
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Convert to RGBA", buttonSize3,
"This converts the texture to RGBA format in place. This is not revertible.",
!canConvertInPlace || _left.Format is DXGIFormat.B8G8R8A8UNorm or DXGIFormat.B8G8R8A8Typeless or DXGIFormat.B8G8R8A8UNormSRGB))
{
ImGui.NewLine();
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.Bitmap, _left.MipMaps > 1);
AddReloadTask(_left.Path, false);
}
}
@ -212,17 +204,19 @@ public partial class ModEditWindow
_center.Draw(_textures, imageSize);
}
private void AddReloadTask(string path)
private void AddReloadTask(string path, bool right)
{
_center.SaveTask.ContinueWith(t =>
{
if (!t.IsCompletedSuccessfully)
return;
if (_left.Path != path)
var tex = right ? _right : _left;
if (tex.Path != path)
return;
_dalamud.Framework.RunOnFrameworkThread(() => _left.Reload(_textures));
_dalamud.Framework.RunOnFrameworkThread(() => tex.Reload(_textures));
});
}