Ejemplo n.º 1
0
    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;
            }
        }
    }
Ejemplo n.º 2
0
    public static byte[] Encode(Bitmap bitmap)
    {
        WriteStream outStream = new WriteStream();

        PNGHeader header = new PNGHeader {
            Signature = PNGSignature,
        };

        outStream.Write(header, true);
        WriteChunk(outStream, "IHDR", new PNGIHDR {
            Width             = bitmap.Width,
            Height            = bitmap.Height,
            BitDepth          = 8,
            ColorType         = 6,
            CompressionMethod = 0,
            FilterMethod      = 0,
            InterlaceMethod   = 0,
        }.ToBytes(true));

        byte[] scanlines    = new byte[bitmap.Width * bitmap.Height * 4 + bitmap.Height];
        int    scanlineSize = bitmap.Width * 4;

        for (int scanline = 0; scanline < bitmap.Height; scanline++)
        {
            int offset = scanline * scanlineSize;
            Array.Copy(bitmap.Pixels, offset, scanlines, offset + scanline + 1, scanlineSize);
        }

        using (MemoryStream idatStream = new MemoryStream()) {
            idatStream.Write(new PNGIDATHeader()
            {
                ZLibMethodFlags = 0x78,
                AdditionalFlags = 0x1,
            }.ToBytes(true));

            using (DeflateStream compressionStream = new DeflateStream(idatStream, CompressionMode.Compress, true))
                compressionStream.Write(scanlines);

            idatStream.Write(new PNGIDATFooter()
            {
                CheckValue = Alder32(scanlines),
            }.ToBytes(true));

            WriteChunk(outStream, "IDAT", idatStream.ToArray());
        }

        WriteChunk(outStream, "IEND", new byte[0]);

        return(outStream.ToArray());
    }