diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj
index aad4891..5415b7d 100644
--- a/AssetStudio/AssetStudio.csproj
+++ b/AssetStudio/AssetStudio.csproj
@@ -9,6 +9,7 @@
+
diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs
index 65d57ad..cac7ca7 100644
--- a/AssetStudio/Classes/Texture2D.cs
+++ b/AssetStudio/Classes/Texture2D.cs
@@ -19,6 +19,8 @@ namespace AssetStudio
public ResourceReader image_data;
public StreamingInfo m_StreamData;
public StreamingInfo m_DataStreamData; //Tuanjie
+ [JsonIgnore]
+ private ulong dataHash; //we need to store hashes because of sprites
public Texture2D() { }
@@ -179,6 +181,28 @@ namespace AssetStudio
: new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
}
+ public ulong GetImageDataHash()
+ {
+ if (dataHash != 0)
+ return dataHash;
+
+ if (image_data.Size == 0)
+ return 0;
+
+ var buff = BigArrayPool.Shared.Rent(image_data.Size);
+ try
+ {
+ _ = image_data.GetData(buff);
+ dataHash = System.IO.Hashing.XxHash3.HashToUInt64(buff.AsSpan(0, image_data.Size));
+ }
+ finally
+ {
+ BigArrayPool.Shared.Return(buff);
+ }
+
+ return dataHash;
+ }
+
// https://docs.unity3d.com/2023.3/Documentation/Manual/class-TextureImporterOverride.html
private int GetImageDataSize(TextureFormat textureFormat)
{
diff --git a/AssetStudioCLI/Components/AssetItem.cs b/AssetStudioCLI/Components/AssetItem.cs
index b167c3a..4be7549 100644
--- a/AssetStudioCLI/Components/AssetItem.cs
+++ b/AssetStudioCLI/Components/AssetItem.cs
@@ -13,6 +13,7 @@ namespace AssetStudioCLI
public ClassIDType Type;
public string Text;
public string UniqueID;
+ public ulong Hash;
public GameObjectNode Node;
public AssetItem(Object asset)
@@ -23,6 +24,7 @@ namespace AssetStudioCLI
TypeString = Type.ToString();
m_PathID = asset.m_PathID;
FullSize = asset.byteSize;
+ Hash = 0;
}
}
}
diff --git a/AssetStudioCLI/Options/CLIOptions.cs b/AssetStudioCLI/Options/CLIOptions.cs
index 597fbce..235234b 100644
--- a/AssetStudioCLI/Options/CLIOptions.cs
+++ b/AssetStudioCLI/Options/CLIOptions.cs
@@ -46,6 +46,7 @@ namespace AssetStudioCLI.Options
AssetName,
AssetName_PathID,
PathID,
+ AssetName_Hash,
}
internal enum ExportListType
@@ -245,10 +246,11 @@ namespace AssetStudioCLI.Options
optionDefaultValue: FilenameFormat.AssetName,
optionName: "-f, --filename-format ",
optionDescription: "Specify the file name format for exported assets\n" +
- "\n" +
+ "\n" +
"AssetName - Asset file names will look like \"assetName.extension\"\n" +
"AssetName_pathID - Asset file names will look like \"assetName @pathID.extension\"\n" +
- "PathID - Asset file names will look like \"pathID.extension\"\n",
+ "PathID - Asset file names will look like \"pathID.extension\"\n" +
+ "AssetName_hash - (images only) image names will look like \"assetName @hash.extension\"\n",
optionExample: "Example: \"-f assetName_pathID\"\n",
optionHelpGroup: HelpGroups.General
);
@@ -931,6 +933,9 @@ namespace AssetStudioCLI.Options
case "pathid":
o_filenameFormat.Value = FilenameFormat.PathID;
break;
+ case "assetname_hash":
+ o_filenameFormat.Value = FilenameFormat.AssetName_Hash;
+ break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option.Color(brightYellow)}] option. Unsupported file name format: [{value.Color(brightRed)}].\n");
ShowOptionDescription(o_filenameFormat);
diff --git a/AssetStudioCLI/ParallelExporter.cs b/AssetStudioCLI/ParallelExporter.cs
index 11e465f..f09c95d 100644
--- a/AssetStudioCLI/ParallelExporter.cs
+++ b/AssetStudioCLI/ParallelExporter.cs
@@ -19,7 +19,10 @@ namespace AssetStudioCLI
if (CLIOptions.convertTexture)
{
var type = CLIOptions.o_imageFormat.Value;
- if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
+ var converter = new Texture2DConverter(m_Texture2D);
+ if (CLIOptions.o_filenameFormat.Value == FilenameFormat.AssetName_Hash)
+ item.Hash = converter.GetTextureHash();
+ if (!TryExportFile(exportPath, item, $".{type}", out var exportFullPath))
return false;
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
@@ -45,7 +48,7 @@ namespace AssetStudioCLI
debugLog += sb.ToString();
}
- if (!m_Texture2D.TryConvertToImage(flip: true, out var image))
+ if (!converter.TryConvertToBgra32Image(flip: true, out var image))
{
Logger.Error($"{debugLog}Export error. Failed to convert texture \"{m_Texture2D.m_Name}\" into image");
return false;
@@ -74,11 +77,13 @@ namespace AssetStudioCLI
{
debugLog = "";
var type = CLIOptions.o_imageFormat.Value;
- var alphaMask = SpriteMaskMode.On;
- if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
+ var converter = new SpriteConverter((Sprite)item.Asset);
+ if (CLIOptions.o_filenameFormat.Value == FilenameFormat.AssetName_Hash)
+ item.Hash = converter.GetSpriteHash();
+ if (!TryExportFile(exportPath, item, $".{type}", out var exportFullPath))
return false;
- var image = ((Sprite)item.Asset).GetImage(alphaMask);
- if (image != null)
+
+ if (converter.TryConvertToImage(out var image, maskMode: SpriteMaskMode.On))
{
using (image)
{
@@ -192,8 +197,12 @@ namespace AssetStudioCLI
case FilenameFormat.PathID:
fileName = item.m_PathID.ToString();
break;
+ case FilenameFormat.AssetName_Hash when item.Hash > 0:
+ fileName = $"{fileName} @{item.Hash:x}";
+ break;
}
- fullPath = Path.Combine(dir, fileName + extension);
+ extension = extension.ToLower();
+ fullPath = Path.Combine(dir, $"{fileName}{extension}");
if (ExportPathDict.TryAdd(fullPath, true))
{
if (CanWrite(fullPath, dir, canOverwrite))
@@ -201,9 +210,9 @@ namespace AssetStudioCLI
return true;
}
}
- else if (filenameFormat == FilenameFormat.AssetName)
+ else if (filenameFormat == FilenameFormat.AssetName || (filenameFormat == FilenameFormat.AssetName_Hash && item.Hash == 0))
{
- fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
+ fullPath = Path.Combine(dir, $"{fileName}{item.UniqueID}{extension}");
if (CanWrite(fullPath, dir, canOverwrite))
{
return true;
diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs
index 342a263..452a294 100644
--- a/AssetStudioGUI/AssetStudioGUIForm.cs
+++ b/AssetStudioGUI/AssetStudioGUIForm.cs
@@ -53,7 +53,7 @@ namespace AssetStudioGUI
#endregion
#region SpriteControl
- private SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On;
+ private SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.Preview;
#endregion
#region TexControl
@@ -441,16 +441,16 @@ namespace AssetStudioGUI
break;
}
}
- else if (lastSelectedItem?.Type == ClassIDType.Sprite && !((Sprite)lastSelectedItem.Asset).m_RD.alphaTexture.IsNull)
+ else if (lastSelectedItem?.Type == ClassIDType.Sprite && new SpriteConverter((Sprite)lastSelectedItem.Asset).m_AlphaTexture2D != null)
{
switch (e.KeyCode)
{
case Keys.A:
- spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.On ? SpriteMaskMode.Off : SpriteMaskMode.On;
+ spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.Preview ? SpriteMaskMode.Off : SpriteMaskMode.Preview;
need = true;
break;
case Keys.M:
- spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.MaskOnly ? SpriteMaskMode.On : SpriteMaskMode.MaskOnly;
+ spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.MaskOnly ? SpriteMaskMode.Preview : SpriteMaskMode.MaskOnly;
need = true;
break;
}
@@ -976,15 +976,14 @@ namespace AssetStudioGUI
private void PreviewTexture2D(AssetItem assetItem, Texture2D m_Texture2D)
{
- if (!m_Texture2D.TryConvertToImage(true, out var image))
+ var bitmap = new DirectBitmap(m_Texture2D, flip: true);
+ if (bitmap.Bitmap == null)
{
StatusStripUpdate("Unsupported image for preview");
+ bitmap.Dispose();
return;
}
- var bitmap = new DirectBitmap(image);
- image.Dispose();
-
assetItem.InfoText =
$"Width: {m_Texture2D.m_Width}" +
$"\nHeight: {m_Texture2D.m_Height}" +
@@ -1488,15 +1487,15 @@ namespace AssetStudioGUI
private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite)
{
- var image = m_Sprite.GetImage(spriteMaskMode: spriteMaskVisibleMode);
- if (image != null)
+ var converter = new SpriteConverter(m_Sprite);
+ if (converter.TryConvertToImage(out var image, maskMode: spriteMaskVisibleMode))
{
var bitmap = new DirectBitmap(image);
image.Dispose();
assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n";
PreviewTexture(bitmap);
- if (!m_Sprite.m_RD.alphaTexture.IsNull)
+ if (converter.m_AlphaTexture2D != null)
{
assetItem.InfoText += $"Alpha Mask: {spriteMaskVisibleMode}\n";
StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only.");
@@ -1510,9 +1509,11 @@ namespace AssetStudioGUI
private void PreviewTexture(DirectBitmap bitmap)
{
+ previewPanel.Image.Dispose();
imageTexture?.Dispose();
imageTexture = bitmap;
previewPanel.Image = imageTexture.Bitmap;
+
if (imageTexture.Width > previewPanel.Width || imageTexture.Height > previewPanel.Height)
previewPanel.SizeMode = PictureBoxSizeMode.Zoom;
else
diff --git a/AssetStudioGUI/Components/AssetItem.cs b/AssetStudioGUI/Components/AssetItem.cs
index 05f4ff9..0bc34c8 100644
--- a/AssetStudioGUI/Components/AssetItem.cs
+++ b/AssetStudioGUI/Components/AssetItem.cs
@@ -14,6 +14,7 @@ namespace AssetStudioGUI
public ClassIDType Type;
public string InfoText;
public string UniqueID;
+ public ulong Hash;
public GameObjectTreeNode TreeNode;
public AssetItem(Object asset)
@@ -24,6 +25,7 @@ namespace AssetStudioGUI
TypeString = Type.ToString();
m_PathID = asset.m_PathID;
FullSize = asset.byteSize;
+ Hash = 0;
}
public void SetSubItems()
diff --git a/AssetStudioGUI/DirectBitmap.cs b/AssetStudioGUI/DirectBitmap.cs
index baaa483..2d88782 100644
--- a/AssetStudioGUI/DirectBitmap.cs
+++ b/AssetStudioGUI/DirectBitmap.cs
@@ -20,12 +20,28 @@ namespace AssetStudioGUI
m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject());
}
+ public DirectBitmap(Texture2D texture2D, bool flip)
+ {
+ Width = texture2D.m_Width;
+ Height = texture2D.m_Height;
+ var converter = new Texture2DConverter(texture2D);
+ Bits = BigArrayPool.Shared.Rent(converter.OutputDataSize);
+ if (converter.GetBgra32RawImageData(Bits, flip))
+ {
+ m_handle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
+ m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject());
+ }
+ }
+
private void Dispose(bool disposing)
{
if (disposing)
{
- m_bitmap.Dispose();
- m_handle.Free();
+ if (m_bitmap != null)
+ {
+ m_bitmap.Dispose();
+ m_handle.Free();
+ }
BigArrayPool.Shared.Return(Bits);
}
m_bitmap = null;
diff --git a/AssetStudioGUI/ExportOptions.Designer.cs b/AssetStudioGUI/ExportOptions.Designer.cs
index 7c243d9..efc15cb 100644
--- a/AssetStudioGUI/ExportOptions.Designer.cs
+++ b/AssetStudioGUI/ExportOptions.Designer.cs
@@ -201,14 +201,16 @@
// filenameFormatComboBox
//
this.filenameFormatComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.filenameFormatComboBox.DropDownWidth = 198;
this.filenameFormatComboBox.FormattingEnabled = true;
this.filenameFormatComboBox.Items.AddRange(new object[] {
"asset name",
"asset name@pathID",
- "pathID"});
+ "pathID",
+ "asset name@hash (images only)"});
this.filenameFormatComboBox.Location = new System.Drawing.Point(210, 25);
this.filenameFormatComboBox.Name = "filenameFormatComboBox";
- this.filenameFormatComboBox.Size = new System.Drawing.Size(139, 23);
+ this.filenameFormatComboBox.Size = new System.Drawing.Size(149, 23);
this.filenameFormatComboBox.TabIndex = 3;
//
// exportSpriteWithAlphaMask
diff --git a/AssetStudioGUI/ParallelExport.cs b/AssetStudioGUI/ParallelExport.cs
index ab3c993..c97b88b 100644
--- a/AssetStudioGUI/ParallelExport.cs
+++ b/AssetStudioGUI/ParallelExport.cs
@@ -18,7 +18,10 @@ namespace AssetStudioGUI
if (Properties.Settings.Default.convertTexture)
{
var type = Properties.Settings.Default.convertType;
- if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
+ var converter = new Texture2DConverter(m_Texture2D);
+ if (Properties.Settings.Default.filenameFormat == 3) //asset name@hash
+ item.Hash = converter.GetTextureHash();
+ if (!TryExportFile(exportPath, item, $".{type}", out var exportFullPath))
return false;
if (GUILogger.ShowDebugMessage)
@@ -44,9 +47,9 @@ namespace AssetStudioGUI
debugLog += sb.ToString();
}
- if (!m_Texture2D.TryConvertToImage(flip: true, out var image))
+ if (!converter.TryConvertToBgra32Image(flip: true, out var image))
{
- Logger.Warning($"Failed to convert texture \"{m_Texture2D.m_Name}\" into image");
+ Logger.Warning($"{debugLog}Failed to convert texture \"{m_Texture2D.m_Name}\" into image");
return false;
}
using (image)
@@ -73,11 +76,14 @@ namespace AssetStudioGUI
{
debugLog = "";
var type = Properties.Settings.Default.convertType;
- var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.Export : SpriteMaskMode.Off;
- if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
+ var converter = new SpriteConverter((Sprite)item.Asset);
+ if (Properties.Settings.Default.filenameFormat == 3) //asset name@hash
+ item.Hash = converter.GetSpriteHash();
+ if (!TryExportFile(exportPath, item, $".{type}", out var exportFullPath))
return false;
- var image = ((Sprite)item.Asset).GetImage(spriteMaskMode: spriteMaskMode);
- if (image != null)
+
+ var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.On : SpriteMaskMode.Off;
+ if (converter.TryConvertToImage(out var image, maskMode: spriteMaskMode))
{
using (image)
{
@@ -191,8 +197,12 @@ namespace AssetStudioGUI
case 2: //pathID
fileName = item.m_PathID.ToString();
break;
+ case 3 when item.Hash > 0: //asset name@hash
+ fileName = $"{fileName} @{item.Hash:x}";
+ break;
}
- fullPath = Path.Combine(dir, fileName + extension);
+ extension = extension.ToLower();
+ fullPath = Path.Combine(dir, $"{fileName}{extension}");
if (ExportPathDict.TryAdd(fullPath, true))
{
if (CanWrite(fullPath, dir, canOverwrite))
@@ -200,9 +210,9 @@ namespace AssetStudioGUI
return true;
}
}
- else if (filenameFormatIndex == 0) //assetName
+ else if (filenameFormatIndex == 0 || (filenameFormatIndex == 3 && item.Hash == 0)) //assetName, asset name@hash
{
- fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
+ fullPath = Path.Combine(dir, $"{fileName}{item.UniqueID}{extension}");
if (CanWrite(fullPath, dir, canOverwrite))
{
return true;
diff --git a/AssetStudioUtility/ImageExtensions.cs b/AssetStudioUtility/ImageExtensions.cs
index 0807d7f..d274993 100644
--- a/AssetStudioUtility/ImageExtensions.cs
+++ b/AssetStudioUtility/ImageExtensions.cs
@@ -2,6 +2,8 @@
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Webp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
using System.IO;
namespace AssetStudio
@@ -48,5 +50,31 @@ namespace AssetStudio
image.WriteToStream(stream, imageFormat);
return stream;
}
+
+ public static void ApplyRgbMask(this Image srcTex, Image texMask, bool fastResize = false)
+ {
+ using (texMask)
+ {
+ if (srcTex.Width != texMask.Width || srcTex.Height != texMask.Height)
+ {
+ var resampler = fastResize ? KnownResamplers.NearestNeighbor : KnownResamplers.Bicubic;
+ texMask.Mutate(x => x.Resize(srcTex.Width, srcTex.Height, resampler));
+ }
+
+ srcTex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) =>
+ {
+ for (var y = 0; y < texMask.Height; y++)
+ {
+ var texRow = sourceTex.GetRowSpan(y);
+ var maskRow = targetTexMask.GetRowSpan(y);
+ for (var x = 0; x < maskRow.Length; x++)
+ {
+ var grayscale = (byte)((maskRow[x].R + maskRow[x].G + maskRow[x].B) / 3);
+ texRow[x].A = grayscale;
+ }
+ }
+ });
+ }
+ }
}
}
diff --git a/AssetStudioUtility/SpriteHelper.cs b/AssetStudioUtility/SpriteConverter.cs
similarity index 55%
rename from AssetStudioUtility/SpriteHelper.cs
rename to AssetStudioUtility/SpriteConverter.cs
index 7e2ea3b..0c7f3ce 100644
--- a/AssetStudioUtility/SpriteHelper.cs
+++ b/AssetStudioUtility/SpriteConverter.cs
@@ -4,6 +4,7 @@ using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
+using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -15,92 +16,133 @@ namespace AssetStudio
{
Off,
On,
+ Preview,
MaskOnly,
- Export
}
- public static class SpriteHelper
+ public class SpriteConverter
{
- public static Image GetImage(this Sprite m_Sprite, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
+ private Sprite sprite;
+ private Rectf textureRect;
+ private Vector2 textureRectOffset;
+ private float downscaleMultiplier;
+ private SpriteSettings settingsRaw;
+ private bool cutImageWithCanvas;
+ private int resultWidth;
+ private int resultHeight;
+
+ public Texture2D m_Texture2D { get; }
+ public Texture2D m_AlphaTexture2D { get; }
+
+ public SpriteConverter(Sprite m_Sprite, bool cutWithCanvas = true)
{
+ sprite = m_Sprite;
+ cutImageWithCanvas = cutWithCanvas;
+
if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas))
{
- if (m_SpriteAtlas.m_RenderDataMap.TryGetValue(m_Sprite.m_RenderDataKey, out var spriteAtlasData) && spriteAtlasData.texture.TryGet(out var m_Texture2D))
+ if (m_SpriteAtlas.m_RenderDataMap.TryGetValue(m_Sprite.m_RenderDataKey, out var spriteAtlasData) && spriteAtlasData.texture.TryGet(out var texture2D))
{
- return CutImage(m_Sprite, m_Texture2D, spriteAtlasData.textureRect, spriteAtlasData.textureRectOffset, spriteAtlasData.downscaleMultiplier, spriteAtlasData.settingsRaw);
+ m_Texture2D = texture2D;
+ if (spriteAtlasData.alphaTexture.TryGet(out var alphaTexture2D))
+ m_AlphaTexture2D = alphaTexture2D;
+ textureRect = spriteAtlasData.textureRect;
+ textureRectOffset = spriteAtlasData.textureRectOffset;
+ downscaleMultiplier = spriteAtlasData.downscaleMultiplier;
+ settingsRaw = spriteAtlasData.settingsRaw;
}
}
- else
+ else if (m_Sprite.m_RD.texture.TryGet(out var texture2D))
{
- if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off)
- {
- Image tex = null;
- if (spriteMaskMode != SpriteMaskMode.MaskOnly)
- {
- tex = CutImageWithCanvas(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 = CutImageWithCanvas(m_Sprite, m_AlphaTexture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw);
-
- switch (spriteMaskMode)
- {
- case SpriteMaskMode.On:
- tex.ApplyRGBMask(alphaTex, isPreview: true);
- return tex;
- case SpriteMaskMode.Export:
- tex.ApplyRGBMask(alphaTex);
- return tex;
- case SpriteMaskMode.MaskOnly:
- return alphaTex;
- }
- }
- else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D))
- {
- return CutImageWithCanvas(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw);
- }
+ m_Texture2D = texture2D;
+ if (m_Sprite.m_RD.alphaTexture.TryGet(out var alphaTexture2D))
+ m_AlphaTexture2D = alphaTexture2D;
+ textureRect = m_Sprite.m_RD.textureRect;
+ textureRectOffset = m_Sprite.m_RD.textureRectOffset;
+ downscaleMultiplier = m_Sprite.m_RD.downscaleMultiplier;
+ settingsRaw = m_Sprite.m_RD.settingsRaw;
}
- return null;
+
+ if (textureRect == null)
+ return;
+
+ resultWidth = cutImageWithCanvas
+ ? (int)sprite.m_Rect.width
+ : (int)textureRect.width;
+ resultHeight = cutImageWithCanvas
+ ? (int)sprite.m_Rect.height
+ : (int)textureRect.height;
}
- private static void ApplyRGBMask(this Image tex, Image texMask, bool isPreview = false)
+ public bool TryConvertToImage(out Image spriteImage, SpriteMaskMode maskMode)
{
- using (texMask)
+ spriteImage = null;
+ if (m_Texture2D == null)
+ return false;
+
+ if (m_AlphaTexture2D != null)
{
- if (tex.Width != texMask.Width || tex.Height != texMask.Height)
+ var alphaTex = CutSprite(m_AlphaTexture2D);
+ if (maskMode != SpriteMaskMode.MaskOnly)
{
- var resampler = isPreview ? KnownResamplers.NearestNeighbor : KnownResamplers.Bicubic;
- texMask.Mutate(x => x.Resize(tex.Width, tex.Height, resampler));
+ spriteImage = CutSprite(m_Texture2D);
}
- tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) =>
+ switch (maskMode)
{
- for (int y = 0; y < texMask.Height; y++)
- {
- var texRow = sourceTex.GetRowSpan(y);
- var maskRow = targetTexMask.GetRowSpan(y);
- for (int x = 0; x < maskRow.Length; x++)
- {
- var grayscale = (byte)((maskRow[x].R + maskRow[x].G + maskRow[x].B) / 3);
- texRow[x].A = grayscale;
- }
- }
- });
+ case SpriteMaskMode.MaskOnly:
+ spriteImage = alphaTex;
+ break;
+ case SpriteMaskMode.Preview when alphaTex != null:
+ spriteImage.ApplyRgbMask(alphaTex, fastResize: true);
+ break;
+ case SpriteMaskMode.On when alphaTex != null:
+ spriteImage.ApplyRgbMask(alphaTex);
+ break;
+ }
+ return spriteImage != null;
+ }
+
+ spriteImage = CutSprite(m_Texture2D);
+ return spriteImage != null;
+ }
+
+ private Image CutSprite(Texture2D texture2D)
+ {
+ var croppedImage = CutImage(texture2D);
+ if (croppedImage == null)
+ return null;
+
+ if (!cutImageWithCanvas)
+ return croppedImage;
+
+ using (croppedImage)
+ {
+ var canvas = new Image(resultWidth, resultHeight);
+ canvas.Mutate(ctx => ctx.DrawImage(
+ croppedImage,
+ new Point((int)textureRectOffset.X, (int)(resultHeight - textureRectOffset.Y - croppedImage.Height)),
+ 1f)
+ );
+
+ return canvas;
}
}
- private static Image CutImage(Sprite m_Sprite, Texture2D m_Texture2D, Rectf textureRect, Vector2 textureRectOffset, float downscaleMultiplier, SpriteSettings settingsRaw)
+ private Image CutImage(Texture2D texture2D)
{
- if (!m_Texture2D.TryConvertToImage(false, out var textureImage))
+ if (!texture2D.TryConvertToImage(false, out var textureImage))
return null;
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
{
- var width = (int)(m_Texture2D.m_Width / downscaleMultiplier);
- var height = (int)(m_Texture2D.m_Height / downscaleMultiplier);
+ var width = (int)(texture2D.m_Width / downscaleMultiplier);
+ var height = (int)(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 rectX = (int)textureRect.x;
+ var rectY = (int)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);
@@ -133,9 +175,9 @@ namespace AssetStudio
{
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 matrix = Matrix3x2.CreateScale(sprite.m_PixelsToUnits);
+ matrix *= Matrix3x2.CreateTranslation(sprite.m_Rect.width * sprite.m_Pivot.X - textureRectOffset.X, sprite.m_Rect.height * sprite.m_Pivot.Y - textureRectOffset.Y);
+ var triangles = GetTriangles(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)
@@ -177,7 +219,7 @@ namespace AssetStudio
}
catch (Exception e)
{
- Logger.Warning($"{m_Sprite.m_Name} Unable to render the packed sprite correctly.\n{e}");
+ Logger.Warning($"{sprite.m_Name} Unable to render the packed sprite correctly.\n{e}");
}
}
@@ -186,19 +228,6 @@ namespace AssetStudio
return spriteImage;
}
- private static Image CutImageWithCanvas(Sprite m_Sprite, Texture2D m_Texture2D, Rectf textureRect, Vector2 textureRectOffset, float downscaleMultiplier, SpriteSettings settingsRaw)
- {
- var cropped = CutImage(m_Sprite, m_Texture2D, textureRect, textureRectOffset, downscaleMultiplier, settingsRaw);
- if (cropped == null)
- return null;
-
- var canvas = new Image((int)MathF.Floor(m_Sprite.m_Rect.width), (int)MathF.Floor(m_Sprite.m_Rect.height));
- canvas.Mutate(ctx => ctx.DrawImage(cropped, new Point((int)MathF.Floor(textureRectOffset.X), (int)MathF.Floor(m_Sprite.m_Rect.height - textureRectOffset.Y - cropped.Height)), 1f));
-
- cropped.Dispose();
- return canvas;
- }
-
private static Vector2[][] GetTriangles(SpriteRenderData m_RD)
{
if (m_RD.vertices != null) //5.6 down
@@ -254,5 +283,40 @@ namespace AssetStudio
return triangles.ToArray();
}
}
+
+ public ulong GetSpriteHash()
+ {
+ ulong spriteHash = 0;
+ if (m_Texture2D == null)
+ return spriteHash;
+
+ spriteHash = m_Texture2D.GetImageDataHash();
+
+ if (m_AlphaTexture2D != null)
+ {
+ spriteHash ^= m_AlphaTexture2D.GetImageDataHash();
+ }
+
+ return (m_Texture2D.m_Width, m_Texture2D.m_Height) == (resultWidth, resultHeight)
+ ? spriteHash
+ : spriteHash ^ GetRectHash();
+ }
+
+ private ulong GetRectHash()
+ {
+ Span buff = stackalloc byte[40];
+ BinaryPrimitives.WriteInt32LittleEndian(buff, (int)textureRect.x);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(4), (int)textureRect.y);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(8), (int)textureRect.width);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(12), (int)textureRect.height);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(16), (int)textureRectOffset.X);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(20), (int)textureRectOffset.Y);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(24), (int)sprite.m_Rect.x);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(28), (int)sprite.m_Rect.y);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(32), (int)sprite.m_Rect.width);
+ BinaryPrimitives.WriteInt32LittleEndian(buff.Slice(36), (int)sprite.m_Rect.height);
+
+ return System.IO.Hashing.XxHash3.HashToUInt64(buff);
+ }
}
}
diff --git a/AssetStudioUtility/SpriteExtensions.cs b/AssetStudioUtility/SpriteExtensions.cs
new file mode 100644
index 0000000..67bc392
--- /dev/null
+++ b/AssetStudioUtility/SpriteExtensions.cs
@@ -0,0 +1,13 @@
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace AssetStudio
+{
+ public static class SpriteExtensions
+ {
+ public static bool TryGetImage(this Sprite m_Sprite, out Image spriteImage, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
+ {
+ return new SpriteConverter(m_Sprite).TryConvertToImage(out spriteImage, spriteMaskMode);
+ }
+ }
+}
diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs
index d93b12b..da9d70e 100644
--- a/AssetStudioUtility/Texture2DConverter.cs
+++ b/AssetStudioUtility/Texture2DConverter.cs
@@ -1,4 +1,7 @@
-using System;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using Texture2DDecoder;
@@ -7,39 +10,41 @@ namespace AssetStudio
{
public class Texture2DConverter
{
- private ResourceReader reader;
+ private Texture2D m_Texture2D;
private int m_Width;
private int m_Height;
private TextureFormat m_TextureFormat;
- private byte[] m_PlatformBlob;
- private UnityVersion version;
- private BuildTarget platform;
- private int outPutDataSize;
+ private int texDataSize;
+ private int outputDataSize;
+ private bool canConvert;
private bool switchSwizzled;
private int gobsPerBlock;
- private SixLabors.ImageSharp.Size blockSize;
+ private Size blockSize;
- public int OutputDataSize => outPutDataSize;
+ public int OutputDataSize => outputDataSize;
public Texture2DConverter(Texture2D m_Texture2D)
{
- reader = m_Texture2D.image_data;
+ this.m_Texture2D = m_Texture2D;
m_Width = m_Texture2D.m_Width;
m_Height = m_Texture2D.m_Height;
+ texDataSize = m_Texture2D.image_data.Size;
+ if (texDataSize == 0 || m_Width == 0 || m_Height == 0)
+ return;
+
m_TextureFormat = m_Texture2D.m_TextureFormat;
- m_PlatformBlob = m_Texture2D.m_PlatformBlob;
- version = m_Texture2D.version;
- platform = m_Texture2D.platform;
// not guaranteed, you can have a swizzled texture without m_PlatformBlob
// but officially, I don't think this can happen. maybe check which engine
// version this started happening in...
- switchSwizzled = platform == BuildTarget.Switch && m_PlatformBlob.Length != 0;
+ switchSwizzled = m_Texture2D.platform == BuildTarget.Switch && m_Texture2D.m_PlatformBlob.Length != 0;
if (switchSwizzled)
{
SetupSwitchSwizzle();
}
- outPutDataSize = m_Width * m_Height * 4;
+ outputDataSize = m_Width * m_Height * 4;
+
+ canConvert = true;
}
private void SetupSwitchSwizzle()
@@ -47,7 +52,7 @@ namespace AssetStudio
//apparently there is another value to worry about, but seeing as it's
//always 0 and I have nothing else to test against, this will probably
//work fine for now
- gobsPerBlock = 1 << BitConverter.ToInt32(m_PlatformBlob, 8);
+ gobsPerBlock = 1 << BitConverter.ToInt32(m_Texture2D.m_PlatformBlob, 8);
switch (m_TextureFormat)
{
@@ -65,197 +70,255 @@ namespace AssetStudio
blockSize = Texture2DSwitchDeswizzler.GetTextureFormatBlockSize(m_TextureFormat);
}
- public bool DecodeTexture2D(byte[] bytes)
+ public ulong GetTextureHash() => canConvert
+ ? m_Texture2D.GetImageDataHash()
+ : 0;
+
+ public bool GetBgra32RawImageData(byte[] destArray, bool flip)
{
- if (reader.Size == 0 || m_Width == 0 || m_Height == 0)
- {
+ if (!canConvert)
return false;
+
+ var dataSpan = destArray.AsSpan(0, outputDataSize);
+ if (!DecodeTexture2D(dataSpan))
+ return false;
+
+ if (flip)
+ {
+ var stride = m_Width * 4;
+ Span tmp = new byte[stride];
+ for (var i = 0; i < m_Height / 2; i++) //flip
+ {
+ var j = m_Height - i - 1;
+ var srcOffset = i * stride;
+ var dstOffset = j * stride;
+ dataSpan.Slice(dstOffset, stride).CopyTo(tmp);
+ dataSpan.Slice(srcOffset, stride).CopyTo(dataSpan.Slice(dstOffset));
+ tmp.CopyTo(dataSpan.Slice(srcOffset));
+ }
}
- var flag = false;
- var buff = BigArrayPool.Shared.Rent(reader.Size);
+
+ return true;
+ }
+
+ public bool TryConvertToBgra32Image(bool flip, out Image image)
+ {
+ image = null;
+ if (!canConvert)
+ return false;
+
+ var imageBuff = BigArrayPool.Shared.Rent(outputDataSize);
+ var imageBuffSpan = imageBuff.AsSpan(0, outputDataSize);
try
{
- _ = reader.GetData(buff);
+ if (!DecodeTexture2D(imageBuffSpan))
+ return false;
+
+ image = Image.LoadPixelData(imageBuffSpan, m_Width, m_Height);
+
+ if (flip)
+ {
+ image.Mutate(x => x.Flip(FlipMode.Vertical));
+ }
+ return true;
+ }
+ finally
+ {
+ BigArrayPool.Shared.Return(imageBuff, clearArray: true);
+ }
+ }
+
+ private bool DecodeTexture2D(Span destSpan)
+ {
+ if (!canConvert)
+ return false;
+
+ var flag = false;
+ var buff = BigArrayPool.Shared.Rent(texDataSize);
+ try
+ {
+ _ = m_Texture2D.image_data.GetData(buff);
+
if (switchSwizzled)
{
- var spanBuff = buff.AsSpan(0, reader.Size);
- var textureSize = new SixLabors.ImageSharp.Size(m_Width, m_Height);
+ var spanBuff = buff.AsSpan(0, texDataSize);
+ var textureSize = new Size(m_Width, m_Height);
Texture2DSwitchDeswizzler.Unswizzle(ref spanBuff, textureSize, blockSize, gobsPerBlock);
}
switch (m_TextureFormat)
{
case TextureFormat.Alpha8: //test pass
- flag = DecodeAlpha8(buff, bytes);
+ flag = DecodeAlpha8(buff, destSpan);
break;
case TextureFormat.ARGB4444: //test pass
SwapBytesForXbox(buff);
- flag = DecodeARGB4444(buff, bytes);
+ flag = DecodeARGB4444(buff, destSpan);
break;
case TextureFormat.RGB24: //test pass
- flag = DecodeRGB24(buff, bytes);
+ flag = DecodeRGB24(buff, destSpan);
break;
case TextureFormat.RGBA32: //test pass
- flag = DecodeRGBA32(buff, bytes);
+ flag = DecodeRGBA32(buff, destSpan);
break;
case TextureFormat.ARGB32: //test pass
- flag = DecodeARGB32(buff, bytes);
+ flag = DecodeARGB32(buff, destSpan);
break;
case TextureFormat.RGB565: //test pass
SwapBytesForXbox(buff);
- flag = DecodeRGB565(buff, bytes);
+ flag = DecodeRGB565(buff, destSpan);
break;
case TextureFormat.R16: //test pass
- flag = DecodeR16(buff, bytes);
+ flag = DecodeR16(buff, destSpan);
break;
case TextureFormat.DXT1: //test pass
SwapBytesForXbox(buff);
- flag = DecodeDXT1(buff, bytes);
+ flag = DecodeDXT1(buff, destSpan);
break;
case TextureFormat.DXT3:
break;
case TextureFormat.DXT5: //test pass
SwapBytesForXbox(buff);
- flag = DecodeDXT5(buff, bytes);
+ flag = DecodeDXT5(buff, destSpan);
break;
case TextureFormat.RGBA4444: //test pass
- flag = DecodeRGBA4444(buff, bytes);
+ flag = DecodeRGBA4444(buff, destSpan);
break;
case TextureFormat.BGRA32: //test pass
- flag = DecodeBGRA32(buff, bytes);
+ flag = DecodeBGRA32(buff, destSpan);
break;
case TextureFormat.RHalf:
- flag = DecodeRHalf(buff, bytes);
+ flag = DecodeRHalf(buff, destSpan);
break;
case TextureFormat.RGHalf:
- flag = DecodeRGHalf(buff, bytes);
+ flag = DecodeRGHalf(buff, destSpan);
break;
case TextureFormat.RGBAHalf: //test pass
- flag = DecodeRGBAHalf(buff, bytes);
+ flag = DecodeRGBAHalf(buff, destSpan);
break;
case TextureFormat.RFloat:
- flag = DecodeRFloat(buff, bytes);
+ flag = DecodeRFloat(buff, destSpan);
break;
case TextureFormat.RGFloat:
- flag = DecodeRGFloat(buff, bytes);
+ flag = DecodeRGFloat(buff, destSpan);
break;
case TextureFormat.RGBAFloat:
- flag = DecodeRGBAFloat(buff, bytes);
+ flag = DecodeRGBAFloat(buff, destSpan);
break;
case TextureFormat.YUY2: //test pass
- flag = DecodeYUY2(buff, bytes);
+ flag = DecodeYUY2(buff, destSpan);
break;
case TextureFormat.RGB9e5Float: //test pass
- flag = DecodeRGB9e5Float(buff, bytes);
+ flag = DecodeRGB9e5Float(buff, destSpan);
break;
case TextureFormat.BC6H: //test pass
- flag = DecodeBC6H(buff, bytes);
+ flag = DecodeBC6H(buff, destSpan);
break;
case TextureFormat.BC7: //test pass
- flag = DecodeBC7(buff, bytes);
+ flag = DecodeBC7(buff, destSpan);
break;
case TextureFormat.BC4: //test pass
- flag = DecodeBC4(buff, bytes);
+ flag = DecodeBC4(buff, destSpan);
break;
case TextureFormat.BC5: //test pass
- flag = DecodeBC5(buff, bytes);
+ flag = DecodeBC5(buff, destSpan);
break;
case TextureFormat.DXT1Crunched: //test pass
- flag = DecodeDXT1Crunched(buff, bytes);
+ flag = DecodeDXT1Crunched(buff, destSpan);
break;
case TextureFormat.DXT5Crunched: //test pass
- flag = DecodeDXT5Crunched(buff, bytes);
+ flag = DecodeDXT5Crunched(buff, destSpan);
break;
case TextureFormat.PVRTC_RGB2: //test pass
case TextureFormat.PVRTC_RGBA2: //test pass
- flag = DecodePVRTC(buff, bytes, true);
+ flag = DecodePVRTC(buff, destSpan, true);
break;
case TextureFormat.PVRTC_RGB4: //test pass
case TextureFormat.PVRTC_RGBA4: //test pass
- flag = DecodePVRTC(buff, bytes, false);
+ flag = DecodePVRTC(buff, destSpan, false);
break;
case TextureFormat.ETC_RGB4: //test pass
case TextureFormat.ETC_RGB4_3DS:
- flag = DecodeETC1(buff, bytes);
+ flag = DecodeETC1(buff, destSpan);
break;
case TextureFormat.ATC_RGB4: //test pass
- flag = DecodeATCRGB4(buff, bytes);
+ flag = DecodeATCRGB4(buff, destSpan);
break;
case TextureFormat.ATC_RGBA8: //test pass
- flag = DecodeATCRGBA8(buff, bytes);
+ flag = DecodeATCRGBA8(buff, destSpan);
break;
case TextureFormat.EAC_R: //test pass
- flag = DecodeEACR(buff, bytes);
+ flag = DecodeEACR(buff, destSpan);
break;
case TextureFormat.EAC_R_SIGNED:
- flag = DecodeEACRSigned(buff, bytes);
+ flag = DecodeEACRSigned(buff, destSpan);
break;
case TextureFormat.EAC_RG: //test pass
- flag = DecodeEACRG(buff, bytes);
+ flag = DecodeEACRG(buff, destSpan);
break;
case TextureFormat.EAC_RG_SIGNED:
- flag = DecodeEACRGSigned(buff, bytes);
+ flag = DecodeEACRGSigned(buff, destSpan);
break;
case TextureFormat.ETC2_RGB: //test pass
- flag = DecodeETC2(buff, bytes);
+ flag = DecodeETC2(buff, destSpan);
break;
case TextureFormat.ETC2_RGBA1: //test pass
- flag = DecodeETC2A1(buff, bytes);
+ flag = DecodeETC2A1(buff, destSpan);
break;
case TextureFormat.ETC2_RGBA8: //test pass
case TextureFormat.ETC_RGBA8_3DS:
- flag = DecodeETC2A8(buff, bytes);
+ flag = DecodeETC2A8(buff, destSpan);
break;
case TextureFormat.ASTC_RGB_4x4: //test pass
case TextureFormat.ASTC_RGBA_4x4: //test pass
case TextureFormat.ASTC_HDR_4x4: //test pass
- flag = DecodeASTC(buff, bytes, 4);
+ flag = DecodeASTC(buff, destSpan, 4);
break;
case TextureFormat.ASTC_RGB_5x5: //test pass
case TextureFormat.ASTC_RGBA_5x5: //test pass
case TextureFormat.ASTC_HDR_5x5: //test pass
- flag = DecodeASTC(buff, bytes, 5);
+ flag = DecodeASTC(buff, destSpan, 5);
break;
case TextureFormat.ASTC_RGB_6x6: //test pass
case TextureFormat.ASTC_RGBA_6x6: //test pass
case TextureFormat.ASTC_HDR_6x6: //test pass
- flag = DecodeASTC(buff, bytes, 6);
+ flag = DecodeASTC(buff, destSpan, 6);
break;
case TextureFormat.ASTC_RGB_8x8: //test pass
case TextureFormat.ASTC_RGBA_8x8: //test pass
case TextureFormat.ASTC_HDR_8x8: //test pass
- flag = DecodeASTC(buff, bytes, 8);
+ flag = DecodeASTC(buff, destSpan, 8);
break;
case TextureFormat.ASTC_RGB_10x10: //test pass
case TextureFormat.ASTC_RGBA_10x10: //test pass
case TextureFormat.ASTC_HDR_10x10: //test pass
- flag = DecodeASTC(buff, bytes, 10);
+ flag = DecodeASTC(buff, destSpan, 10);
break;
case TextureFormat.ASTC_RGB_12x12: //test pass
case TextureFormat.ASTC_RGBA_12x12: //test pass
case TextureFormat.ASTC_HDR_12x12: //test pass
- flag = DecodeASTC(buff, bytes, 12);
+ flag = DecodeASTC(buff, destSpan, 12);
break;
case TextureFormat.RG16: //test pass
- flag = DecodeRG16(buff, bytes);
+ flag = DecodeRG16(buff, destSpan);
break;
case TextureFormat.R8: //test pass
- flag = DecodeR8(buff, bytes);
+ flag = DecodeR8(buff, destSpan);
break;
case TextureFormat.ETC_RGB4Crunched: //test pass
- flag = DecodeETC1Crunched(buff, bytes);
+ flag = DecodeETC1Crunched(buff, destSpan);
break;
case TextureFormat.ETC2_RGBA8Crunched: //test pass
- flag = DecodeETC2A8Crunched(buff, bytes);
+ flag = DecodeETC2A8Crunched(buff, destSpan);
break;
case TextureFormat.RG32: //test pass
- flag = DecodeRG32(buff, bytes);
+ flag = DecodeRG32(buff, destSpan);
break;
case TextureFormat.RGB48: //test pass
- flag = DecodeRGB48(buff, bytes);
+ flag = DecodeRGB48(buff, destSpan);
break;
case TextureFormat.RGBA64: //test pass
- flag = DecodeRGBA64(buff, bytes);
+ flag = DecodeRGBA64(buff, destSpan);
break;
}
}
@@ -263,221 +326,216 @@ namespace AssetStudio
{
BigArrayPool.Shared.Return(buff, clearArray: true);
}
+
return flag;
}
- private void SwapBytesForXbox(Span image_data)
+ private void SwapBytesForXbox(Span buff)
{
- if (platform == BuildTarget.XBOX360)
+ if (m_Texture2D.platform == BuildTarget.XBOX360)
{
- for (var i = 0; i < reader.Size / 2; i++)
+ for (var i = 0; i < texDataSize / 2; i++)
{
- image_data.Slice(i * 2, 2).Reverse();
+ buff.Slice(i * 2, 2).Reverse();
}
}
}
- private bool DecodeAlpha8(ReadOnlySpan image_data, Span buff)
+ private bool DecodeAlpha8(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
- buff.Fill(0xFF);
+ destSpan.Fill(0xFF);
for (var i = 0; i < size; i++)
{
- buff[i * 4 + 3] = image_data[i];
+ destSpan[i * 4 + 3] = buff[i]; //A
}
return true;
}
- private bool DecodeARGB4444(ReadOnlySpan image_data, Span buff)
+ private bool DecodeARGB4444(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
var pixelNew = new byte[4].AsSpan();
for (var i = 0; i < size; i++)
{
- var pixelOldShort = BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2));
+ var pixelOldShort = BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2));
pixelNew[0] = (byte)(pixelOldShort & 0x000f);
pixelNew[1] = (byte)((pixelOldShort & 0x00f0) >> 4);
pixelNew[2] = (byte)((pixelOldShort & 0x0f00) >> 8);
pixelNew[3] = (byte)((pixelOldShort & 0xf000) >> 12);
for (var j = 0; j < 4; j++)
pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]);
- pixelNew.CopyTo(buff.Slice(i * 4));
+ pixelNew.CopyTo(destSpan.Slice(i * 4));
}
return true;
}
- private bool DecodeRGB24(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGB24(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
- buff[i * 4] = image_data[i * 3 + 2];
- buff[i * 4 + 1] = image_data[i * 3 + 1];
- buff[i * 4 + 2] = image_data[i * 3 + 0];
- buff[i * 4 + 3] = 255;
+ destSpan[i * 4] = buff[i * 3 + 2];
+ destSpan[i * 4 + 1] = buff[i * 3 + 1];
+ destSpan[i * 4 + 2] = buff[i * 3 + 0];
+ destSpan[i * 4 + 3] = 255;
}
return true;
}
- private bool DecodeRGBA32(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGBA32(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = image_data[i + 2];
- buff[i + 1] = image_data[i + 1];
- buff[i + 2] = image_data[i + 0];
- buff[i + 3] = image_data[i + 3];
+ destSpan[i] = buff[i + 2];
+ destSpan[i + 1] = buff[i + 1];
+ destSpan[i + 2] = buff[i + 0];
+ destSpan[i + 3] = buff[i + 3];
}
return true;
}
- private bool DecodeARGB32(ReadOnlySpan image_data, Span buff)
+ private bool DecodeARGB32(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = image_data[i + 3];
- buff[i + 1] = image_data[i + 2];
- buff[i + 2] = image_data[i + 1];
- buff[i + 3] = image_data[i + 0];
+ destSpan[i] = buff[i + 3];
+ destSpan[i + 1] = buff[i + 2];
+ destSpan[i + 2] = buff[i + 1];
+ destSpan[i + 3] = buff[i + 0];
}
return true;
}
- private bool DecodeRGB565(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGB565(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
- var p = BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2));
- buff[i * 4] = (byte)((p << 3) | (p >> 2 & 7));
- buff[i * 4 + 1] = (byte)((p >> 3 & 0xfc) | (p >> 9 & 3));
- buff[i * 4 + 2] = (byte)((p >> 8 & 0xf8) | (p >> 13));
- buff[i * 4 + 3] = 255;
+ var p = BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2));
+ destSpan[i * 4] = (byte)((p << 3) | (p >> 2 & 7));
+ destSpan[i * 4 + 1] = (byte)((p >> 3 & 0xfc) | (p >> 9 & 3));
+ destSpan[i * 4 + 2] = (byte)((p >> 8 & 0xf8) | (p >> 13));
+ destSpan[i * 4 + 3] = 255;
}
return true;
}
- private bool DecodeR16(ReadOnlySpan image_data, Span buff)
+ private bool DecodeR16(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
- buff[i * 4] = 0; //b
- buff[i * 4 + 1] = 0; //g
- buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2))); //r
- buff[i * 4 + 3] = 255; //a
+ destSpan[i * 4] = 0; //B
+ destSpan[i * 4 + 1] = 0; //G
+ destSpan[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2))); //R
+ destSpan[i * 4 + 3] = 255; //A
}
return true;
}
- private bool DecodeDXT1(ReadOnlySpan image_data, Span buff)
+ private bool DecodeDXT1(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeDXT1(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeDXT1(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeDXT5(ReadOnlySpan image_data, Span buff)
+ private bool DecodeDXT5(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeDXT5(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeDXT5(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeRGBA4444(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGBA4444(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
var pixelNew = new byte[4].AsSpan();
for (var i = 0; i < size; i++)
{
- var pixelOldShort = BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2));
+ var pixelOldShort = BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2));
pixelNew[0] = (byte)((pixelOldShort & 0x00f0) >> 4);
pixelNew[1] = (byte)((pixelOldShort & 0x0f00) >> 8);
pixelNew[2] = (byte)((pixelOldShort & 0xf000) >> 12);
pixelNew[3] = (byte)(pixelOldShort & 0x000f);
for (var j = 0; j < 4; j++)
pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]);
- pixelNew.CopyTo(buff.Slice(i * 4));
+ pixelNew.CopyTo(destSpan.Slice(i * 4));
}
return true;
}
- private bool DecodeBGRA32(ReadOnlySpan image_data, Span buff)
+ private bool DecodeBGRA32(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ buff.Slice(0, outputDataSize).CopyTo(destSpan);
+ return true;
+ }
+
+ private bool DecodeRHalf(ReadOnlySpan buff, Span destSpan)
+ {
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = image_data[i];
- buff[i + 1] = image_data[i + 1];
- buff[i + 2] = image_data[i + 2];
- buff[i + 3] = image_data[i + 3];
+ destSpan[i] = 0;
+ destSpan[i + 1] = 0;
+ destSpan[i + 2] = (byte)MathF.Round((float)buff.ReadHalf(i / 2) * 255f);
+ destSpan[i + 3] = 255;
}
return true;
}
- private bool DecodeRHalf(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGHalf(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = 0;
- buff[i + 1] = 0;
- buff[i + 2] = (byte)MathF.Round((float)image_data.ReadHalf(i / 2) * 255f);
- buff[i + 3] = 255;
+ destSpan[i] = 0;
+ destSpan[i + 1] = (byte)MathF.Round((float)buff.ReadHalf(i + 2) * 255f);
+ destSpan[i + 2] = (byte)MathF.Round((float)buff.ReadHalf(i) * 255f);
+ destSpan[i + 3] = 255;
}
return true;
}
- private bool DecodeRGHalf(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGBAHalf(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = 0;
- buff[i + 1] = (byte)MathF.Round((float)image_data.ReadHalf(i + 2) * 255f);
- buff[i + 2] = (byte)MathF.Round((float)image_data.ReadHalf(i) * 255f);
- buff[i + 3] = 255;
+ destSpan[i] = (byte)MathF.Round((float)buff.ReadHalf(i * 2 + 4) * 255f);
+ destSpan[i + 1] = (byte)MathF.Round((float)buff.ReadHalf(i * 2 + 2) * 255f);
+ destSpan[i + 2] = (byte)MathF.Round((float)buff.ReadHalf(i * 2) * 255f);
+ destSpan[i + 3] = (byte)MathF.Round((float)buff.ReadHalf(i * 2 + 6) * 255f);
}
return true;
}
- private bool DecodeRGBAHalf(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRFloat(byte[] buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2 + 4) * 255f);
- buff[i + 1] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2 + 2) * 255f);
- buff[i + 2] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2) * 255f);
- buff[i + 3] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2 + 6) * 255f);
+ destSpan[i] = 0;
+ destSpan[i + 1] = 0;
+ destSpan[i + 2] = (byte)MathF.Round(BitConverter.ToSingle(buff, i) * 255f);
+ destSpan[i + 3] = 255;
}
return true;
}
- private bool DecodeRFloat(byte[] image_data, Span buff)
+ private bool DecodeRGFloat(byte[] buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = 0;
- buff[i + 1] = 0;
- buff[i + 2] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i) * 255f);
- buff[i + 3] = 255;
+ destSpan[i] = 0;
+ destSpan[i + 1] = (byte)MathF.Round(BitConverter.ToSingle(buff, i * 2 + 4) * 255f);
+ destSpan[i + 2] = (byte)MathF.Round(BitConverter.ToSingle(buff, i * 2) * 255f);
+ destSpan[i + 3] = 255;
}
return true;
}
- private bool DecodeRGFloat(byte[] image_data, Span buff)
+ private bool DecodeRGBAFloat(byte[] buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = 0;
- buff[i + 1] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i * 2 + 4) * 255f);
- buff[i + 2] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i * 2) * 255f);
- buff[i + 3] = 255;
- }
- return true;
- }
-
- private bool DecodeRGBAFloat(byte[] image_data, Span buff)
- {
- for (var i = 0; i < outPutDataSize; i += 4)
- {
- buff[i] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i * 4 + 8) * 255f);
- buff[i + 1] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i * 4 + 4) * 255f);
- buff[i + 2] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i * 4) * 255f);
- buff[i + 3] = (byte)MathF.Round(BitConverter.ToSingle(image_data, i * 4 + 12) * 255f);
+ destSpan[i] = (byte)MathF.Round(BitConverter.ToSingle(buff, i * 4 + 8) * 255f);
+ destSpan[i + 1] = (byte)MathF.Round(BitConverter.ToSingle(buff, i * 4 + 4) * 255f);
+ destSpan[i + 2] = (byte)MathF.Round(BitConverter.ToSingle(buff, i * 4) * 255f);
+ destSpan[i + 3] = (byte)MathF.Round(BitConverter.ToSingle(buff, i * 4 + 12) * 255f);
}
return true;
}
@@ -488,7 +546,7 @@ namespace AssetStudio
return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue));
}
- private bool DecodeYUY2(ReadOnlySpan image_data, Span buff)
+ private bool DecodeYUY2(ReadOnlySpan buff, Span destSpan)
{
int p = 0;
int o = 0;
@@ -497,195 +555,183 @@ namespace AssetStudio
{
for (int i = 0; i < halfWidth; ++i)
{
- int y0 = image_data[p++];
- int u0 = image_data[p++];
- int y1 = image_data[p++];
- int v0 = image_data[p++];
+ int y0 = buff[p++];
+ int u0 = buff[p++];
+ int y1 = buff[p++];
+ int v0 = buff[p++];
int c = y0 - 16;
int d = u0 - 128;
int e = v0 - 128;
- buff[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); // b
- buff[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // g
- buff[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); // r
- buff[o++] = 255;
+ destSpan[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); //B
+ destSpan[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); //G
+ destSpan[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); //R
+ destSpan[o++] = 255; //A
c = y1 - 16;
- buff[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); // b
- buff[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // g
- buff[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); // r
- buff[o++] = 255;
+ destSpan[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); //B
+ destSpan[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); //G
+ destSpan[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); //R
+ destSpan[o++] = 255; //A
}
}
return true;
}
- private bool DecodeRGB9e5Float(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGB9e5Float(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- var n = BinaryPrimitives.ReadInt32LittleEndian(image_data.Slice(i));
+ var n = BinaryPrimitives.ReadInt32LittleEndian(buff.Slice(i));
var scale = n >> 27 & 0x1f;
var scalef = MathF.Pow(2, scale - 24);
var b = n >> 18 & 0x1ff;
var g = n >> 9 & 0x1ff;
var r = n & 0x1ff;
- buff[i] = (byte)MathF.Round(b * scalef * 255f);
- buff[i + 1] = (byte)MathF.Round(g * scalef * 255f);
- buff[i + 2] = (byte)MathF.Round(r * scalef * 255f);
- buff[i + 3] = 255;
+ destSpan[i] = (byte)MathF.Round(b * scalef * 255f);
+ destSpan[i + 1] = (byte)MathF.Round(g * scalef * 255f);
+ destSpan[i + 2] = (byte)MathF.Round(r * scalef * 255f);
+ destSpan[i + 3] = 255;
}
return true;
}
- private bool DecodeBC4(ReadOnlySpan image_data, Span buff)
+ private bool DecodeBC4(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeBC4(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeBC4(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeBC5(ReadOnlySpan image_data, Span buff)
+ private bool DecodeBC5(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeBC5(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeBC5(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeBC6H(ReadOnlySpan image_data, Span buff)
+ private bool DecodeBC6H(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeBC6(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeBC6(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeBC7(ReadOnlySpan image_data, Span buff)
+ private bool DecodeBC7(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeBC7(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeBC7(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeDXT1Crunched(ReadOnlySpan image_data, Span buff)
+ private bool DecodeDXT1Crunched(ReadOnlySpan buff, Span destSpan)
{
- if (UnpackCrunch(image_data, out var result))
+ if (TryUnpackCrunch(buff, out var result))
{
- if (DecodeDXT1(result, buff))
- {
- return true;
- }
+ return DecodeDXT1(result, destSpan);
}
return false;
}
- private bool DecodeDXT5Crunched(ReadOnlySpan image_data, Span buff)
+ private bool DecodeDXT5Crunched(ReadOnlySpan buff, Span destSpan)
{
- if (UnpackCrunch(image_data, out var result))
+ if (TryUnpackCrunch(buff, out var result))
{
- if (DecodeDXT5(result, buff))
- {
- return true;
- }
+ return DecodeDXT5(result, destSpan);
}
return false;
}
- private bool DecodePVRTC(ReadOnlySpan image_data, Span buff, bool is2bpp)
+ private bool DecodePVRTC(ReadOnlySpan buff, Span destSpan, bool is2bpp)
{
- return TextureDecoder.DecodePVRTC(image_data, m_Width, m_Height, buff, is2bpp);
+ return TextureDecoder.DecodePVRTC(buff, m_Width, m_Height, destSpan, is2bpp);
}
- private bool DecodeETC1(ReadOnlySpan image_data, Span buff)
+ private bool DecodeETC1(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeETC1(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeETC1(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeATCRGB4(ReadOnlySpan image_data, Span buff)
+ private bool DecodeATCRGB4(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeATCRGB4(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeATCRGB4(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeATCRGBA8(ReadOnlySpan image_data, Span buff)
+ private bool DecodeATCRGBA8(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeATCRGBA8(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeATCRGBA8(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeEACR(ReadOnlySpan image_data, Span buff)
+ private bool DecodeEACR(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeEACR(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeEACR(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeEACRSigned(ReadOnlySpan image_data, Span buff)
+ private bool DecodeEACRSigned(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeEACRSigned(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeEACRSigned(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeEACRG(ReadOnlySpan image_data, Span buff)
+ private bool DecodeEACRG(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeEACRG(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeEACRG(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeEACRGSigned(ReadOnlySpan image_data, Span buff)
+ private bool DecodeEACRGSigned(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeEACRGSigned(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeEACRGSigned(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeETC2(ReadOnlySpan image_data, Span buff)
+ private bool DecodeETC2(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeETC2(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeETC2(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeETC2A1(ReadOnlySpan image_data, Span buff)
+ private bool DecodeETC2A1(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeETC2A1(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeETC2A1(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeETC2A8(ReadOnlySpan image_data, Span buff)
+ private bool DecodeETC2A8(ReadOnlySpan buff, Span destSpan)
{
- return TextureDecoder.DecodeETC2A8(image_data, m_Width, m_Height, buff);
+ return TextureDecoder.DecodeETC2A8(buff, m_Width, m_Height, destSpan);
}
- private bool DecodeASTC(ReadOnlySpan image_data, Span buff, int blocksize)
+ private bool DecodeASTC(ReadOnlySpan buff, Span destSpan, int blocksize)
{
- return TextureDecoder.DecodeASTC(image_data, m_Width, m_Height, blocksize, blocksize, buff);
+ return TextureDecoder.DecodeASTC(buff, m_Width, m_Height, blocksize, blocksize, destSpan);
}
- private bool DecodeRG16(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRG16(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
- buff[i * 4] = 0; //B
- buff[i * 4 + 1] = image_data[i * 2 + 1];//G
- buff[i * 4 + 2] = image_data[i * 2];//R
- buff[i * 4 + 3] = 255;//A
+ destSpan[i * 4] = 0; //B
+ destSpan[i * 4 + 1] = buff[i * 2 + 1]; //G
+ destSpan[i * 4 + 2] = buff[i * 2]; //R
+ destSpan[i * 4 + 3] = 255; //A
}
return true;
}
- private bool DecodeR8(ReadOnlySpan image_data, Span buff)
+ private bool DecodeR8(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
- buff[i * 4] = 0; //B
- buff[i * 4 + 1] = 0; //G
- buff[i * 4 + 2] = image_data[i];//R
- buff[i * 4 + 3] = 255;//A
+ destSpan[i * 4] = 0; //B
+ destSpan[i * 4 + 1] = 0; //G
+ destSpan[i * 4 + 2] = buff[i]; //R
+ destSpan[i * 4 + 3] = 255; //A
}
return true;
}
- private bool DecodeETC1Crunched(ReadOnlySpan image_data, Span buff)
+ private bool DecodeETC1Crunched(ReadOnlySpan buff, Span destSpan)
{
- if (UnpackCrunch(image_data, out var result))
+ if (TryUnpackCrunch(buff, out var result))
{
- if (DecodeETC1(result, buff))
- {
- return true;
- }
+ return DecodeETC1(result, destSpan);
}
return false;
}
- private bool DecodeETC2A8Crunched(ReadOnlySpan image_data, Span buff)
+ private bool DecodeETC2A8Crunched(ReadOnlySpan buff, Span destSpan)
{
- if (UnpackCrunch(image_data, out var result))
+ if (TryUnpackCrunch(buff, out var result))
{
- if (DecodeETC2A8(result, buff))
- {
- return true;
- }
+ return DecodeETC2A8(result, destSpan);
}
return false;
}
@@ -696,54 +742,54 @@ namespace AssetStudio
return (byte)(((component * 255) + 32895) >> 16);
}
- private bool DecodeRG32(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRG32(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = 0; //b
- buff[i + 1] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i + 2))); //g
- buff[i + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i))); //r
- buff[i + 3] = byte.MaxValue; //a
+ destSpan[i] = 0; //B
+ destSpan[i + 1] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i + 2))); //G
+ destSpan[i + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i))); //R
+ destSpan[i + 3] = byte.MaxValue; //A
}
return true;
}
- private bool DecodeRGB48(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGB48(ReadOnlySpan buff, Span destSpan)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
- buff[i * 4] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 6 + 4))); //b
- buff[i * 4 + 1] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 6 + 2))); //g
- buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 6))); //r
- buff[i * 4 + 3] = byte.MaxValue; //a
+ destSpan[i * 4] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 6 + 4))); //B
+ destSpan[i * 4 + 1] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 6 + 2))); //G
+ destSpan[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 6))); //R
+ destSpan[i * 4 + 3] = byte.MaxValue; //A
}
return true;
}
- private bool DecodeRGBA64(ReadOnlySpan image_data, Span buff)
+ private bool DecodeRGBA64(ReadOnlySpan buff, Span destSpan)
{
- for (var i = 0; i < outPutDataSize; i += 4)
+ for (var i = 0; i < outputDataSize; i += 4)
{
- buff[i] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2 + 4))); //b
- buff[i + 1] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2 + 2))); //g
- buff[i + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2))); //r
- buff[i + 3] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(image_data.Slice(i * 2 + 6))); //a
+ destSpan[i] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2 + 4))); //B
+ destSpan[i + 1] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2 + 2))); //G
+ destSpan[i + 2] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2))); //R
+ destSpan[i + 3] = DownScaleFrom16BitTo8Bit(BinaryPrimitives.ReadUInt16LittleEndian(buff.Slice(i * 2 + 6))); //A
}
return true;
}
- private bool UnpackCrunch(ReadOnlySpan image_data, out ReadOnlySpan result)
+ private bool TryUnpackCrunch(ReadOnlySpan buff, out ReadOnlySpan result)
{
- if (version >= (2017, 3) //2017.3 and up
+ if (m_Texture2D.version >= (2017, 3) //2017.3 and up
|| m_TextureFormat == TextureFormat.ETC_RGB4Crunched
|| m_TextureFormat == TextureFormat.ETC2_RGBA8Crunched)
{
- result = TextureDecoder.UnpackUnityCrunch(image_data);
+ result = TextureDecoder.UnpackUnityCrunch(buff);
}
else
{
- result = TextureDecoder.UnpackCrunch(image_data);
+ result = TextureDecoder.UnpackCrunch(buff);
}
return !result.IsEmpty;
}
diff --git a/AssetStudioUtility/Texture2DExtensions.cs b/AssetStudioUtility/Texture2DExtensions.cs
index e854295..1ba05a6 100644
--- a/AssetStudioUtility/Texture2DExtensions.cs
+++ b/AssetStudioUtility/Texture2DExtensions.cs
@@ -1,8 +1,6 @@
-using System;
-using System.IO;
-using SixLabors.ImageSharp;
+using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using System.IO;
namespace AssetStudio
{
@@ -10,32 +8,13 @@ namespace AssetStudio
{
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 false;
-
- image = Image.LoadPixelData(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height);
-
- if (flip)
- {
- image.Mutate(x => x.Flip(FlipMode.Vertical));
- }
- return true;
- }
- finally
- {
- BigArrayPool.Shared.Return(buff, clearArray: true);
- }
+ return new Texture2DConverter(m_Texture2D).TryConvertToBgra32Image(flip, out image);
}
public static MemoryStream ConvertToStream(this Texture2D m_Texture2D, ImageFormat imageFormat, bool flip)
{
- if (!TryConvertToImage(m_Texture2D, flip, out var image))
+ var converter = new Texture2DConverter(m_Texture2D);
+ if (!converter.TryConvertToBgra32Image(flip, out var image))
return null;
using (image)
diff --git a/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj b/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj
index 8ae9dad..d88dc6b 100644
--- a/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj
+++ b/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj
@@ -136,6 +136,7 @@
true
NotUsing
stdafx.h
+ MultiThreadedDebug
Windows
@@ -172,6 +173,7 @@
true
NotUsing
stdafx.h
+ MultiThreadedDebug
Windows