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 List <MipMap> LoadImage(byte[] rawDDSData, ImageEngineFormat surfaceFormat, int width, int height, out DDSGeneral.DDS_HEADER header) { header = DDSGeneral.Build_DDS_Header(1, height, width, surfaceFormat); List <MipMap> MipMaps = null; // Create new fully formatted DDS i.e. one with a header. MemoryStream stream = new MemoryStream(); BinaryWriter bw = new BinaryWriter(stream); DDSGeneral.Write_DDS_Header(header, bw); bw.Write(rawDDSData); switch (surfaceFormat) { case ImageEngineFormat.DDS_DXT1: case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT4: case ImageEngineFormat.DDS_DXT5: if (WindowsWICCodecsAvailable) { MipMaps = WIC_Codecs.LoadWithCodecs(stream, 0, 0, true); } else { MipMaps = DDSGeneral.LoadDDS(stream, header, new Format(surfaceFormat), 0); } break; case ImageEngineFormat.DDS_ARGB: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_RGB: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_V8U8: MipMaps = DDSGeneral.LoadDDS(stream, header, new Format(surfaceFormat), 0); break; default: throw new InvalidDataException("Image format is unknown."); } bw.Dispose(); // Also disposes MemoryStream return(MipMaps); }
/// <summary> /// Loads image from stream. /// </summary> /// <param name="stream">Full image stream.</param> /// <param name="Format">Detected Format.</param> /// <param name="extension">File Extension. Used to determine format more easily.</param> /// <param name="maxWidth">Maximum width to allow when loading. Resized if enforceResize = true.</param> /// <param name="maxHeight">Maximum height to allow when loading. Resized if enforceResize = true.</param> /// <param name="enforceResize">True = Resizes image to match either maxWidth or maxHeight.</param> /// <param name="header">DDS header of image.</param> /// <param name="mergeAlpha">ONLY valid when enforceResize is true. True = Flattens alpha down, directly affecting RGB.</param> /// <returns>List of Mipmaps.</returns> internal static List <MipMap> LoadImage(Stream stream, out Format Format, string extension, int maxWidth, int maxHeight, bool enforceResize, out DDSGeneral.DDS_HEADER header, bool mergeAlpha) { // KFreon: See if image is built-in codec agnostic. header = null; Format = ImageFormats.ParseFormat(stream, extension, ref header); List <MipMap> MipMaps = null; switch (Format.SurfaceFormat) { case ImageEngineFormat.BMP: case ImageEngineFormat.JPG: case ImageEngineFormat.PNG: MipMaps = WIC_Codecs.LoadWithCodecs(stream, maxWidth, maxHeight, false); break; case ImageEngineFormat.DDS_DXT1: case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT4: case ImageEngineFormat.DDS_DXT5: if (WindowsWICCodecsAvailable) { MipMaps = WIC_Codecs.LoadWithCodecs(stream, maxWidth, maxHeight, true); } else { MipMaps = DDSGeneral.LoadDDS(stream, header, Format, maxHeight > maxWidth ? maxHeight : maxWidth); } break; case ImageEngineFormat.DDS_ARGB: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_RGB: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_V8U8: MipMaps = DDSGeneral.LoadDDS(stream, header, Format, maxHeight > maxWidth ? maxHeight : maxWidth); break; case ImageEngineFormat.TGA: var img = new TargaImage(stream); byte[] pixels = UsefulThings.WinForms.Imaging.GetPixelDataFromBitmap(img.Image); WriteableBitmap wbmp = UsefulThings.WPF.Images.CreateWriteableBitmap(pixels, img.Image.Width, img.Image.Height); var mip1 = new MipMap(wbmp); MipMaps = new List <MipMap>() { mip1 }; img.Dispose(); break; default: throw new InvalidDataException("Image format is unknown."); } if (MipMaps == null || MipMaps.Count == 0) { throw new InvalidDataException("No mipmaps loaded."); } // KFreon: No resizing requested if (maxHeight == 0 && maxWidth == 0) { return(MipMaps); } // KFreon: Test if we need to resize var top = MipMaps.First(); if (top.Width == maxWidth || top.Height == maxHeight) { return(MipMaps); } int max = maxWidth > maxHeight ? maxWidth : maxHeight; // KFreon: Attempt to resize var sizedMips = MipMaps.Where(m => m.Width > m.Height ? m.Width <= max : m.Height <= max); if (sizedMips != null && sizedMips.Any()) // KFreon: If there's already a mip, return that. { MipMaps = sizedMips.ToList(); } else if (enforceResize) { // Get top mip and clear others. var mip = MipMaps[0]; MipMaps.Clear(); MipMap output = null; int divisor = mip.Width > mip.Height ? mip.Width / max : mip.Height / max; output = Resize(mip, 1f / divisor, mergeAlpha); MipMaps.Add(output); } return(MipMaps); }
internal static List <MipMap> LoadImage(Stream imageStream, AbstractHeader header, int maxDimension, double scale, ImageFormats.ImageEngineFormatDetails formatDetails) { imageStream.Seek(0, SeekOrigin.Begin); List <MipMap> MipMaps = null; int decodeWidth = header.Width > header.Height ? maxDimension : 0; int decodeHeight = header.Width < header.Height ? maxDimension : 0; switch (header.Format) { case ImageEngineFormat.DDS_DXT1: case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT4: case ImageEngineFormat.DDS_DXT5: MipMaps = WIC_Codecs.LoadWithCodecs(imageStream, decodeWidth, decodeHeight, scale, true, formatDetails); if (MipMaps == null) { // Windows codecs unavailable/failed. Load with mine. MipMaps = DDSGeneral.LoadDDS((MemoryStream)imageStream, (DDS_Header)header, maxDimension, formatDetails); } break; case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_ARGB_4: case ImageEngineFormat.DDS_RGB_8: case ImageEngineFormat.DDS_V8U8: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_ARGB_8: case ImageEngineFormat.DDS_ARGB_32F: case ImageEngineFormat.DDS_ABGR_8: case ImageEngineFormat.DDS_G16_R16: case ImageEngineFormat.DDS_R5G6B5: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_CUSTOM: case ImageEngineFormat.DDS_DX10: MipMaps = DDSGeneral.LoadDDS((MemoryStream)imageStream, (DDS_Header)header, maxDimension, formatDetails); break; case ImageEngineFormat.GIF: case ImageEngineFormat.JPG: case ImageEngineFormat.PNG: case ImageEngineFormat.BMP: case ImageEngineFormat.TIF: MipMaps = WIC_Codecs.LoadWithCodecs(imageStream, decodeWidth, decodeHeight, scale, false, formatDetails); break; case ImageEngineFormat.TGA: using (var tga = new TargaImage(imageStream, ((TGA_Header)header).header)) MipMaps = new List <MipMap>() { new MipMap(tga.ImageData, tga.Header.Width, tga.Header.Height, formatDetails) }; break; default: throw new FormatException($"Format unknown: {header.Format}."); } return(MipMaps); }
/// <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); } }