using System; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; using Texture2DDecoder; namespace AssetStudio { public class Texture2DConverter { private int m_Width; private int m_Height; private TextureFormat m_TextureFormat; private int image_data_size; private byte[] image_data; private int[] version; private BuildTarget platform; public Texture2DConverter(Texture2D m_Texture2D) { image_data = m_Texture2D.image_data.GetData(); image_data_size = image_data.Length; 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; } public Bitmap ConvertToBitmap(bool flip) { if (image_data == null || image_data.Length == 0) return null; var buff = DecodeTexture2D(); if (buff == null) { return null; } var bitmap = new Bitmap(m_Width, m_Height, PixelFormat.Format32bppArgb); var bmpData = bitmap.LockBits(new Rectangle(0, 0, m_Width, m_Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); Marshal.Copy(buff, 0, bmpData.Scan0, buff.Length); bitmap.UnlockBits(bmpData); if (flip) { bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); } return bitmap; } public byte[] DecodeTexture2D() { byte[] bytes = null; switch (m_TextureFormat) { case TextureFormat.Alpha8: //test pass bytes = DecodeAlpha8(); break; case TextureFormat.ARGB4444: //test pass SwapBytesForXbox(); bytes = DecodeARGB4444(); break; case TextureFormat.RGB24: //test pass bytes = DecodeRGB24(); break; case TextureFormat.RGBA32: //test pass bytes = DecodeRGBA32(); break; case TextureFormat.ARGB32: //test pass bytes = DecodeARGB32(); break; case TextureFormat.RGB565: //test pass SwapBytesForXbox(); bytes = DecodeRGB565(); break; case TextureFormat.R16: //test pass bytes = DecodeR16(); break; case TextureFormat.DXT1: //test pass SwapBytesForXbox(); bytes = DecodeDXT1(); break; case TextureFormat.DXT5: //test pass SwapBytesForXbox(); bytes = DecodeDXT5(); break; case TextureFormat.RGBA4444: //test pass bytes = DecodeRGBA4444(); break; case TextureFormat.BGRA32: //test pass bytes = DecodeBGRA32(); break; case TextureFormat.RHalf: bytes = DecodeRHalf(); break; case TextureFormat.RGHalf: bytes = DecodeRGHalf(); break; case TextureFormat.RGBAHalf: //test pass bytes = DecodeRGBAHalf(); break; case TextureFormat.RFloat: bytes = DecodeRFloat(); break; case TextureFormat.RGFloat: bytes = DecodeRGFloat(); break; case TextureFormat.RGBAFloat: bytes = DecodeRGBAFloat(); break; case TextureFormat.YUY2: //test pass bytes = DecodeYUY2(); break; case TextureFormat.RGB9e5Float: //test pass bytes = DecodeRGB9e5Float(); break; case TextureFormat.BC4: //test pass bytes = DecodeBC4(); break; case TextureFormat.BC5: //test pass bytes = DecodeBC5(); break; case TextureFormat.BC6H: //test pass bytes = DecodeBC6H(); break; case TextureFormat.BC7: //test pass bytes = DecodeBC7(); break; case TextureFormat.DXT1Crunched: //test pass if (UnpackCrunch()) { bytes = DecodeDXT1(); } break; case TextureFormat.DXT5Crunched: //test pass if (UnpackCrunch()) { bytes = DecodeDXT5(); } break; case TextureFormat.PVRTC_RGB2: //test pass case TextureFormat.PVRTC_RGBA2: //test pass bytes = DecodePVRTC(true); break; case TextureFormat.PVRTC_RGB4: //test pass case TextureFormat.PVRTC_RGBA4: //test pass bytes = DecodePVRTC(false); break; case TextureFormat.ETC_RGB4: //test pass case TextureFormat.ETC_RGB4_3DS: bytes = DecodeETC1(); break; case TextureFormat.ATC_RGB4: //test pass bytes = DecodeATCRGB4(); break; case TextureFormat.ATC_RGBA8: //test pass bytes = DecodeATCRGBA8(); break; case TextureFormat.EAC_R: //test pass bytes = DecodeEACR(); break; case TextureFormat.EAC_R_SIGNED: bytes = DecodeEACRSigned(); break; case TextureFormat.EAC_RG: //test pass bytes = DecodeEACRG(); break; case TextureFormat.EAC_RG_SIGNED: bytes = DecodeEACRGSigned(); break; case TextureFormat.ETC2_RGB: //test pass bytes = DecodeETC2(); break; case TextureFormat.ETC2_RGBA1: //test pass bytes = DecodeETC2A1(); break; case TextureFormat.ETC2_RGBA8: //test pass case TextureFormat.ETC_RGBA8_3DS: bytes = DecodeETC2A8(); break; case TextureFormat.ASTC_RGB_4x4: //test pass case TextureFormat.ASTC_RGBA_4x4: //test pass case TextureFormat.ASTC_HDR_4x4: //test pass bytes = DecodeASTC(4); break; case TextureFormat.ASTC_RGB_5x5: //test pass case TextureFormat.ASTC_RGBA_5x5: //test pass case TextureFormat.ASTC_HDR_5x5: //test pass bytes = DecodeASTC(5); break; case TextureFormat.ASTC_RGB_6x6: //test pass case TextureFormat.ASTC_RGBA_6x6: //test pass case TextureFormat.ASTC_HDR_6x6: //test pass bytes = DecodeASTC(6); break; case TextureFormat.ASTC_RGB_8x8: //test pass case TextureFormat.ASTC_RGBA_8x8: //test pass case TextureFormat.ASTC_HDR_8x8: //test pass bytes = DecodeASTC(8); break; case TextureFormat.ASTC_RGB_10x10: //test pass case TextureFormat.ASTC_RGBA_10x10: //test pass case TextureFormat.ASTC_HDR_10x10: //test pass bytes = DecodeASTC(10); break; case TextureFormat.ASTC_RGB_12x12: //test pass case TextureFormat.ASTC_RGBA_12x12: //test pass case TextureFormat.ASTC_HDR_12x12: //test pass bytes = DecodeASTC(12); break; case TextureFormat.RG16: //test pass bytes = DecodeRG16(); break; case TextureFormat.R8: //test pass bytes = DecodeR8(); break; case TextureFormat.ETC_RGB4Crunched: //test pass if (UnpackCrunch()) { bytes = DecodeETC1(); } break; case TextureFormat.ETC2_RGBA8Crunched: //test pass if (UnpackCrunch()) { bytes = DecodeETC2A8(); } break; } return bytes; } private void SwapBytesForXbox() { if (platform == BuildTarget.XBOX360) { for (var i = 0; i < image_data_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 byte[] DecodeAlpha8() { var buff = Enumerable.Repeat(0xFF, m_Width * m_Height * 4).ToArray(); for (var i = 0; i < m_Width * m_Height; i++) { buff[i * 4 + 3] = image_data[i]; } return buff; } private byte[] DecodeARGB4444() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { var pixelNew = new byte[4]; 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 buff; } private byte[] DecodeRGB24() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; 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 buff; } private byte[] DecodeRGBA32() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeARGB32() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRGB565() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; 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 buff; } private byte[] DecodeR16() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { buff[i * 4 + 2] = image_data[i * 2 + 1]; //r buff[i * 4 + 3] = 255; //a } return buff; } private byte[] DecodeDXT1() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeDXT1(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeDXT5() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeDXT5(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeRGBA4444() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { var pixelNew = new byte[4]; 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 buff; } private byte[] DecodeBGRA32() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRHalf() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRGHalf() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRGBAHalf() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRFloat() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRGFloat() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeRGBAFloat() { var buff = new byte[m_Width * m_Height * 4]; 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 buff; } private static byte ClampByte(int x) { return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue)); } private byte[] DecodeYUY2() { var buff = new byte[m_Width * m_Height * 4]; 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 buff; } private byte[] DecodeRGB9e5Float() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; 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 buff; } private byte[] DecodeBC4() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeBC4(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeBC5() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeBC5(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeBC6H() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeBC6(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeBC7() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeBC7(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodePVRTC(bool is2bpp) { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodePVRTC(image_data, m_Width, m_Height, buff, is2bpp)) { return null; } return buff; } private byte[] DecodeETC1() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeETC1(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeATCRGB4() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeATCRGB4(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeATCRGBA8() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeATCRGBA8(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeEACR() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeEACR(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeEACRSigned() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeEACRSigned(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeEACRG() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeEACRG(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeEACRGSigned() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeEACRGSigned(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeETC2() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeETC2(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeETC2A1() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeETC2A1(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeETC2A8() { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeETC2A8(image_data, m_Width, m_Height, buff)) { return null; } return buff; } private byte[] DecodeASTC(int blocksize) { var buff = new byte[m_Width * m_Height * 4]; if (!TextureDecoder.DecodeASTC(image_data, m_Width, m_Height, blocksize, blocksize, buff)) { return null; } return buff; } private byte[] DecodeRG16() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i += 2) { buff[i * 2 + 1] = image_data[i + 1];//G buff[i * 2 + 2] = image_data[i];//R buff[i * 2 + 3] = 255;//A } return buff; } private byte[] DecodeR8() { var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { buff[i * 4 + 2] = image_data[i];//R buff[i * 4 + 3] = 255;//A } return buff; } private bool UnpackCrunch() { 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) { image_data = result; image_data_size = result.Length; return true; } return false; } } }