public void Load(Stream stream) { chunks = new List <APNGChunk>(); pngs = new List <PNG>(); // Create a new header (should be 1 per file) and // read it from the stream var header = new APNGHeader(); try { header.Read(stream); } catch (Exception) { stream.Close(); throw; } APNGChunk chunk; PNG png = null; // Read chunks from the stream until we reach the MEND chunk do { // Read a generic Chunk chunk = new APNGChunk(); try { chunk.Read(stream); } catch (Exception) { stream.Close(); throw; } // Take a look at the chunk type and decide what derived class to // use to create a specific chunk switch (chunk.ChunkType) { case acTLChunk.NAME: if (headerChunk != null) { throw new ApplicationException(String.Format( "Only one chunk of type {0} is allowed", chunk.ChunkType)); } chunk = headerChunk = new acTLChunk(chunk); break; case MENDChunk.NAME: chunk = new MENDChunk(chunk); break; case TERMChunk.NAME: chunk = new TERMChunk(chunk); break; case BACKChunk.NAME: chunk = new BACKChunk(chunk); break; case BKGDChunk.NAME: chunk = new BKGDChunk(chunk); break; case fcTLChunk.NAME: // This is the beginning of a new embedded PNG chunk = new fcTLChunk(chunk); png = new PNG { FCTL = chunk as fcTLChunk, IHDR = ihdrChunk }; pngs.Add(png); break; case IHDRChunk.NAME: chunk = new IHDRChunk(chunk); ihdrChunk = chunk as IHDRChunk; break; case IDATChunk.NAME: chunk = new IDATChunk(chunk); if (png != null) { png.IDAT = chunk as IDATChunk; } break; case fdATChunk.NAME: chunk = new fdATChunk(chunk); if (png != null) { chunk.ChunkType = IDATChunk.NAME; png.IDAT = new IDATChunk(chunk); } break; case IENDChunk.NAME: chunk = new IENDChunk(chunk); for (var i = 0; i < pngs.Count; i++) { pngs[i].IEND = chunk as IENDChunk; } break; default: break; } // Add the chunk to our list of chunks chunks.Add(chunk); }while (chunk.ChunkType != IENDChunk.NAME); }
/// <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; }