using System; using System.Runtime.CompilerServices; using Texture2DDecoder; namespace AssetStudio { public class Texture2DConverter { private ResourceReader reader; private int m_Width; private int m_Height; private TextureFormat m_TextureFormat; private int[] version; private BuildTarget platform; private int outputSize; public Texture2DConverter(Texture2D m_Texture2D) { reader = m_Texture2D.image_data; m_Width = m_Texture2D.m_Width; m_Height = m_Texture2D.m_Height; 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) { if (reader.Size == 0 || m_Width == 0 || m_Height == 0) { return false; } var flag = false; var buff = BigArrayPool.Shared.Rent(reader.Size); reader.GetData(buff); switch (m_TextureFormat) { case TextureFormat.Alpha8: //test pass flag = DecodeAlpha8(buff, bytes); break; case TextureFormat.ARGB4444: //test pass SwapBytesForXbox(buff); flag = DecodeARGB4444(buff, bytes); break; case TextureFormat.RGB24: //test pass flag = DecodeRGB24(buff, bytes); break; case TextureFormat.RGBA32: //test pass flag = DecodeRGBA32(buff, bytes); break; case TextureFormat.ARGB32: //test pass flag = DecodeARGB32(buff, bytes); break; case TextureFormat.RGB565: //test pass SwapBytesForXbox(buff); flag = DecodeRGB565(buff, bytes); break; case TextureFormat.R16: //test pass case TextureFormat.R16_2: //test pass flag = DecodeR16(buff, bytes); break; case TextureFormat.DXT1: //test pass SwapBytesForXbox(buff); flag = DecodeDXT1(buff, bytes); break; case TextureFormat.DXT3: break; case TextureFormat.DXT5: //test pass SwapBytesForXbox(buff); flag = DecodeDXT5(buff, bytes); break; case TextureFormat.RGBA4444: //test pass flag = DecodeRGBA4444(buff, bytes); break; case TextureFormat.BGRA32: //test pass flag = DecodeBGRA32(buff, bytes); break; case TextureFormat.RHalf: flag = DecodeRHalf(buff, bytes); break; case TextureFormat.RGHalf: flag = DecodeRGHalf(buff, bytes); break; case TextureFormat.RGBAHalf: //test pass flag = DecodeRGBAHalf(buff, bytes); break; case TextureFormat.RFloat: flag = DecodeRFloat(buff, bytes); break; case TextureFormat.RGFloat: flag = DecodeRGFloat(buff, bytes); break; case TextureFormat.RGBAFloat: flag = DecodeRGBAFloat(buff, bytes); break; case TextureFormat.YUY2: //test pass flag = DecodeYUY2(buff, bytes); break; case TextureFormat.RGB9e5Float: //test pass flag = DecodeRGB9e5Float(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; case TextureFormat.DXT5Crunched: //test pass flag = DecodeDXT5Crunched(buff, bytes); break; case TextureFormat.PVRTC_RGB2: //test pass case TextureFormat.PVRTC_RGBA2: //test pass flag = DecodePVRTC(buff, bytes, true); break; case TextureFormat.PVRTC_RGB4: //test pass case TextureFormat.PVRTC_RGBA4: //test pass flag = DecodePVRTC(buff, bytes, false); break; case TextureFormat.ETC_RGB4: //test pass case TextureFormat.ETC_RGB4_3DS: flag = DecodeETC1(buff, bytes); break; case TextureFormat.ATC_RGB4: //test pass flag = DecodeATCRGB4(buff, bytes); break; case TextureFormat.ATC_RGBA8: //test pass flag = DecodeATCRGBA8(buff, bytes); break; case TextureFormat.EAC_R: //test pass flag = DecodeEACR(buff, bytes); break; case TextureFormat.EAC_R_SIGNED: flag = DecodeEACRSigned(buff, bytes); break; case TextureFormat.EAC_RG: //test pass flag = DecodeEACRG(buff, bytes); break; case TextureFormat.EAC_RG_SIGNED: flag = DecodeEACRGSigned(buff, bytes); break; case TextureFormat.ETC2_RGB: //test pass flag = DecodeETC2(buff, bytes); break; case TextureFormat.ETC2_RGBA1: //test pass flag = DecodeETC2A1(buff, bytes); break; case TextureFormat.ETC2_RGBA8: //test pass case TextureFormat.ETC_RGBA8_3DS: flag = DecodeETC2A8(buff, bytes); 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); 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); 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); 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); 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); 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); break; case TextureFormat.RG16: //test pass flag = DecodeRG16(buff, bytes); break; case TextureFormat.R8: //test pass flag = DecodeR8(buff, bytes); break; case TextureFormat.ETC_RGB4Crunched: //test pass flag = DecodeETC1Crunched(buff, bytes); break; 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; } private void SwapBytesForXbox(byte[] image_data) { if (platform == BuildTarget.XBOX360) { for (var i = 0; i < reader.Size / 2; i++) { var b = image_data[i * 2]; image_data[i * 2] = image_data[i * 2 + 1]; image_data[i * 2 + 1] = b; } } } 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 < size; i++) { buff[i * 4 + 3] = image_data[i]; } return true; } private bool DecodeARGB4444(byte[] image_data, byte[] buff) { var size = m_Width * m_Height; var pixelNew = new byte[4]; for (var i = 0; i < size; i++) { var pixelOldShort = BitConverter.ToUInt16(image_data, 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, i * 4); } return true; } private bool DecodeRGB24(byte[] image_data, byte[] buff) { 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; } return true; } private bool DecodeRGBA32(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; 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]; } return true; } private bool DecodeARGB32(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; 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]; } return true; } private bool DecodeRGB565(byte[] image_data, byte[] buff) { 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)); 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; } return true; } private bool DecodeR16(byte[] image_data, byte[] buff) { 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(BitConverter.ToUInt16(image_data, i * 2)); //r buff[i * 4 + 3] = 255; //a } return true; } private bool DecodeDXT1(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeDXT1(image_data, m_Width, m_Height, buff); } private bool DecodeDXT5(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeDXT5(image_data, m_Width, m_Height, buff); } private bool DecodeRGBA4444(byte[] image_data, byte[] buff) { var size = m_Width * m_Height; var pixelNew = new byte[4]; for (var i = 0; i < size; i++) { var pixelOldShort = BitConverter.ToUInt16(image_data, 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, i * 4); } return true; } private bool DecodeBGRA32(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; 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]; } return true; } private bool DecodeRHalf(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; i += 4) { buff[i] = 0; buff[i + 1] = 0; buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i / 2) * 255f); buff[i + 3] = 255; } return true; } private bool DecodeRGHalf(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; i += 4) { buff[i] = 0; buff[i + 1] = (byte)Math.Round(Half.ToHalf(image_data, i + 2) * 255f); buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i) * 255f); buff[i + 3] = 255; } return true; } private bool DecodeRGBAHalf(byte[] image_data, byte[] buff) { 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); buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i * 2) * 255f); buff[i + 3] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 6) * 255f); } return true; } private bool DecodeRFloat(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; i += 4) { buff[i] = 0; buff[i + 1] = 0; buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i) * 255f); buff[i + 3] = 255; } return true; } private bool DecodeRGFloat(byte[] image_data, byte[] buff) { 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); buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 2) * 255f); buff[i + 3] = 255; } return true; } private bool DecodeRGBAFloat(byte[] image_data, byte[] buff) { for (var i = 0; i < buff.Length; 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); buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4) * 255f); buff[i + 3] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 12) * 255f); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ClampByte(int x) { return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue)); } private bool DecodeYUY2(byte[] image_data, byte[] buff) { int p = 0; int o = 0; int halfWidth = m_Width / 2; for (int j = 0; j < m_Height; j++) { 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 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; 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; } } return true; } private bool DecodeRGB9e5Float(byte[] image_data, byte[] buff) { for (var i = 0; i < outputSize; i += 4) { var n = BitConverter.ToInt32(image_data, i); var scale = n >> 27 & 0x1f; var scalef = Math.Pow(2, scale - 24); var b = n >> 18 & 0x1ff; var g = n >> 9 & 0x1ff; var r = n & 0x1ff; buff[i] = (byte)Math.Round(b * scalef * 255f); buff[i + 1] = (byte)Math.Round(g * scalef * 255f); buff[i + 2] = (byte)Math.Round(r * scalef * 255f); buff[i + 3] = 255; } return true; } private bool DecodeBC4(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeBC4(image_data, m_Width, m_Height, buff); } private bool DecodeBC5(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeBC5(image_data, m_Width, m_Height, buff); } private bool DecodeBC6H(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeBC6(image_data, m_Width, m_Height, buff); } private bool DecodeBC7(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeBC7(image_data, m_Width, m_Height, buff); } private bool DecodeDXT1Crunched(byte[] image_data, byte[] buff) { if (UnpackCrunch(image_data, out var result)) { if (DecodeDXT1(result, buff)) { return true; } } return false; } private bool DecodeDXT5Crunched(byte[] image_data, byte[] buff) { if (UnpackCrunch(image_data, out var result)) { if (DecodeDXT5(result, buff)) { return true; } } return false; } private bool DecodePVRTC(byte[] image_data, byte[] buff, bool is2bpp) { return TextureDecoder.DecodePVRTC(image_data, m_Width, m_Height, buff, is2bpp); } private bool DecodeETC1(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeETC1(image_data, m_Width, m_Height, buff); } private bool DecodeATCRGB4(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeATCRGB4(image_data, m_Width, m_Height, buff); } private bool DecodeATCRGBA8(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeATCRGBA8(image_data, m_Width, m_Height, buff); } private bool DecodeEACR(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeEACR(image_data, m_Width, m_Height, buff); } private bool DecodeEACRSigned(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeEACRSigned(image_data, m_Width, m_Height, buff); } private bool DecodeEACRG(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeEACRG(image_data, m_Width, m_Height, buff); } private bool DecodeEACRGSigned(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeEACRGSigned(image_data, m_Width, m_Height, buff); } private bool DecodeETC2(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeETC2(image_data, m_Width, m_Height, buff); } private bool DecodeETC2A1(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeETC2A1(image_data, m_Width, m_Height, buff); } private bool DecodeETC2A8(byte[] image_data, byte[] buff) { return TextureDecoder.DecodeETC2A8(image_data, m_Width, m_Height, buff); } private bool DecodeASTC(byte[] image_data, byte[] buff, int blocksize) { return TextureDecoder.DecodeASTC(image_data, m_Width, m_Height, blocksize, blocksize, buff); } private bool DecodeRG16(byte[] image_data, byte[] buff) { var size = m_Width * m_Height; for (var i = 0; i < size; i += 2) { 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) { 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 } return true; } [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 DecodeETC1Crunched(byte[] image_data, byte[] buff) { if (UnpackCrunch(image_data, out var result)) { if (DecodeETC1(result, buff)) { return true; } } return false; } private bool DecodeETC2A8Crunched(byte[] image_data, byte[] buff) { if (UnpackCrunch(image_data, out var result)) { if (DecodeETC2A8(result, buff)) { return true; } } return false; } private bool UnpackCrunch(byte[] image_data, out byte[] result) { if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) //2017.3 and up || m_TextureFormat == TextureFormat.ETC_RGB4Crunched || m_TextureFormat == TextureFormat.ETC2_RGBA8Crunched) { result = TextureDecoder.UnpackUnityCrunch(image_data); } else { result = TextureDecoder.UnpackCrunch(image_data); } if (result != null) { return true; } return false; } } }