public static void Decode(ByteStream data, Bitmap dest) { PNGHeader header = data.Struct <PNGHeader>(true); Debug.Assert(header.Signature == PNGSignature, "Specified file was not a PNG file."); ByteStream idatStream = new ByteStream(); while (true) { PNGChunkHeader chunkHeader = data.Struct <PNGChunkHeader>(true); string chunkType = Encoding.ASCII.GetString(chunkHeader.Type, 4); byte[] chunkBytes = data.Read(chunkHeader.Length); ByteStream chunkData = new ByteStream(chunkBytes); PNGChunkFooter chunkFooter = data.Struct <PNGChunkFooter>(true); Debug.Assert(Crc32(chunkHeader.ToBytes().Concat(chunkBytes).ToArray(), 4) == chunkFooter.CRC, chunkType + " chunk's CRC mismatched!"); switch (chunkType) { case "IHDR": PNGIHDR ihdr = chunkData.Struct <PNGIHDR>(true); Debug.Assert(ihdr.BitDepth == 8 && ihdr.ColorType == 6 && ihdr.CompressionMethod == 0 && ihdr.FilterMethod == 0 && ihdr.InterlaceMethod == 0, "The specified PNG file uses an unsupported format."); dest.Width = ihdr.Width; dest.Height = ihdr.Height; dest.Pixels = new byte[ihdr.Width * ihdr.Height * 4]; break; case "IDAT": idatStream.Write(chunkData.Read(chunkHeader.Length)); break; case "IEND": idatStream.Seek(0, SeekOrigin.Begin); PNGIDATHeader idatHeader = idatStream.Struct <PNGIDATHeader>(true); byte[] idatData; PNGIDATFooter idatFooter; using (MemoryStream target = new MemoryStream()) using (DeflateStream decompressionStream = new DeflateStream(idatStream, CompressionMode.Decompress)) { decompressionStream.CopyTo(target); idatData = target.ToArray(); idatStream.Seek(-4); idatFooter = idatStream.Struct <PNGIDATFooter>(true); } Debug.Assert(idatFooter.CheckValue == Alder32(idatData), "IDAT chunk compression check value mismatch!"); int scanlineSize = dest.Width * 4; for (int scanline = 0; scanline < dest.Height; scanline++) { int offset = scanline * scanlineSize; Array.Copy(idatData, offset + scanline + 1, dest.Pixels, offset, scanlineSize); } return; } } }
private static void WriteChunk(MemoryStream stream, string type, byte[] data) { PNGChunkHeader header = new PNGChunkHeader { Length = data.Length, }; for (int i = 0; i < 4; i++) { header.Type[i] = (byte)type[i]; } PNGChunkFooter footer = new PNGChunkFooter { CRC = Crc32(header.ToBytes().Concat(data).ToArray(), 4), }; stream.Write(header.ToBytes(true)); stream.Write(data); stream.Write(footer.ToBytes(true)); }