Example #1
0
        /// <summary>
        /// Gets the frame as PNG FileStream.
        /// </summary>
        public MemoryStream GetStream()
        {
            var ihdrChunk = new IHDRChunk(IHDRChunk);
            if (fcTLChunk != null)
            {
                // Fix frame size with fcTL data.
                ihdrChunk.ModifyChunkData(0, Helper.ConvertEndian(fcTLChunk.Width));
                ihdrChunk.ModifyChunkData(4, Helper.ConvertEndian(fcTLChunk.Height));
            }

            // Write image data
            using (var ms = new MemoryStream())
            {
                ms.WriteBytes(Signature);
                ms.WriteBytes(ihdrChunk.RawData);

                foreach (IDATChunk idatChunk in idatChunks)
                    ms.WriteBytes(idatChunk.RawData);

                foreach (ITextChunk chunk in TextChunks)
                    ms.WriteBytes((chunk as Chunk).RawData);

                ms.WriteBytes(IENDChunk.RawData);

                ms.Position = 0;
                return ms;
            }
        }
Example #2
0
        /// <summary>
        ///     Gets the frame as PNG FileStream.
        /// </summary>
        public MemoryStream GetStream()
        {
            var ihdrChunk = new IHDRChunk(IHDRChunk);
            if (fcTLChunk != null)
            {
                // Fix frame size with fcTL data.
                ihdrChunk.ModifyChunkData(0, Helper.ConvertEndian(fcTLChunk.Width));
                ihdrChunk.ModifyChunkData(4, Helper.ConvertEndian(fcTLChunk.Height));
            }

            // Write image data
            using (var ms = new MemoryStream())
            {
                ms.WriteBytes(Signature);
                ms.WriteBytes(ihdrChunk.RawData);
                otherChunks.ForEach(o => ms.WriteBytes(o.RawData));
                idatChunks.ForEach(i => ms.WriteBytes(i.RawData));
                ms.WriteBytes(IENDChunk.RawData);

                ms.Position = 0;
                return ms;
            }
        }
Example #3
0
        /// <summary>
        ///     Gets the frame as PNG FileStream.
        /// </summary>
        public MemoryStream GetStream()
        {
            var ihdrChunk = new IHDRChunk(IHDRChunk);

            if (fcTLChunk != null)
            {
                // Fix frame size with fcTL data.
                ihdrChunk.ModifyChunkData(0, Helper.ConvertEndian(fcTLChunk.Width));
                ihdrChunk.ModifyChunkData(4, Helper.ConvertEndian(fcTLChunk.Height));
            }

            // Write image data
            using (var ms = new MemoryStream())
            {
                ms.WriteBytes(Signature);
                ms.WriteBytes(ihdrChunk.RawData);
                otherChunks.ForEach(o => ms.WriteBytes(o.RawData));
                idatChunks.ForEach(i => ms.WriteBytes(i.RawData));
                ms.WriteBytes(IENDChunk.RawData);

                ms.Position = 0;
                return(ms);
            }
        }
Example #4
0
        public APNG(byte[] fileBytes)
        {
            ms = new MemoryStream(fileBytes);

            // check file signature.
            if (!Helper.IsBytesEqual(ms.ReadBytes(Frame.Signature.Length), Frame.Signature))
            {
                throw new Exception("File signature incorrect.");
            }

            // Read IHDR chunk.
            IHDRChunk = new IHDRChunk(ms);
            if (IHDRChunk.ChunkType != "IHDR")
            {
                throw new Exception("IHDR chunk must located before any other chunks.");
            }

            // Now let's loop in chunks
            Chunk chunk;
            Frame frame               = null;
            var   otherChunks         = new List <OtherChunk>();
            bool  isIDATAlreadyParsed = false;

            do
            {
                if (ms.Position == ms.Length)
                {
                    throw new Exception("IEND chunk expected.");
                }

                chunk = new Chunk(ms);

                switch (chunk.ChunkType)
                {
                case "IHDR":
                    throw new Exception("Only single IHDR is allowed.");

                case "acTL":
                    if (IsSimplePNG)
                    {
                        throw new Exception("acTL chunk must located before any IDAT and fdAT");
                    }

                    acTLChunk = new acTLChunk(chunk);
                    break;

                case "IDAT":
                    // To be an APNG, acTL must located before any IDAT and fdAT.
                    if (acTLChunk == null)
                    {
                        IsSimplePNG = true;
                    }

                    // Only default image has IDAT.
                    defaultImage.IHDRChunk = IHDRChunk;
                    defaultImage.AddIDATChunk(new IDATChunk(chunk));
                    isIDATAlreadyParsed = true;
                    break;

                case "fcTL":
                    // Simple PNG should ignore this.
                    if (IsSimplePNG)
                    {
                        continue;
                    }

                    if (frame != null && frame.IDATChunks.Count == 0)
                    {
                        throw new Exception("One frame must have only one fcTL chunk.");
                    }

                    // IDAT already parsed means this fcTL is used by FRAME IMAGE.
                    if (isIDATAlreadyParsed)
                    {
                        // register current frame object and build a new frame object
                        // for next use
                        if (frame != null)
                        {
                            frames.Add(frame);
                        }
                        frame = new Frame
                        {
                            IHDRChunk = IHDRChunk,
                            fcTLChunk = new fcTLChunk(chunk)
                        };
                    }
                    // Otherwise this fcTL is used by the DEFAULT IMAGE.
                    else
                    {
                        defaultImage.fcTLChunk = new fcTLChunk(chunk);
                    }
                    break;

                case "fdAT":
                    // Simple PNG should ignore this.
                    if (IsSimplePNG)
                    {
                        continue;
                    }

                    // fdAT is only used by frame image.
                    if (frame == null || frame.fcTLChunk == null)
                    {
                        throw new Exception("fcTL chunk expected.");
                    }

                    frame.AddIDATChunk(new fdATChunk(chunk).ToIDATChunk());
                    break;

                case "IEND":
                    // register last frame object
                    if (frame != null)
                    {
                        frames.Add(frame);
                    }

                    if (DefaultImage.IDATChunks.Count != 0)
                    {
                        DefaultImage.IENDChunk = new IENDChunk(chunk);
                    }
                    foreach (Frame f in frames)
                    {
                        f.IENDChunk = new IENDChunk(chunk);
                    }
                    break;

                default:
                    otherChunks.Add(new OtherChunk(chunk));
                    break;
                }
            } while (chunk.ChunkType != "IEND");

            // We have one more thing to do:
            // If the default image if part of the animation,
            // we should insert it into frames list.
            if (defaultImage.fcTLChunk != null)
            {
                frames.Insert(0, defaultImage);
                DefaultImageIsAnimated = true;
            }

            // Now we should apply every chunk in otherChunks to every frame.
            frames.ForEach(f => otherChunks.ForEach(f.AddOtherChunk));
        }
Example #5
0
        public APNG(byte[] fileBytes)
        {
            ms = new MemoryStream(fileBytes);

            // check file signature.
            if (!Helper.IsBytesEqual(ms.ReadBytes(Frame.Signature.Length), Frame.Signature))
                throw new Exception("File signature incorrect.");

            // Read IHDR chunk.
            IHDRChunk = new IHDRChunk(ms);
            if (IHDRChunk.ChunkType != "IHDR")
                throw new Exception("IHDR chunk must located before any other chunks.");

            // Now let's loop in chunks
            Chunk chunk;
            Frame frame = null;
            List<ITextChunk> text_chunks = new List<ITextChunk>();
            bool isIDATAlreadyParsed = false;
            do
            {
                if (ms.Position == ms.Length)
                    throw new Exception("IEND chunk expected.");

                chunk = new Chunk(ms);

                switch (chunk.ChunkType)
                {
                    case "IHDR":
                        throw new Exception("Only single IHDR is allowed.");
                        break;

                    case "acTL":
                        if (IsSimplePNG)
                            throw new Exception("acTL chunk must located before any IDAT and fdAT");

                        TextChunks = text_chunks.ToArray();
                        text_chunks.Clear();

                        acTLChunk = new acTLChunk(chunk);
                        break;

                    case "IDAT":
                        // To be an APNG, acTL must located before any IDAT and fdAT.
                        if (acTLChunk == null)
                        {
                            TextChunks = text_chunks.ToArray();
                            text_chunks.Clear();
                            IsSimplePNG = true;
                        }

                        // Only default image has IDAT.
                        defaultImage.IHDRChunk = IHDRChunk;
                        defaultImage.AddIDATChunk(new IDATChunk(chunk));
                        isIDATAlreadyParsed = true;
                        break;

                    case "fcTL":
                        // Simple PNG should ignore this.
                        if (IsSimplePNG)
                            continue;

                        if (frame != null && frame.IDATChunks.Count == 0)
                            throw new Exception("One frame must have only one fcTL chunk.");

                        // IDAT already parsed means this fcTL is used by FRAME IMAGE.
                        if (isIDATAlreadyParsed)
                        {
                            // register current frame object and build a new frame object
                            // for next use
                            if (frame != null)
                            {
                                frame.TextChunks = text_chunks.ToArray();
                                frames.Add(frame);
                                text_chunks.Clear();
                            }

                            frame = new Frame
                                        {
                                            IHDRChunk = IHDRChunk,
                                            fcTLChunk = new fcTLChunk(chunk)
                                        };
                        }
                            // Otherwise this fcTL is used by the DEFAULT IMAGE.
                        else
                        {
                            defaultImage.fcTLChunk = new fcTLChunk(chunk);
                        }
                        break;
                    case "fdAT":
                        // Simple PNG should ignore this.
                        if (IsSimplePNG)
                            continue;

                        // fdAT is only used by frame image.
                        if (frame == null || frame.fcTLChunk == null)
                            throw new Exception("fcTL chunk expected.");

                        frame.AddIDATChunk(new fdATChunk(chunk).ToIDATChunk());
                        break;

                    case "IEND":
                        // register last frame object
                        if (frame != null)
                        {
                            frame.TextChunks = text_chunks.ToArray();
                            frames.Add(frame);
                            text_chunks.Clear();
                        }

                        if (DefaultImage.IDATChunks.Count != 0)
                            DefaultImage.IENDChunk = new IENDChunk(chunk);
                        foreach (Frame f in frames)
                        {
                            f.IENDChunk = new IENDChunk(chunk);
                        }
                        break;

                    case "iTXt":
                        text_chunks.Add(new iTXtChunk(chunk));
                        break;

                    case "tEXt":
                        text_chunks.Add(new tEXtChunk(chunk));
                        break;

                    case "zTXt":
                        text_chunks.Add(new zTXtChunk(chunk));
                        break;

                    default:
                        //TODO: Handle other chunks.
                        break;
                }
            } while (chunk.ChunkType != "IEND");

            // We have one more thing to do:
            // If the default image if part of the animation,
            // we should insert it into frames list.
            if (defaultImage.fcTLChunk != null)
            {
                frames.Insert(0, defaultImage);
                DefaultImageIsAnimeated = true;
            }
        }