using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace AssetStudio { public class Texture2DConverter { //Texture2D private int m_Width; private int m_Height; private TextureFormat m_TextureFormat; private int image_data_size; private byte[] image_data; private int[] version; //DDS Start private byte[] dwMagic = { 0x44, 0x44, 0x53, 0x20, 0x7c }; private int dwFlags = 0x1 + 0x2 + 0x4 + 0x1000; //public int dwHeight; m_Height //public int dwWidth; m_Width private int dwPitchOrLinearSize; private int dwMipMapCount = 0x1; private int dwSize = 0x20; private int dwFlags2; private int dwFourCC; private int dwRGBBitCount; private int dwRBitMask; private int dwGBitMask; private int dwBBitMask; private int dwABitMask; private int dwCaps = 0x1000; private int dwCaps2 = 0x0; //DDS End //PVR Start private int pvrVersion = 0x03525650; private int pvrFlags = 0x0; private long pvrPixelFormat; private int pvrColourSpace = 0x0; private int pvrChannelType = 0x0; //public int pvrHeight; m_Height //public int pvrWidth; m_Width private int pvrDepth = 0x1; private int pvrNumSurfaces = 0x1; //For texture arrays private int pvrNumFaces = 0x1; //For cube maps //public int pvrMIPMapCount; dwMipMapCount private int pvrMetaDataSize = 0x0; //PVR End //KTX Start private int glType = 0; private int glTypeSize = 1; private int glFormat = 0; private int glInternalFormat; private int glBaseInternalFormat; //public int pixelWidth; m_Width //public int pixelHeight; m_Height private int pixelDepth = 0; private int numberOfArrayElements = 0; private int numberOfFaces = 1; private int numberOfMipmapLevels = 1; private int bytesOfKeyValueData = 0; //KTX End //TextureConverter private QFORMAT q_format; //texgenpack private texgenpack_texturetype texturetype; [DllImport("PVRTexLibWrapper.dll", CallingConvention = CallingConvention.Cdecl)] private static extern bool DecompressPVR(byte[] buffer, IntPtr bmp, int len); [DllImport("TextureConverterWrapper.dll", CallingConvention = CallingConvention.Cdecl)] private static extern bool Ponvert(byte[] buffer, IntPtr bmp, int nWidth, int nHeight, int len, int type, int bmpsize, bool fixAlpha); [DllImport("crunch.dll", CallingConvention = CallingConvention.Cdecl)] private static extern bool DecompressCRN(byte[] pSrc_file_data, int src_file_size, out IntPtr uncompressedData, out int uncompressedSize); [DllImport("crunchunity.dll", CallingConvention = CallingConvention.Cdecl)] private static extern bool DecompressUnityCRN(byte[] pSrc_file_data, int src_file_size, out IntPtr uncompressedData, out int uncompressedSize); [DllImport("texgenpack.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void Decode(int texturetype, byte[] texturedata, int width, int height, IntPtr bmp); public Texture2DConverter(Texture2D m_Texture2D) { image_data_size = m_Texture2D.image_data_size; image_data = m_Texture2D.image_data.Value; m_Width = m_Texture2D.m_Width; m_Height = m_Texture2D.m_Height; m_TextureFormat = m_Texture2D.m_TextureFormat; var mMipMap = m_Texture2D.m_MipMap; version = m_Texture2D.version; var platform = m_Texture2D.platform; if (version[0] < 5 || (version[0] == 5 && version[1] < 2))//5.2 down { if (mMipMap) { dwFlags += 0x20000; dwMipMapCount = Convert.ToInt32(Math.Log(Math.Max(m_Width, m_Height)) / Math.Log(2)); dwCaps += 0x400008; } } else { dwFlags += 0x20000; dwMipMapCount = m_Texture2D.m_MipCount; dwCaps += 0x400008; } switch (m_TextureFormat) { //TODO 导出到DDS容器时应该用原像素还是转换以后的像素? case TextureFormat.Alpha8: //test pass { /*dwFlags2 = 0x2; dwRGBBitCount = 0x8; dwRBitMask = 0x0; dwGBitMask = 0x0; dwBBitMask = 0x0; dwABitMask = 0xFF; */ //转BGRA32 var BGRA32 = Enumerable.Repeat(0xFF, image_data_size * 4).ToArray(); for (var i = 0; i < image_data_size; i++) { BGRA32[i * 4 + 3] = image_data[i]; } SetBGRA32Info(BGRA32); break; } case TextureFormat.ARGB4444: //test pass { SwapBytesForXbox(platform); /*dwFlags2 = 0x41; dwRGBBitCount = 0x10; dwRBitMask = 0xF00; dwGBitMask = 0xF0; dwBBitMask = 0xF; dwABitMask = 0xF000;*/ //转BGRA32 var BGRA32 = new byte[image_data_size * 2]; for (var i = 0; i < image_data_size / 2; 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); // convert range for (var j = 0; j < 4; j++) pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]); pixelNew.CopyTo(BGRA32, i * 4); } SetBGRA32Info(BGRA32); break; } case TextureFormat.RGB24: //test pass { /*dwFlags2 = 0x40; dwRGBBitCount = 0x18; dwRBitMask = 0xFF; dwGBitMask = 0xFF00; dwBBitMask = 0xFF0000; dwABitMask = 0x0;*/ //转BGRA32 var BGRA32 = new byte[image_data_size / 3 * 4]; for (var i = 0; i < image_data_size / 3; i++) { BGRA32[i * 4] = image_data[i * 3 + 2]; BGRA32[i * 4 + 1] = image_data[i * 3 + 1]; BGRA32[i * 4 + 2] = image_data[i * 3 + 0]; BGRA32[i * 4 + 3] = 255; } SetBGRA32Info(BGRA32); break; } case TextureFormat.RGBA32: //test pass { /*dwFlags2 = 0x41; dwRGBBitCount = 0x20; dwRBitMask = 0xFF; dwGBitMask = 0xFF00; dwBBitMask = 0xFF0000; dwABitMask = -16777216;*/ //转BGRA32 var BGRA32 = new byte[image_data_size]; for (var i = 0; i < image_data_size; i += 4) { BGRA32[i] = image_data[i + 2]; BGRA32[i + 1] = image_data[i + 1]; BGRA32[i + 2] = image_data[i + 0]; BGRA32[i + 3] = image_data[i + 3]; } SetBGRA32Info(BGRA32); break; } case TextureFormat.ARGB32://test pass { /*dwFlags2 = 0x41; dwRGBBitCount = 0x20; dwRBitMask = 0xFF00; dwGBitMask = 0xFF0000; dwBBitMask = -16777216; dwABitMask = 0xFF;*/ //转BGRA32 var BGRA32 = new byte[image_data_size]; for (var i = 0; i < image_data_size; i += 4) { BGRA32[i] = image_data[i + 3]; BGRA32[i + 1] = image_data[i + 2]; BGRA32[i + 2] = image_data[i + 1]; BGRA32[i + 3] = image_data[i + 0]; } SetBGRA32Info(BGRA32); break; } case TextureFormat.RGB565: //test pass { SwapBytesForXbox(platform); dwFlags2 = 0x40; dwRGBBitCount = 0x10; dwRBitMask = 0xF800; dwGBitMask = 0x7E0; dwBBitMask = 0x1F; dwABitMask = 0x0; break; } case TextureFormat.R16: //test pass { //转BGRA32 var BGRA32 = new byte[image_data_size * 2]; for (var i = 0; i < image_data_size; i += 2) { float f = Half.ToHalf(image_data, i); BGRA32[i * 2 + 2] = (byte)Math.Ceiling(f * 255);//R BGRA32[i * 2 + 3] = 255;//A } SetBGRA32Info(BGRA32); break; } case TextureFormat.DXT1: //test pass case TextureFormat.DXT1Crunched: //test pass { SwapBytesForXbox(platform); if (mMipMap) { dwPitchOrLinearSize = m_Height * m_Width / 2; } dwFlags2 = 0x4; dwFourCC = 0x31545844; dwRGBBitCount = 0x0; dwRBitMask = 0x0; dwGBitMask = 0x0; dwBBitMask = 0x0; dwABitMask = 0x0; q_format = QFORMAT.Q_FORMAT_S3TC_DXT1_RGB; break; } case TextureFormat.DXT5: //test pass case TextureFormat.DXT5Crunched: //test pass { SwapBytesForXbox(platform); if (mMipMap) { dwPitchOrLinearSize = m_Height * m_Width / 2; } dwFlags2 = 0x4; dwFourCC = 0x35545844; dwRGBBitCount = 0x0; dwRBitMask = 0x0; dwGBitMask = 0x0; dwBBitMask = 0x0; dwABitMask = 0x0; q_format = QFORMAT.Q_FORMAT_S3TC_DXT5_RGBA; break; } case TextureFormat.RGBA4444: //test pass { /*dwFlags2 = 0x41; dwRGBBitCount = 0x10; dwRBitMask = 0xF000; dwGBitMask = 0xF00; dwBBitMask = 0xF0; dwABitMask = 0xF;*/ //转BGRA32 var BGRA32 = new byte[image_data_size * 2]; for (var i = 0; i < image_data_size / 2; 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); // convert range for (var j = 0; j < 4; j++) pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]); pixelNew.CopyTo(BGRA32, i * 4); } SetBGRA32Info(BGRA32); break; } case TextureFormat.BGRA32: //test pass { dwFlags2 = 0x41; dwRGBBitCount = 0x20; dwRBitMask = 0xFF0000; dwGBitMask = 0xFF00; dwBBitMask = 0xFF; dwABitMask = -16777216; break; } case TextureFormat.RHalf: //test pass { q_format = QFORMAT.Q_FORMAT_R_16F; glInternalFormat = KTXHeader.GL_R16F; glBaseInternalFormat = KTXHeader.GL_RED; break; } case TextureFormat.RGHalf: //test pass { q_format = QFORMAT.Q_FORMAT_RG_HF; glInternalFormat = KTXHeader.GL_RG16F; glBaseInternalFormat = KTXHeader.GL_RG; break; } case TextureFormat.RGBAHalf: //test pass { q_format = QFORMAT.Q_FORMAT_RGBA_HF; glInternalFormat = KTXHeader.GL_RGBA16F; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.RFloat: //test pass { q_format = QFORMAT.Q_FORMAT_R_F; glInternalFormat = KTXHeader.GL_R32F; glBaseInternalFormat = KTXHeader.GL_RED; break; } case TextureFormat.RGFloat: //test pass { q_format = QFORMAT.Q_FORMAT_RG_F; glInternalFormat = KTXHeader.GL_RG32F; glBaseInternalFormat = KTXHeader.GL_RG; break; } case TextureFormat.RGBAFloat: //test pass { q_format = QFORMAT.Q_FORMAT_RGBA_F; glInternalFormat = KTXHeader.GL_RGBA32F; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.YUY2: //test pass { pvrPixelFormat = 17; break; } case TextureFormat.RGB9e5Float: //TODO Test failure { q_format = QFORMAT.Q_FORMAT_RGB9_E5; break; } case TextureFormat.BC4: //test pass { texturetype = texgenpack_texturetype.RGTC1; glInternalFormat = KTXHeader.GL_COMPRESSED_RED_RGTC1; glBaseInternalFormat = KTXHeader.GL_RED; break; } case TextureFormat.BC5: //test pass { texturetype = texgenpack_texturetype.RGTC2; glInternalFormat = KTXHeader.GL_COMPRESSED_RG_RGTC2; glBaseInternalFormat = KTXHeader.GL_RG; break; } case TextureFormat.BC6H: //test pass { texturetype = texgenpack_texturetype.BPTC_FLOAT; glInternalFormat = KTXHeader.GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; glBaseInternalFormat = KTXHeader.GL_RGB; break; } case TextureFormat.BC7: //test pass { texturetype = texgenpack_texturetype.BPTC; glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA_BPTC_UNORM; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.PVRTC_RGB2: //test pass { pvrPixelFormat = 0; glInternalFormat = KTXHeader.GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; glBaseInternalFormat = KTXHeader.GL_RGB; break; } case TextureFormat.PVRTC_RGBA2: //test pass { pvrPixelFormat = 1; glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.PVRTC_RGB4: //test pass { pvrPixelFormat = 2; glInternalFormat = KTXHeader.GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; glBaseInternalFormat = KTXHeader.GL_RGB; break; } case TextureFormat.PVRTC_RGBA4: //test pass { pvrPixelFormat = 3; glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.ETC_RGB4Crunched: //test pass case TextureFormat.ETC_RGB4_3DS: //test pass case TextureFormat.ETC_RGB4: //test pass { pvrPixelFormat = 6; glInternalFormat = KTXHeader.GL_ETC1_RGB8_OES; glBaseInternalFormat = KTXHeader.GL_RGB; break; } case TextureFormat.ATC_RGB4: //test pass { q_format = QFORMAT.Q_FORMAT_ATITC_RGB; glInternalFormat = KTXHeader.GL_ATC_RGB_AMD; glBaseInternalFormat = KTXHeader.GL_RGB; break; } case TextureFormat.ATC_RGBA8: //test pass { q_format = QFORMAT.Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA; glInternalFormat = KTXHeader.GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.EAC_R: //test pass { q_format = QFORMAT.Q_FORMAT_EAC_R_UNSIGNED; glInternalFormat = KTXHeader.GL_COMPRESSED_R11_EAC; glBaseInternalFormat = KTXHeader.GL_RED; break; } case TextureFormat.EAC_R_SIGNED: //test pass { q_format = QFORMAT.Q_FORMAT_EAC_R_SIGNED; glInternalFormat = KTXHeader.GL_COMPRESSED_SIGNED_R11_EAC; glBaseInternalFormat = KTXHeader.GL_RED; break; } case TextureFormat.EAC_RG: //test pass { q_format = QFORMAT.Q_FORMAT_EAC_RG_UNSIGNED; glInternalFormat = KTXHeader.GL_COMPRESSED_RG11_EAC; glBaseInternalFormat = KTXHeader.GL_RG; break; } case TextureFormat.EAC_RG_SIGNED: //test pass { q_format = QFORMAT.Q_FORMAT_EAC_RG_SIGNED; glInternalFormat = KTXHeader.GL_COMPRESSED_SIGNED_RG11_EAC; glBaseInternalFormat = KTXHeader.GL_RG; break; } case TextureFormat.ETC2_RGB: //test pass { pvrPixelFormat = 22; glInternalFormat = KTXHeader.GL_COMPRESSED_RGB8_ETC2; glBaseInternalFormat = KTXHeader.GL_RGB; break; } case TextureFormat.ETC2_RGBA1: //test pass { pvrPixelFormat = 24; glInternalFormat = KTXHeader.GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.ETC2_RGBA8Crunched: //test pass case TextureFormat.ETC_RGBA8_3DS: //test pass case TextureFormat.ETC2_RGBA8: //test pass { pvrPixelFormat = 23; glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA8_ETC2_EAC; glBaseInternalFormat = KTXHeader.GL_RGBA; break; } case TextureFormat.ASTC_RGB_4x4: //test pass case TextureFormat.ASTC_RGBA_4x4: //test pass { pvrPixelFormat = 27; break; } case TextureFormat.ASTC_RGB_5x5: //test pass case TextureFormat.ASTC_RGBA_5x5: //test pass { pvrPixelFormat = 29; break; } case TextureFormat.ASTC_RGB_6x6: //test pass case TextureFormat.ASTC_RGBA_6x6: //test pass { pvrPixelFormat = 31; break; } case TextureFormat.ASTC_RGB_8x8: //test pass case TextureFormat.ASTC_RGBA_8x8: //test pass { pvrPixelFormat = 34; break; } case TextureFormat.ASTC_RGB_10x10: //test pass case TextureFormat.ASTC_RGBA_10x10: //test pass { pvrPixelFormat = 38; break; } case TextureFormat.ASTC_RGB_12x12: //test pass case TextureFormat.ASTC_RGBA_12x12: //test pass { pvrPixelFormat = 40; break; } case TextureFormat.RG16: //test pass { //转BGRA32 var BGRA32 = new byte[image_data_size * 2]; for (var i = 0; i < image_data_size; i += 2) { BGRA32[i * 2 + 1] = image_data[i + 1];//G BGRA32[i * 2 + 2] = image_data[i];//R BGRA32[i * 2 + 3] = 255;//A } SetBGRA32Info(BGRA32); break; } case TextureFormat.R8: //test pass { //转BGRA32 var BGRA32 = new byte[image_data_size * 4]; for (var i = 0; i < image_data_size; i++) { BGRA32[i * 4 + 2] = image_data[i];//R BGRA32[i * 4 + 3] = 255;//A } SetBGRA32Info(BGRA32); break; } } } private void SetBGRA32Info(byte[] BGRA32) { image_data = BGRA32; image_data_size = BGRA32.Length; dwFlags2 = 0x41; dwRGBBitCount = 0x20; dwRBitMask = 0xFF0000; dwGBitMask = 0xFF00; dwBBitMask = 0xFF; dwABitMask = -16777216; } private void SwapBytesForXbox(BuildTarget platform) { if (platform == BuildTarget.XBOX360) //swap bytes for Xbox confirmed, PS3 not encountered { for (var i = 0; i < image_data_size / 2; i++) { var b0 = image_data[i * 2]; image_data[i * 2] = image_data[i * 2 + 1]; image_data[i * 2 + 1] = b0; } } } public string GetExtensionName() { switch (m_TextureFormat) { case TextureFormat.Alpha8: case TextureFormat.ARGB4444: case TextureFormat.RGB24: case TextureFormat.RGBA32: case TextureFormat.ARGB32: case TextureFormat.RGB565: case TextureFormat.R16: case TextureFormat.DXT1: case TextureFormat.DXT5: case TextureFormat.RGBA4444: case TextureFormat.BGRA32: case TextureFormat.RG16: case TextureFormat.R8: return ".dds"; case TextureFormat.DXT1Crunched: case TextureFormat.DXT5Crunched: case TextureFormat.ETC_RGB4Crunched: case TextureFormat.ETC2_RGBA8Crunched: return ".crn"; case TextureFormat.YUY2: case TextureFormat.PVRTC_RGB2: case TextureFormat.PVRTC_RGBA2: case TextureFormat.PVRTC_RGB4: case TextureFormat.PVRTC_RGBA4: case TextureFormat.ETC_RGB4: case TextureFormat.ETC2_RGB: case TextureFormat.ETC2_RGBA1: case TextureFormat.ETC2_RGBA8: case TextureFormat.ASTC_RGB_4x4: case TextureFormat.ASTC_RGB_5x5: case TextureFormat.ASTC_RGB_6x6: case TextureFormat.ASTC_RGB_8x8: case TextureFormat.ASTC_RGB_10x10: case TextureFormat.ASTC_RGB_12x12: case TextureFormat.ASTC_RGBA_4x4: case TextureFormat.ASTC_RGBA_5x5: case TextureFormat.ASTC_RGBA_6x6: case TextureFormat.ASTC_RGBA_8x8: case TextureFormat.ASTC_RGBA_10x10: case TextureFormat.ASTC_RGBA_12x12: case TextureFormat.ETC_RGB4_3DS: case TextureFormat.ETC_RGBA8_3DS: return ".pvr"; case TextureFormat.RHalf: case TextureFormat.RGHalf: case TextureFormat.RGBAHalf: case TextureFormat.RFloat: case TextureFormat.RGFloat: case TextureFormat.RGBAFloat: case TextureFormat.BC4: case TextureFormat.BC5: case TextureFormat.BC6H: case TextureFormat.BC7: case TextureFormat.ATC_RGB4: case TextureFormat.ATC_RGBA8: case TextureFormat.EAC_R: case TextureFormat.EAC_R_SIGNED: case TextureFormat.EAC_RG: case TextureFormat.EAC_RG_SIGNED: return ".ktx"; default: return ".tex"; } } public byte[] ConvertToContainer() { if (image_data == null || image_data.Length == 0) return null; switch (m_TextureFormat) { case TextureFormat.Alpha8: case TextureFormat.ARGB4444: case TextureFormat.RGB24: case TextureFormat.RGBA32: case TextureFormat.ARGB32: case TextureFormat.RGB565: case TextureFormat.R16: case TextureFormat.DXT1: case TextureFormat.DXT5: case TextureFormat.RGBA4444: case TextureFormat.BGRA32: case TextureFormat.RG16: case TextureFormat.R8: return ConvertToDDS(); case TextureFormat.YUY2: case TextureFormat.PVRTC_RGB2: case TextureFormat.PVRTC_RGBA2: case TextureFormat.PVRTC_RGB4: case TextureFormat.PVRTC_RGBA4: case TextureFormat.ETC_RGB4: case TextureFormat.ETC2_RGB: case TextureFormat.ETC2_RGBA1: case TextureFormat.ETC2_RGBA8: case TextureFormat.ASTC_RGB_4x4: case TextureFormat.ASTC_RGB_5x5: case TextureFormat.ASTC_RGB_6x6: case TextureFormat.ASTC_RGB_8x8: case TextureFormat.ASTC_RGB_10x10: case TextureFormat.ASTC_RGB_12x12: case TextureFormat.ASTC_RGBA_4x4: case TextureFormat.ASTC_RGBA_5x5: case TextureFormat.ASTC_RGBA_6x6: case TextureFormat.ASTC_RGBA_8x8: case TextureFormat.ASTC_RGBA_10x10: case TextureFormat.ASTC_RGBA_12x12: case TextureFormat.ETC_RGB4_3DS: case TextureFormat.ETC_RGBA8_3DS: return ConvertToPVR(); case TextureFormat.RHalf: case TextureFormat.RGHalf: case TextureFormat.RGBAHalf: case TextureFormat.RFloat: case TextureFormat.RGFloat: case TextureFormat.RGBAFloat: case TextureFormat.BC4: case TextureFormat.BC5: case TextureFormat.BC6H: case TextureFormat.BC7: case TextureFormat.ATC_RGB4: case TextureFormat.ATC_RGBA8: case TextureFormat.EAC_R: case TextureFormat.EAC_R_SIGNED: case TextureFormat.EAC_RG: case TextureFormat.EAC_RG_SIGNED: return ConvertToKTX(); default: return image_data; } } private byte[] ConvertToDDS() { var imageBuffer = new byte[128 + image_data_size]; dwMagic.CopyTo(imageBuffer, 0); BitConverter.GetBytes(dwFlags).CopyTo(imageBuffer, 8); BitConverter.GetBytes(m_Height).CopyTo(imageBuffer, 12); BitConverter.GetBytes(m_Width).CopyTo(imageBuffer, 16); BitConverter.GetBytes(dwPitchOrLinearSize).CopyTo(imageBuffer, 20); BitConverter.GetBytes(dwMipMapCount).CopyTo(imageBuffer, 28); BitConverter.GetBytes(dwSize).CopyTo(imageBuffer, 76); BitConverter.GetBytes(dwFlags2).CopyTo(imageBuffer, 80); BitConverter.GetBytes(dwFourCC).CopyTo(imageBuffer, 84); BitConverter.GetBytes(dwRGBBitCount).CopyTo(imageBuffer, 88); BitConverter.GetBytes(dwRBitMask).CopyTo(imageBuffer, 92); BitConverter.GetBytes(dwGBitMask).CopyTo(imageBuffer, 96); BitConverter.GetBytes(dwBBitMask).CopyTo(imageBuffer, 100); BitConverter.GetBytes(dwABitMask).CopyTo(imageBuffer, 104); BitConverter.GetBytes(dwCaps).CopyTo(imageBuffer, 108); BitConverter.GetBytes(dwCaps2).CopyTo(imageBuffer, 112); image_data.CopyTo(imageBuffer, 128); return imageBuffer; } private byte[] ConvertToPVR() { var mstream = new MemoryStream(); using (var writer = new BinaryWriter(mstream)) { writer.Write(pvrVersion); writer.Write(pvrFlags); writer.Write(pvrPixelFormat); writer.Write(pvrColourSpace); writer.Write(pvrChannelType); writer.Write(m_Height); writer.Write(m_Width); writer.Write(pvrDepth); writer.Write(pvrNumSurfaces); writer.Write(pvrNumFaces); writer.Write(dwMipMapCount); writer.Write(pvrMetaDataSize); writer.Write(image_data); return mstream.ToArray(); } } private byte[] ConvertToKTX() { var mstream = new MemoryStream(); using (var writer = new BinaryWriter(mstream)) { writer.Write(KTXHeader.IDENTIFIER); writer.Write(KTXHeader.ENDIANESS_LE); writer.Write(glType); writer.Write(glTypeSize); writer.Write(glFormat); writer.Write(glInternalFormat); writer.Write(glBaseInternalFormat); writer.Write(m_Width); writer.Write(m_Height); writer.Write(pixelDepth); writer.Write(numberOfArrayElements); writer.Write(numberOfFaces); writer.Write(numberOfMipmapLevels); writer.Write(bytesOfKeyValueData); writer.Write(image_data_size); writer.Write(image_data); return mstream.ToArray(); } } public Bitmap ConvertToBitmap(bool flip) { if (image_data == null || image_data.Length == 0) return null; Bitmap bitmap; switch (m_TextureFormat) { case TextureFormat.Alpha8: case TextureFormat.ARGB4444: case TextureFormat.RGB24: case TextureFormat.RGBA32: case TextureFormat.ARGB32: case TextureFormat.R16: case TextureFormat.RGBA4444: case TextureFormat.BGRA32: case TextureFormat.RG16: case TextureFormat.R8: bitmap = BGRA32ToBitmap(); break; case TextureFormat.RGB565: bitmap = RGB565ToBitmap(); break; case TextureFormat.YUY2: case TextureFormat.PVRTC_RGB2: case TextureFormat.PVRTC_RGBA2: case TextureFormat.PVRTC_RGB4: case TextureFormat.PVRTC_RGBA4: case TextureFormat.ETC_RGB4: case TextureFormat.ETC2_RGB: case TextureFormat.ETC2_RGBA1: case TextureFormat.ETC2_RGBA8: case TextureFormat.ASTC_RGB_4x4: case TextureFormat.ASTC_RGB_5x5: case TextureFormat.ASTC_RGB_6x6: case TextureFormat.ASTC_RGB_8x8: case TextureFormat.ASTC_RGB_10x10: case TextureFormat.ASTC_RGB_12x12: case TextureFormat.ASTC_RGBA_4x4: case TextureFormat.ASTC_RGBA_5x5: case TextureFormat.ASTC_RGBA_6x6: case TextureFormat.ASTC_RGBA_8x8: case TextureFormat.ASTC_RGBA_10x10: case TextureFormat.ASTC_RGBA_12x12: case TextureFormat.ETC_RGB4_3DS: case TextureFormat.ETC_RGBA8_3DS: bitmap = PVRToBitmap(ConvertToPVR()); break; case TextureFormat.DXT1: case TextureFormat.DXT5: case TextureFormat.RHalf: case TextureFormat.RGHalf: case TextureFormat.RGBAHalf: case TextureFormat.RFloat: case TextureFormat.RGFloat: case TextureFormat.RGBAFloat: case TextureFormat.RGB9e5Float: case TextureFormat.ATC_RGB4: case TextureFormat.ATC_RGBA8: case TextureFormat.EAC_R: case TextureFormat.EAC_R_SIGNED: case TextureFormat.EAC_RG: case TextureFormat.EAC_RG_SIGNED: bitmap = TextureConverter(); break; case TextureFormat.BC4: case TextureFormat.BC5: case TextureFormat.BC6H: case TextureFormat.BC7: bitmap = Texgenpack(); break; case TextureFormat.DXT1Crunched: case TextureFormat.DXT5Crunched: DecompressCRN(); bitmap = TextureConverter(); break; case TextureFormat.ETC_RGB4Crunched: case TextureFormat.ETC2_RGBA8Crunched: DecompressCRN(); bitmap = PVRToBitmap(ConvertToPVR()); break; default: return null; } if (bitmap != null && flip) bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); return bitmap; } private Bitmap BGRA32ToBitmap() { var hObject = GCHandle.Alloc(image_data, GCHandleType.Pinned); var pObject = hObject.AddrOfPinnedObject(); var bitmap = new Bitmap(m_Width, m_Height, m_Width * 4, PixelFormat.Format32bppArgb, pObject); hObject.Free(); return bitmap; } private Bitmap RGB565ToBitmap() { //stride = m_Width * 2 + m_Width * 2 % 4 //所以m_Width * 2不为4的倍数时,需要在每行补上相应的像素 byte[] buff; var padding = m_Width * 2 % 4; var stride = m_Width * 2 + padding; if (padding != 0) { buff = new byte[stride * m_Height]; for (int i = 0; i < m_Height; i++) { Buffer.BlockCopy(image_data, i * m_Width * 2, buff, i * stride, m_Width * 2); } } else { buff = image_data; } var hObject = GCHandle.Alloc(buff, GCHandleType.Pinned); var pObject = hObject.AddrOfPinnedObject(); var bitmap = new Bitmap(m_Width, m_Height, stride, PixelFormat.Format16bppRgb565, pObject); hObject.Free(); return bitmap; } private Bitmap PVRToBitmap(byte[] pvrdata) { var bitmap = new Bitmap(m_Width, m_Height); var rect = new Rectangle(0, 0, m_Width, m_Height); var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); var len = Math.Abs(bmd.Stride) * bmd.Height; if (!DecompressPVR(pvrdata, bmd.Scan0, len)) { bitmap.UnlockBits(bmd); bitmap.Dispose(); return null; } bitmap.UnlockBits(bmd); return bitmap; } private Bitmap TextureConverter() { var bitmap = new Bitmap(m_Width, m_Height); var rect = new Rectangle(0, 0, m_Width, m_Height); var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); var len = Math.Abs(bmd.Stride) * bmd.Height; var fixAlpha = glBaseInternalFormat == KTXHeader.GL_RED || glBaseInternalFormat == KTXHeader.GL_RG; if (!Ponvert(image_data, bmd.Scan0, m_Width, m_Height, image_data_size, (int)q_format, len, fixAlpha)) { bitmap.UnlockBits(bmd); bitmap.Dispose(); return null; } bitmap.UnlockBits(bmd); return bitmap; } private void DecompressCRN() { IntPtr uncompressedData; int uncompressedSize; bool 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 = DecompressUnityCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize); } else { result = DecompressCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize); } if (result) { var uncompressedBytes = new byte[uncompressedSize]; Marshal.Copy(uncompressedData, uncompressedBytes, 0, uncompressedSize); Marshal.FreeHGlobal(uncompressedData); image_data = uncompressedBytes; image_data_size = uncompressedSize; } } private Bitmap Texgenpack() { var bitmap = new Bitmap(m_Width, m_Height); var rect = new Rectangle(0, 0, m_Width, m_Height); var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Decode((int)texturetype, image_data, m_Width, m_Height, bmd.Scan0); bitmap.UnlockBits(bmd); return bitmap; } } public static class KTXHeader { public static byte[] IDENTIFIER = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; public static byte[] ENDIANESS_LE = { 1, 2, 3, 4 }; // constants for glInternalFormat public static int GL_ETC1_RGB8_OES = 0x8D64; public static int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; public static int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; public static int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; public static int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; public static int GL_ATC_RGB_AMD = 0x8C92; public static int GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE; public static int GL_COMPRESSED_RGB8_ETC2 = 0x9274; public static int GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276; public static int GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278; public static int GL_COMPRESSED_R11_EAC = 0x9270; public static int GL_COMPRESSED_SIGNED_R11_EAC = 0x9271; public static int GL_COMPRESSED_RG11_EAC = 0x9272; public static int GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273; public static int GL_COMPRESSED_RED_RGTC1 = 0x8DBB; public static int GL_COMPRESSED_RG_RGTC2 = 0x8DBD; public static int GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F; public static int GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C; public static int GL_R16F = 0x822D; public static int GL_RG16F = 0x822F; public static int GL_RGBA16F = 0x881A; public static int GL_R32F = 0x822E; public static int GL_RG32F = 0x8230; public static int GL_RGBA32F = 0x8814; // constants for glBaseInternalFormat public static int GL_RED = 0x1903; public static int GL_RGB = 0x1907; public static int GL_RGBA = 0x1908; public static int GL_RG = 0x8227; } //from TextureConverter.h public enum QFORMAT { // General formats Q_FORMAT_RGBA_8UI = 1, Q_FORMAT_RGBA_8I, Q_FORMAT_RGB5_A1UI, Q_FORMAT_RGBA_4444, Q_FORMAT_RGBA_16UI, Q_FORMAT_RGBA_16I, Q_FORMAT_RGBA_32UI, Q_FORMAT_RGBA_32I, Q_FORMAT_PALETTE_8_RGBA_8888, Q_FORMAT_PALETTE_8_RGBA_5551, Q_FORMAT_PALETTE_8_RGBA_4444, Q_FORMAT_PALETTE_4_RGBA_8888, Q_FORMAT_PALETTE_4_RGBA_5551, Q_FORMAT_PALETTE_4_RGBA_4444, Q_FORMAT_PALETTE_1_RGBA_8888, Q_FORMAT_PALETTE_8_RGB_888, Q_FORMAT_PALETTE_8_RGB_565, Q_FORMAT_PALETTE_4_RGB_888, Q_FORMAT_PALETTE_4_RGB_565, Q_FORMAT_R2_GBA10UI, Q_FORMAT_RGB10_A2UI, Q_FORMAT_RGB10_A2I, Q_FORMAT_RGBA_F, Q_FORMAT_RGBA_HF, Q_FORMAT_RGB9_E5, // Last five bits are exponent bits (Read following section in GLES3 spec: "3.8.17 Shared Exponent Texture Color Conversion") Q_FORMAT_RGB_8UI, Q_FORMAT_RGB_8I, Q_FORMAT_RGB_565, Q_FORMAT_RGB_16UI, Q_FORMAT_RGB_16I, Q_FORMAT_RGB_32UI, Q_FORMAT_RGB_32I, Q_FORMAT_RGB_F, Q_FORMAT_RGB_HF, Q_FORMAT_RGB_11_11_10_F, Q_FORMAT_RG_F, Q_FORMAT_RG_HF, Q_FORMAT_RG_32UI, Q_FORMAT_RG_32I, Q_FORMAT_RG_16I, Q_FORMAT_RG_16UI, Q_FORMAT_RG_8I, Q_FORMAT_RG_8UI, Q_FORMAT_RG_S88, Q_FORMAT_R_32UI, Q_FORMAT_R_32I, Q_FORMAT_R_F, Q_FORMAT_R_16F, Q_FORMAT_R_16I, Q_FORMAT_R_16UI, Q_FORMAT_R_8I, Q_FORMAT_R_8UI, Q_FORMAT_LUMINANCE_ALPHA_88, Q_FORMAT_LUMINANCE_8, Q_FORMAT_ALPHA_8, Q_FORMAT_LUMINANCE_ALPHA_F, Q_FORMAT_LUMINANCE_F, Q_FORMAT_ALPHA_F, Q_FORMAT_LUMINANCE_ALPHA_HF, Q_FORMAT_LUMINANCE_HF, Q_FORMAT_ALPHA_HF, Q_FORMAT_DEPTH_16, Q_FORMAT_DEPTH_24, Q_FORMAT_DEPTH_24_STENCIL_8, Q_FORMAT_DEPTH_32, Q_FORMAT_BGR_565, Q_FORMAT_BGRA_8888, Q_FORMAT_BGRA_5551, Q_FORMAT_BGRX_8888, Q_FORMAT_BGRA_4444, // Compressed formats Q_FORMAT_ATITC_RGBA, Q_FORMAT_ATC_RGBA_EXPLICIT_ALPHA = Q_FORMAT_ATITC_RGBA, Q_FORMAT_ATITC_RGB, Q_FORMAT_ATC_RGB = Q_FORMAT_ATITC_RGB, Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA, Q_FORMAT_ETC1_RGB8, Q_FORMAT_3DC_X, Q_FORMAT_3DC_XY, Q_FORMAT_ETC2_RGB8, Q_FORMAT_ETC2_RGBA8, Q_FORMAT_ETC2_RGB8_PUNCHTHROUGH_ALPHA1, Q_FORMAT_ETC2_SRGB8, Q_FORMAT_ETC2_SRGB8_ALPHA8, Q_FORMAT_ETC2_SRGB8_PUNCHTHROUGH_ALPHA1, Q_FORMAT_EAC_R_SIGNED, Q_FORMAT_EAC_R_UNSIGNED, Q_FORMAT_EAC_RG_SIGNED, Q_FORMAT_EAC_RG_UNSIGNED, Q_FORMAT_S3TC_DXT1_RGB, Q_FORMAT_S3TC_DXT1_RGBA, Q_FORMAT_S3TC_DXT3_RGBA, Q_FORMAT_S3TC_DXT5_RGBA, // YUV formats Q_FORMAT_AYUV_32, Q_FORMAT_I444_24, Q_FORMAT_YUYV_16, Q_FORMAT_UYVY_16, Q_FORMAT_I420_12, Q_FORMAT_YV12_12, Q_FORMAT_NV21_12, Q_FORMAT_NV12_12, // ASTC Format Q_FORMAT_ASTC_8, Q_FORMAT_ASTC_16, }; public enum texgenpack_texturetype { RGTC1, RGTC2, BPTC_FLOAT, BPTC } }