public void Load(Stream s) { Header = new DDS_Header(); Header.magic = ReadUInt(s); Header.dwSize = ReadUInt(s); Header.dwFlags = ReadUInt(s); Header.dwHeight = ReadUInt(s); Header.dwWidth = ReadUInt(s); Header.dwPitchOrLinearSize = ReadUInt(s); Header.dwDepth = ReadUInt(s); Header.dwMipMapCount = ReadUInt(s); Header.dwReserved1 = new uint[11]; for (int i = 0; i < 11; i++) { Header.dwReserved1[i] = ReadUInt(s); } Header.ddspf = ReadPixelFormat(s); Header.dwCaps = ReadUInt(s); Header.dwCaps2 = ReadUInt(s); Header.dwCaps3 = ReadUInt(s); Header.dwCaps4 = ReadUInt(s); Header.dwReserved2 = ReadUInt(s); if (Header.ddspf.dwFourCC == 0x30315844) { Header.extension = new DDS_HEADER_DXT10(); Header.extension.dxgiFormat = (DXGI_FORMAT)ReadUInt(s); Header.extension.resourceDimension = (D3D10_RESOURCE_DIMENSION)ReadUInt(s); Header.extension.miscFlag = ReadUInt(s); Header.extension.arraySize = ReadUInt(s); Header.extension.miscFlags2 = (ALPHA_MODE)ReadUInt(s); } }
public static TextureInfo GetTextureInfo(Texture texture) { // Load DDS header so we can extract some info from it var ddsHeader = new DDS_Header(new MemoryStream(texture.Data)); TexturePixelFormat format; switch (ddsHeader.Format) { case ImageEngineFormat.DDS_DXT1: format = TexturePixelFormat.DXT1; break; case ImageEngineFormat.DDS_DXT3: format = TexturePixelFormat.DXT3; break; case ImageEngineFormat.DDS_DXT5: format = TexturePixelFormat.DXT5; break; case ImageEngineFormat.DDS_ARGB_8: format = TexturePixelFormat.ARGB; break; default: throw new ArgumentOutOfRangeException(); } return(new TextureInfo(texture, ddsHeader.Width, ddsHeader.Height, ddsHeader.dwMipMapCount, format)); }
public static byte[] DecodeToDDS(FieldTexturePS3 texture) { var surfaceFormat = ImageEngineFormat.DDS_DXT1; if (texture.Flags.HasFlag(FieldTextureFlags.DXT3)) { surfaceFormat = ImageEngineFormat.DDS_DXT3; } else if (texture.Flags.HasFlag(FieldTextureFlags.DXT5)) { surfaceFormat = ImageEngineFormat.DDS_DXT5; } var ddsBytes = new byte[0x80 + texture.DataLength]; // create & write header var ddsHeader = new DDS_Header(texture.MipMapCount, texture.Height, texture.Width, surfaceFormat); ddsHeader.WriteToArray(ddsBytes, 0); // write pixel data Array.Copy(texture.Data, 0, ddsBytes, 0x80, texture.DataLength); return(ddsBytes); }
/// <summary> /// Determines image type via headers. /// Keeps stream position. /// </summary> /// <param name="imgData">Image data, incl header.</param> /// <returns>Type of image.</returns> public static SupportedExtensions DetermineImageType(Stream imgData) { SupportedExtensions ext = SupportedExtensions.UNKNOWN; // KFreon: Save position and go back to start long originalPos = imgData.Position; imgData.Seek(0, SeekOrigin.Begin); var bits = new byte[8]; imgData.Read(bits, 0, 8); // BMP if (BMP_Header.CheckIdentifier(bits)) { ext = SupportedExtensions.BMP; } // PNG if (PNG_Header.CheckIdentifier(bits)) { ext = SupportedExtensions.PNG; } // JPG if (JPG_Header.CheckIdentifier(bits)) { ext = SupportedExtensions.JPG; } // DDS if (DDS_Header.CheckIdentifier(bits)) { ext = SupportedExtensions.DDS; } // GIF if (GIF_Header.CheckIdentifier(bits)) { ext = SupportedExtensions.GIF; } if (TIFF_Header.CheckIdentifier(bits)) { ext = SupportedExtensions.TIF; } // TGA (assumed if no other matches if (ext == SupportedExtensions.UNKNOWN) { ext = SupportedExtensions.TGA; } // KFreon: Reset stream position imgData.Seek(originalPos, SeekOrigin.Begin); return(ext); }
protected override void _Read(BinaryReader reader) { long baseOffset = reader.BaseStream.Position; byte[] buffer = reader.ReadBytes((int)reader.BaseStream.Length); MemoryStream memoryStream = new MemoryStream(buffer, 0, buffer.Length, true, true); DDS_Header header = new DDS_Header(memoryStream); FormatDetails = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat); MipMaps = DDSGeneral.LoadDDS(memoryStream, header, 0, FormatDetails); Width = header.Width; Height = header.Height; memoryStream.Close(); }
internal static AbstractHeader LoadHeader(Stream stream) { stream.Seek(0, SeekOrigin.Begin); // Determine type of image ImageFormats.SupportedExtensions ext = ImageFormats.DetermineImageType(stream); // Parse header AbstractHeader header = null; switch (ext) { case ImageFormats.SupportedExtensions.BMP: header = new BMP_Header(stream); break; case ImageFormats.SupportedExtensions.DDS: header = new DDS_Header(stream); break; case ImageFormats.SupportedExtensions.JPG: header = new JPG_Header(stream); break; case ImageFormats.SupportedExtensions.PNG: header = new PNG_Header(stream); break; case ImageFormats.SupportedExtensions.TGA: header = new TGA_Header(stream); break; case ImageFormats.SupportedExtensions.GIF: header = new GIF_Header(stream); break; case ImageFormats.SupportedExtensions.TIF: header = new TIFF_Header(stream); break; default: throw new NotSupportedException("Image type unknown."); } return(header); }
public static byte[] DecodeToDDS(GNFTexture texture) { var surfaceFormat = ImageEngineFormat.DDS_DXT1; if (texture.PixelFormat == 0x50) { surfaceFormat = ImageEngineFormat.DDS_DXT3; } var ddsBytes = new byte[0x80 + texture.Data.Length]; // create & write header var ddsHeader = new DDS_Header(1, texture.Height, texture.Width, surfaceFormat); ddsHeader.WriteToArray(ddsBytes, 0); // write pixel data Array.Copy(texture.Data, 0, ddsBytes, 0x80, texture.Data.Length); return(ddsBytes); }
internal static byte[] Save(List <MipMap> mipMaps, ImageFormats.ImageEngineFormatDetails destFormatDetails, AlphaSettings alphaSetting) { // Set compressor for Block Compressed textures Action <byte[], int, int, byte[], int, AlphaSettings, ImageFormats.ImageEngineFormatDetails> compressor = destFormatDetails.BlockEncoder; bool needCheckSize = destFormatDetails.IsBlockCompressed; int height = mipMaps[0].Height; int width = mipMaps[0].Width; if (needCheckSize && !CheckSize_DXT(width, height)) { throw new InvalidOperationException($"DXT compression formats require dimensions to be multiples of 4. Got: {width}x{height}."); } // Create header and write to destination DDS_Header header = new DDS_Header(mipMaps.Count, height, width, destFormatDetails.Format, destFormatDetails.DX10Format); int headerLength = destFormatDetails.HeaderSize; int fullSize = GetCompressedSizeOfImage(mipMaps.Count, destFormatDetails, width, height); /*if (destFormatDetails.ComponentSize != 1) * fullSize += (fullSize - headerLength) * destFormatDetails.ComponentSize;*/// Size adjustment for destination to allow for different component sizes. byte[] destination = new byte[fullSize]; header.WriteToArray(destination, 0); int blockSize = destFormatDetails.BlockSize; if (destFormatDetails.IsBlockCompressed) { int mipOffset = headerLength; foreach (MipMap mipmap in mipMaps) { if (ImageEngine.IsCancellationRequested) { break; } var temp = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting); if (temp != -1) // When dimensions too low. { mipOffset = temp; } } } else { // UNCOMPRESSED var action = new Action <int>(mipIndex => { if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // Remove alpha by setting AMask = 0 var ddspf = header.ddspf; ddspf.dwABitMask = 0; header.ddspf = ddspf; } // Get MipOffset int offset = GetMipOffset(mipIndex, destFormatDetails, width, height); WriteUncompressedMipMap(destination, offset, mipMaps[mipIndex], destFormatDetails, header.ddspf); }); if (ImageEngine.EnableThreading) { Parallel.For(0, mipMaps.Count, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, (mip, loopState) => { if (ImageEngine.IsCancellationRequested) { loopState.Stop(); } action(mip); }); } else { for (int i = 0; i < mipMaps.Count; i++) { if (ImageEngine.IsCancellationRequested) { break; } action(i); } } } return(ImageEngine.IsCancellationRequested ? null : destination); }
internal static List <MipMap> LoadDDS(MemoryStream compressed, DDS_Header header, int desiredMaxDimension, ImageFormats.ImageEngineFormatDetails formatDetails) { MipMap[] MipMaps = null; int mipWidth = header.Width; int mipHeight = header.Height; ImageEngineFormat format = header.Format; int estimatedMips = header.dwMipMapCount; int mipOffset = formatDetails.HeaderSize; int originalOffset = mipOffset; if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, 4, formatDetails, out mipOffset)) // Update number of mips too { estimatedMips = 1; } if (estimatedMips == 0) { estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight); } mipOffset = originalOffset; // Needs resetting after checking there's mips in this image. // Ensure there's at least 1 mipmap if (estimatedMips == 0) { estimatedMips = 1; } int orig_estimatedMips = estimatedMips; // Store original count for later (uncompressed only I think) // KFreon: Decide which mip to start loading at - going to just load a few mipmaps if asked instead of loading all, then choosing later. That's slow. if (desiredMaxDimension != 0 && estimatedMips > 1) { if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, desiredMaxDimension, formatDetails, out mipOffset)) // Update number of mips too { throw new InvalidDataException($"Requested mipmap does not exist in this image. Top Image Size: {mipWidth}x{mipHeight}, requested mip max dimension: {desiredMaxDimension}."); } // Not the first mipmap. if (mipOffset > formatDetails.HeaderSize) { double divisor = mipHeight > mipWidth ? mipHeight / desiredMaxDimension : mipWidth / desiredMaxDimension; mipHeight = (int)(mipHeight / divisor); mipWidth = (int)(mipWidth / divisor); if (mipWidth == 0 || mipHeight == 0) // Reset as a dimension is too small to resize { mipHeight = header.Height; mipWidth = header.Width; mipOffset = formatDetails.HeaderSize; } else { // Update estimated mips due to changing dimensions. estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight); } } else // The first mipmap { mipOffset = formatDetails.HeaderSize; } } // Move to requested mipmap compressed.Position = mipOffset; // Block Compressed texture chooser. Action <byte[], int, byte[], int, int, bool> DecompressBCBlock = formatDetails.BlockDecoder; MipMaps = new MipMap[estimatedMips]; int blockSize = formatDetails.BlockSize; // KFreon: Read mipmaps if (formatDetails.IsBlockCompressed) // Threading done in the decompression, not here. { for (int m = 0; m < estimatedMips; m++) { if (ImageEngine.IsCancellationRequested) { break; } // KFreon: If mip is too small, skip out. This happens most often with non-square textures. I think it's because the last mipmap is square instead of the same aspect. // Don't do the mip size check here (<4) since we still need to have a MipMap object for those lower than this for an accurate count. if (mipWidth <= 0 || mipHeight <= 0) // Needed cos it doesn't throw when reading past the end for some reason. { break; } MipMap mipmap = ReadCompressedMipMap(compressed, mipWidth, mipHeight, mipOffset, formatDetails, DecompressBCBlock); MipMaps[m] = mipmap; mipOffset += (int)(mipWidth * mipHeight * (blockSize / 16d)); // Also put the division in brackets cos if the mip dimensions are high enough, the multiplications can exceed int.MaxValue) mipWidth /= 2; mipHeight /= 2; } } else { int startMip = orig_estimatedMips - estimatedMips; // UNCOMPRESSED - Can't really do threading in "decompression" so do it for the mipmaps. var action = new Action <int>(mipIndex => { // Calculate mipOffset and dimensions int offset, width, height; offset = GetMipOffset(mipIndex, formatDetails, header.Width, header.Height); double divisor = mipIndex == 0 ? 1d : 2 << (mipIndex - 1); // Divisor represents 2^mipIndex - Math.Pow seems very slow. width = (int)(header.Width / divisor); height = (int)(header.Height / divisor); MipMap mipmap = null; try { mipmap = ReadUncompressedMipMap(compressed, offset, width, height, header.ddspf, formatDetails); } catch (Exception e) { Debug.WriteLine(e.ToString()); } MipMaps[mipIndex] = mipmap; }); if (ImageEngine.EnableThreading) { Parallel.For(startMip, orig_estimatedMips, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, (mip, loopState) => { if (ImageEngine.IsCancellationRequested) { loopState.Stop(); } action(mip); }); } else { for (int i = startMip; i < orig_estimatedMips; i++) { if (ImageEngine.IsCancellationRequested) { break; } action(i); } } } List <MipMap> mips = new List <MipMap>(MipMaps.Where(t => t != null)); if (mips.Count == 0) { throw new InvalidOperationException($"No mipmaps loaded. Estimated mips: {estimatedMips}, mip dimensions: {mipWidth}x{mipHeight}"); } return(mips); }
public static SBSurface Import(string FileName) { SBSurface surface = new SBSurface(); using (BinaryReaderExt reader = new BinaryReaderExt(new FileStream(FileName, FileMode.Open))) { DDS_Header header = new DDS_Header(); header.Read(reader); surface.Name = Path.GetFileNameWithoutExtension(FileName); surface.Width = header.dwWidth; surface.Height = header.dwHeight; if (header.dwFlags.HasFlag(DDSD.DEPTH)) { surface.Depth = header.dwDepth; } else { surface.Depth = 1; } if (header.ddspf.dwFourCC == 0x31545844) { surface.InternalFormat = InternalFormat.CompressedRgbaS3tcDxt1Ext; } else if (header.ddspf.dwFourCC == 0x30315844) { surface.InternalFormat = DXGItoInternal(header.DXT10Header.dxgiFormat); if (surface.InternalFormat == 0) { System.Windows.Forms.MessageBox.Show("DDS format not supported " + header.DXT10Header.dxgiFormat); return(null); } } else { if (((FourCC_DXGI)header.ddspf.dwFourCC) == FourCC_DXGI.D3DFMT_A32B32G32R32F) { surface.InternalFormat = InternalFormat.Rgba32f; surface.PixelFormat = PixelFormat.Rgba; surface.PixelType = PixelType.Float; } else { System.Windows.Forms.MessageBox.Show("DDS format not supported " + header.ddspf.dwFourCC.ToString("X")); return(null); } } // TODO: read other mips int w = surface.Width; int h = surface.Height; //SBConsole.WriteLine(header.dwCaps.ToString() + " " + header.dwCaps2.ToString() + " " + header.dwFlags.ToString()); for (int array = 0; array < (header.dwCaps2.HasFlag(DDSCAPS2.CUBEMAP_ALLFACES) ? 6 : 1); array++) { w = surface.Width; h = surface.Height; var mip = new MipArray(); for (int i = 0; i < (header.dwFlags.HasFlag(DDSD.MIPMAPCOUNT) ? header.dwMipMapCount : 1); i++) { var mipSize = Math.Max(1, ((w + 3) / 4)) * Math.Max(1, ((h + 3) / 4)) * (int)TextureFormatInfo.GetBPP(surface.InternalFormat); if (mipSize % TextureFormatInfo.GetBPP(surface.InternalFormat) != 0) { mipSize += (int)(TextureFormatInfo.GetBPP(surface.InternalFormat) - (mipSize % TextureFormatInfo.GetBPP(surface.InternalFormat))); } var data = reader.ReadBytes(mipSize); mip.Mipmaps.Add(data); w /= 2; h /= 2; } surface.Arrays.Add(mip); } if (reader.Position != reader.BaseStream.Length) { SBConsole.WriteLine("Warning: error reading dds " + reader.Position.ToString("X")); } } return(surface); }
public static void Export(string fileName, SBSurface surface) { if (!internalFormatToDXGI.ContainsKey(surface.InternalFormat) && !internalFormatToD3D.ContainsKey(surface.InternalFormat)) { System.Windows.Forms.MessageBox.Show("Unsupported DDS export format " + surface.InternalFormat.ToString()); return; } var Header = new DDS_Header() { dwFlags = (DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT | DDSD.LINEARSIZE), dwHeight = surface.Height, dwWidth = surface.Width, dwPitchOrLinearSize = GetPitchOrLinearSize(surface.InternalFormat, surface.Width), dwDepth = surface.Depth, dwMipMapCount = surface.Arrays[0].Mipmaps.Count, dwReserved1 = new uint[11], ddspf = new DDS_PIXELFORMAT() { }, dwCaps = 0, dwCaps2 = 0 }; if (surface.Arrays.Count > 0 && surface.Arrays[0].Mipmaps.Count > 1) { Header.dwFlags |= DDSD.MIPMAPCOUNT; } if (surface.IsCubeMap) { Header.dwCaps |= DDSCAPS.TEXTURE | DDSCAPS.COMPLEX; Header.dwCaps2 |= DDSCAPS2.CUBEMAP_ALLFACES; } //TODO: format Header.ddspf.dwFlags = DDPF.FOURCC; Header.ddspf.dwFourCC = 0x30315844; if (internalFormatToD3D.ContainsKey(surface.InternalFormat)) { Header.ddspf.dwFourCC = (int)internalFormatToD3D[surface.InternalFormat]; } if (internalFormatToDXGI.ContainsKey(surface.InternalFormat)) { Header.DXT10Header.dxgiFormat = internalFormatToDXGI[surface.InternalFormat]; Header.DXT10Header.resourceDimension = D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE2D; Header.DXT10Header.arraySize = (uint)surface.Arrays.Count; } using (BinaryWriterExt writer = new BinaryWriterExt(new FileStream(fileName, FileMode.Create))) { Header.Write(writer); foreach (var arr in surface.Arrays) { foreach (var mip in arr.Mipmaps) { writer.Write(mip); } } } }
public void Load(Stream s) { Header = new DDS_Header(); Header.magic = ReadUInt(s); Header.dwSize = ReadUInt(s); Header.dwFlags = ReadUInt(s); Header.dwHeight = ReadUInt(s); Header.dwWidth = ReadUInt(s); Header.dwPitchOrLinearSize = ReadUInt(s); Header.dwDepth = ReadUInt(s); Header.dwMipMapCount = ReadUInt(s); Header.dwReserved1 = new uint[11]; for (int i = 0; i < 11; i++) Header.dwReserved1[i] = ReadUInt(s); Header.ddspf = ReadPixelFormat(s); Header.dwCaps = ReadUInt(s); Header.dwCaps2 = ReadUInt(s); Header.dwCaps3 = ReadUInt(s); Header.dwCaps4 = ReadUInt(s); Header.dwReserved2 = ReadUInt(s); if (Header.ddspf.dwFourCC == 0x30315844) { Header.extension = new DDS_HEADER_DXT10(); Header.extension.dxgiFormat = (DXGI_FORMAT)ReadUInt(s); Header.extension.resourceDimension = (D3D10_RESOURCE_DIMENSION)ReadUInt(s); Header.extension.miscFlag = ReadUInt(s); Header.extension.arraySize = ReadUInt(s); Header.extension.miscFlags2 = (ALPHA_MODE)ReadUInt(s); } }
/// <summary> /// Internal read implementation of the sub classes. /// </summary> /// <param name="reader"></param> /// <exception cref="Exception">Expected DDS RGB24 or RGBA32 color format!</exception> /// <exception cref="NotImplementedException">TODO</exception> protected override void _Read(BinaryReader reader) { long baseOffset = reader.BaseStream.Position; int gbixOffset = 0; int pvrtOffset = 0; uint identifier = reader.ReadUInt32(); if (identifier == m_gbix) //"GBIX" { HasGlobalIndex = true; GlobalIndexSize = reader.ReadUInt32(); GlobalIndex = reader.ReadBytes((int)GlobalIndexSize); reader.BaseStream.Seek(4, SeekOrigin.Current); //Skip "PVRT" gbixOffset = 0x00; pvrtOffset = 0x08 + (int)GlobalIndexSize; } else { identifier = reader.ReadUInt32(); if (identifier == m_gbix) { HasGlobalIndex = true; GlobalIndexSize = reader.ReadUInt32(); GlobalIndex = reader.ReadBytes((int)GlobalIndexSize); gbixOffset = 0x04; pvrtOffset = 0x0C + (int)GlobalIndexSize; } else if (identifier == m_pvrt) { gbixOffset = -1; pvrtOffset = 0x04; } else { gbixOffset = -1; pvrtOffset = 0x00; reader.BaseStream.Seek(-4, SeekOrigin.Current); } } // Read information about the texture ContentSize = reader.ReadUInt32(); PixelFormat = (PvrPixelFormat)reader.ReadByte(); DataFormat = (PvrDataFormat)reader.ReadByte(); reader.BaseStream.Seek(2, SeekOrigin.Current); Width = reader.ReadUInt16(); Height = reader.ReadUInt16(); if (DataFormat == PvrDataFormat.DDS || DataFormat == PvrDataFormat.DDS_2) { if (!(PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24 || PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32)) { throw new Exception("Expected DDS RGB24 or RGBA32 color format!"); } long ddsOffset = reader.BaseStream.Position; DDS_Header header = new DDS_Header(reader.BaseStream); DDSFormatDetails format = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat); reader.BaseStream.Seek(ddsOffset, SeekOrigin.Begin); byte[] ddsBuffer = reader.ReadBytes(header.dwPitchOrLinearSize + header.dwSize + 128); MemoryStream memoryStream = new MemoryStream(ddsBuffer, 0, ddsBuffer.Length, true, true); MipMaps = DDSGeneral.LoadDDS(memoryStream, header, 0, format); memoryStream.Close(); Width = header.Width; Height = header.Height; } else { // Get the codecs and make sure we can decode using them PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat); DataCodec = PvrDataCodec.GetDataCodec(DataFormat); if (DataCodec != null && PixelCodec != null) { DataCodec.PixelCodec = PixelCodec; } // Set the number of palette entries int paletteEntries = DataCodec.PaletteEntries; if (DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL || DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL_MIPMAP) { if (Width <= 16) { paletteEntries = 64; // Actually 16 } else if (Width <= 32) { paletteEntries = 256; // Actually 64 } else if (Width <= 64) { paletteEntries = 512; // Actually 128 } else { paletteEntries = 1024; // Actually 256 } } // Set the palette and data offsets int paletteOffset = 0; int dataOffset = 0; if (paletteEntries == 0 || DataCodec.NeedsExternalPalette) { paletteOffset = -1; dataOffset = pvrtOffset + 0x10; } else { paletteOffset = pvrtOffset + 0x10; dataOffset = paletteOffset + (paletteEntries * (PixelCodec.Bpp >> 3)); } // Get the compression format and determine if we need to decompress this texture reader.BaseStream.Seek(baseOffset, SeekOrigin.Begin); uint first = reader.ReadUInt32(); reader.BaseStream.Seek(baseOffset + pvrtOffset + 4, SeekOrigin.Begin); uint second = reader.ReadUInt32(); if (first == second - pvrtOffset + dataOffset + 8) { CompressionFormat = PvrCompressionFormat.RLE; } else { CompressionFormat = PvrCompressionFormat.NONE; } CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat); if (CompressionFormat != PvrCompressionFormat.NONE && CompressionCodec != null) { //TODO: Convert to stream compatible code throw new NotImplementedException("TODO"); //m_encodedData = CompressionCodec.Decompress(m_encodedData, dataOffset, PixelCodec, DataCodec); // Now place the offsets in the appropiate area if (CompressionFormat == PvrCompressionFormat.RLE) { if (gbixOffset != -1) { gbixOffset -= 4; } pvrtOffset -= 4; if (paletteOffset != -1) { paletteOffset -= 4; } dataOffset -= 4; } } // If the texture contains mipmaps, gets the offsets of them int[] mipmapOffsets; if (DataCodec.HasMipmaps) { int mipmapOffset = 0; mipmapOffsets = new int[(int)Math.Log(Width, 2) + 1]; // Calculate the padding for the first mipmap offset if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { mipmapOffset = (DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x1 mipmap } else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT) { mipmapOffset = (3 * DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x2 mipmap } for (int i = mipmapOffsets.Length - 1, size = 1; i >= 0; i--, size <<= 1) { mipmapOffsets[i] = mipmapOffset; mipmapOffset += Math.Max((size * size * DataCodec.Bpp) >> 3, 1); } } else { mipmapOffsets = new int[1] { 0 }; } //DecodeMipmaps() if (paletteOffset != -1) // The texture contains an embedded palette { reader.BaseStream.Seek(baseOffset + paletteOffset, SeekOrigin.Begin); DataCodec.SetPalette(reader, paletteEntries); } MipMaps = new List <MipMap>(); if (DataCodec.HasMipmaps) { for (int i = 0, size = Width; i < mipmapOffsets.Length; i++, size >>= 1) { reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[i], SeekOrigin.Begin); byte[] pixels = DataCodec.Decode(reader, size, size, PixelCodec); MipMaps.Add(new MipMap(pixels, size, size)); } } else { reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[0], SeekOrigin.Begin); byte[] pixels = DataCodec.Decode(reader, Width, Height, PixelCodec); MipMaps.Add(new MipMap(pixels, Width, Height)); } } if (HasGlobalIndex) { reader.BaseStream.Seek(baseOffset + ContentSize + 0xC, SeekOrigin.Begin); } else { reader.BaseStream.Seek(baseOffset + ContentSize, SeekOrigin.Begin); } }
byte[] AttemptSaveUsingOriginalData(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension, int mipToSave, AlphaSettings alphaSetting) { int start = 0; int destStart = 0; int length = OriginalData.Length; int newWidth = Width; int newHeight = Height; DDS_Header tempHeader = null; byte[] data = null; byte[] tempOriginalData = OriginalData; if (destFormatDetails.IsDDS) { destStart = destFormatDetails.HeaderSize; start = destStart; int mipCount = 0; if (mipToSave != 0) { mipCount = 1; newWidth = MipMaps[mipToSave].Width; newHeight = MipMaps[mipToSave].Height; start = ImageFormats.GetCompressedSize(mipToSave, destFormatDetails, Width, Height); length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight); } else if (desiredMaxDimension != 0 && desiredMaxDimension < Width && desiredMaxDimension < Height) { int index = MipMaps.FindIndex(t => t.Width < desiredMaxDimension && t.Height < desiredMaxDimension); // If none found, do a proper save and see what happens. if (index == -1) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } mipCount -= index; newWidth = MipMaps[index].Width; newHeight = MipMaps[index].Height; start = ImageFormats.GetCompressedSize(index, destFormatDetails, Width, Height); length = ImageFormats.GetCompressedSize(mipCount, destFormatDetails, newWidth, newHeight); } else { if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // Can't edit alpha directly in premultiplied formats. Not easily anyway. if (destFormatDetails.IsPremultipliedFormat) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } // DDS Formats only switch (destFormatDetails.Format) { // Excluded cos they have no true alpha case ImageEngineFormat.DDS_A8: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_V8U8: case ImageEngineFormat.DDS_G16_R16: case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_R5G6B5: case ImageEngineFormat.DDS_RGB_8: case ImageEngineFormat.DDS_DXT1: break; // Exluded cos they're alpha isn't easily edited case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT4: break; // Excluded cos they're currently unsupported case ImageEngineFormat.DDS_CUSTOM: case ImageEngineFormat.DDS_DX10: case ImageEngineFormat.DDS_ARGB_4: break; case ImageEngineFormat.DDS_ABGR_8: case ImageEngineFormat.DDS_ARGB_32F: case ImageEngineFormat.DDS_ARGB_8: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT5: tempOriginalData = new byte[OriginalData.Length]; Array.Copy(OriginalData, tempOriginalData, OriginalData.Length); // Edit alpha values int alphaStart = 128; int alphaJump = 0; byte[] alphaBlock = null; if (destFormatDetails.IsBlockCompressed) { alphaJump = 16; alphaBlock = new byte[8]; for (int i = 0; i < 8; i++) { alphaBlock[i] = 255; } } else { alphaJump = destFormatDetails.ComponentSize * 4; alphaBlock = new byte[destFormatDetails.ComponentSize]; switch (destFormatDetails.ComponentSize) { case 1: alphaBlock[0] = 255; break; case 2: alphaBlock = BitConverter.GetBytes(ushort.MaxValue); break; case 4: alphaBlock = BitConverter.GetBytes(1f); break; } } for (int i = alphaStart; i < OriginalData.Length; i += alphaJump) { Array.Copy(alphaBlock, 0, tempOriginalData, i, alphaBlock.Length); } break; } } switch (GenerateMips) { case MipHandling.KeepExisting: mipCount = NumMipMaps; break; case MipHandling.Default: if (NumMipMaps > 1) { mipCount = NumMipMaps; } else { goto case MipHandling.GenerateNew; // Eww goto... } break; case MipHandling.GenerateNew: ImageEngine.DestroyMipMaps(MipMaps); ImageEngine.TestDDSMipSize(MipMaps, destFormatDetails, Width, Height, out double fixXScale, out double fixYScale, GenerateMips); // Wrong sizing, so can't use original data anyway. if (fixXScale != 0 || fixYScale != 0) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } mipCount = DDSGeneral.BuildMipMaps(MipMaps); // Compress mipmaps excl top byte[] formattedMips = DDSGeneral.Save(MipMaps.GetRange(1, MipMaps.Count - 1), destFormatDetails, alphaSetting); if (formattedMips == null) { return(null); } // Get top mip size and create destination array length = ImageFormats.GetCompressedSize(0, destFormatDetails, newWidth, newHeight); // Should be the length of the top mipmap. data = new byte[formattedMips.Length + length]; // Copy smaller mips to destination Array.Copy(formattedMips, destFormatDetails.HeaderSize, data, length, formattedMips.Length - destFormatDetails.HeaderSize); break; case MipHandling.KeepTopOnly: mipCount = 1; length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight); break; } } // Header tempHeader = new DDS_Header(mipCount, newHeight, newWidth, destFormatDetails.Format, destFormatDetails.DX10Format); } // Use existing array, otherwise create one. data = data ?? new byte[length]; Array.Copy(tempOriginalData, start, data, destStart, length - destStart); // Write header if existing (DDS Only) if (tempHeader != null) { tempHeader.WriteToArray(data, 0); } return(data); }
public static byte[] DecodeToDDS(GNFTexture texture) { var imageFormat = ImageEngineFormat.DDS_DXT5; var dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_UNKNOWN; switch (texture.SurfaceFormat) { case GNF.SurfaceFormat.BC1: imageFormat = ImageEngineFormat.DDS_DXT1; break; case GNF.SurfaceFormat.BC2: imageFormat = ImageEngineFormat.DDS_DXT2; break; case GNF.SurfaceFormat.BC3: imageFormat = ImageEngineFormat.DDS_DXT5; break; case GNF.SurfaceFormat.BC4: imageFormat = ImageEngineFormat.DDS_ATI1; break; case GNF.SurfaceFormat.BC5: imageFormat = ImageEngineFormat.DDS_ATI2_3Dc; break; case GNF.SurfaceFormat.BC6: imageFormat = ImageEngineFormat.DDS_DX10; dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16; break; case GNF.SurfaceFormat.BC7: imageFormat = ImageEngineFormat.DDS_DX10; switch (texture.ChannelType) { case ChannelType.Srgb: dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB; break; default: dx10ImageFormat = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM; break; } break; } var ddsHeaderSize = 0x80; if (dx10ImageFormat != DDS_Header.DXGI_FORMAT.DXGI_FORMAT_UNKNOWN) { ddsHeaderSize += 20; } var ddsBytes = new byte[ddsHeaderSize + texture.Data.Length]; // create & write header var ddsHeader = new DDS_Header(1, texture.Height, texture.Width, imageFormat, dx10ImageFormat); ddsHeader.WriteToArray(ddsBytes, 0); // unswizzle var data = Swizzler.UnSwizzle(texture.Data, texture.Width, texture.Height, imageFormat == ImageEngineFormat.DDS_DXT1 ? 8 : 16, SwizzleType.PS4); // write pixel data Array.Copy(data, 0, ddsBytes, ddsHeaderSize, texture.Data.Length); return(ddsBytes); }