/// <summary> /// Loads a DDS texture from an input stream. /// </summary> public static Texture2DInfo LoadDDSTexture(Stream inputStream, bool flipVertically = false) { using (var reader = new UnityBinaryReader(inputStream)) { // Check the magic string. var magicString = reader.ReadBytes(4); if (!StringUtils.Equals(magicString, "DDS ")) { throw new FileFormatException("Invalid DDS file magic string: \"" + System.Text.Encoding.ASCII.GetString(magicString) + "\"."); } // Deserialize the DDS file header. var header = new DDSHeader(); header.Deserialize(reader); // Figure out the texture format and load the texture data. bool hasMipmaps; uint DDSMipmapLevelCount; TextureFormat textureFormat; int bytesPerPixel; byte[] textureData; ExtractDDSTextureFormatAndData(header, reader, out hasMipmaps, out DDSMipmapLevelCount, out textureFormat, out bytesPerPixel, out textureData); // Post-process the texture to generate missing mipmaps and possibly flip it vertically. PostProcessDDSTexture((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel, hasMipmaps, (int)DDSMipmapLevelCount, textureData, flipVertically); return(new Texture2DInfo((int)header.dwWidth, (int)header.dwHeight, textureFormat, hasMipmaps, textureData)); } }
/// <summary> /// Extracts a DDS file's texture format and pixel data. /// </summary> private static void ExtractDDSTextureFormatAndData(DDSHeader header, UnityBinaryReader reader, out bool hasMipmaps, out uint DDSMipmapLevelCount, out TextureFormat textureFormat, out int bytesPerPixel, out byte[] textureData) { hasMipmaps = Utils.ContainsBitFlags(header.dwCaps, (uint)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(header.pixelFormat.flags, (uint)DDSPixelFormatFlags.RGB)) { // some permutation of RGB if (!Utils.ContainsBitFlags(header.pixelFormat.flags, (uint)DDSPixelFormatFlags.AlphaPixels)) { throw new NotImplementedException("Unsupported DDS file pixel format."); } // some permutation of RGBA else { // There should be 32 bits per pixel. if (header.pixelFormat.RGBBitCount != 32) { throw new FileFormatException("Invalid DDS file pixel format."); } // BGRA32 if ((header.pixelFormat.BBitMask == 0x000000FF) && (header.pixelFormat.GBitMask == 0x0000FF00) && (header.pixelFormat.RBitMask == 0x00FF0000) && (header.pixelFormat.ABitMask == 0xFF000000)) { textureFormat = TextureFormat.BGRA32; bytesPerPixel = 4; } // ARGB32 else if ((header.pixelFormat.ABitMask == 0x000000FF) && (header.pixelFormat.RBitMask == 0x0000FF00) && (header.pixelFormat.GBitMask == 0x00FF0000) && (header.pixelFormat.BBitMask == 0xFF000000)) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } if (!hasMipmaps) { textureData = new byte[header.dwPitchOrLinearSize * header.dwHeight]; } else { // Create a data buffer to hold all mipmap levels down to 1x1. textureData = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel)]; } reader.ReadRestOfBytes(textureData, 0); } } else if (StringUtils.Equals(header.pixelFormat.fourCC, "DXT1")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT1ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else if (StringUtils.Equals(header.pixelFormat.fourCC, "DXT3")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT3ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else if (StringUtils.Equals(header.pixelFormat.fourCC, "DXT5")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT5ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } }
/// <summary> /// Extracts a DDS file's texture format and pixel data. /// </summary> private static void ExtractDDSTextureFormatAndData(DDSHeader header, UnityBinaryReader reader, out bool hasMipmaps, out uint DDSMipmapLevelCount, out TextureFormat textureFormat, out int bytesPerPixel, out byte[] textureData) { hasMipmaps = Utils.ContainsBitFlags(header.dwCaps, (uint)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(header.pixelFormat.flags, (uint)DDSPixelFormatFlags.RGB)) { // some permutation of RGB if(!Utils.ContainsBitFlags(header.pixelFormat.flags, (uint)DDSPixelFormatFlags.AlphaPixels)) { throw new NotImplementedException("Unsupported DDS file pixel format."); } // some permutation of RGBA else { // There should be 32 bits per pixel. if(header.pixelFormat.RGBBitCount != 32) { throw new FileFormatException("Invalid DDS file pixel format."); } // BGRA32 if((header.pixelFormat.BBitMask == 0x000000FF) && (header.pixelFormat.GBitMask == 0x0000FF00) && (header.pixelFormat.RBitMask == 0x00FF0000) && (header.pixelFormat.ABitMask == 0xFF000000)) { textureFormat = TextureFormat.BGRA32; bytesPerPixel = 4; } // ARGB32 else if((header.pixelFormat.ABitMask == 0x000000FF) && (header.pixelFormat.RBitMask == 0x0000FF00) && (header.pixelFormat.GBitMask == 0x00FF0000) && (header.pixelFormat.BBitMask == 0xFF000000)) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } if(!hasMipmaps) { textureData = new byte[header.dwPitchOrLinearSize * header.dwHeight]; } else { // Create a data buffer to hold all mipmap levels down to 1x1. textureData = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel)]; } reader.ReadRestOfBytes(textureData, 0); } } else if(StringUtils.Equals(header.pixelFormat.fourCC, "DXT1")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT1ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else if(StringUtils.Equals(header.pixelFormat.fourCC, "DXT3")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT3ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else if(StringUtils.Equals(header.pixelFormat.fourCC, "DXT5")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT5ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } }
/// <summary> /// Loads a DDS texture from an input stream. /// </summary> public static Texture2DInfo LoadDDSTexture(Stream inputStream, bool flipVertically = false) { using(var reader = new UnityBinaryReader(inputStream)) { // Check the magic string. var magicString = reader.ReadBytes(4); if(!StringUtils.Equals(magicString, "DDS ")) { throw new FileFormatException("Invalid DDS file magic string: \"" + System.Text.Encoding.ASCII.GetString(magicString) + "\"."); } // Deserialize the DDS file header. var header = new DDSHeader(); header.Deserialize(reader); // Figure out the texture format and load the texture data. bool hasMipmaps; uint DDSMipmapLevelCount; TextureFormat textureFormat; int bytesPerPixel; byte[] textureData; ExtractDDSTextureFormatAndData(header, reader, out hasMipmaps, out DDSMipmapLevelCount, out textureFormat, out bytesPerPixel, out textureData); // Post-process the texture to generate missing mipmaps and possibly flip it vertically. PostProcessDDSTexture((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel, hasMipmaps, (int)DDSMipmapLevelCount, textureData, flipVertically); return new Texture2DInfo((int)header.dwWidth, (int)header.dwHeight, textureFormat, hasMipmaps, textureData); } }