Exemple #1
0
        public static Png Open(Stream stream, PngOpenerSettings settings)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanRead)
            {
                throw new ArgumentException($"The provided stream of type {stream.GetType().FullName} was not readable.");
            }

            var validHeader = HasValidHeader(stream);

            if (!validHeader.IsValid)
            {
                throw new ArgumentException($"The provided stream did not start with the PNG header. Got {validHeader}.");
            }

            var crc         = new byte[4];
            var imageHeader = ReadImageHeader(stream, crc);

            var hasEncounteredImageEnd = false;

            Palette palette = null;

            using (var output = new MemoryStream())
            {
                using (var memoryStream = new MemoryStream())
                {
                    while (TryReadChunkHeader(stream, out var header))
                    {
                        if (hasEncounteredImageEnd)
                        {
                            if (settings?.DisallowTrailingData == true)
                            {
                                throw new InvalidOperationException($"Found another chunk {header} after already reading the IEND chunk.");
                            }

                            break;
                        }

                        var bytes = new byte[header.Length];
                        var read  = stream.Read(bytes, 0, bytes.Length);
                        if (read != bytes.Length)
                        {
                            throw new InvalidOperationException($"Did not read {header.Length} bytes for the {header} header, only found: {read}.");
                        }

                        if (header.IsCritical)
                        {
                            switch (header.Name)
                            {
                            case "PLTE":
                                if (header.Length % 3 != 0)
                                {
                                    throw new InvalidOperationException($"Palette data must be multiple of 3, got {header.Length}.");
                                }

                                // Ignore palette data unless the header.ColorType indicates that the image is paletted.
                                if (imageHeader.ColorType.HasFlag(ColorType.PaletteUsed))
                                {
                                    palette = new Palette(bytes);
                                }

                                break;

                            case "IDAT":
                                memoryStream.Write(bytes, 0, bytes.Length);
                                break;

                            case "IEND":
                                hasEncounteredImageEnd = true;
                                break;

                            default:
                                throw new NotSupportedException($"Encountered critical header {header} which was not recognised.");
                            }
                        }
                        else
                        {
                            switch (header.Name)
                            {
                            case "tRNS":
                                // Add transparency to palette, if the PLTE chunk has been read.
                                if (palette != null)
                                {
                                    palette.SetAlphaValues(bytes);
                                }
                                break;
                            }
                        }

                        read = stream.Read(crc, 0, crc.Length);
                        if (read != 4)
                        {
                            throw new InvalidOperationException($"Did not read 4 bytes for the CRC, only found: {read}.");
                        }

                        var result    = (int)Crc32.Calculate(Encoding.ASCII.GetBytes(header.Name), bytes);
                        var crcActual = (crc[0] << 24) + (crc[1] << 16) + (crc[2] << 8) + crc[3];

                        if (result != crcActual)
                        {
                            throw new InvalidOperationException($"CRC calculated {result} did not match file {crcActual} for chunk: {header.Name}.");
                        }

                        settings?.ChunkVisitor?.Visit(stream, imageHeader, header, bytes, crc);
                    }

                    memoryStream.Flush();
                    memoryStream.Seek(2, SeekOrigin.Begin);

                    using (var deflateStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
                    {
                        deflateStream.CopyTo(output);
                        deflateStream.Close();
                    }
                }

                var bytesOut = output.ToArray();

                var(bytesPerPixel, samplesPerPixel) = Decoder.GetBytesAndSamplesPerPixel(imageHeader);

                bytesOut = Decoder.Decode(bytesOut, imageHeader, bytesPerPixel, samplesPerPixel);

                return(new Png(imageHeader, new RawPngData(bytesOut, bytesPerPixel, palette, imageHeader), palette?.HasAlphaValues ?? false));
            }
        }
Exemple #2
0
        /// <summary>
        /// Create a new <see cref="RawPngData"/>.
        /// </summary>
        /// <param name="data">The decoded pixel data as bytes.</param>
        /// <param name="bytesPerPixel">The number of bytes in each pixel.</param>
        /// <param name="width">The width of the image in pixels.</param>
        /// <param name="interlaceMethod">The interlace method used.</param>
        /// <param name="palette">The palette for images using indexed colors.</param>
        /// <param name="colorType">The color type.</param>
        public RawPngData(byte[] data, int bytesPerPixel, int width, InterlaceMethod interlaceMethod, Palette palette, ColorType colorType)
        {
            if (width < 0)
            {
                throw new ArgumentOutOfRangeException($"Width must be greater than or equal to 0, got {width}.");
            }

            this.data          = data ?? throw new ArgumentNullException(nameof(data));
            this.bytesPerPixel = bytesPerPixel;
            this.width         = width;
            this.palette       = palette;
            this.colorType     = colorType;
            rowOffset          = interlaceMethod == InterlaceMethod.Adam7 ? 0 : 1;
        }