Example #1
0
        public Png(Stream s)
        {
            byte[] internalBuffer = new byte[8];

            // Check header signature
            s.Read(internalBuffer, 0, Signature.Length);

            for (int i = 0; i < Signature.Length; i++)
            {
                if (internalBuffer[i] != Signature[i])
                {
                    throw new InvalidDataException("Invalid PNG file - header signature mismatch");
                }
            }

            // Load image
            bool           headerParsed = false;
            bool           isPaletted   = false;
            bool           is24Bit      = false;
            RawList <byte> data         = new RawList <byte>();

            EmbeddedData = new Dictionary <string, string>();

            while (true)
            {
                int length = ReadInt32BigEndian(s, ref internalBuffer);
                s.Read(internalBuffer, 0, 4);
                string type = Encoding.ASCII.GetString(internalBuffer, 0, 4);

                if (!headerParsed && type != "IHDR")
                {
                    throw new InvalidDataException("Invalid PNG file - header does not appear first");
                }

                int blockEndPosition = (int)s.Position + length;

                switch (type)
                {
                case "IHDR": {
                    if (headerParsed)
                    {
                        throw new InvalidDataException("Invalid PNG file - duplicate header");
                    }

                    Width  = ReadInt32BigEndian(s, ref internalBuffer);
                    Height = ReadInt32BigEndian(s, ref internalBuffer);

                    byte         bitDepth  = s.ReadUInt8(ref internalBuffer);
                    PngColorType colorType = (PngColorType)s.ReadUInt8(ref internalBuffer);
                    isPaletted = IsPaletted(bitDepth, colorType);
                    is24Bit    = (colorType == PngColorType.Color);

                    var dataLength = Width * Height;
                    if (!isPaletted)
                    {
                        dataLength *= 4;
                    }

                    Data = new byte[dataLength];

                    byte compression = s.ReadUInt8(ref internalBuffer);
                    /*byte filter = */ s.ReadUInt8(ref internalBuffer);
                    byte interlace = s.ReadUInt8(ref internalBuffer);

                    if (compression != 0)
                    {
                        throw new InvalidDataException("Compression method (" + compression + ") not supported");
                    }
                    if (interlace != 0)
                    {
                        throw new InvalidDataException("Interlacing (" + interlace + ") not supported");
                    }

                    headerParsed = true;
                    break;
                }

                case "PLTE": {
                    Palette = new ColorRgba[256];
                    for (int i = 0; i < length / 3; i++)
                    {
                        byte r = s.ReadUInt8(ref internalBuffer);
                        byte g = s.ReadUInt8(ref internalBuffer);
                        byte b = s.ReadUInt8(ref internalBuffer);
                        Palette[i] = new ColorRgba(r, g, b);
                    }
                    break;
                }

                case "tRNS": {
                    if (Palette == null)
                    {
                        throw new InvalidDataException("Non-palette indexed images are not supported");
                    }
                    for (var i = 0; i < length; i++)
                    {
                        Palette[i].A = s.ReadUInt8(ref internalBuffer);
                    }
                    break;
                }

                case "IDAT": {
                    int newLength = data.Count + length;
                    data.Count = newLength;
                    s.Read(data.Data, newLength - length, length);
                    break;
                }

                case "tEXt": {
                    byte[] content = new byte[length];
                    s.Read(content, 0, length);

                    for (int i = 0; i < length; i++)
                    {
                        if (content[i] == 0)
                        {
                            string key   = Encoding.ASCII.GetString(content, 0, i);
                            string value = Encoding.ASCII.GetString(content, i + 1, length - (i + 1));
                            EmbeddedData.Add(key, value);
                            break;
                        }
                    }
                    break;
                }

                case "IEND": {
                    using (var ms = new MemoryStream(data.Data)) {
                        ms.Position += 2;

                        using (var ds = new DeflateStream(ms, CompressionMode.Decompress, true)) {
                            int pxStride  = (isPaletted ? 1 : (is24Bit ? 3 : 4));
                            int srcStride = Width * pxStride;
                            int dstStride = Width * (isPaletted ? 1 : 4);

                            byte[] buffer     = new byte[srcStride];
                            byte[] bufferPrev = new byte[srcStride];

                            for (var y = 0; y < Height; y++)
                            {
                                // Read filter
                                PngFilter filter = (PngFilter)ds.ReadUInt8(ref internalBuffer);

                                // Read data
                                ds.Read(buffer, 0, srcStride);

                                for (var i = 0; i < srcStride; i++)
                                {
                                    if (i < pxStride)
                                    {
                                        buffer[i] = UnapplyFilter(filter, buffer[i], 0, bufferPrev[i], 0);
                                    }
                                    else
                                    {
                                        buffer[i] = UnapplyFilter(filter, buffer[i], buffer[i - pxStride], bufferPrev[i], bufferPrev[i - pxStride]);
                                    }
                                }

                                if (is24Bit)
                                {
                                    for (var i = 0; i < buffer.Length / 3; i++)
                                    {
                                        Buffer.BlockCopy(buffer, 3 * i, Data, y * dstStride + 4 * i, 3);
                                        Data[y * dstStride + 4 * i + 3] = 255;
                                    }
                                }
                                else
                                {
                                    Buffer.BlockCopy(buffer, 0, Data, y * dstStride, srcStride);
                                }

                                bufferPrev = buffer;
                            }
                        }
                    }
                    return;
                }

                default: {
                    //Console.WriteLine("Unknown PNG section: " + type);
                    s.Position += length;
                    break;
                }
                }

                if (s.Position != blockEndPosition)
                {
                    throw new InvalidDataException("Block " + type + " has incorrect length");
                }

                // Skip CRC
                s.Position += 4;
            }
        }