diff --git a/TelltaleTextureTool/TelltaleTextureTool/Converter.cs b/TelltaleTextureTool/TelltaleTextureTool/Converter.cs index fa776de..ea17ab9 100644 --- a/TelltaleTextureTool/TelltaleTextureTool/Converter.cs +++ b/TelltaleTextureTool/TelltaleTextureTool/Converter.cs @@ -175,7 +175,7 @@ public static void ConvertTextureFromD3DtxToOthers(string sourceFilePath, string Texture texture = new(array, TextureType.D3DTX); - texture.ChangePreviewImage(options, true); + texture.TransformTexture(options, true, false); texture.SaveTexture(Path.Combine(destinationDirectory, Path.GetFileNameWithoutExtension(sourceFilePath)), newTextureType); texture.Release(); @@ -249,7 +249,7 @@ public static void ConvertTextureFromOthersToD3Dtx(string sourceFilePath, string options.IsSRGB = true; } - texture.ChangePreviewImage(options, true); + texture.TransformTexture(options, true, false); // Get the image texture.GetDDSInformation(out D3DTXMetadata metadata, out ImageSection[] sections, flags); @@ -259,8 +259,6 @@ public static void ConvertTextureFromOthersToD3Dtx(string sourceFilePath, string metadata.Platform = options.PlatformType; } - Console.WriteLine("DXGI: " + texture.Metadata.Format); - Console.WriteLine(metadata.Format); // Modify the d3dtx file using our dds data d3dtxMaster.ModifyD3DTX(metadata, sections); diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/Image/ImageData.cs b/TelltaleTextureTool/TelltaleTextureTool/GUI/Image/ImageData.cs index 920ef8e..e8b0966 100644 --- a/TelltaleTextureTool/TelltaleTextureTool/GUI/Image/ImageData.cs +++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/Image/ImageData.cs @@ -8,6 +8,8 @@ using System.Runtime.InteropServices; using TelltaleTextureTool.TelltaleEnums; using TelltaleTextureTool.Graphics; +using Avalonia; +using Avalonia.Platform; namespace TelltaleTextureTool; @@ -15,15 +17,13 @@ public class ImageData { public ImageProperties ImageProperties { get; set; } = new ImageProperties(); public Texture DDSImage { get; set; } = new Texture(); - + public TextureType CurrentTextureType { get; set; } public uint MaxMip { get; set; } public uint MaxFace { get; set; } private string CurrentFilePath { get; set; } = string.Empty; private bool IsSamePath { get; set; } private bool HasPixelData { get; set; } - private TextureType CurrentTextureType { get; set; } - private TelltaleToolGame Game { get; set; } private bool IsLegacyConsole { get; set; } @@ -70,7 +70,7 @@ public void ApplyEffects(ImageAdvancedOptions options) { try { - DDSImage.ChangePreviewImage(options); + DDSImage.TransformTexture(options, false, true); DDSImage.GetBounds(out uint maxMip, out uint maxFace); MaxMip = maxMip; @@ -188,11 +188,11 @@ private void GetImageDataFromCommon(out ImageProperties imageProperties) /// /// /// The bitmap from the mip and face. - public Bitmap GetBitmapFromScratchImage(uint mip = 0, uint face = 0) + public WriteableBitmap GetBitmapFromScratchImage(uint mip = 0, uint face = 0) { if (TextureType.Unknown == CurrentTextureType) { - return new Bitmap(MemoryStream.Null); + return null; } DDSImage.GetBounds(out uint maxMip, out uint maxFace); @@ -210,26 +210,24 @@ public Bitmap GetBitmapFromScratchImage(uint mip = 0, uint face = 0) DDSImage.GetData(mip, face, out ulong width, out ulong height, out ulong pitch, out ulong length, out byte[] pixelData); - // Converts the data into writeableBitmap. (TODO Insert a link to the code) - var imageInfo = new SKImageInfo((int)width, (int)height, SKColorType.Rgba8888); - var handle = GCHandle.Alloc(pixelData, GCHandleType.Pinned); - var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(pixelData, 0); - using var data = SKData.Create(ptr, (int)length, (_, _) => handle.Free()); - using var skImage = SKImage.FromPixels(imageInfo, data, (int)pitch); - using var bitmap = SKBitmap.FromImage(skImage); - - // Create a memory stream to hold the PNG data - var memoryStream = new MemoryStream(); + // Create a WriteableBitmap in RGBA8888 format + var bitmap = new WriteableBitmap( + new PixelSize((int)width, (int)height), + new Vector(96, 96), // Set DPI as necessary + PixelFormat.Rgba8888, + AlphaFormat.Unpremul); - // Encode the bitmap to PNG and write it to the memory stream - var wstream = new SKManagedWStream(memoryStream); - - var success = bitmap.Encode(wstream, SKEncodedImageFormat.Png, 95); - Console.WriteLine(success ? "Image converted successfully" : "Image conversion failed"); + // Lock the WriteableBitmap's back buffer to write pixel data + using (var framebuffer = bitmap.Lock()) + { + IntPtr framebufferPtr = framebuffer.Address; + int framebufferRowBytes = framebuffer.RowBytes; - memoryStream.Position = 0; + // Copy pixelData to the WriteableBitmap's memory + Marshal.Copy(pixelData, 0, framebufferPtr, (int)length); + } - return new Bitmap(memoryStream); + return bitmap; } private static void GetImageDataFromInvalid(out ImageProperties imageProperties) diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs b/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs index a3daf4f..78d1abc 100644 --- a/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs +++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs @@ -33,7 +33,7 @@ public class EnumDisplayNameConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value == null) + if (value is null) return string.Empty; // Get the field in the enum type that matches the current enum value @@ -395,7 +395,7 @@ public async Task DeleteFileButton_Click() var result = await MessageBoxManager.GetMessageBoxStandard(messageBox) .ShowWindowDialogAsync(mainWindow); - if (result != ButtonResult.Yes) return; + if (result is not ButtonResult.Yes) return; Directory.Delete(textureFilePath); } @@ -489,7 +489,7 @@ public async Task ContextMenuOpenFileCommand() { try { - if (DataGridSelectedItem == null) + if (DataGridSelectedItem is null) return; var workingDirectoryFile = @@ -514,7 +514,7 @@ public async Task ContextMenuOpenFolderCommand() try { // if there is no valid item selected, don't continue - if (DataGridSelectedItem == null) + if (DataGridSelectedItem is null) return; // get our selected file object from the working directory @@ -540,9 +540,9 @@ public async Task ContextMenuOpenFileExplorerCommand() { try { - if (DirectoryPath == null) return; + if (DirectoryPath is null) return; - if (DataGridSelectedItem == null) + if (DataGridSelectedItem is null) { if (Directory.Exists(DirectoryPath)) await OpenFileExplorer(DirectoryPath); @@ -564,7 +564,7 @@ public async Task ContextMenuOpenFileExplorerCommand() [RelayCommand] public async Task RefreshDirectoryButton_Click() { - if (DirectoryPath != null && DirectoryPath != string.Empty) + if (DirectoryPath is not null && DirectoryPath != string.Empty) { await RefreshUiAsync(); } @@ -601,7 +601,7 @@ public async Task ConvertButton_Click() { try { - if (DataGridSelectedItem == null) return; + if (DataGridSelectedItem is null) return; var workingDirectoryFile = DataGridSelectedItem; @@ -624,7 +624,7 @@ public async Task ConvertButton_Click() AllowMultiple = false, }); - if (folderPath is null || folderPath.Count == 0) + if (folderPath is null || folderPath.Count is 0) { return; } @@ -697,7 +697,7 @@ public async Task DebugButton_Click() { try { - if (DataGridSelectedItem == null) return; + if (DataGridSelectedItem is null) return; var workingDirectoryFile = DataGridSelectedItem; @@ -706,7 +706,7 @@ public async Task DebugButton_Click() string debugInfo = string.Empty; - if (workingDirectoryFile.FileType == ".d3dtx") + if (workingDirectoryFile.FileType is ".d3dtx") { var d3dtx = new D3DTX_Master(); d3dtx.ReadD3DTXFile(textureFilePath, ImageAdvancedOptions.GameID, ImageAdvancedOptions.IsLegacyConsole); @@ -777,7 +777,7 @@ public async Task ReturnDirectory_Click() { try { - if (Directory.GetParent(DirectoryPath) == null) return; + if (Directory.GetParent(DirectoryPath) is null) return; WorkingDirectoryFiles.Clear(); await mainManager.SetWorkingDirectoryPath(Directory.GetParent(DirectoryPath).ToString()); DataGridSelectedItem = null; @@ -828,7 +828,7 @@ private void ChangeComboBoxItemsByItemExtension(string itemExtension) {string.Empty, _folderTypes} }; - if (itemExtension == null) + if (itemExtension is null) { FromFormatsList = null; ToFormatsList = null; @@ -908,7 +908,7 @@ public async void RowDoubleTappedCommand(object? sender, TappedEventArgs args) if (source is null) return; if (source is Border) { - if (DataGridSelectedItem == null) + if (DataGridSelectedItem is null) return; var workingDirectoryFile = @@ -946,7 +946,7 @@ public async void RowDoubleTappedCommand(object? sender, TappedEventArgs args) private void UpdateUIElementsAsync() { - if (DataGridSelectedItem != null) + if (DataGridSelectedItem is not null) { var workingDirectoryFile = DataGridSelectedItem; var path = workingDirectoryFile.FilePath; @@ -960,7 +960,7 @@ private void UpdateUIElementsAsync() throw new Exception("File or directory do not exist anymore! Refreshing the directory."); } - DebugButtonStatus = extension == ".d3dtx" || extension == ".dds"; + DebugButtonStatus = extension is ".d3dtx" || extension is ".dds"; SaveButtonStatus = File.Exists(path); DeleteButtonStatus = true; ContextOpenFolderStatus = Directory.Exists(path); @@ -1029,7 +1029,7 @@ public async Task PreviewImage() { UpdateUIElementsAsync(); - if (DataGridSelectedItem == null) + if (DataGridSelectedItem is null) return; var workingDirectoryFile = DataGridSelectedItem; @@ -1049,9 +1049,14 @@ public async Task PreviewImage() ImageData.Initialize(filePath, textureType, ImageAdvancedOptions.GameID, ImageAdvancedOptions.IsLegacyConsole); + if (textureType is TextureType.Unknown) + { + ImageData.Reset(); + } + ImageAdvancedOptions = ImageData.GetImageAdvancedOptions(ImageAdvancedOptions); - if (textureType != TextureType.Unknown) + if (textureType is not TextureType.Unknown) { ImageData.ApplyEffects(ImageAdvancedOptions); } @@ -1068,7 +1073,7 @@ public async Task PreviewImage() ImageProperties = ImageData.ImageProperties; - if (textureType != TextureType.Unknown) + if (textureType is not TextureType.Unknown) { ImagePreview = ImageData.GetBitmapFromScratchImage(MipValue, FaceValue); } @@ -1092,7 +1097,7 @@ public async Task UpdateBitmap() { try { - if (DataGridSelectedItem == null) + if (DataGridSelectedItem is null) return; var workingDirectoryFile = DataGridSelectedItem; @@ -1103,7 +1108,7 @@ public async Task UpdateBitmap() if (extension != string.Empty) textureType = GetTextureTypeFromItem(extension.ToUpperInvariant().Remove(0, 1)); - if (textureType == TextureType.Unknown) + if (textureType is TextureType.Unknown) { return; } @@ -1118,7 +1123,10 @@ public async Task UpdateBitmap() ImageProperties = ImageData.ImageProperties; - ImagePreview = ImageData.GetBitmapFromScratchImage(MipValue, FaceValue); + if (textureType is not TextureType.Unknown) + { + ImagePreview = ImageData.GetBitmapFromScratchImage(MipValue, FaceValue); + } } catch (Exception ex) { @@ -1130,11 +1138,13 @@ public async Task UpdateBitmap() protected override async void OnPropertyChanged(PropertyChangedEventArgs e) { base.OnPropertyChanged(e); - if (e.PropertyName == nameof(MipValue) || e.PropertyName == nameof(FaceValue)) + if (e.PropertyName is nameof(MipValue) || e.PropertyName is nameof(FaceValue)) { - ImagePreview = ImageData.GetBitmapFromScratchImage(MipValue, FaceValue); + if (ImageData.CurrentTextureType is not TextureType.Unknown) + ImagePreview = ImageData.GetBitmapFromScratchImage(MipValue, FaceValue); + else { ImagePreview = new SvgImage { Source = SvgSource.Load(ErrorSvgFilename, _assetsUri) }; } } - if (e.PropertyName == nameof(ImageAdvancedOptions)) + if (e.PropertyName is nameof(ImageAdvancedOptions)) { await UpdateBitmap(); } diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/TextureManager.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/TextureManager.cs index 53cf3b8..b59e2a7 100644 --- a/TelltaleTextureTool/TelltaleTextureTool/Graphics/TextureManager.cs +++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/TextureManager.cs @@ -88,7 +88,7 @@ public static string GetTextureDebugInfo(string filePath, TextureType textureTyp Console.WriteLine(e.Message); scratchImage.Release(); throw new Exception("Failed to load image!"); - } + } string debugInfo = GetTextureDebugInfo(scratchImage.GetMetadata()); @@ -392,16 +392,14 @@ unsafe public static void DefaultCopy(Vector4* outPixels, Vector4* inPixels, ulo /// public unsafe partial class Texture { - public string FilePath { get; set; } = string.Empty; - private ScratchImage Image { get; set; } public TexMetadata Metadata { get; set; } - - private ScratchImage OriginalImage { get; set; } - private TexMetadata OriginalMetadata { get; set; } - public ImageAdvancedOptions CurrentOptions { get; set; } = new ImageAdvancedOptions(); public TextureType TextureType { get; set; } = TextureType.Unknown; + + private ScratchImage Image { get; set; } + private ScratchImage OriginalImage { get; set; } private DXGIFormat PreviewFormat { get; set; } + private string FilePath { get; set; } = string.Empty; private ulong PreviewWidth { get; set; } private ulong PreviewHeight { get; set; } private uint PreviewMip { get; set; } = 0; @@ -440,7 +438,6 @@ private void InitializeSingleScratchImage(byte[] ddsData, bool isCopy, DDSFlags Blob blob = DirectXTex.CreateBlob(); TexMetadata meta = new(); - Console.WriteLine("DDS LENGTH: " + ddsData.Length); fixed (byte* srcPtr = src) { DirectXTex.LoadFromDDSMemory(srcPtr, (nuint)src.Length, flags, ref meta, ref Image).ThrowIf(); @@ -454,7 +451,6 @@ private void InitializeSingleScratchImage(byte[] ddsData, bool isCopy, DDSFlags else { OriginalImage = Image; - OriginalMetadata = meta; } blob.Release(); @@ -479,35 +475,8 @@ public string GetDDSDebugInfo() return GetTextureDebugInfo(Metadata); } - public void ConvertToRGBA() - { - ScratchImage destImage = DirectXTex.CreateScratchImage(); - - TexMetadata ogMeta = Image.GetMetadata(); - Decompress(DXGIFormat.R8G8B8A8_UNORM); - - if (Image.GetMetadata().Format != (uint)DXGIFormat.R8G8B8A8_UNORM) - { - DirectXTex.Convert2(Image.GetImages(), Image.GetImageCount(), ref ogMeta, (int)DXGIFormat.R8G8B8A8_UNORM, TexFilterFlags.Default, 0.5f, ref destImage).ThrowIf(); - - Image.Release(); - Image = destImage; - } - else - { - destImage.Release(); - } - - Metadata = Image.GetMetadata(); - } - public byte[] GetSectionPixelData(uint mip, uint face) { - if ((uint)Image.GetMetadata().Format != (uint)DXGIFormat.R8G8B8A8_UNORM) - { - ConvertToRGBA(); - } - uint slice = 0; // Swap slice and face if it's a 3D texture, because they don't have faces. @@ -519,14 +488,46 @@ public byte[] GetSectionPixelData(uint mip, uint face) var image = DirectXTex.GetImage(Image, (ulong)mip, (ulong)face, (ulong)slice); - PreviewFormat = (DXGIFormat)image->Format; - PreviewWidth = image->Width; - PreviewHeight = image->Height; - PreviewMip = mip; + ScratchImage destImage = DirectXTex.CreateScratchImage(); + byte[] pixels; + + try + { + DirectXTex.InitializeFromImage(destImage, *image, false, CPFlags.None); + + if (DirectXTex.IsCompressed(destImage.GetMetadata().Format)) + { + ScratchImage newDestImage = DirectXTex.CreateScratchImage(); + + DirectXTex.Decompress(destImage.GetImage(0, 0, 0), (int)DXGIFormat.R8G8B8A8_UNORM, ref newDestImage).ThrowIf(); + + destImage.Release(); + destImage = newDestImage; + } + + if (destImage.GetMetadata().Format != (uint)DXGIFormat.R8G8B8A8_UNORM) + { + ScratchImage newDestImage = DirectXTex.CreateScratchImage(); + + DirectXTex.Convert(destImage.GetImage(0, 0, 0), (int)DXGIFormat.R8G8B8A8_UNORM, TexFilterFlags.Default, 0.5f, ref newDestImage).ThrowIf(); - byte[] pixels = new byte[image->SlicePitch]; + destImage.Release(); + destImage = newDestImage; + } + + PreviewFormat = (DXGIFormat)destImage.GetMetadata().Format; + PreviewWidth = destImage.GetMetadata().Width; + PreviewHeight = destImage.GetMetadata().Height; + PreviewMip = mip; - Marshal.Copy((nint)image->Pixels, pixels, 0, pixels.Length); + pixels = new byte[destImage.GetImage(0, 0, 0)->SlicePitch]; + + Marshal.Copy((nint)destImage.GetImage(0, 0, 0)->Pixels, pixels, 0, pixels.Length); + } + finally + { + destImage.Release(); + } return pixels; } @@ -635,8 +636,6 @@ public void TransformImage(ImageEffect conversionMode = ImageEffect.DEFAULT) DirectXTex.TransformImage2(Image.GetImages(), Image.GetImageCount(), ref texMetadata, transformFunction, ref transformedImage).ThrowIf(); - Console.WriteLine("Transforming image" + Image.GetImageCount()); - Image.Release(); Image = transformedImage; @@ -693,7 +692,6 @@ private void Initialize(string filePath, TextureType textureType, bool isCopy = OriginalImage.Release(); } OriginalImage = scratchImage; - OriginalMetadata = texMetadata; } } @@ -707,8 +705,6 @@ private void Initialize(string filePath, TextureType textureType, bool isCopy = private void ResetImageToOriginal() { - Metadata = OriginalMetadata; - if (TextureType != TextureType.D3DTX) { Initialize(FilePath, TextureType, true, DDSFlags.None); @@ -720,7 +716,13 @@ private void ResetImageToOriginal() } } - public void ChangePreviewImage(ImageAdvancedOptions options, bool keepOriginal = false) + /// + /// Changes the image itself based on the options provided. + /// + /// + /// + /// + public void TransformTexture(ImageAdvancedOptions options, bool keepOriginal = false, bool convertingOnly = false) { ResetImageToOriginal(); @@ -801,6 +803,7 @@ public void ChangePreviewImage(ImageAdvancedOptions options, bool keepOriginal = Compress((DXGIFormat)OriginalImage.GetMetadata().Format); } + if (options.EnableSwizzle && options.IsSwizzle) { Swizzle(options.PlatformType);