|
|
|
@ -161,6 +161,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable |
|
|
|
|
|
|
|
ImageFrame<TPixel>? clonedFrame = null; |
|
|
|
ImageFrame<TPixel> currentFrame = image.Frames.RootFrame; |
|
|
|
int currentFrameIndex = 0; |
|
|
|
|
|
|
|
bool clearTransparency = this.encoder.TransparentColorMode is PngTransparentColorMode.Clear; |
|
|
|
if (clearTransparency) |
|
|
|
@ -189,29 +190,50 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable |
|
|
|
|
|
|
|
if (image.Frames.Count > 1) |
|
|
|
{ |
|
|
|
this.WriteAnimationControlChunk(stream, (uint)image.Frames.Count, pngMetadata.RepeatCount); |
|
|
|
this.WriteAnimationControlChunk(stream, (uint)(image.Frames.Count - (pngMetadata.AnimateRootFrame ? 0 : 1)), pngMetadata.RepeatCount); |
|
|
|
} |
|
|
|
|
|
|
|
// If the first frame isn't animated, write it as usual and skip it when writing animated frames
|
|
|
|
if (!pngMetadata.AnimateRootFrame || image.Frames.Count == 1) |
|
|
|
{ |
|
|
|
FrameControl frameControl = new((uint)this.width, (uint)this.height); |
|
|
|
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); |
|
|
|
currentFrameIndex++; |
|
|
|
} |
|
|
|
|
|
|
|
// Write the first frame.
|
|
|
|
if (image.Frames.Count > 1) |
|
|
|
{ |
|
|
|
// Write the first animated frame.
|
|
|
|
currentFrame = image.Frames[currentFrameIndex]; |
|
|
|
PngFrameMetadata frameMetadata = GetPngFrameMetadata(currentFrame); |
|
|
|
PngDisposalMethod previousDisposal = frameMetadata.DisposalMethod; |
|
|
|
FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0); |
|
|
|
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); |
|
|
|
uint sequenceNumber = 1; |
|
|
|
if (pngMetadata.AnimateRootFrame) |
|
|
|
{ |
|
|
|
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
sequenceNumber += this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, true); |
|
|
|
} |
|
|
|
|
|
|
|
currentFrameIndex++; |
|
|
|
|
|
|
|
// Capture the global palette for reuse on subsequent frames.
|
|
|
|
ReadOnlyMemory<TPixel>? previousPalette = quantized?.Palette.ToArray(); |
|
|
|
|
|
|
|
// Write following frames.
|
|
|
|
uint increment = 0; |
|
|
|
ImageFrame<TPixel> previousFrame = image.Frames.RootFrame; |
|
|
|
|
|
|
|
// This frame is reused to store de-duplicated pixel buffers.
|
|
|
|
using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size()); |
|
|
|
|
|
|
|
for (int i = 1; i < image.Frames.Count; i++) |
|
|
|
for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++) |
|
|
|
{ |
|
|
|
ImageFrame<TPixel>? prev = previousDisposal == PngDisposalMethod.RestoreToBackground ? null : previousFrame; |
|
|
|
currentFrame = image.Frames[i]; |
|
|
|
ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; |
|
|
|
currentFrame = image.Frames[currentFrameIndex]; |
|
|
|
ImageFrame<TPixel>? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null; |
|
|
|
|
|
|
|
frameMetadata = GetPngFrameMetadata(currentFrame); |
|
|
|
bool blend = frameMetadata.BlendMethod == PngBlendMethod.Over; |
|
|
|
@ -232,22 +254,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable |
|
|
|
} |
|
|
|
|
|
|
|
// Each frame control sequence number must be incremented by the number of frame data chunks that follow.
|
|
|
|
frameControl = this.WriteFrameControlChunk(stream, frameMetadata, bounds, (uint)i + increment); |
|
|
|
frameControl = this.WriteFrameControlChunk(stream, frameMetadata, bounds, sequenceNumber); |
|
|
|
|
|
|
|
// Dispose of previous quantized frame and reassign.
|
|
|
|
quantized?.Dispose(); |
|
|
|
quantized = this.CreateQuantizedImageAndUpdateBitDepth(pngMetadata, encodingFrame, bounds, previousPalette); |
|
|
|
increment += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true); |
|
|
|
sequenceNumber += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true) + 1; |
|
|
|
|
|
|
|
previousFrame = currentFrame; |
|
|
|
previousDisposal = frameMetadata.DisposalMethod; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
FrameControl frameControl = new((uint)this.width, (uint)this.height); |
|
|
|
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); |
|
|
|
} |
|
|
|
|
|
|
|
this.WriteEndChunk(stream); |
|
|
|
|
|
|
|
|