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();