void dIHDR(PNGChunk c) { IHDRChunk ic = new IHDRChunk(c); ind(); wo("Width: " + ic.Width); wo("Height: " + ic.Height); wo("BitDepth: " + ic.BitDepth); wo("ColorType: " + ic.ColorType + " " + decodeColorType(ic)); oud(); }
private static BitmapSource MakeFrame(IHDRChunk header, Frame rawFrame, Frame previousFrame, BitmapSource previousRenderedFrame) { var fs = rawFrame.GetBitmapSource(); var visual = new DrawingVisual(); using (var context = visual.RenderOpen()) { switch (rawFrame.fcTLChunk.DisposeOp) { case DisposeOps.APNGDisposeOpNone: // restore previousRenderedFrame //if (previousRenderedFrame != null) //{ // var fullRect = new Rect(0, 0, header.Width, header.Height); // context.DrawImage(previousRenderedFrame, fullRect); //} break; case DisposeOps.APNGDisposeOpPrevious: // restore previousFrame if (previousFrame != null) { var pFrameRect = new Rect(previousFrame.fcTLChunk.XOffset, previousFrame.fcTLChunk.YOffset, previousFrame.fcTLChunk.Width, previousFrame.fcTLChunk.Height); context.DrawImage(previousFrame.GetBitmapSource(), pFrameRect); } break; case DisposeOps.APNGDisposeOpBackground: // do nothing break; } // draw current frame var frameRect = new Rect(rawFrame.fcTLChunk.XOffset, rawFrame.fcTLChunk.YOffset, rawFrame.fcTLChunk.Width, rawFrame.fcTLChunk.Height); context.DrawImage(fs, frameRect); } var bitmap = new RenderTargetBitmap( header.Width, header.Height, Math.Floor(fs.DpiX), Math.Floor(fs.DpiY), PixelFormats.Pbgra32); bitmap.Render(visual); return(bitmap); }
public FrameInfo(IHDRChunk header, Frame frame) { FullRect = new Rect(0, 0, header.Width, header.Height); FrameRect = new Rect(frame.fcTLChunk.XOffset, frame.fcTLChunk.YOffset, frame.fcTLChunk.Width, frame.fcTLChunk.Height); BlendOp = frame.fcTLChunk.BlendOp; DisposeOp = frame.fcTLChunk.DisposeOp; Pixels = frame.GetBitmapSource(); Pixels.Freeze(); Delay = TimeSpan.FromSeconds((double)frame.fcTLChunk.DelayNum / (frame.fcTLChunk.DelayDen == 0 ? 100 : frame.fcTLChunk.DelayDen)); }
string decodeColorType(IHDRChunk ic) { List <string> ans = new List <string>(); if (ic.HasPalette) { ans.Add("palette"); } if (ic.HasColor) { ans.Add("color"); } if (ic.HasAlpha) { ans.Add("alpha"); } return(String.Join(",", ans.ToArray())); }
/// <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 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); }
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> /// Load the specified stream. /// </summary> /// <param name="stream">Stream representation 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)); }
private static BitmapSource MakeNextFrame(IHDRChunk header, Frame nextFrame, Frame currentFrame, BitmapSource currentRenderedFrame, BitmapSource previousStateRenderedFrame) { var fullRect = new Rect(0, 0, header.Width, header.Height); var frameRect = new Rect(nextFrame.fcTLChunk.XOffset, nextFrame.fcTLChunk.YOffset, nextFrame.fcTLChunk.Width, nextFrame.fcTLChunk.Height); var fs = nextFrame.GetBitmapSource(); var visual = new DrawingVisual(); using (var context = visual.RenderOpen()) { // protect region if (nextFrame.fcTLChunk.BlendOp == BlendOps.APNGBlendOpSource) { var freeRegion = new CombinedGeometry(GeometryCombineMode.Xor, new RectangleGeometry(fullRect), new RectangleGeometry(frameRect)); context.PushOpacityMask( new DrawingBrush(new GeometryDrawing(Brushes.Transparent, null, freeRegion))); } if (currentFrame != null && currentRenderedFrame != null) { switch (currentFrame.fcTLChunk.DisposeOp) { case DisposeOps.APNGDisposeOpNone: // restore currentRenderedFrame if (currentRenderedFrame != null) { context.DrawImage(currentRenderedFrame, fullRect); } break; case DisposeOps.APNGDisposeOpPrevious: // restore previousStateRenderedFrame if (previousStateRenderedFrame != null) { context.DrawImage(previousStateRenderedFrame, fullRect); } break; case DisposeOps.APNGDisposeOpBackground: // do nothing break; } } // unprotect region and draw the next frame if (nextFrame.fcTLChunk.BlendOp == BlendOps.APNGBlendOpSource) { context.Pop(); } context.DrawImage(fs, frameRect); } var bitmap = new RenderTargetBitmap( header.Width, header.Height, Math.Floor(fs.DpiX), Math.Floor(fs.DpiY), PixelFormats.Pbgra32); bitmap.Render(visual); return(bitmap); }