From 845527587c8bd8788ac79e401527ddf71e8b33dc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 23 Oct 2023 22:52:53 +1000 Subject: [PATCH] Handle disposal methods. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 25 ++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 776e52a331..317207da0f 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -210,7 +210,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals if (currentFrame is null) { - this.InitializeFrame(previousFrameControl.Value, image, out currentFrame); + this.InitializeFrame(previousFrameControl.Value, image, previousFrame, out currentFrame); } this.currentStream.Position += 4; @@ -612,14 +612,31 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// The type the pixels will be /// The frame control information for the frame /// The image that we will populate + /// The previous frame. /// The created frame - private void InitializeFrame(FrameControl frameControl, Image image, out ImageFrame frame) + private void InitializeFrame( + FrameControl frameControl, + Image image, + ImageFrame? previousFrame, + out ImageFrame frame) where TPixel : unmanaged, IPixel { - frame = image.Frames.CreateFrame(); + // We create a clone of the previous frame and add it. + // We will overpaint the difference of pixels on the current frame to create a complete image. + // This ensures that we have enough pixel data to process without distortion. #2450 + frame = image.Frames.AddFrame(previousFrame ?? image.Frames.RootFrame); - PngFrameMetadata frameMetadata = frame.Metadata.GetPngFrameMetadata(); + // If the first `fcTL` chunk uses a `dispose_op` of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND. + if (frameControl.DisposeOperation == PngDisposalMethod.Background + || (previousFrame is null && frameControl.DisposeOperation == PngDisposalMethod.Previous)) + { + Rectangle restoreArea = new((int)frameControl.XOffset, (int)frameControl.YOffset, (int)frameControl.Width, (int)frameControl.Height); + Rectangle interest = Rectangle.Intersect(frame.Bounds(), restoreArea); + Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(interest); + pixelRegion.Clear(); + } + PngFrameMetadata frameMetadata = frame.Metadata.GetPngFrameMetadata(); frameMetadata.FromChunk(frameControl); this.previousScanline?.Dispose();