Skip to content

Commit 5cd2d29

Browse files
committed
Make PngEncoder respect DefaultImageAnimated
1 parent c232835 commit 5cd2d29

File tree

2 files changed

+53
-13
lines changed

2 files changed

+53
-13
lines changed

src/ImageSharp/Formats/Png/PngEncoderCore.cs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
167167

168168
ImageFrame<TPixel>? clonedFrame = null;
169169
ImageFrame<TPixel> currentFrame = image.Frames.RootFrame;
170+
int currentFrameIndex = 0;
170171

171172
bool clearTransparency = this.encoder.TransparentColorMode is PngTransparentColorMode.Clear;
172173
if (clearTransparency)
@@ -196,28 +197,49 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
196197
if (image.Frames.Count > 1)
197198
{
198199
this.WriteAnimationControlChunk(stream, (uint)image.Frames.Count, pngMetadata.RepeatCount);
200+
}
201+
202+
// If the first frame isn't animated, write it as usual and skip it when writing animated frames
203+
if (!pngMetadata.DefaultImageAnimated || image.Frames.Count == 1)
204+
{
205+
FrameControl frameControl = new((uint)this.width, (uint)this.height);
206+
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false);
207+
currentFrameIndex++;
208+
}
199209

200-
// Write the first frame.
210+
if (image.Frames.Count > 1)
211+
{
212+
// Write the first animated frame.
213+
currentFrame = image.Frames[currentFrameIndex];
201214
PngFrameMetadata frameMetadata = GetPngFrameMetadata(currentFrame);
202215
PngDisposalMethod previousDisposal = frameMetadata.DisposalMethod;
203216
FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0);
204-
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false);
217+
uint sequenceNumber = 1;
218+
if (pngMetadata.DefaultImageAnimated)
219+
{
220+
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false);
221+
}
222+
else
223+
{
224+
sequenceNumber += this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, true);
225+
}
226+
227+
currentFrameIndex++;
205228

206229
// Capture the global palette for reuse on subsequent frames.
207230
ReadOnlyMemory<TPixel>? previousPalette = quantized?.Palette.ToArray();
208231

209232
// Write following frames.
210-
uint increment = 0;
211233
ImageFrame<TPixel> previousFrame = image.Frames.RootFrame;
212234

213235
// This frame is reused to store de-duplicated pixel buffers.
214236
using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size());
215237

216-
for (int i = 1; i < image.Frames.Count; i++)
238+
for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++)
217239
{
218240
ImageFrame<TPixel>? prev = previousDisposal == PngDisposalMethod.RestoreToBackground ? null : previousFrame;
219-
currentFrame = image.Frames[i];
220-
ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
241+
currentFrame = image.Frames[currentFrameIndex];
242+
ImageFrame<TPixel>? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null;
221243

222244
frameMetadata = GetPngFrameMetadata(currentFrame);
223245
bool blend = frameMetadata.BlendMethod == PngBlendMethod.Over;
@@ -238,22 +260,17 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
238260
}
239261

240262
// Each frame control sequence number must be incremented by the number of frame data chunks that follow.
241-
frameControl = this.WriteFrameControlChunk(stream, frameMetadata, bounds, (uint)i + increment);
263+
frameControl = this.WriteFrameControlChunk(stream, frameMetadata, bounds, sequenceNumber);
242264

243265
// Dispose of previous quantized frame and reassign.
244266
quantized?.Dispose();
245267
quantized = this.CreateQuantizedImageAndUpdateBitDepth(pngMetadata, encodingFrame, bounds, previousPalette);
246-
increment += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true);
268+
sequenceNumber += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true) + 1;
247269

248270
previousFrame = currentFrame;
249271
previousDisposal = frameMetadata.DisposalMethod;
250272
}
251273
}
252-
else
253-
{
254-
FrameControl frameControl = new((uint)this.width, (uint)this.height);
255-
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false);
256-
}
257274

258275
this.WriteEndChunk(stream);
259276

tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,29 @@ public void Encode_AnimatedFormatTransform_FromWebp<TPixel>(TestImageProvider<TP
587587
}
588588
}
589589

590+
[Theory]
591+
[WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)]
592+
public void Encode_DefaultNotAnimated<TPixel>(TestImageProvider<TPixel> provider)
593+
where TPixel : unmanaged, IPixel<TPixel>
594+
{
595+
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
596+
using MemoryStream memStream = new();
597+
image.Save(memStream, PngEncoder);
598+
memStream.Position = 0;
599+
600+
image.DebugSave(provider: provider, encoder: PngEncoder, null, false);
601+
602+
using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
603+
ImageComparer.Exact.VerifySimilarity(output, image);
604+
605+
Assert.Equal(2, image.Frames.Count);
606+
Assert.Equal(image.Frames.Count, output.Frames.Count);
607+
608+
PngMetadata originalMetadata = image.Metadata.GetPngMetadata();
609+
PngMetadata outputMetadata = output.Metadata.GetPngMetadata();
610+
Assert.Equal(originalMetadata.DefaultImageAnimated, outputMetadata.DefaultImageAnimated);
611+
}
612+
590613
[Theory]
591614
[MemberData(nameof(PngTrnsFiles))]
592615
public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType)

0 commit comments

Comments
 (0)