private void ReadMipMapsContent(Stream stream, IList <TgvMipMap> mipMaps, bool compressed) { for (int i = 0; i < mipMaps.Count; i++) { var buffer = new byte[4]; stream.Seek(mipMaps[i].Offset, SeekOrigin.Begin); if (compressed) { stream.Read(buffer, 0, buffer.Length); if (!MiscUtilities.ComparerByteArrays(buffer, ZIPO)) { throw new InvalidDataException("Mipmap has to start with \"ZIPO\"!"); } stream.Read(buffer, 0, buffer.Length); mipMaps[i].MipSize = (uint)BitConverter.ToInt32(buffer, 0); //Tu nie by³o rzutowania } // odejmujemy 8 bo rozmiar zawiera 8 bajtów dodatkowych, 4 magii i 4 rozmiaru mipampy buffer = new byte[mipMaps[i].Length - 8]; stream.Read(buffer, 0, buffer.Length); if (compressed) { ICompressor comp = new ZlibCompressor(); buffer = comp.Decompress(buffer); } mipMaps[i].Content = buffer; } }
// Constructor. public ScanlineWriter(ZlibCompressor compressor, int width, PixelFormat format) { // Initialize the object. this.compressor = compressor; usePaeth = true; filter = new byte [1]; y = 0; // Get the scanline size parameters. switch (format) { case PixelFormat.Format16bppRgb555: case PixelFormat.Format16bppRgb565: case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: { bytesPerLine = width * 3; bytesPerPixel = 3; } break; case PixelFormat.Format1bppIndexed: case PixelFormat.Format4bppIndexed: case PixelFormat.Format8bppIndexed: { bytesPerLine = width; bytesPerPixel = 1; usePaeth = false; } break; case PixelFormat.Format16bppArgb1555: case PixelFormat.Format32bppPArgb: case PixelFormat.Format32bppArgb: { bytesPerLine = width * 4; bytesPerPixel = 4; } break; case PixelFormat.Format16bppGrayScale: { bytesPerLine = width * 2; bytesPerPixel = 2; } break; case PixelFormat.Format48bppRgb: { bytesPerLine = width * 6; bytesPerPixel = 6; } break; case PixelFormat.Format64bppPArgb: case PixelFormat.Format64bppArgb: { bytesPerLine = width * 8; bytesPerPixel = 8; } break; } // Allocate space for the scanline buffers. Buffer = new byte [bytesPerLine]; prevScanline = new byte [bytesPerLine]; paeth = (usePaeth ? new byte [bytesPerLine] : null); }
// Save a PNG image to the specified stream. public static void Save(Stream stream, PortableImage image) { Frame frame = image.GetFrame(0); byte[] buffer = new byte [1024]; ChunkWriter writer = new ChunkWriter(stream); int colorType, bitDepth; int sigRed, sigGreen, sigBlue, sigAlpha; int paletteSize, posn; int[] palette; ZlibCompressor compressor; ScanlineWriter scanlineWriter; OutputFunc func; int y; // Determine the color type and bit depth for the image. sigRed = -1; sigGreen = -1; sigBlue = -1; sigAlpha = -1; paletteSize = 0; switch (frame.PixelFormat) { case PixelFormat.Format16bppRgb555: { sigRed = 5; sigGreen = 5; sigBlue = 5; colorType = 2; bitDepth = 8; } break; case PixelFormat.Format16bppRgb565: { sigRed = 5; sigGreen = 6; sigBlue = 5; colorType = 2; bitDepth = 8; } break; case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: { colorType = 2; bitDepth = 8; } break; case PixelFormat.Format1bppIndexed: { colorType = 3; bitDepth = 1; paletteSize = 2; } break; case PixelFormat.Format4bppIndexed: { colorType = 3; bitDepth = 4; paletteSize = 16; } break; case PixelFormat.Format8bppIndexed: { colorType = 3; bitDepth = 8; paletteSize = 256; } break; case PixelFormat.Format16bppArgb1555: { sigRed = 5; sigGreen = 5; sigBlue = 5; sigAlpha = 1; colorType = 6; bitDepth = 8; } break; case PixelFormat.Format32bppPArgb: case PixelFormat.Format32bppArgb: { colorType = 6; bitDepth = 8; } break; case PixelFormat.Format16bppGrayScale: { colorType = 0; bitDepth = 16; } break; case PixelFormat.Format48bppRgb: { colorType = 2; bitDepth = 16; } break; case PixelFormat.Format64bppPArgb: case PixelFormat.Format64bppArgb: { colorType = 6; bitDepth = 16; } break; default: throw new FormatException("unknown format"); } // Write out the PNG magic number. stream.Write(magic, 0, magic.Length); // Write the header chunk. Utils.WriteInt32B(buffer, 0, frame.Width); Utils.WriteInt32B(buffer, 4, frame.Height); buffer[8] = (byte)bitDepth; buffer[9] = (byte)colorType; buffer[10] = (byte)0; // Compression method. buffer[11] = (byte)0; // Filter method. buffer[12] = (byte)0; // Interlace method. writer.Write(PngReader.IHDR, buffer, 0, 13); // Write the significant bits chunk if necessary. if (sigAlpha != -1) { buffer[0] = (byte)sigRed; buffer[1] = (byte)sigGreen; buffer[2] = (byte)sigBlue; buffer[3] = (byte)sigAlpha; writer.Write(PngReader.sBIT, buffer, 0, 4); } else if (sigRed != -1) { buffer[0] = (byte)sigRed; buffer[1] = (byte)sigGreen; buffer[2] = (byte)sigBlue; writer.Write(PngReader.sBIT, buffer, 0, 3); } // Write the palette and transparency chunks. if (paletteSize > 0) { Array.Clear(buffer, 0, buffer.Length); palette = frame.Palette; if (palette != null) { for (posn = 0; posn < palette.Length && posn < paletteSize; ++posn) { buffer[posn * 3] = (byte)(palette[posn] >> 16); buffer[posn * 2 + 1] = (byte)(palette[posn] >> 8); buffer[posn * 2 + 2] = (byte)(palette[posn]); } } writer.Write(PngReader.PLTE, buffer, 0, paletteSize * 3); if (frame.TransparentPixel >= 0 && frame.TransparentPixel < paletteSize) { for (posn = 0; posn < paletteSize; ++posn) { buffer[posn] = (byte)0xFF; } buffer[frame.TransparentPixel] = (byte)0x00; writer.Write(PngReader.tRNS, buffer, 0, frame.TransparentPixel + 1); } } // Compress and write the scanlines to the output stream. compressor = new ZlibCompressor(writer); scanlineWriter = new ScanlineWriter (compressor, frame.Width, frame.PixelFormat); func = GetOutputFunc(frame.PixelFormat); for (y = 0; y < frame.Height; ++y) { func(frame, y, scanlineWriter.Buffer); scanlineWriter.FlushScanline(); } compressor.Finish(); // Write the end chunk. writer.Write(PngReader.IEND, buffer, 0, 0); }
/// <summary> /// /// </summary> /// <param name="file"></param> /// <returns></returns> /// <remarks> /// Credits to enohka for this code. /// See more at: http://github.com/enohka/moddingSuite /// </remarks> public virtual byte[] Write(TgvImage file) { using (MemoryStream stream = new MemoryStream()) { file.PixelFormatString = TgvUtilities.GetTgvFromPixelFormat(file.Format); //"DXT5_SRGB" var buffer = BitConverter.GetBytes(VersionMagic); stream.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(file.IsCompressed ? 1 : 0); stream.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(file.Width); stream.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(file.Height); stream.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(file.Width); stream.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(file.Height); stream.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes((short)file.MipMapCount); stream.Write(buffer, 0, buffer.Length); var fmtLen = (short)file.PixelFormatString.Length; buffer = BitConverter.GetBytes(fmtLen); stream.Write(buffer, 0, buffer.Length); buffer = Encoding.ASCII.GetBytes(file.PixelFormatString); stream.Write(buffer, 0, buffer.Length); stream.Seek(MathUtilities.RoundToNextDivBy4(fmtLen) - fmtLen, SeekOrigin.Current); stream.Write(file.SourceChecksum, 0, file.SourceChecksum.Length); var mipOffset = (uint)(stream.Position); stream.Seek(8 * file.MipMapCount, SeekOrigin.Current); var sortedMipMaps = file.MipMaps.OrderBy(x => x.MipSize).ToList(); // Create the content and write all MipMaps, // since we compress on this part its the first part where we know the size of a MipMap foreach (var sortedMipMap in sortedMipMaps) { sortedMipMap.Offset = (uint)stream.Position; if (file.IsCompressed) { var zipoMagic = Encoding.ASCII.GetBytes("ZIPO"); stream.Write(zipoMagic, 0, zipoMagic.Length); //buffer = BitConverter.GetBytes(mipImgsizes[sortedMipMaps.IndexOf(sortedMipMap)]); //To Ÿle dzia³a. W orygianlnym pliku bajty s¹ w innej kolejnoœci. Pozatym to zak³ada ze potêg¹ jest indeks, //a w przypadku ma³ej liczby bitmap to nie ma sensu uint mipSize = sortedMipMap.MipSize; //GetMipMapSize(file.Width, file.Height, sortedMipMaps.IndexOf(sortedMipMap), sortedMipMaps.Count); buffer = BitConverter.GetBytes(mipSize); stream.Write(buffer, 0, buffer.Length); ICompressor comp = new ZlibCompressor(); buffer = comp.Compress(sortedMipMap.Content); stream.Write(buffer, 0, buffer.Length); sortedMipMap.Length = (uint)buffer.Length; } else { stream.Write(sortedMipMap.Content, 0, sortedMipMap.Content.Length); sortedMipMap.Length = (uint)sortedMipMap.Content.Length; } //Wygl¹da na to ¿e mipMapy musza mieæ offset wzglêden pocz¹tku pliku podzielny przez 4. //Trzeba dope³niæ do liczby podzielnej przez 4 przed zapisem nastêpnego //To dope³nienie zak³ada ¿e nag³ówek ma d³ugoœæ podzielna przez 4, tak ¿e pierwsza MipMapa nie jest przesuniêta. //Plik Edata wie o tym poszerzeniu rozmiaru, bo odczytuje d³ugoœæ pliku z rozmiaru contentu. bool multiMipMaps = file.MipMaps.Count > 1; bool needSupplementTo4 = (stream.Position % 4) != 0; if (multiMipMaps && needSupplementTo4) { int supplementSize = (int)(MathUtilities.RoundToNextDivBy4(stream.Position) - stream.Position); byte[] supplementBuffer = new byte[supplementSize]; stream.Write(supplementBuffer, 0, supplementBuffer.Length); } } stream.Seek(mipOffset, SeekOrigin.Begin); // Write the offset collection in the header. for (int i = 0; i < file.MipMapCount; i++) { buffer = BitConverter.GetBytes(sortedMipMaps[i].Offset); stream.Write(buffer, 0, buffer.Length); } // Write the size collection into the header. for (int i = 0; i < file.MipMapCount; i++) { buffer = BitConverter.GetBytes(sortedMipMaps[i].Length + 8); //+ 4 magii i + 4 rozmiaru mapy stream.Write(buffer, 0, buffer.Length); } return(stream.ToArray()); } }
// Constructor. public ScanlineWriter(ZlibCompressor compressor, int width, PixelFormat format) { // Initialize the object. this.compressor = compressor; this.usePaeth = true; this.filter = new byte [1]; this.y = 0; // Get the scanline size parameters. switch(format) { case PixelFormat.Format16bppRgb555: case PixelFormat.Format16bppRgb565: case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: { bytesPerLine = width * 3; bytesPerPixel = 3; } break; case PixelFormat.Format1bppIndexed: case PixelFormat.Format4bppIndexed: case PixelFormat.Format8bppIndexed: { bytesPerLine = width; bytesPerPixel = 1; usePaeth = false; } break; case PixelFormat.Format16bppArgb1555: case PixelFormat.Format32bppPArgb: case PixelFormat.Format32bppArgb: { bytesPerLine = width * 4; bytesPerPixel = 4; } break; case PixelFormat.Format16bppGrayScale: { bytesPerLine = width * 2; bytesPerPixel = 2; } break; case PixelFormat.Format48bppRgb: { bytesPerLine = width * 6; bytesPerPixel = 6; } break; case PixelFormat.Format64bppPArgb: case PixelFormat.Format64bppArgb: { bytesPerLine = width * 8; bytesPerPixel = 8; } break; } // Allocate space for the scanline buffers. scanline = new byte [bytesPerLine]; prevScanline = new byte [bytesPerLine]; paeth = (usePaeth ? new byte [bytesPerLine] : null); }
// Save a PNG image to the specified stream. public static void Save(Stream stream, Image image) { Frame frame = image.GetFrame(0); byte[] buffer = new byte [1024]; ChunkWriter writer = new ChunkWriter(stream); int colorType, bitDepth; int sigRed, sigGreen, sigBlue, sigAlpha; int paletteSize, posn; int[] palette; ZlibCompressor compressor; ScanlineWriter scanlineWriter; OutputFunc func; int y; // Determine the color type and bit depth for the image. sigRed = -1; sigGreen = -1; sigBlue = -1; sigAlpha = -1; paletteSize = 0; switch(frame.PixelFormat) { case PixelFormat.Format16bppRgb555: { sigRed = 5; sigGreen = 5; sigBlue = 5; colorType = 2; bitDepth = 8; } break; case PixelFormat.Format16bppRgb565: { sigRed = 5; sigGreen = 6; sigBlue = 5; colorType = 2; bitDepth = 8; } break; case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: { colorType = 2; bitDepth = 8; } break; case PixelFormat.Format1bppIndexed: { colorType = 3; bitDepth = 1; paletteSize = 2; } break; case PixelFormat.Format4bppIndexed: { colorType = 3; bitDepth = 4; paletteSize = 16; } break; case PixelFormat.Format8bppIndexed: { colorType = 3; bitDepth = 8; paletteSize = 256; } break; case PixelFormat.Format16bppArgb1555: { sigRed = 5; sigGreen = 5; sigBlue = 5; sigAlpha = 1; colorType = 6; bitDepth = 8; } break; case PixelFormat.Format32bppPArgb: case PixelFormat.Format32bppArgb: { colorType = 6; bitDepth = 8; } break; case PixelFormat.Format16bppGrayScale: { colorType = 0; bitDepth = 16; } break; case PixelFormat.Format48bppRgb: { colorType = 2; bitDepth = 16; } break; case PixelFormat.Format64bppPArgb: case PixelFormat.Format64bppArgb: { colorType = 6; bitDepth = 16; } break; default: throw new FormatException("unknown format"); } // Write out the PNG magic number. stream.Write(magic, 0, magic.Length); // Write the header chunk. Utils.WriteInt32B(buffer, 0, frame.Width); Utils.WriteInt32B(buffer, 4, frame.Height); buffer[8] = (byte)bitDepth; buffer[9] = (byte)colorType; buffer[10] = (byte)0; // Compression method. buffer[11] = (byte)0; // Filter method. buffer[12] = (byte)0; // Interlace method. writer.Write(PngReader.IHDR, buffer, 0, 13); // Write the significant bits chunk if necessary. if(sigAlpha != -1) { buffer[0] = (byte)sigRed; buffer[1] = (byte)sigGreen; buffer[2] = (byte)sigBlue; buffer[3] = (byte)sigAlpha; writer.Write(PngReader.sBIT, buffer, 0, 4); } else if(sigRed != -1) { buffer[0] = (byte)sigRed; buffer[1] = (byte)sigGreen; buffer[2] = (byte)sigBlue; writer.Write(PngReader.sBIT, buffer, 0, 3); } // Write the palette and transparency chunks. if(paletteSize > 0) { Array.Clear(buffer, 0, buffer.Length); palette = frame.Palette; if(palette != null) { for(posn = 0; posn < palette.Length && posn < paletteSize; ++posn) { buffer[posn * 3] = (byte)(palette[posn] >> 16); buffer[posn * 2 + 1] = (byte)(palette[posn] >> 8); buffer[posn * 2 + 2] = (byte)(palette[posn]); } } writer.Write(PngReader.PLTE, buffer, 0, paletteSize * 3); if(frame.TransparentPixel >= 0 && frame.TransparentPixel < paletteSize) { for(posn = 0; posn < paletteSize; ++posn) { buffer[posn] = (byte)0xFF; } buffer[frame.TransparentPixel] = (byte)0x00; writer.Write(PngReader.tRNS, buffer, 0, frame.TransparentPixel + 1); } } // Compress and write the scanlines to the output stream. compressor = new ZlibCompressor(writer); scanlineWriter = new ScanlineWriter (compressor, frame.Width, frame.PixelFormat); func = GetOutputFunc(frame.PixelFormat); for(y = 0; y < frame.Height; ++y) { func(frame, y, scanlineWriter.Buffer); scanlineWriter.FlushScanline(); } compressor.Finish(); // Write the end chunk. writer.Write(PngReader.IEND, buffer, 0, 0); }