From 44a1240f5fd4bc404173c34fe0c02796e527d595 Mon Sep 17 00:00:00 2001 From: VaDiM Date: Sun, 12 Mar 2023 03:17:31 +0300 Subject: [PATCH] [GUI] Improve memory usage of image previews - also a bit increased performance of alpha mask resizing for previews --- AssetStudioGUI/AssetStudioGUIForm.cs | 6 +++--- AssetStudioGUI/DirectBitmap.cs | 14 +++++++++---- AssetStudioGUI/Exporter.cs | 4 ++-- AssetStudioUtility/ImageExtensions.cs | 14 ------------- AssetStudioUtility/SpriteHelper.cs | 30 +++++++++++++++------------ 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index fbdfe04..5bc6eb3 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -797,7 +797,7 @@ namespace AssetStudioGUI var image = m_Texture2D.ConvertToImage(true); if (image != null) { - var bitmap = new DirectBitmap(image.ConvertToBytes(), m_Texture2D.m_Width, m_Texture2D.m_Height); + 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) @@ -1215,10 +1215,10 @@ namespace AssetStudioGUI private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) { - var image = m_Sprite.GetImage(spriteMaskVisibleMode); + var image = m_Sprite.GetImage(spriteMaskMode: spriteMaskVisibleMode); if (image != null) { - var bitmap = new DirectBitmap(image.ConvertToBytes(), image.Width, image.Height); + var bitmap = new DirectBitmap(image); image.Dispose(); assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; PreviewTexture(bitmap); diff --git a/AssetStudioGUI/DirectBitmap.cs b/AssetStudioGUI/DirectBitmap.cs index 101d955..3d55767 100644 --- a/AssetStudioGUI/DirectBitmap.cs +++ b/AssetStudioGUI/DirectBitmap.cs @@ -1,4 +1,7 @@ -using System; +using AssetStudio; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; @@ -7,13 +10,16 @@ namespace AssetStudioGUI { public sealed class DirectBitmap : IDisposable { - public DirectBitmap(byte[] buff, int width, int height) + public DirectBitmap(Image image) { - Width = width; - Height = height; + Width = image.Width; + Height = image.Height; + var buff = BigArrayPool.Shared.Rent(Width * Height * 4); + image.CopyPixelDataTo(buff); Bits = buff; m_handle = GCHandle.Alloc(Bits, GCHandleType.Pinned); m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject()); + BigArrayPool.Shared.Return(buff); } private void Dispose(bool disposing) diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index fc51638..700e456 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -231,10 +231,10 @@ namespace AssetStudioGUI public static bool ExportSprite(AssetItem item, string exportPath) { var type = Properties.Settings.Default.convertType; - var alphaMask = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.On : SpriteMaskMode.Off; + var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.Export : SpriteMaskMode.Off; if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) return false; - var image = ((Sprite)item.Asset).GetImage(alphaMask); + var image = ((Sprite)item.Asset).GetImage(spriteMaskMode: spriteMaskMode); if (image != null) { using (image) diff --git a/AssetStudioUtility/ImageExtensions.cs b/AssetStudioUtility/ImageExtensions.cs index 1593689..0807d7f 100644 --- a/AssetStudioUtility/ImageExtensions.cs +++ b/AssetStudioUtility/ImageExtensions.cs @@ -2,10 +2,7 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Webp; -using SixLabors.ImageSharp.PixelFormats; -using System; using System.IO; -using System.Runtime.InteropServices; namespace AssetStudio { @@ -51,16 +48,5 @@ namespace AssetStudio image.WriteToStream(stream, imageFormat); return stream; } - - public static byte[] ConvertToBytes(this Image image) where TPixel : unmanaged, IPixel - { - using (image) - { - Span imageSpan = new byte[image.Width * image.Height * 4]; - image.CopyPixelDataTo(imageSpan); - - return MemoryMarshal.AsBytes(imageSpan).ToArray(); - } - } } } diff --git a/AssetStudioUtility/SpriteHelper.cs b/AssetStudioUtility/SpriteHelper.cs index 5c6a356..ab3795c 100644 --- a/AssetStudioUtility/SpriteHelper.cs +++ b/AssetStudioUtility/SpriteHelper.cs @@ -15,12 +15,13 @@ namespace AssetStudio { Off, On, - MaskOnly + MaskOnly, + Export } public static class SpriteHelper { - public static Image GetImage(this Sprite m_Sprite, SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On) + public static Image GetImage(this Sprite m_Sprite, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On) { if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas)) { @@ -31,20 +32,19 @@ namespace AssetStudio } else { - if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskVisibleMode != SpriteMaskMode.Off) + if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off) { var tex = CutImage(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw); var alphaTex = CutImage(m_Sprite, m_AlphaTexture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw); - if (tex.Width != alphaTex.Width || tex.Height != alphaTex.Height) - { - alphaTex.Mutate(x => x.Resize(tex.Width, tex.Height)); - } - - switch (spriteMaskVisibleMode) + switch (spriteMaskMode) { case SpriteMaskMode.On: - return ApplyRGBMask(tex, alphaTex); + tex.ApplyRGBMask(alphaTex, isPreview: true); + return tex; + case SpriteMaskMode.Export: + tex.ApplyRGBMask(alphaTex); + return tex; case SpriteMaskMode.MaskOnly: tex.Dispose(); return alphaTex; @@ -58,10 +58,16 @@ namespace AssetStudio return null; } - private static Image ApplyRGBMask(Image tex, Image texMask) + private static void ApplyRGBMask(this Image tex, Image texMask, bool isPreview = false) { using (texMask) { + if (tex.Width != texMask.Width || tex.Height != texMask.Height) + { + var resampler = isPreview ? KnownResamplers.NearestNeighbor : KnownResamplers.Bicubic; + texMask.Mutate(x => x.Resize(tex.Width, tex.Height, resampler)); + } + tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) => { for (int y = 0; y < texMask.Height; y++) @@ -75,8 +81,6 @@ namespace AssetStudio } } }); - - return tex; } }