/// <summary> /// Creates an Animated PNG from a Image. /// </summary> /// <returns>The file.</returns> /// <param name="image">Image.</param> public static APNG FromImage(Image image) { APNG apng = new APNG(); apng.Load(APNG.ImageToStream(image)); return(apng); }
/// <summary> /// Creates an Animated PNG from a stream. /// </summary> /// <returns>The file.</returns> /// <param name="stream">The stream.</param> public static APNG FromStream(MemoryStream stream) { APNG apng = new APNG(); apng.Load(stream); return(apng); }
/// <summary> /// Creates an Animated PNG from a file. /// </summary> /// <returns>The file.</returns> /// <param name="filename">Filename.</param> public static APNG FromFile(string filename) { APNG apng = new APNG(); apng.Load(filename); return(apng); }
private static void ProcessingThread(byte[] apngData, AnimationInfo animationInfo) { APNG.APNG apng = APNG.APNG.FromStream(new System.IO.MemoryStream(apngData)); int frameCount = apng.FrameCount; animationInfo.frameCount = frameCount; animationInfo.initialized = true; animationInfo.frames = new System.Collections.Generic.List <FrameInfo>(frameCount); FrameInfo prevFrame = default; for (int i = 0; i < frameCount; i++) { Frame apngFrame = apng.Frames[i]; using (Bitmap bitmap = apngFrame.ToBitmap()) { FrameInfo frameInfo = new FrameInfo(bitmap.Width, bitmap.Height); bitmap.MakeTransparent(System.Drawing.Color.Black); bitmap.RotateFlip(RotateFlipType.Rotate180FlipX); BitmapData frame = bitmap.LockBits(new Rectangle(Point.Empty, apng.ActualSize), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); Marshal.Copy(frame.Scan0, frameInfo.colors, 0, frameInfo.colors.Length); bitmap.UnlockBits(frame); if (apngFrame.fcTLChunk.BlendOp == APNG.Chunks.BlendOps.APNGBlendOpOver && i > 0) { // BGRA var last = prevFrame.colors; var src = frameInfo.colors; for (var clri = frameInfo.colors.Length - 1; i > 2; i -= 4) { var srcA = src[clri - 3] * byteInverse; var lastA = last[clri - 3] * byteInverse; float blendedA = srcA + (1 - srcA) * lastA; src[clri - 3] = (byte)Math.Round(blendedA * 255); for (var c = 0; c < 3; c++) { var srcC = src[clri - i] * byteInverse; var lastC = last[clri - i] * byteInverse; src[clri - i] = (byte)Math.Round((srcA * srcC + (1 - srcA) * lastA * lastC * 255f) / blendedA); } } } frameInfo.delay = apngFrame.FrameRate; animationInfo.frames.Add(frameInfo); prevFrame = frameInfo; } } }
private static void ProcessingThread(byte[] apngData, AnimationInfo animationInfo) { APNG.APNG apng = APNG.APNG.FromStream(new System.IO.MemoryStream(apngData)); int frameCount = apng.FrameCount; animationInfo.frameCount = frameCount; animationInfo.initialized = true; for (int i = 0; i < frameCount; i++) { Frame frame = apng.Frames[i]; System.Drawing.Bitmap bitmap = frame.ToBitmap(); LockBitmap lockBitmap = new LockBitmap(bitmap); lockBitmap.LockBits(); FrameInfo frameInfo = new FrameInfo(bitmap.Width, bitmap.Height); for (int x = 0; x < frameInfo.width; x++) { for (int y = 0; y < frameInfo.height; y++) { System.Drawing.Color sourceColor = lockBitmap.GetPixel(x, y); Color32 lastFrame = new Color32(); if (i > 0) { lastFrame = animationInfo.frames.Last().colors[(frameInfo.height - y - 1) * frameInfo.width + x]; } if (frame.fcTLChunk.BlendOp == APNG.Chunks.BlendOps.APNGBlendOpSource) { frameInfo.colors[(frameInfo.height - y - 1) * frameInfo.width + x] = new Color32(sourceColor.R, sourceColor.G, sourceColor.B, sourceColor.A); } if (frame.fcTLChunk.BlendOp == APNG.Chunks.BlendOps.APNGBlendOpOver) { float blendedA = ((sourceColor.A / 255f + (1 - (sourceColor.A / 255f)) * (lastFrame.a / 255f))); float blendedR = ((sourceColor.A / 255f) * (sourceColor.R / 255f) + (1 - (sourceColor.A / 255f)) * (lastFrame.a / 255f) * (lastFrame.r / 255f)) / blendedA; float blendedG = ((sourceColor.A / 255f) * (sourceColor.G / 255f) + (1 - (sourceColor.A / 255f)) * (lastFrame.a / 255f) * (lastFrame.g / 255f)) / blendedA; float blendedB = ((sourceColor.A / 255f) * (sourceColor.B / 255f) + (1 - (sourceColor.A / 255f)) * (lastFrame.a / 255f) * (lastFrame.b / 255f)) / blendedA; frameInfo.colors[(frameInfo.height - y - 1) * frameInfo.width + x] = new Color32((byte)(blendedR * 255), (byte)(blendedG * 255), (byte)(blendedB * 255), (byte)(blendedA * 255)); } } } frameInfo.delay = frame.FrameRate; animationInfo.frames.Add(frameInfo); } }
/// <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; }