From b1205808e2f02e6ce49f49dcf975de5bb0cf6d0d Mon Sep 17 00:00:00 2001 From: Perfare Date: Tue, 22 Mar 2022 01:00:20 +0800 Subject: [PATCH] Fix and improve Texture2D convert --- AssetStudio/Classes/Texture2D.cs | 9 +- AssetStudioGUI/AssetStudioGUIForm.cs | 4 +- AssetStudioUtility/ImageExtensions.cs | 2 +- AssetStudioUtility/Texture2DConverter.cs | 124 +++++++++++++++++------ 4 files changed, 103 insertions(+), 36 deletions(-) diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index 45ff7ed..73d4050 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -154,7 +154,8 @@ namespace AssetStudio RGB565 = 7, R16 = 9, DXT1, - DXT5 = 12, + DXT3, + DXT5, RGBA4444, BGRA32, RHalf, @@ -165,11 +166,11 @@ namespace AssetStudio RGBAFloat, YUY2, RGB9e5Float, - BC4 = 26, - BC5, BC6H = 24, BC7, - DXT1Crunched = 28, + BC4, + BC5, + DXT1Crunched, DXT5Crunched, PVRTC_RGB2, PVRTC_RGBA2, diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 8dc3a21..7a8f2dd 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -759,7 +759,7 @@ namespace AssetStudioGUI var image = m_Texture2D.ConvertToImage(true); if (image != null) { - var bitmap = new DirectBitmap(image.ConvertToBgra32Bytes(), m_Texture2D.m_Width, m_Texture2D.m_Height); + var bitmap = new DirectBitmap(image.ConvertToBytes(), m_Texture2D.m_Width, m_Texture2D.m_Height); 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) @@ -1167,7 +1167,7 @@ namespace AssetStudioGUI var image = m_Sprite.GetImage(); if (image != null) { - var bitmap = new DirectBitmap(image.ConvertToBgra32Bytes(), image.Width, image.Height); + var bitmap = new DirectBitmap(image.ConvertToBytes(), image.Width, image.Height); image.Dispose(); assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; PreviewTexture(bitmap); diff --git a/AssetStudioUtility/ImageExtensions.cs b/AssetStudioUtility/ImageExtensions.cs index faafa10..a6cbad7 100644 --- a/AssetStudioUtility/ImageExtensions.cs +++ b/AssetStudioUtility/ImageExtensions.cs @@ -43,7 +43,7 @@ namespace AssetStudio return stream; } - public static byte[] ConvertToBgra32Bytes(this Image image) + public static byte[] ConvertToBytes(this Image image) where TPixel : unmanaged, IPixel { if (image.TryGetSinglePixelSpan(out var pixelSpan)) { diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index 2459817..ff7e4b2 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Texture2DDecoder; namespace AssetStudio @@ -11,6 +12,7 @@ namespace AssetStudio private TextureFormat m_TextureFormat; private int[] version; private BuildTarget platform; + private int outPutSize; public Texture2DConverter(Texture2D m_Texture2D) { @@ -20,6 +22,7 @@ namespace AssetStudio m_TextureFormat = m_Texture2D.m_TextureFormat; version = m_Texture2D.version; platform = m_Texture2D.platform; + outPutSize = m_Width * m_Height * 4; } public bool DecodeTexture2D(byte[] bytes) @@ -60,6 +63,8 @@ namespace AssetStudio SwapBytesForXbox(buff); flag = DecodeDXT1(buff, bytes); break; + case TextureFormat.DXT3: + break; case TextureFormat.DXT5: //test pass SwapBytesForXbox(buff); flag = DecodeDXT5(buff, bytes); @@ -94,18 +99,18 @@ namespace AssetStudio case TextureFormat.RGB9e5Float: //test pass flag = DecodeRGB9e5Float(buff, bytes); break; - case TextureFormat.BC4: //test pass - flag = DecodeBC4(buff, bytes); - break; - case TextureFormat.BC5: //test pass - flag = DecodeBC5(buff, bytes); - break; case TextureFormat.BC6H: //test pass flag = DecodeBC6H(buff, bytes); break; case TextureFormat.BC7: //test pass flag = DecodeBC7(buff, bytes); break; + case TextureFormat.BC4: //test pass + flag = DecodeBC4(buff, bytes); + break; + case TextureFormat.BC5: //test pass + flag = DecodeBC5(buff, bytes); + break; case TextureFormat.DXT1Crunched: //test pass flag = DecodeDXT1Crunched(buff, bytes); break; @@ -194,6 +199,15 @@ namespace AssetStudio case TextureFormat.ETC2_RGBA8Crunched: //test pass flag = DecodeETC2A8Crunched(buff, bytes); break; + case TextureFormat.RG32: //test pass + flag = DecodeRG32(buff, bytes); + break; + case TextureFormat.RGB48: //test pass + flag = DecodeRGB48(buff, bytes); + break; + case TextureFormat.RGBA64: //test pass + flag = DecodeRGBA64(buff, bytes); + break; } BigArrayPool.Shared.Return(buff); return flag; @@ -214,9 +228,10 @@ namespace AssetStudio private bool DecodeAlpha8(byte[] image_data, byte[] buff) { + var size = m_Width * m_Height; var span = new Span(buff); span.Fill(0xFF); - for (var i = 0; i < m_Width * m_Height; i++) + for (var i = 0; i < size; i++) { buff[i * 4 + 3] = image_data[i]; } @@ -225,8 +240,9 @@ namespace AssetStudio private bool DecodeARGB4444(byte[] image_data, byte[] buff) { + var size = m_Width * m_Height; var pixelNew = new byte[4]; - for (var i = 0; i < m_Width * m_Height; i++) + for (var i = 0; i < size; i++) { var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2); pixelNew[0] = (byte)(pixelOldShort & 0x000f); @@ -242,7 +258,8 @@ namespace AssetStudio private bool DecodeRGB24(byte[] image_data, byte[] buff) { - for (var i = 0; i < m_Width * m_Height; i++) + 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]; @@ -254,7 +271,7 @@ namespace AssetStudio private bool DecodeRGBA32(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = image_data[i + 2]; buff[i + 1] = image_data[i + 1]; @@ -266,7 +283,7 @@ namespace AssetStudio private bool DecodeARGB32(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = image_data[i + 3]; buff[i + 1] = image_data[i + 2]; @@ -278,7 +295,8 @@ namespace AssetStudio private bool DecodeRGB565(byte[] image_data, byte[] buff) { - for (var i = 0; i < m_Width * m_Height; i++) + var size = m_Width * m_Height; + for (var i = 0; i < size; i++) { var p = BitConverter.ToUInt16(image_data, i * 2); buff[i * 4] = (byte)((p << 3) | (p >> 2 & 7)); @@ -291,11 +309,12 @@ namespace AssetStudio private bool DecodeR16(byte[] image_data, byte[] buff) { - for (var i = 0; i < m_Width * m_Height; i++) + 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 * 2 + 1]; //r + buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2)); //r buff[i * 4 + 3] = 255; //a } return true; @@ -313,8 +332,9 @@ namespace AssetStudio private bool DecodeRGBA4444(byte[] image_data, byte[] buff) { + var size = m_Width * m_Height; var pixelNew = new byte[4]; - for (var i = 0; i < m_Width * m_Height; i++) + for (var i = 0; i < size; i++) { var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2); pixelNew[0] = (byte)((pixelOldShort & 0x00f0) >> 4); @@ -330,7 +350,7 @@ namespace AssetStudio private bool DecodeBGRA32(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = image_data[i]; buff[i + 1] = image_data[i + 1]; @@ -342,7 +362,7 @@ namespace AssetStudio private bool DecodeRHalf(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = 0; buff[i + 1] = 0; @@ -354,7 +374,7 @@ namespace AssetStudio private bool DecodeRGHalf(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = 0; buff[i + 1] = (byte)Math.Round(Half.ToHalf(image_data, i + 2) * 255f); @@ -366,7 +386,7 @@ namespace AssetStudio private bool DecodeRGBAHalf(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 4) * 255f); buff[i + 1] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 2) * 255f); @@ -378,7 +398,7 @@ namespace AssetStudio private bool DecodeRFloat(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = 0; buff[i + 1] = 0; @@ -390,7 +410,7 @@ namespace AssetStudio private bool DecodeRGFloat(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = 0; buff[i + 1] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 2 + 4) * 255f); @@ -402,7 +422,7 @@ namespace AssetStudio private bool DecodeRGBAFloat(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { buff[i] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 8) * 255f); buff[i + 1] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 4) * 255f); @@ -412,6 +432,7 @@ namespace AssetStudio return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ClampByte(int x) { return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue)); @@ -449,7 +470,7 @@ namespace AssetStudio private bool DecodeRGB9e5Float(byte[] image_data, byte[] buff) { - for (var i = 0; i < buff.Length; i += 4) + for (var i = 0; i < outPutSize; i += 4) { var n = BitConverter.ToInt32(image_data, i); var scale = n >> 27 & 0x1f; @@ -571,19 +592,21 @@ namespace AssetStudio private bool DecodeRG16(byte[] image_data, byte[] buff) { - for (var i = 0; i < m_Width * m_Height; i += 2) + var size = m_Width * m_Height; + for (var i = 0; i < size; i++) { - buff[i * 2] = 0; //B - buff[i * 2 + 1] = image_data[i + 1];//G - buff[i * 2 + 2] = image_data[i];//R - buff[i * 2 + 3] = 255;//A + 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 } return true; } private bool DecodeR8(byte[] image_data, byte[] buff) { - for (var i = 0; i < m_Width * m_Height; i++) + var size = m_Width * m_Height; + for (var i = 0; i < size; i++) { buff[i * 4] = 0; //B buff[i * 4 + 1] = 0; //G @@ -617,6 +640,49 @@ namespace AssetStudio return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte DownScaleFrom16BitTo8Bit(ushort component) + { + return (byte)(((component * 255) + 32895) >> 16); + } + + private bool DecodeRG32(byte[] image_data, byte[] buff) + { + for (var i = 0; i < outPutSize; i += 4) + { + buff[i] = 0; //b + buff[i + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i + 2)); //g + buff[i + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i)); //r + buff[i + 3] = byte.MaxValue; //a + } + return true; + } + + private bool DecodeRGB48(byte[] image_data, byte[] buff) + { + var size = m_Width * m_Height; + for (var i = 0; i < size; i++) + { + buff[i * 4] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6 + 4)); //b + buff[i * 4 + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6 + 2)); //g + buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6)); //r + buff[i * 4 + 3] = byte.MaxValue; //a + } + return true; + } + + private bool DecodeRGBA64(byte[] image_data, byte[] buff) + { + for (var i = 0; i < outPutSize; i += 4) + { + buff[i] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 4)); //b + buff[i + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 2)); //g + buff[i + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2)); //r + buff[i + 3] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 6)); //a + } + return true; + } + private bool UnpackCrunch(byte[] image_data, out byte[] result) { if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) //2017.3 and up