/// <summary> /// Loads a DDS texture from an input stream. /// </summary> public static Texture2DInfo LoadDDSTexture(Stream inputStream, bool flipVertically = false) { using (var r = new UnityBinaryReader(inputStream)) { // Check the magic string. var magicString = r.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.Read(r); // Figure out the texture format and load the texture data. ExtractDDSTextureFormatAndData(header, r, out bool hasMipmaps, out uint ddsMipmapLevelCount, out TextureFormat textureFormat, out int bytesPerPixel, out byte[] 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> static void ExtractDDSTextureFormatAndData(DDSHeader header, UnityBinaryReader 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 (StringUtils.Equals(header.ddspf.dwFourCC, "DXT1")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = r.ReadRestOfBytes(); textureData = DecodeDXT1ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.ddspf, DDSMipmapLevelCount); } else if (StringUtils.Equals(header.ddspf.dwFourCC, "DXT3")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = r.ReadRestOfBytes(); textureData = DecodeDXT3ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.ddspf, DDSMipmapLevelCount); } else if (StringUtils.Equals(header.ddspf.dwFourCC, "DXT5")) { 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."); } }