static public PNG_IHDR FromBytes(byte[] bytes)
            {
                int    size = Marshal.SizeOf(typeof(PNG_IHDR));
                IntPtr ptr  = Marshal.AllocHGlobal(size);

                Marshal.Copy(bytes, 0, ptr, size);
                PNG_IHDR result = (PNG_IHDR)Marshal.PtrToStructure(ptr, typeof(PNG_IHDR));

                Marshal.FreeHGlobal(ptr);
                result.Width  = IPAddress.HostToNetworkOrder(result.Width);
                result.Height = IPAddress.HostToNetworkOrder(result.Height);
                return(result);
            }
        static PNG_Chunk[] ProcessChunks(PNG_Chunk[] chunks)
        {
            List <PNG_Chunk> result = new List <PNG_Chunk>();

            PNG_IHDR header = new PNG_IHDR();

            using (MemoryStream rawData = new MemoryStream())
            {
                foreach (PNG_Chunk chunk in chunks)
                {
                    if (chunk.Name.Equals(PNG_CgBIChunk))
                    {
                        continue;
                    }

                    if (chunk.Name.Equals(PNG_HeaderChunk))
                    {
                        header = PNG_IHDR.FromBytes(chunk.Data);
                    }

                    if (chunk.Name.Equals(PNG_DataChunk))
                    {
                        rawData.Write(chunk.Data, 0, chunk.Data.Length);
                        continue;
                    }

                    if (chunk.Name.Equals(PNG_EndChunk))
                    {
                        const int dataChunkSize = 16 * 1024;

                        rawData.Position = 0;
                        using (MemoryStream buffer = new MemoryStream())
                        {
                            using (InflaterInputStream inflate = new InflaterInputStream(rawData, new Inflater(true)))
                            {
                                inflate.IsStreamOwner = false;
                                IPATools.Utilities.Utils.CopyStream(inflate, buffer);
                                //inflate.CopyTo(buffer);
                            }

                            buffer.Position = 0;
                            rawData.SetLength(0);
                            IPATools.Utilities.Utils.CopyStream(buffer, rawData);
                            //buffer.CopyTo(rawData);
                        }

                        {
                            byte[] imageData = rawData.GetBuffer();

                            int scanlineSize = header.Width * 4 + 1;
                            int scanlinePos  = 0;

                            int bpp = 4;

                            for (int y = 0; y < header.Height; ++y, scanlinePos += scanlineSize)
                            {
                                PNG_Filter filter = (PNG_Filter)imageData[scanlinePos];

                                switch (filter)
                                {
                                case PNG_Filter.Sub:
                                    for (int x = 1 + bpp; x < scanlineSize; ++x)
                                    {
                                        imageData[scanlinePos + x] = (byte)((int)imageData[scanlinePos + x] + (int)imageData[scanlinePos + x - bpp]);
                                    }
                                    break;

                                case PNG_Filter.Up:
                                    if (y > 0)
                                    {
                                        for (int x = 1; x < scanlineSize; ++x)
                                        {
                                            imageData[scanlinePos + x] = (byte)((int)imageData[scanlinePos + x] + (int)imageData[scanlinePos + x - bpp - scanlineSize]);
                                        }
                                    }
                                    break;

                                case PNG_Filter.Average:
                                    if (y > 0)
                                    {
                                        for (int x = 1 + bpp; x < scanlineSize; ++x)
                                        {
                                            imageData[scanlinePos + x] = (byte)
                                                                         (((int)imageData[scanlinePos + x - bpp] +
                                                                           (int)imageData[scanlinePos + x - bpp - scanlineSize]) >> 1);
                                        }
                                    }
                                    break;

                                case PNG_Filter.Paeth:
                                    if (y > 0)
                                    {
                                        for (int x = 1 + bpp; x < scanlineSize; ++x)
                                        {
                                            imageData[scanlinePos + x] =
                                                (byte)FilterPaethPredictor(
                                                    imageData[scanlinePos + x - bpp],
                                                    imageData[scanlinePos + x - bpp - scanlineSize],
                                                    imageData[scanlinePos + x - scanlineSize]);
                                        }
                                    }
                                    break;
                                }

                                for (int x = 0; x < header.Width; ++x)
                                {
                                    byte r = imageData[scanlinePos + x * 4 + 1];
                                    byte g = imageData[scanlinePos + x * 4 + 2];
                                    byte b = imageData[scanlinePos + x * 4 + 3];
                                    byte a = imageData[scanlinePos + x * 4 + 4];

                                    if (a > 0)
                                    {
                                        r = (byte)(255 * r / a);
                                        g = (byte)(255 * g / a);
                                        b = (byte)(255 * b / a);
                                    }

                                    imageData[scanlinePos + x * 4 + 1] = b;
                                    imageData[scanlinePos + x * 4 + 2] = g;
                                    imageData[scanlinePos + x * 4 + 3] = r;
                                    imageData[scanlinePos + x * 4 + 4] = a;
                                }

                                imageData[scanlinePos] = (byte)PNG_Filter.None;

                                //imageData[scanlinePos] = (byte)PNG_Filter.Sub;
                                //for (int x = 1 + bpp; x < scanlineSize; ++x)
                                //{
                                //    byte c = imageData[scanlinePos + x];
                                //    byte l = imageData[scanlinePos + x - bpp];
                                //    imageData[scanlinePos + x] = (byte)(l - c);
                                //}
                            }
                        }

                        Deflater deflater = new Deflater(9);

                        Crc32 crc32 = new Crc32();

                        rawData.Position = 0;
                        long dataLeft = rawData.Length;
                        while (dataLeft > 0)
                        {
                            byte[] chunkData = new byte[dataChunkSize];

                            int dataInChunk = rawData.Read(chunkData, 0, dataChunkSize);

                            dataLeft -= dataInChunk;

                            byte[] deflatedChunkData = new byte[dataChunkSize];
                            deflater.SetInput(chunkData, 0, dataInChunk);
                            if (dataLeft > 0)
                            {
                                deflater.Flush();
                            }
                            else
                            {
                                deflater.Finish();
                            }
                            int deflatedBytes = deflater.Deflate(deflatedChunkData, 0, dataInChunk);

                            PNG_Chunk dataChunk = new PNG_Chunk();
                            dataChunk.Name = PNG_DataChunk;
                            dataChunk.Data = new byte[deflatedBytes];

                            for (int i = 0; i < deflatedBytes; ++i)
                            {
                                dataChunk.Data[i] = deflatedChunkData[i];
                            }

                            crc32.Reset();
                            crc32.Update(ASCIIEncoding.ASCII.GetBytes(dataChunk.Name));
                            crc32.Update(dataChunk.Data);
                            dataChunk.CRC = (uint)crc32.Value;

                            result.Add(dataChunk);
                        }
                    }

                    result.Add(chunk);
                }
            }

            return(result.ToArray());
        }