/// <summary> /// Decodes DXT data to ARGB. /// </summary> private static byte[] DecodeDXTToARGB(int DXTVersion, byte[] compressedData, uint width, uint height, DDSPixelFormat pixelFormat, uint mipmapCount) { bool alphaFlag = Utils.ContainsBitFlags(pixelFormat.flags, (uint)DDSPixelFormatFlags.AlphaPixels); bool containsAlpha = alphaFlag || ((pixelFormat.RGBBitCount == 32) && (pixelFormat.ABitMask != 0)); var reader = new UnityBinaryReader(new MemoryStream(compressedData)); var argb = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)width, (int)height, 4)]; int mipMapWidth = (int)width; int mipMapHeight = (int)height; int baseARGBIndex = 0; for (int mipMapIndex = 0; mipMapIndex < mipmapCount; mipMapIndex++) { for (int rowIndex = 0; rowIndex < mipMapHeight; rowIndex += 4) { for (int columnIndex = 0; columnIndex < mipMapWidth; columnIndex += 4) { Color32[] colors = null; // Doing a switch instead of using a delegate for speed. switch (DXTVersion) { case 1: colors = DecodeDXT1TexelBlock(reader, containsAlpha); break; case 3: colors = DecodeDXT3TexelBlock(reader); break; case 5: colors = DecodeDXT5TexelBlock(reader); break; default: throw new NotImplementedException("Tried decoding a DDS file using an unsupported DXT format: DXT" + DXTVersion.ToString()); } CopyDecodedTexelBlock(colors, argb, baseARGBIndex, rowIndex, columnIndex, mipMapWidth, mipMapHeight); } } baseARGBIndex += (mipMapWidth * mipMapHeight * 4); mipMapWidth /= 2; mipMapHeight /= 2; } return(argb); }
/// <summary> /// Decodes DXT data to ARGB. /// </summary> static byte[] DecodeDXTToARGB(int DXTVersion, byte[] compressedData, uint width, uint height, DDSPixelFormat pixelFormat, uint mipmapCount) { var alphaFlag = Utils.ContainsBitFlags((int)pixelFormat.dwFlags, (int)DDSPixelFormats.AlphaPixels); var containsAlpha = alphaFlag || (pixelFormat.dwRGBBitCount == 32 && pixelFormat.dwABitMask != 0); using (var r = new BinaryFileReader(new MemoryStream(compressedData))) { var argb = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)width, (int)height, 4)]; var mipMapWidth = (int)width; var mipMapHeight = (int)height; var baseARGBIndex = 0; for (var mipMapIndex = 0; mipMapIndex < mipmapCount; mipMapIndex++) { for (var rowIndex = 0; rowIndex < mipMapHeight; rowIndex += 4) { for (var columnIndex = 0; columnIndex < mipMapWidth; columnIndex += 4) { if (r.Position == r.BaseStream.Length) { return(argb); } Color32[] colors = null; switch (DXTVersion) // Doing a switch instead of using a delegate for speed. { case 1: colors = DecodeDXT1TexelBlock(r, containsAlpha); break; case 3: colors = DecodeDXT3TexelBlock(r); break; case 5: colors = DecodeDXT5TexelBlock(r); break; default: throw new NotImplementedException($"Tried decoding a DDS file using an unsupported DXT format: DXT {DXTVersion}"); } CopyDecodedTexelBlock(colors, argb, baseARGBIndex, rowIndex, columnIndex, mipMapWidth, mipMapHeight); } } baseARGBIndex += mipMapWidth * mipMapHeight * 4; mipMapWidth /= 2; mipMapHeight /= 2; } return(argb); } }
/// <summary> /// Extracts a DDS file's texture format and pixel data. /// </summary> static void ExtractDDSTextureFormatAndData(DDSHeader header, GenericReader r, out bool hasMipmaps, out uint DDSMipmapLevelCount, out TextureFormat textureFormat, out int bytesPerPixel, out byte[] textureData) { hasMipmaps = Utils.ContainsBitFlags((int)header.dwCaps, (int)DDSCaps.Mipmap); // Non-mipmapped textures still have one mipmap level: the texture itself. DDSMipmapLevelCount = hasMipmaps ? header.dwMipMapCount : 1; // If the DDS file contains uncompressed data. if (Utils.ContainsBitFlags((int)header.ddspf.dwFlags, (int)DDSPixelFormats.RGB)) { // some permutation of RGB if (!Utils.ContainsBitFlags((int)header.ddspf.dwFlags, (int)DDSPixelFormats.AlphaPixels)) { throw new NotImplementedException("Unsupported DDS file pixel format."); } // some permutation of RGBA else { // There should be 32 bits per pixel. if (header.ddspf.dwRGBBitCount != 32) { throw new FileFormatException("Invalid DDS file pixel format."); } // BGRA32 if (header.ddspf.dwBBitMask == 0x000000FF && header.ddspf.dwGBitMask == 0x0000FF00 && header.ddspf.dwRBitMask == 0x00FF0000 && header.ddspf.dwABitMask == 0xFF000000) { textureFormat = TextureFormat.BGRA32; bytesPerPixel = 4; } // ARGB32 else if (header.ddspf.dwABitMask == 0x000000FF && header.ddspf.dwRBitMask == 0x0000FF00 && header.ddspf.dwGBitMask == 0x00FF0000 && header.ddspf.dwBBitMask == 0xFF000000) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } if (!hasMipmaps) { textureData = new byte[header.dwPitchOrLinearSize * header.dwHeight]; } // Create a data buffer to hold all mipmap levels down to 1x1. else { textureData = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel)]; } r.ReadRestOfBytes(textureData, 0); } } else if ("DXT1".EqualsASCIIBytes(header.ddspf.dwFourCC)) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = r.ReadRestOfBytes(); textureData = DecodeDXT1ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.ddspf, DDSMipmapLevelCount); } else if ("DXT3".EqualsASCIIBytes(header.ddspf.dwFourCC)) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = r.ReadRestOfBytes(); textureData = DecodeDXT3ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.ddspf, DDSMipmapLevelCount); } else if ("DXT5".EqualsASCIIBytes(header.ddspf.dwFourCC)) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = r.ReadRestOfBytes(); textureData = DecodeDXT5ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.ddspf, DDSMipmapLevelCount); } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } }