private int CalculateScanlineStep(PngColorTypeInformation colorTypeInformation)
        {
            int scanlineStep = 1;

            if (header.BitDepth >= 8)
            {
                scanlineStep = (colorTypeInformation.ChannelsPerColor * header.BitDepth) / 8;
            }
            return(scanlineStep);
        }
        private int CalculateScanlineLength(PngColorTypeInformation colorTypeInformation)
        {
            int scanlineLength = header.Width * header.BitDepth * colorTypeInformation.ChannelsPerColor;
            int amount         = scanlineLength % 8;

            if (amount != 0)
            {
                scanlineLength += 8 - amount;
            }
            return(scanlineLength / 8);
        }
        public void Decode(Image2 image, Stream stream)
        {
            Image2 currentImage = image;

            currentStream = stream;
            currentStream.Seek(8, SeekOrigin.Current);
            bool isEndChunkReached = false;

            byte[] palette      = null;
            byte[] paletteAlpha = null;
            using (MemoryStream dataStream = new MemoryStream()){
                PngChunk currentChunk;
                while ((currentChunk = ReadChunk()) != null)
                {
                    if (isEndChunkReached)
                    {
                        throw new Exception("Image does not end with end chunk.");
                    }
                    if (currentChunk.Type == PngChunkTypes.Header)
                    {
                        ReadHeaderChunk(currentChunk.Data);
                        ValidateHeader();
                    }
                    else if (currentChunk.Type == PngChunkTypes.Physical)
                    {
                        ReadPhysicalChunk(currentImage, currentChunk.Data);
                    }
                    else if (currentChunk.Type == PngChunkTypes.Data)
                    {
                        dataStream.Write(currentChunk.Data, 0, currentChunk.Data.Length);
                    }
                    else if (currentChunk.Type == PngChunkTypes.Palette)
                    {
                        palette = currentChunk.Data;
                    }
                    else if (currentChunk.Type == PngChunkTypes.PaletteAlpha)
                    {
                        paletteAlpha = currentChunk.Data;
                    }
                    else if (currentChunk.Type == PngChunkTypes.Text)
                    {
                        ReadTextChunk(currentImage, currentChunk.Data);
                    }
                    else if (currentChunk.Type == PngChunkTypes.End)
                    {
                        isEndChunkReached = true;
                    }
                }
                if (header.Width > image.MaxWidth || header.Height > image.MaxHeight)
                {
                    throw new ArgumentOutOfRangeException($"The input png '{header.Width}x{header.Height}' is bigger than the " +
                                                          $"max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
                }
                Color2[] pixels = new Color2[header.Width * header.Height];
                PngColorTypeInformation colorTypeInformation = colorTypes[header.ColorType];
                if (colorTypeInformation != null)
                {
                    IColorReader colorReader = colorTypeInformation.CreateColorReader(palette, paletteAlpha);
                    ReadScanlines(dataStream, pixels, colorReader, colorTypeInformation);
                }
                image.SetPixels(header.Width, header.Height, pixels);
            }
        }
        private void ReadScanlines(MemoryStream dataStream, Color2[] pixels, IColorReader colorReader,
                                   PngColorTypeInformation colorTypeInformation)
        {
            dataStream.Position = 0;
            int scanlineLength = CalculateScanlineLength(colorTypeInformation);
            int scanlineStep   = CalculateScanlineStep(colorTypeInformation);

            byte[] lastScanline = new byte[scanlineLength];
            byte[] currentScanline = new byte[scanlineLength];
            int    filter = 0, column = -1;

            using (ZlibInflateStream compressedStream = new ZlibInflateStream(dataStream)){
                int readByte;
                while ((readByte = compressedStream.ReadByte()) >= 0)
                {
                    if (column == -1)
                    {
                        filter = readByte;
                        column++;
                    }
                    else
                    {
                        currentScanline[column] = (byte)readByte;
                        byte a;
                        byte c;
                        if (column >= scanlineStep)
                        {
                            a = currentScanline[column - scanlineStep];
                            c = lastScanline[column - scanlineStep];
                        }
                        else
                        {
                            a = 0;
                            c = 0;
                        }
                        byte b = lastScanline[column];
                        if (filter == 1)
                        {
                            currentScanline[column] = (byte)(currentScanline[column] + a);
                        }
                        else if (filter == 2)
                        {
                            currentScanline[column] = (byte)(currentScanline[column] + b);
                        }
                        else if (filter == 3)
                        {
                            currentScanline[column] = (byte)(currentScanline[column] + (byte)((a + b) / 2));
                        }
                        else if (filter == 4)
                        {
                            currentScanline[column] = (byte)(currentScanline[column] + PaethPredicator(a, b, c));
                        }
                        column++;
                        if (column == scanlineLength)
                        {
                            colorReader.ReadScanline(currentScanline, pixels, header);
                            column = -1;
                            Swap(ref currentScanline, ref lastScanline);
                        }
                    }
                }
            }
        }