From bb1cba89e18b56927b98a99ccf74894d0010958d Mon Sep 17 00:00:00 2001 From: VaDiM Date: Wed, 17 Dec 2025 07:22:54 +0300 Subject: [PATCH] Some refactor for Texture2DExtensions --- AssetStudioCLI/ParallelExporter.cs | 3 +- AssetStudioGUI/AssetStudioGUIForm.cs | 116 ++++++------- AssetStudioGUI/ParallelExport.cs | 3 +- .../CubismLive2DExtractor/Live2DExtractor.cs | 7 +- AssetStudioUtility/SpriteHelper.cs | 162 +++++++++--------- AssetStudioUtility/Texture2DExtensions.cs | 21 ++- 6 files changed, 154 insertions(+), 158 deletions(-) diff --git a/AssetStudioCLI/ParallelExporter.cs b/AssetStudioCLI/ParallelExporter.cs index 75cf504..11e465f 100644 --- a/AssetStudioCLI/ParallelExporter.cs +++ b/AssetStudioCLI/ParallelExporter.cs @@ -45,8 +45,7 @@ namespace AssetStudioCLI debugLog += sb.ToString(); } - var image = m_Texture2D.ConvertToImage(flip: true); - if (image == null) + if (!m_Texture2D.TryConvertToImage(flip: true, out var image)) { Logger.Error($"{debugLog}Export error. Failed to convert texture \"{m_Texture2D.m_Name}\" into image"); return false; diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 7665deb..793ac36 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -964,68 +964,66 @@ namespace AssetStudioGUI private void PreviewTexture2D(AssetItem assetItem, Texture2D m_Texture2D) { - var image = m_Texture2D.ConvertToImage(true); - if (image != null) - { - var bitmap = new DirectBitmap(image); - image.Dispose(); - - assetItem.InfoText = - $"Width: {m_Texture2D.m_Width}" + - $"\nHeight: {m_Texture2D.m_Height}" + - $"\nFormat: {m_Texture2D.m_TextureFormat}"; - switch (m_Texture2D.m_TextureSettings.m_FilterMode) - { - case 0: assetItem.InfoText += "\nFilter mode: Point "; break; - case 1: assetItem.InfoText += "\nFilter mode: Bilinear "; break; - case 2: assetItem.InfoText += "\nFilter mode: Trilinear "; break; - } - assetItem.InfoText += $"\nAnisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}\nMip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}"; - switch (m_Texture2D.m_TextureSettings.m_WrapMode) - { - case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break; - case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break; - } - assetItem.InfoText += "\nChannels: "; - var validChannel = 0; - for (var i = 0; i < 4; i++) - { - if (textureChannels[i]) - { - assetItem.InfoText += textureChannelNames[i]; - validChannel++; - } - } - if (validChannel == 0) - assetItem.InfoText += "None"; - if (validChannel != 4) - { - var bytes = bitmap.Bits; - for (var i = 0; i < bitmap.Height; i++) - { - var offset = Math.Abs(bitmap.Stride) * i; - for (var j = 0; j < bitmap.Width; j++) - { - bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; - bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; - bytes[offset + 2] = textureChannels[2] ? bytes[offset + 2] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; - bytes[offset + 3] = textureChannels[3] ? bytes[offset + 3] : byte.MaxValue; - offset += 4; - } - } - } - var switchSwizzled = m_Texture2D.m_PlatformBlob.Length != 0; - assetItem.InfoText += assetItem.Asset.platform == BuildTarget.Switch - ? $"\nUses texture swizzling: {switchSwizzled}" - : ""; - PreviewTexture(bitmap); - - StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle"); - } - else + if (!m_Texture2D.TryConvertToImage(true, out var image)) { StatusStripUpdate("Unsupported image for preview"); + return; } + + var bitmap = new DirectBitmap(image); + image.Dispose(); + + assetItem.InfoText = + $"Width: {m_Texture2D.m_Width}" + + $"\nHeight: {m_Texture2D.m_Height}" + + $"\nFormat: {m_Texture2D.m_TextureFormat}"; + switch (m_Texture2D.m_TextureSettings.m_FilterMode) + { + case 0: assetItem.InfoText += "\nFilter mode: Point "; break; + case 1: assetItem.InfoText += "\nFilter mode: Bilinear "; break; + case 2: assetItem.InfoText += "\nFilter mode: Trilinear "; break; + } + assetItem.InfoText += $"\nAnisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}\nMip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}"; + switch (m_Texture2D.m_TextureSettings.m_WrapMode) + { + case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break; + case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break; + } + assetItem.InfoText += "\nChannels: "; + var validChannel = 0; + for (var i = 0; i < 4; i++) + { + if (textureChannels[i]) + { + assetItem.InfoText += textureChannelNames[i]; + validChannel++; + } + } + if (validChannel == 0) + assetItem.InfoText += "None"; + if (validChannel != 4) + { + var bytes = bitmap.Bits; + for (var i = 0; i < bitmap.Height; i++) + { + var offset = Math.Abs(bitmap.Stride) * i; + for (var j = 0; j < bitmap.Width; j++) + { + bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; + bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; + bytes[offset + 2] = textureChannels[2] ? bytes[offset + 2] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; + bytes[offset + 3] = textureChannels[3] ? bytes[offset + 3] : byte.MaxValue; + offset += 4; + } + } + } + var switchSwizzled = m_Texture2D.m_PlatformBlob.Length != 0; + assetItem.InfoText += assetItem.Asset.platform == BuildTarget.Switch + ? $"\nUses texture swizzling: {switchSwizzled}" + : ""; + PreviewTexture(bitmap); + + StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle"); } private void PreviewAudioClip(AssetItem assetItem, AudioClip m_AudioClip) diff --git a/AssetStudioGUI/ParallelExport.cs b/AssetStudioGUI/ParallelExport.cs index 0143d12..ab3c993 100644 --- a/AssetStudioGUI/ParallelExport.cs +++ b/AssetStudioGUI/ParallelExport.cs @@ -44,8 +44,7 @@ namespace AssetStudioGUI debugLog += sb.ToString(); } - var image = m_Texture2D.ConvertToImage(flip: true); - if (image == null) + if (!m_Texture2D.TryConvertToImage(flip: true, out var image)) { Logger.Warning($"Failed to convert texture \"{m_Texture2D.m_Name}\" into image"); return false; diff --git a/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs b/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs index 93319e4..2c1dca1 100644 --- a/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs +++ b/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs @@ -275,11 +275,14 @@ namespace CubismLive2DExtractor return; } - using (var image = texture2D.ConvertToImage(flip: true)) + if (texture2D.TryConvertToImage(flip: true, out var image)) { using (var file = File.OpenWrite(savePath)) { - image.WriteToStream(file, ImageFormat.Png); + using (image) + { + image.WriteToStream(file, ImageFormat.Png); + } } textureBag.Add($"textures/{texture2D.m_Name}.png"); } diff --git a/AssetStudioUtility/SpriteHelper.cs b/AssetStudioUtility/SpriteHelper.cs index 8e1e47b..fba71e5 100644 --- a/AssetStudioUtility/SpriteHelper.cs +++ b/AssetStudioUtility/SpriteHelper.cs @@ -89,103 +89,101 @@ namespace AssetStudio private static Image CutImage(Sprite m_Sprite, Texture2D m_Texture2D, Rectf textureRect, Vector2 textureRectOffset, float downscaleMultiplier, SpriteSettings settingsRaw) { - var originalImage = m_Texture2D.ConvertToImage(false); - if (originalImage != null) - { - if (downscaleMultiplier > 0f && downscaleMultiplier != 1f) - { - var width = (int)(m_Texture2D.m_Width / downscaleMultiplier); - var height = (int)(m_Texture2D.m_Height / downscaleMultiplier); - originalImage.Mutate(x => x.Resize(width, height)); - } - var rectX = (int)MathF.Floor(textureRect.x); - var rectY = (int)MathF.Floor(textureRect.y); - var rectRight = (int)MathF.Ceiling(textureRect.x + textureRect.width); - var rectBottom = (int)MathF.Ceiling(textureRect.y + textureRect.height); - rectRight = Math.Min(rectRight, originalImage.Width); - rectBottom = Math.Min(rectBottom, originalImage.Height); - var rect = new Rectangle(rectX, rectY, rectRight - rectX, rectBottom - rectY); - var spriteImage = originalImage.Clone(x => x.Crop(rect)); - originalImage.Dispose(); - if (settingsRaw.packed == 1) - { - //RotateAndFlip - switch (settingsRaw.packingRotation) - { - case SpritePackingRotation.FlipHorizontal: - spriteImage.Mutate(x => x.Flip(FlipMode.Horizontal)); - break; - case SpritePackingRotation.FlipVertical: - spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); - break; - case SpritePackingRotation.Rotate180: - spriteImage.Mutate(x => x.Rotate(180)); - break; - case SpritePackingRotation.Rotate90: - spriteImage.Mutate(x => x.Rotate(270)); - break; - } - } + if (!m_Texture2D.TryConvertToImage(false, out var textureImage)) + return null; - //Tight - if (settingsRaw.packingMode == SpritePackingMode.Tight) + if (downscaleMultiplier > 0f && downscaleMultiplier != 1f) + { + var width = (int)(m_Texture2D.m_Width / downscaleMultiplier); + var height = (int)(m_Texture2D.m_Height / downscaleMultiplier); + textureImage.Mutate(x => x.Resize(width, height)); + } + + var rectX = (int)MathF.Floor(textureRect.x); + var rectY = (int)MathF.Floor(textureRect.y); + var rectRight = (int)MathF.Ceiling(textureRect.x + textureRect.width); + var rectBottom = (int)MathF.Ceiling(textureRect.y + textureRect.height); + rectRight = Math.Min(rectRight, textureImage.Width); + rectBottom = Math.Min(rectBottom, textureImage.Height); + var rect = new Rectangle(rectX, rectY, rectRight - rectX, rectBottom - rectY); + var spriteImage = textureImage.Clone(x => x.Crop(rect)); + textureImage.Dispose(); + if (settingsRaw.packed == 1) + { + //RotateAndFlip + switch (settingsRaw.packingRotation) { - try + case SpritePackingRotation.FlipHorizontal: + spriteImage.Mutate(x => x.Flip(FlipMode.Horizontal)); + break; + case SpritePackingRotation.FlipVertical: + spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); + break; + case SpritePackingRotation.Rotate180: + spriteImage.Mutate(x => x.Rotate(180)); + break; + case SpritePackingRotation.Rotate90: + spriteImage.Mutate(x => x.Rotate(270)); + break; + } + } + + //Tight + if (settingsRaw.packingMode == SpritePackingMode.Tight) + { + try + { + var matrix = Matrix3x2.CreateScale(m_Sprite.m_PixelsToUnits); + matrix *= Matrix3x2.CreateTranslation(m_Sprite.m_Rect.width * m_Sprite.m_Pivot.X - textureRectOffset.X, m_Sprite.m_Rect.height * m_Sprite.m_Pivot.Y - textureRectOffset.Y); + var triangles = GetTriangles(m_Sprite.m_RD); + var points = triangles.Select(x => x.Select(y => new PointF(y.X, y.Y))); + var pathBuilder = new PathBuilder(matrix); + foreach (var p in points) { - var matrix = Matrix3x2.CreateScale(m_Sprite.m_PixelsToUnits); - matrix *= Matrix3x2.CreateTranslation(m_Sprite.m_Rect.width * m_Sprite.m_Pivot.X - textureRectOffset.X, m_Sprite.m_Rect.height * m_Sprite.m_Pivot.Y - textureRectOffset.Y); - var triangles = GetTriangles(m_Sprite.m_RD); - var points = triangles.Select(x => x.Select(y => new PointF(y.X, y.Y))); - var pathBuilder = new PathBuilder(matrix); - foreach (var p in points) + pathBuilder.AddLines(p); + pathBuilder.CloseFigure(); + } + var path = pathBuilder.Build(); + var options = new DrawingOptions + { + GraphicsOptions = new GraphicsOptions { - pathBuilder.AddLines(p); - pathBuilder.CloseFigure(); + Antialias = false, + AlphaCompositionMode = PixelAlphaCompositionMode.DestOut } - var path = pathBuilder.Build(); - var options = new DrawingOptions + }; + if (triangles.Length < 1024) + { + var rectP = new RectangularPolygon(0, 0, rect.Width, rect.Height); + try { - GraphicsOptions = new GraphicsOptions - { - Antialias = false, - AlphaCompositionMode = PixelAlphaCompositionMode.DestOut - } - }; - if (triangles.Length < 1024) - { - var rectP = new RectangularPolygon(0, 0, rect.Width, rect.Height); - try - { - spriteImage.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, rectP.Clip(path.Clip()))); - spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); - return spriteImage; - } - catch (ArgumentOutOfRangeException) - { - // ignored - } - } - using (var mask = new Image(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black)) - { - mask.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, path)); - var brush = new ImageBrush(mask); - spriteImage.Mutate(x => x.Fill(options, brush)); + spriteImage.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, rectP.Clip(path.Clip()))); spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); return spriteImage; } + catch (ArgumentOutOfRangeException) + { + // ignored + } } - catch (Exception e) + using (var mask = new Image(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black)) { - Logger.Warning($"{m_Sprite.m_Name} Unable to render the packed sprite correctly.\n{e}"); + mask.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, path)); + var brush = new ImageBrush(mask); + spriteImage.Mutate(x => x.Fill(options, brush)); + spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); + return spriteImage; } } - - //Rectangle - spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); - return spriteImage; + catch (Exception e) + { + Logger.Warning($"{m_Sprite.m_Name} Unable to render the packed sprite correctly.\n{e}"); + } } - return null; + //Rectangle + spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); + return spriteImage; } private static Vector2[][] GetTriangles(SpriteRenderData m_RD) diff --git a/AssetStudioUtility/Texture2DExtensions.cs b/AssetStudioUtility/Texture2DExtensions.cs index 61062bc..e854295 100644 --- a/AssetStudioUtility/Texture2DExtensions.cs +++ b/AssetStudioUtility/Texture2DExtensions.cs @@ -8,23 +8,24 @@ namespace AssetStudio { public static class Texture2DExtensions { - public static Image ConvertToImage(this Texture2D m_Texture2D, bool flip) + public static bool TryConvertToImage(this Texture2D m_Texture2D, bool flip, out Image image) { + image = null; var converter = new Texture2DConverter(m_Texture2D); var buff = BigArrayPool.Shared.Rent(converter.OutputDataSize); var spanBuff = buff.AsSpan(0, converter.OutputDataSize); try { if (!converter.DecodeTexture2D(buff)) - return null; + return false; - var image = Image.LoadPixelData(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height); + image = Image.LoadPixelData(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height); if (flip) { image.Mutate(x => x.Flip(FlipMode.Vertical)); } - return image; + return true; } finally { @@ -34,15 +35,13 @@ namespace AssetStudio public static MemoryStream ConvertToStream(this Texture2D m_Texture2D, ImageFormat imageFormat, bool flip) { - var image = ConvertToImage(m_Texture2D, flip); - if (image != null) + if (!TryConvertToImage(m_Texture2D, flip, out var image)) + return null; + + using (image) { - using (image) - { - return image.ConvertToStream(imageFormat); - } + return image.ConvertToStream(imageFormat); } - return null; } } }