[GUI] Improve memory usage of image previews

- also a bit increased performance of alpha mask resizing for previews
This commit is contained in:
VaDiM 2023-03-12 03:17:31 +03:00
parent 01957a9443
commit 44a1240f5f
5 changed files with 32 additions and 36 deletions

View File

@ -797,7 +797,7 @@ namespace AssetStudioGUI
var image = m_Texture2D.ConvertToImage(true); var image = m_Texture2D.ConvertToImage(true);
if (image != null) if (image != null)
{ {
var bitmap = new DirectBitmap(image.ConvertToBytes(), m_Texture2D.m_Width, m_Texture2D.m_Height); var bitmap = new DirectBitmap(image);
image.Dispose(); image.Dispose();
assetItem.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}"; assetItem.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}";
switch (m_Texture2D.m_TextureSettings.m_FilterMode) switch (m_Texture2D.m_TextureSettings.m_FilterMode)
@ -1215,10 +1215,10 @@ namespace AssetStudioGUI
private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite)
{ {
var image = m_Sprite.GetImage(spriteMaskVisibleMode); var image = m_Sprite.GetImage(spriteMaskMode: spriteMaskVisibleMode);
if (image != null) if (image != null)
{ {
var bitmap = new DirectBitmap(image.ConvertToBytes(), image.Width, image.Height); var bitmap = new DirectBitmap(image);
image.Dispose(); image.Dispose();
assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n";
PreviewTexture(bitmap); PreviewTexture(bitmap);

View File

@ -1,4 +1,7 @@
using System; using AssetStudio;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -7,13 +10,16 @@ namespace AssetStudioGUI
{ {
public sealed class DirectBitmap : IDisposable public sealed class DirectBitmap : IDisposable
{ {
public DirectBitmap(byte[] buff, int width, int height) public DirectBitmap(Image<Bgra32> image)
{ {
Width = width; Width = image.Width;
Height = height; Height = image.Height;
var buff = BigArrayPool<byte>.Shared.Rent(Width * Height * 4);
image.CopyPixelDataTo(buff);
Bits = buff; Bits = buff;
m_handle = GCHandle.Alloc(Bits, GCHandleType.Pinned); m_handle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject()); m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject());
BigArrayPool<byte>.Shared.Return(buff);
} }
private void Dispose(bool disposing) private void Dispose(bool disposing)

View File

@ -231,10 +231,10 @@ namespace AssetStudioGUI
public static bool ExportSprite(AssetItem item, string exportPath) public static bool ExportSprite(AssetItem item, string exportPath)
{ {
var type = Properties.Settings.Default.convertType; 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)) if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false; return false;
var image = ((Sprite)item.Asset).GetImage(alphaMask); var image = ((Sprite)item.Asset).GetImage(spriteMaskMode: spriteMaskMode);
if (image != null) if (image != null)
{ {
using (image) using (image)

View File

@ -2,10 +2,7 @@
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
namespace AssetStudio namespace AssetStudio
{ {
@ -51,16 +48,5 @@ namespace AssetStudio
image.WriteToStream(stream, imageFormat); image.WriteToStream(stream, imageFormat);
return stream; return stream;
} }
public static byte[] ConvertToBytes<TPixel>(this Image<TPixel> image) where TPixel : unmanaged, IPixel<TPixel>
{
using (image)
{
Span<byte> imageSpan = new byte[image.Width * image.Height * 4];
image.CopyPixelDataTo(imageSpan);
return MemoryMarshal.AsBytes(imageSpan).ToArray();
}
}
} }
} }

View File

@ -15,12 +15,13 @@ namespace AssetStudio
{ {
Off, Off,
On, On,
MaskOnly MaskOnly,
Export
} }
public static class SpriteHelper public static class SpriteHelper
{ {
public static Image<Bgra32> GetImage(this Sprite m_Sprite, SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On) public static Image<Bgra32> GetImage(this Sprite m_Sprite, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
{ {
if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas)) if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas))
{ {
@ -31,20 +32,19 @@ namespace AssetStudio
} }
else 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 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); 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) switch (spriteMaskMode)
{
alphaTex.Mutate(x => x.Resize(tex.Width, tex.Height));
}
switch (spriteMaskVisibleMode)
{ {
case SpriteMaskMode.On: 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: case SpriteMaskMode.MaskOnly:
tex.Dispose(); tex.Dispose();
return alphaTex; return alphaTex;
@ -58,10 +58,16 @@ namespace AssetStudio
return null; return null;
} }
private static Image<Bgra32> ApplyRGBMask(Image<Bgra32> tex, Image<Bgra32> texMask) private static void ApplyRGBMask(this Image<Bgra32> tex, Image<Bgra32> texMask, bool isPreview = false)
{ {
using (texMask) 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) => tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) =>
{ {
for (int y = 0; y < texMask.Height; y++) for (int y = 0; y < texMask.Height; y++)
@ -75,8 +81,6 @@ namespace AssetStudio
} }
} }
}); });
return tex;
} }
} }