Esempio n. 1
0
        /// <summary>
        /// Adds an image as the next frame.
        /// </summary>
        /// <param name="image">Png frame.</param>
        public void AddFrame(Image image)
        {
            //TODO: Handle different sizes
            //Temporarily reject improper sizes.
            if (IHDRChunk != null && (image.Width > IHDRChunk.Width || image.Height > IHDRChunk.Height))
            {
                throw new InvalidDataException("Frame must be less than or equal to the size of the other frames.");
            }

            APNG apng = APNG.FromImage(image);

            if (IHDRChunk == null)
            {
                IHDRChunk = apng.IHDRChunk;
            }

            //Create acTL Chunk.
            if (acTLChunk == null)
            {
                acTLChunk           = new acTLChunk();
                acTLChunk.PlayCount = 0;
            }

            uint sequenceNumber = (frames.Count == 0) ? 0 : (uint)(frames[frames.Count - 1].fcTLChunk.SequenceNumber + frames[frames.Count - 1].IDATChunks.Count);
            //Create fcTL Chunk
            fcTLChunk fctl = new fcTLChunk
            {
                SequenceNumber   = sequenceNumber,
                Width            = (uint)image.Width,
                Height           = (uint)image.Height,
                XOffset          = 0,
                YOffset          = 0,
                DelayNumerator   = 100,
                DelayDenominator = 1000,
                DisposeOp        = DisposeOps.APNGDisposeOpNone,
                BlendOp          = BlendOps.APNGBlendOpSource
            };

            //Set the default image if needed.
            if (defaultImage.IDATChunks.Count == 0)
            {
                defaultImage           = apng.DefaultImage;
                defaultImage.fcTLChunk = fctl;
                DefaultImageIsAnimated = true;
            }

            //Add all the frames from the png.
            if (apng.IsSimplePNG)
            {
                Frame frame = apng.DefaultImage;
                frame.fcTLChunk = fctl;

                foreach (OtherChunk chunk in frame.OtherChunks)
                {
                    if (!defaultImage.OtherChunks.Contains(chunk))
                    {
                        defaultImage.OtherChunks.Add(chunk);
                    }
                }
                frame.OtherChunks.Clear();
                frames.Add(frame);
            }
            else
            {
                for (int i = 0; i < apng.FrameCount; ++i)
                {
                    Frame frame = apng.Frames[i];
                    frame.fcTLChunk.SequenceNumber = sequenceNumber;
                    foreach (OtherChunk chunk in frame.OtherChunks)
                    {
                        if (!defaultImage.OtherChunks.Contains(chunk))
                        {
                            defaultImage.OtherChunks.Add(chunk);
                        }
                    }
                    frame.OtherChunks.Clear();
                    frames.Add(frame);
                }
            }
            List <OtherChunk> otherChunks = defaultImage.OtherChunks;

            // Now we should apply every chunk in otherChunks to every frame.
            if (defaultImage != frames[0])
            {
                frames.ForEach(f => otherChunks.ForEach(f.AddOtherChunk));
            }
            else
            {
                for (int i = 1; i < frames.Count; ++i)
                {
                    otherChunks.ForEach(frames[i].AddOtherChunk);
                }
            }

            acTLChunk.FrameCount = (uint)frames.Count;
        }
Esempio n. 2
0
        /// <summary>
        /// Load the specified stream.
        /// </summary>
        /// <param name="stream">Streamrepresentation of the png file.</param>
        internal void Load(MemoryStream stream)
        {
            ms = stream;

            // 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.");
            }

            viewSize = new Size(IHDRChunk.Width, IHDRChunk.Height);

            // 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 is part of the animation,
            // we should insert it into frames list.
            if (defaultImage.fcTLChunk != null)
            {
                frames.Insert(0, defaultImage);
                DefaultImageIsAnimated = true;
            }
            else //If it isn't animated it still needs the other chunks.
            {
                otherChunks.ForEach(defaultImage.AddOtherChunk);
            }

            // Now we should apply every chunk in otherChunks to every frame.
            frames.ForEach(f => otherChunks.ForEach(f.AddOtherChunk));
        }