diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
index 7caaa5868d..fc58ef3440 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
@@ -1,6 +1,7 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
@@ -656,6 +657,36 @@ public static Vector128<byte> BlendVariable(Vector128<byte> left, Vector128<byte
             return AdvSimd.BitwiseSelect(signedMask, right.AsInt16(), left.AsInt16()).AsByte();
         }
 
+        /// <summary>
+        /// Blend packed 32-bit unsigned integers from <paramref name="left"/> and <paramref name="right"/> using <paramref name="mask"/>.
+        /// The high bit of each corresponding <paramref name="mask"/> byte determines the selection.
+        /// If the high bit is set the element of <paramref name="left"/> is selected.
+        /// The element of <paramref name="right"/> is selected otherwise.
+        /// </summary>
+        /// <param name="left">The left vector.</param>
+        /// <param name="right">The right vector.</param>
+        /// <param name="mask">The mask vector.</param>
+        /// <returns>The <see cref="Vector256{T}"/>.</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Vector128<uint> BlendVariable(Vector128<uint> left, Vector128<uint> right, Vector128<uint> mask)
+            => BlendVariable(left.AsByte(), right.AsByte(), mask.AsByte()).AsUInt32();
+
+        /// <summary>
+        /// Count the number of leading zero bits in a mask.
+        /// Similar in behavior to the x86 instruction LZCNT.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        public static ushort LeadingZeroCount(ushort value)
+            => (ushort)(BitOperations.LeadingZeroCount(value) - 16);
+
+        /// <summary>
+        /// Count the number of trailing zero bits in an integer value.
+        /// Similar in behavior to the x86 instruction TZCNT.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        public static ushort TrailingZeroCount(ushort value)
+            => (ushort)(BitOperations.TrailingZeroCount(value << 16) - 16);
+
         /// <summary>
         /// <see cref="ByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
         /// </summary>
diff --git a/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs b/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs
new file mode 100644
index 0000000000..5f4015180b
--- /dev/null
+++ b/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats;
+internal class AnimatedImageFrameMetadata
+{
+    /// <summary>
+    /// Gets or sets the frame color table.
+    /// </summary>
+    public ReadOnlyMemory<Color>? ColorTable { get; set; }
+
+    /// <summary>
+    /// Gets or sets the frame color table mode.
+    /// </summary>
+    public FrameColorTableMode ColorTableMode { get; set; }
+
+    /// <summary>
+    /// Gets or sets the duration of the frame.
+    /// </summary>
+    public TimeSpan Duration { get; set; }
+
+    /// <summary>
+    /// Gets or sets the frame alpha blending mode.
+    /// </summary>
+    public FrameBlendMode BlendMode { get; set; }
+
+    /// <summary>
+    /// Gets or sets the frame disposal mode.
+    /// </summary>
+    public FrameDisposalMode DisposalMode { get; set; }
+}
+
+#pragma warning disable SA1201 // Elements should appear in the correct order
+internal enum FrameBlendMode
+#pragma warning restore SA1201 // Elements should appear in the correct order
+{
+    /// <summary>
+    /// Do not blend. Render the current frame on the canvas by overwriting the rectangle covered by the current frame.
+    /// </summary>
+    Source = 0,
+
+    /// <summary>
+    /// Blend the current frame with the previous frame in the animation sequence within the rectangle covered
+    /// by the current frame.
+    /// If the current has any transparent areas, the corresponding areas of the previous frame will be visible
+    /// through these transparent regions.
+    /// </summary>
+    Over = 1
+}
+
+internal enum FrameDisposalMode
+{
+    /// <summary>
+    /// No disposal specified.
+    /// The decoder is not required to take any action.
+    /// </summary>
+    Unspecified = 0,
+
+    /// <summary>
+    /// Do not dispose. The current frame is not disposed of, or in other words, not cleared or altered when moving to
+    /// the next frame. This means that the next frame is drawn over the current frame, and if the next frame contains
+    /// transparency, the previous frame will be visible through these transparent areas.
+    /// </summary>
+    DoNotDispose = 1,
+
+    /// <summary>
+    /// Restore to background color. When transitioning to the next frame, the area occupied by the current frame is
+    /// filled with the background color specified in the image metadata.
+    /// This effectively erases the current frame by replacing it with the background color before the next frame is displayed.
+    /// </summary>
+    RestoreToBackground = 2,
+
+    /// <summary>
+    /// Restore to previous. This method restores the area affected by the current frame to what it was before the
+    /// current frame was displayed. It essentially "undoes" the current frame, reverting to the state of the image
+    /// before the frame was displayed, then the next frame is drawn. This is useful for animations where only a small
+    /// part of the image changes from frame to frame.
+    /// </summary>
+    RestoreToPrevious = 3
+}
+
+internal enum FrameColorTableMode
+{
+    /// <summary>
+    /// The frame uses the shared color table specified by the image metadata.
+    /// </summary>
+    Global,
+
+    /// <summary>
+    /// The frame uses a color table specified by the frame metadata.
+    /// </summary>
+    Local
+}
diff --git a/src/ImageSharp/Formats/AnimatedImageMetadata.cs b/src/ImageSharp/Formats/AnimatedImageMetadata.cs
new file mode 100644
index 0000000000..d89ec41f07
--- /dev/null
+++ b/src/ImageSharp/Formats/AnimatedImageMetadata.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats;
+internal class AnimatedImageMetadata
+{
+    /// <summary>
+    /// Gets or sets the shared color table.
+    /// </summary>
+    public ReadOnlyMemory<Color>? ColorTable { get; set; }
+
+    /// <summary>
+    /// Gets or sets the shared color table mode.
+    /// </summary>
+    public FrameColorTableMode ColorTableMode { get; set; }
+
+    /// <summary>
+    /// Gets or sets the default background color of the canvas when animating.
+    /// This color may be used to fill the unused space on the canvas around the frames,
+    /// as well as the transparent pixels of the first frame.
+    /// The background color is also used when the disposal mode is <see cref="FrameDisposalMode.RestoreToBackground"/>.
+    /// </summary>
+    public Color BackgroundColor { get; set; }
+
+    /// <summary>
+    /// Gets or sets the number of times any animation is repeated.
+    /// <remarks>
+    /// 0 means to repeat indefinitely, count is set as repeat n-1 times. Defaults to 1.
+    /// </remarks>
+    /// </summary>
+    public ushort RepeatCount { get; set; }
+}
diff --git a/src/ImageSharp/Formats/AnimationUtilities.cs b/src/ImageSharp/Formats/AnimationUtilities.cs
new file mode 100644
index 0000000000..67ee72e95a
--- /dev/null
+++ b/src/ImageSharp/Formats/AnimationUtilities.cs
@@ -0,0 +1,244 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Buffers;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats;
+
+/// <summary>
+/// Utility methods for animated formats.
+/// </summary>
+internal static class AnimationUtilities
+{
+    /// <summary>
+    /// Deduplicates pixels between the previous and current frame returning only the changed pixels and bounds.
+    /// </summary>
+    /// <typeparam name="TPixel">The type of pixel format.</typeparam>
+    /// <param name="configuration">The configuration.</param>
+    /// <param name="previousFrame">The previous frame if present.</param>
+    /// <param name="currentFrame">The current frame.</param>
+    /// <param name="nextFrame">The next frame if present.</param>
+    /// <param name="resultFrame">The resultant output.</param>
+    /// <param name="replacement">The value to use when replacing duplicate pixels.</param>
+    /// <param name="blend">Whether the resultant frame represents an animation blend.</param>
+    /// <param name="clampingMode">The clamping bound to apply when calculating difference bounds.</param>
+    /// <returns>The <see cref="ValueTuple{Boolean, Rectangle}"/> representing the operation result.</returns>
+    public static (bool Difference, Rectangle Bounds) DeDuplicatePixels<TPixel>(
+        Configuration configuration,
+        ImageFrame<TPixel>? previousFrame,
+        ImageFrame<TPixel> currentFrame,
+        ImageFrame<TPixel>? nextFrame,
+        ImageFrame<TPixel> resultFrame,
+        Color replacement,
+        bool blend,
+        ClampingMode clampingMode = ClampingMode.None)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
+        using IMemoryOwner<Rgba32> buffers = memoryAllocator.Allocate<Rgba32>(currentFrame.Width * 4, AllocationOptions.Clean);
+        Span<Rgba32> previous = buffers.GetSpan()[..currentFrame.Width];
+        Span<Rgba32> current = buffers.GetSpan().Slice(currentFrame.Width, currentFrame.Width);
+        Span<Rgba32> next = buffers.GetSpan().Slice(currentFrame.Width * 2, currentFrame.Width);
+        Span<Rgba32> result = buffers.GetSpan()[(currentFrame.Width * 3)..];
+
+        Rgba32 bg = replacement;
+
+        int top = int.MinValue;
+        int bottom = int.MaxValue;
+        int left = int.MaxValue;
+        int right = int.MinValue;
+
+        bool hasDiff = false;
+        for (int y = 0; y < currentFrame.Height; y++)
+        {
+            if (previousFrame != null)
+            {
+                PixelOperations<TPixel>.Instance.ToRgba32(configuration, previousFrame.DangerousGetPixelRowMemory(y).Span, previous);
+            }
+
+            PixelOperations<TPixel>.Instance.ToRgba32(configuration, currentFrame.DangerousGetPixelRowMemory(y).Span, current);
+
+            if (nextFrame != null)
+            {
+                PixelOperations<TPixel>.Instance.ToRgba32(configuration, nextFrame.DangerousGetPixelRowMemory(y).Span, next);
+            }
+
+            ref Vector256<byte> previousBase256 = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(previous));
+            ref Vector256<byte> currentBase256 = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(current));
+            ref Vector256<byte> nextBase256 = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(next));
+            ref Vector256<byte> resultBase256 = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(result));
+
+            int i = 0;
+            uint x = 0;
+            bool hasRowDiff = false;
+            int length = current.Length;
+            int remaining = current.Length;
+
+            if (Avx2.IsSupported && remaining >= 8)
+            {
+                Vector256<uint> r256 = previousFrame != null ? Vector256.Create(bg.PackedValue) : Vector256<uint>.Zero;
+                Vector256<uint> vmb256 = Vector256<uint>.Zero;
+                if (blend)
+                {
+                    vmb256 = Avx2.CompareEqual(vmb256, vmb256);
+                }
+
+                while (remaining >= 8)
+                {
+                    Vector256<uint> p = Unsafe.Add(ref previousBase256, x).AsUInt32();
+                    Vector256<uint> c = Unsafe.Add(ref currentBase256, x).AsUInt32();
+
+                    Vector256<uint> eq = Avx2.CompareEqual(p, c);
+                    Vector256<uint> r = Avx2.BlendVariable(c, r256, Avx2.And(eq, vmb256));
+
+                    if (nextFrame != null)
+                    {
+                        Vector256<int> n = Avx2.ShiftRightLogical(Unsafe.Add(ref nextBase256, x).AsUInt32(), 24).AsInt32();
+                        eq = Avx2.AndNot(Avx2.CompareGreaterThan(Avx2.ShiftRightLogical(c, 24).AsInt32(), n).AsUInt32(), eq);
+                    }
+
+                    Unsafe.Add(ref resultBase256, x) = r.AsByte();
+
+                    uint msk = (uint)Avx2.MoveMask(eq.AsByte());
+                    msk = ~msk;
+
+                    if (msk != 0)
+                    {
+                        // If is diff is found, the left side is marked by the min of previously found left side and the start position.
+                        // The right is the max of the previously found right side and the end position.
+                        int start = i + (BitOperations.TrailingZeroCount(msk) / sizeof(uint));
+                        int end = i + (8 - (BitOperations.LeadingZeroCount(msk) / sizeof(uint)));
+                        left = Math.Min(left, start);
+                        right = Math.Max(right, end);
+                        hasRowDiff = true;
+                        hasDiff = true;
+                    }
+
+                    x++;
+                    i += 8;
+                    remaining -= 8;
+                }
+            }
+
+            if (Sse2.IsSupported && remaining >= 4)
+            {
+                // Update offset since we may be operating on the remainder previously incremented by pixel steps of 8.
+                x *= 2;
+                Vector128<uint> r128 = previousFrame != null ? Vector128.Create(bg.PackedValue) : Vector128<uint>.Zero;
+                Vector128<uint> vmb128 = Vector128<uint>.Zero;
+                if (blend)
+                {
+                    vmb128 = Sse2.CompareEqual(vmb128, vmb128);
+                }
+
+                while (remaining >= 4)
+                {
+                    Vector128<uint> p = Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref previousBase256), x);
+                    Vector128<uint> c = Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref currentBase256), x);
+
+                    Vector128<uint> eq = Sse2.CompareEqual(p, c);
+                    Vector128<uint> r = SimdUtils.HwIntrinsics.BlendVariable(c, r128, Sse2.And(eq, vmb128));
+
+                    if (nextFrame != null)
+                    {
+                        Vector128<int> n = Sse2.ShiftRightLogical(Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref nextBase256), x), 24).AsInt32();
+                        eq = Sse2.AndNot(Sse2.CompareGreaterThan(Sse2.ShiftRightLogical(c, 24).AsInt32(), n).AsUInt32(), eq);
+                    }
+
+                    Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref resultBase256), x) = r;
+
+                    ushort msk = (ushort)(uint)Sse2.MoveMask(eq.AsByte());
+                    msk = (ushort)~msk;
+                    if (msk != 0)
+                    {
+                        // If is diff is found, the left side is marked by the min of previously found left side and the start position.
+                        // The right is the max of the previously found right side and the end position.
+                        int start = i + (SimdUtils.HwIntrinsics.TrailingZeroCount(msk) / sizeof(uint));
+                        int end = i + (4 - (SimdUtils.HwIntrinsics.LeadingZeroCount(msk) / sizeof(uint)));
+                        left = Math.Min(left, start);
+                        right = Math.Max(right, end);
+                        hasRowDiff = true;
+                        hasDiff = true;
+                    }
+
+                    x++;
+                    i += 4;
+                    remaining -= 4;
+                }
+            }
+
+            // TODO: v4 AdvSimd when we can use .NET 8
+            for (i = remaining; i > 0; i--)
+            {
+                x = (uint)(length - i);
+
+                Rgba32 p = Unsafe.Add(ref MemoryMarshal.GetReference(previous), x);
+                Rgba32 c = Unsafe.Add(ref MemoryMarshal.GetReference(current), x);
+                Rgba32 n = Unsafe.Add(ref MemoryMarshal.GetReference(next), x);
+                ref Rgba32 r = ref Unsafe.Add(ref MemoryMarshal.GetReference(result), x);
+
+                bool peq = c.Rgba == (previousFrame != null ? p.Rgba : bg.Rgba);
+                Rgba32 val = (blend & peq) ? replacement : c;
+
+                peq &= nextFrame == null || (n.Rgba >> 24 >= c.Rgba >> 24);
+                r = val;
+
+                if (!peq)
+                {
+                    // If is diff is found, the left side is marked by the min of previously found left side and the diff position.
+                    // The right is the max of the previously found right side and the diff position + 1.
+                    left = Math.Min(left, (int)x);
+                    right = Math.Max(right, (int)x + 1);
+                    hasRowDiff = true;
+                    hasDiff = true;
+                }
+            }
+
+            if (hasRowDiff)
+            {
+                if (top == int.MinValue)
+                {
+                    top = y;
+                }
+
+                bottom = y + 1;
+            }
+
+            PixelOperations<TPixel>.Instance.FromRgba32(configuration, result, resultFrame.DangerousGetPixelRowMemory(y).Span);
+        }
+
+        Rectangle bounds = Rectangle.FromLTRB(
+            left = Numerics.Clamp(left, 0, resultFrame.Width - 1),
+            top = Numerics.Clamp(top, 0, resultFrame.Height - 1),
+            Numerics.Clamp(right, left + 1, resultFrame.Width),
+            Numerics.Clamp(bottom, top + 1, resultFrame.Height));
+
+        // Webp requires even bounds
+        if (clampingMode == ClampingMode.Even)
+        {
+            bounds.Width = Math.Min(resultFrame.Width, bounds.Width + (bounds.X & 1));
+            bounds.Height = Math.Min(resultFrame.Height, bounds.Height + (bounds.Y & 1));
+            bounds.X = Math.Max(0, bounds.X - (bounds.X & 1));
+            bounds.Y = Math.Max(0, bounds.Y - (bounds.Y & 1));
+        }
+
+        return (hasDiff, bounds);
+    }
+}
+
+#pragma warning disable SA1201 // Elements should appear in the correct order
+internal enum ClampingMode
+#pragma warning restore SA1201 // Elements should appear in the correct order
+{
+    None,
+
+    Even,
+}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index bc41c89dcf..aecbbbbc71 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -797,6 +797,8 @@ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream s
                 this.gifMetadata.GlobalColorTable = colorTable;
             }
         }
+
+        this.gifMetadata.BackgroundColorIndex = this.logicalScreenDescriptor.BackgroundColorIndex;
     }
 
     private unsafe struct ScratchBuffer
diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs
index 150ee9ccf0..ab05548ac5 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs
@@ -1,8 +1,6 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
-using SixLabors.ImageSharp.Advanced;
-
 namespace SixLabors.ImageSharp.Formats.Gif;
 
 /// <summary>
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 926cc091c7..f0e1aafd7d 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -4,10 +4,9 @@
 using System.Buffers;
 using System.Numerics;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.X86;
 using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Memory;
 using SixLabors.ImageSharp.Metadata;
 using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
@@ -86,8 +85,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
         Guard.NotNull(image, nameof(image));
         Guard.NotNull(stream, nameof(stream));
 
-        ImageMetadata metadata = image.Metadata;
-        GifMetadata gifMetadata = metadata.GetGifMetadata();
+        GifMetadata gifMetadata = GetGifMetadata(image);
         this.colorTableMode ??= gifMetadata.ColorTableMode;
         bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
 
@@ -96,8 +94,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
 
         // Work out if there is an explicit transparent index set for the frame. We use that to ensure the
         // correct value is set for the background index when quantizing.
-        image.Frames.RootFrame.Metadata.TryGetGifMetadata(out GifFrameMetadata? frameMetadata);
-        int transparencyIndex = GetTransparentIndex(quantized, frameMetadata);
+        GifFrameMetadata frameMetadata = GetGifFrameMetadata(image.Frames.RootFrame, -1);
 
         if (this.quantizer is null)
         {
@@ -105,6 +102,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
             if (gifMetadata.ColorTableMode == GifColorTableMode.Global && gifMetadata.GlobalColorTable?.Length > 0)
             {
                 // We avoid dithering by default to preserve the original colors.
+                int transparencyIndex = GetTransparentIndex(quantized, frameMetadata);
                 this.quantizer = new PaletteQuantizer(gifMetadata.GlobalColorTable.Value, new() { Dither = null }, transparencyIndex);
             }
             else
@@ -131,16 +129,20 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
         WriteHeader(stream);
 
         // Write the LSD.
-        transparencyIndex = GetTransparentIndex(quantized, frameMetadata);
-        byte backgroundIndex = unchecked((byte)transparencyIndex);
-        if (transparencyIndex == -1)
+        int derivedTransparencyIndex = GetTransparentIndex(quantized, null);
+        if (derivedTransparencyIndex >= 0)
         {
-            backgroundIndex = gifMetadata.BackgroundColorIndex;
+            frameMetadata.HasTransparency = true;
+            frameMetadata.TransparencyIndex = ClampIndex(derivedTransparencyIndex);
         }
 
+        byte backgroundIndex = derivedTransparencyIndex >= 0
+            ? frameMetadata.TransparencyIndex
+            : gifMetadata.BackgroundColorIndex;
+
         // Get the number of bits.
         int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
-        this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, backgroundIndex, useGlobalTable, bitDepth, stream);
+        this.WriteLogicalScreenDescriptor(image.Metadata, image.Width, image.Height, backgroundIndex, useGlobalTable, bitDepth, stream);
 
         if (useGlobalTable)
         {
@@ -157,22 +159,78 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
             this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile);
         }
 
-        this.EncodeFirstFrame(stream, frameMetadata, quantized, transparencyIndex);
+        this.EncodeFirstFrame(stream, frameMetadata, quantized);
 
         // Capture the global palette for reuse on subsequent frames and cleanup the quantized frame.
         TPixel[] globalPalette = image.Frames.Count == 1 ? Array.Empty<TPixel>() : quantized.Palette.ToArray();
 
-        quantized.Dispose();
-
-        this.EncodeAdditionalFrames(stream, image, globalPalette);
+        this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMethod);
 
         stream.WriteByte(GifConstants.EndIntroducer);
+
+        quantized?.Dispose();
+    }
+
+    private static GifMetadata GetGifMetadata<TPixel>(Image<TPixel> image)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif))
+        {
+            return (GifMetadata)gif.DeepClone();
+        }
+
+        if (image.Metadata.TryGetPngMetadata(out PngMetadata? png))
+        {
+            AnimatedImageMetadata ani = png.ToAnimatedImageMetadata();
+            return GifMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp))
+        {
+            AnimatedImageMetadata ani = webp.ToAnimatedImageMetadata();
+            return GifMetadata.FromAnimatedMetadata(ani);
+        }
+
+        // Return explicit new instance so we do not mutate the original metadata.
+        return new();
+    }
+
+    private static GifFrameMetadata GetGifFrameMetadata<TPixel>(ImageFrame<TPixel> frame, int transparencyIndex)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif))
+        {
+            return (GifFrameMetadata)gif.DeepClone();
+        }
+
+        GifFrameMetadata? metadata = null;
+        if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png))
+        {
+            AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata();
+            metadata = GifFrameMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp))
+        {
+            AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata();
+            metadata = GifFrameMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (metadata?.ColorTableMode == GifColorTableMode.Global && transparencyIndex > -1)
+        {
+            metadata.HasTransparency = true;
+            metadata.TransparencyIndex = ClampIndex(transparencyIndex);
+        }
+
+        return metadata ?? new();
     }
 
     private void EncodeAdditionalFrames<TPixel>(
         Stream stream,
         Image<TPixel> image,
-        ReadOnlyMemory<TPixel> globalPalette)
+        ReadOnlyMemory<TPixel> globalPalette,
+        int globalTransparencyIndex,
+        GifDisposalMethod previousDisposalMethod)
         where TPixel : unmanaged, IPixel<TPixel>
     {
         if (image.Frames.Count == 1)
@@ -187,24 +245,22 @@ private void EncodeAdditionalFrames<TPixel>(
         ImageFrame<TPixel> previousFrame = image.Frames.RootFrame;
 
         // This frame is reused to store de-duplicated pixel buffers.
-        // This is more expensive memory-wise than de-duplicating indexed buffer but allows us to deduplicate
-        // frames using both local and global palettes.
         using ImageFrame<TPixel> encodingFrame = new(previousFrame.Configuration, previousFrame.Size());
 
         for (int i = 1; i < image.Frames.Count; i++)
         {
             // Gather the metadata for this frame.
             ImageFrame<TPixel> currentFrame = image.Frames[i];
-            ImageFrameMetadata metadata = currentFrame.Metadata;
-            metadata.TryGetGifMetadata(out GifFrameMetadata? gifMetadata);
-            bool useLocal = this.colorTableMode == GifColorTableMode.Local || (gifMetadata?.ColorTableMode == GifColorTableMode.Local);
+            ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
+            GifFrameMetadata gifMetadata = GetGifFrameMetadata(currentFrame, globalTransparencyIndex);
+            bool useLocal = this.colorTableMode == GifColorTableMode.Local || (gifMetadata.ColorTableMode == GifColorTableMode.Local);
 
             if (!useLocal && !hasPaletteQuantizer && i > 0)
             {
                 // The palette quantizer can reuse the same global pixel map across multiple frames since the palette is unchanging.
                 // This allows a reduction of memory usage across multi-frame gifs using a global palette
                 // and also allows use to reuse the cache from previous runs.
-                int transparencyIndex = gifMetadata?.HasTransparency == true ? gifMetadata.TransparencyIndex : -1;
+                int transparencyIndex = gifMetadata.HasTransparency ? gifMetadata.TransparencyIndex : -1;
                 paletteQuantizer = new(this.configuration, this.quantizer!.Options, globalPalette, transparencyIndex);
                 hasPaletteQuantizer = true;
             }
@@ -213,12 +269,15 @@ private void EncodeAdditionalFrames<TPixel>(
                 stream,
                 previousFrame,
                 currentFrame,
+                nextFrame,
                 encodingFrame,
                 useLocal,
                 gifMetadata,
-                paletteQuantizer);
+                paletteQuantizer,
+                previousDisposalMethod);
 
             previousFrame = currentFrame;
+            previousDisposalMethod = gifMetadata.DisposalMethod;
         }
 
         if (hasPaletteQuantizer)
@@ -229,16 +288,15 @@ private void EncodeAdditionalFrames<TPixel>(
 
     private void EncodeFirstFrame<TPixel>(
         Stream stream,
-        GifFrameMetadata? metadata,
-        IndexedImageFrame<TPixel> quantized,
-        int transparencyIndex)
+        GifFrameMetadata metadata,
+        IndexedImageFrame<TPixel> quantized)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        this.WriteGraphicalControlExtension(metadata, transparencyIndex, stream);
+        this.WriteGraphicalControlExtension(metadata, stream);
 
         Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;
         Rectangle interest = indices.FullRectangle();
-        bool useLocal = this.colorTableMode == GifColorTableMode.Local || (metadata?.ColorTableMode == GifColorTableMode.Local);
+        bool useLocal = this.colorTableMode == GifColorTableMode.Local || (metadata.ColorTableMode == GifColorTableMode.Local);
         int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
 
         this.WriteImageDescriptor(interest, useLocal, bitDepth, stream);
@@ -248,367 +306,139 @@ private void EncodeFirstFrame<TPixel>(
             this.WriteColorTable(quantized, bitDepth, stream);
         }
 
-        this.WriteImageData(indices, interest, stream, quantized.Palette.Length, transparencyIndex);
+        this.WriteImageData(indices, stream, quantized.Palette.Length, metadata.TransparencyIndex);
     }
 
     private void EncodeAdditionalFrame<TPixel>(
         Stream stream,
         ImageFrame<TPixel> previousFrame,
         ImageFrame<TPixel> currentFrame,
+        ImageFrame<TPixel>? nextFrame,
         ImageFrame<TPixel> encodingFrame,
         bool useLocal,
-        GifFrameMetadata? metadata,
-        PaletteQuantizer<TPixel> globalPaletteQuantizer)
+        GifFrameMetadata metadata,
+        PaletteQuantizer<TPixel> globalPaletteQuantizer,
+        GifDisposalMethod previousDisposal)
         where TPixel : unmanaged, IPixel<TPixel>
     {
         // Capture any explicit transparency index from the metadata.
         // We use it to determine the value to use to replace duplicate pixels.
-        int transparencyIndex = metadata?.HasTransparency == true ? metadata.TransparencyIndex : -1;
-        Vector4 replacement = Vector4.Zero;
-        if (transparencyIndex >= 0)
-        {
-            if (useLocal)
-            {
-                if (metadata?.LocalColorTable?.Length > 0)
-                {
-                    ReadOnlySpan<Color> palette = metadata.LocalColorTable.Value.Span;
-                    if (transparencyIndex < palette.Length)
-                    {
-                        replacement = palette[transparencyIndex].ToScaledVector4();
-                    }
-                }
-            }
-            else
-            {
-                ReadOnlySpan<TPixel> palette = globalPaletteQuantizer.Palette.Span;
-                if (transparencyIndex < palette.Length)
-                {
-                    replacement = palette[transparencyIndex].ToScaledVector4();
-                }
-            }
-        }
-
-        this.DeDuplicatePixels(previousFrame, currentFrame, encodingFrame, replacement);
+        int transparencyIndex = metadata.HasTransparency ? metadata.TransparencyIndex : -1;
 
-        IndexedImageFrame<TPixel> quantized;
-        if (useLocal)
-        {
-            // Reassign using the current frame and details.
-            if (metadata?.LocalColorTable?.Length > 0)
-            {
-                // We can use the color data from the decoded metadata here.
-                // We avoid dithering by default to preserve the original colors.
-                ReadOnlyMemory<Color> palette = metadata.LocalColorTable.Value;
-                PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex);
-                using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
-                quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, encodingFrame.Bounds());
-            }
-            else
-            {
-                // We must quantize the frame to generate a local color table.
-                IQuantizer quantizer = this.hasQuantizer ? this.quantizer! : KnownQuantizers.Octree;
-                using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
-                quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, encodingFrame.Bounds());
-            }
-        }
-        else
-        {
-            // Quantize the image using the global palette.
-            // Individual frames, though using the shared palette, can use a different transparent index to represent transparency.
-            globalPaletteQuantizer.SetTransparentIndex(transparencyIndex);
-            quantized = globalPaletteQuantizer.QuantizeFrame(encodingFrame, encodingFrame.Bounds());
-        }
+        ImageFrame<TPixel>? previous = previousDisposal == GifDisposalMethod.RestoreToBackground ? null : previousFrame;
 
-        // Recalculate the transparency index as depending on the quantizer used could have a new value.
-        transparencyIndex = GetTransparentIndex(quantized, metadata);
+        // Deduplicate and quantize the frame capturing only required parts.
+        (bool difference, Rectangle bounds) =
+            AnimationUtilities.DeDuplicatePixels(
+                this.configuration,
+                previous,
+                currentFrame,
+                nextFrame,
+                encodingFrame,
+                Color.Transparent,
+                true);
 
-        // Trim down the buffer to the minimum size required.
-        Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;
-        Rectangle interest = TrimTransparentPixels(indices, transparencyIndex);
+        using IndexedImageFrame<TPixel> quantized = this.QuantizeAdditionalFrameAndUpdateMetadata(
+                encodingFrame,
+                bounds,
+                metadata,
+                useLocal,
+                globalPaletteQuantizer,
+                difference,
+                transparencyIndex);
 
-        this.WriteGraphicalControlExtension(metadata, transparencyIndex, stream);
+        this.WriteGraphicalControlExtension(metadata, stream);
 
         int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
-        this.WriteImageDescriptor(interest, useLocal, bitDepth, stream);
+        this.WriteImageDescriptor(bounds, useLocal, bitDepth, stream);
 
         if (useLocal)
         {
             this.WriteColorTable(quantized, bitDepth, stream);
         }
 
-        this.WriteImageData(indices, interest, stream, quantized.Palette.Length, transparencyIndex);
+        Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;
+        this.WriteImageData(indices, stream, quantized.Palette.Length, metadata.TransparencyIndex);
     }
 
-    private void DeDuplicatePixels<TPixel>(
-        ImageFrame<TPixel> backgroundFrame,
-        ImageFrame<TPixel> sourceFrame,
-        ImageFrame<TPixel> resultFrame,
-        Vector4 replacement)
+    private IndexedImageFrame<TPixel> QuantizeAdditionalFrameAndUpdateMetadata<TPixel>(
+        ImageFrame<TPixel> encodingFrame,
+        Rectangle bounds,
+        GifFrameMetadata metadata,
+        bool useLocal,
+        PaletteQuantizer<TPixel> globalPaletteQuantizer,
+        bool hasDuplicates,
+        int transparencyIndex)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        IMemoryOwner<Vector4> buffers = this.memoryAllocator.Allocate<Vector4>(backgroundFrame.Width * 3);
-        Span<Vector4> background = buffers.GetSpan()[..backgroundFrame.Width];
-        Span<Vector4> source = buffers.GetSpan()[backgroundFrame.Width..];
-        Span<Vector4> result = buffers.GetSpan()[(backgroundFrame.Width * 2)..];
-
-        // TODO: This algorithm is greedy and will always replace matching colors, however, theoretically, if the proceeding color
-        // is the same, but not replaced, you would actually be better of not replacing it since longer runs compress better.
-        // This would require a more complex algorithm.
-        for (int y = 0; y < backgroundFrame.Height; y++)
-        {
-            PixelOperations<TPixel>.Instance.ToVector4(this.configuration, backgroundFrame.DangerousGetPixelRowMemory(y).Span, background, PixelConversionModifiers.Scale);
-            PixelOperations<TPixel>.Instance.ToVector4(this.configuration, sourceFrame.DangerousGetPixelRowMemory(y).Span, source, PixelConversionModifiers.Scale);
-
-            ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
-            ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source));
-            ref Vector256<float> resultBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result));
-
-            uint x = 0;
-            int remaining = background.Length;
-            if (Avx2.IsSupported && remaining >= 2)
-            {
-                Vector256<float> replacement256 = Vector256.Create(replacement.X, replacement.Y, replacement.Z, replacement.W, replacement.X, replacement.Y, replacement.Z, replacement.W);
-
-                while (remaining >= 2)
-                {
-                    Vector256<float> b = Unsafe.Add(ref backgroundBase, x);
-                    Vector256<float> s = Unsafe.Add(ref sourceBase, x);
-
-                    Vector256<int> m = Avx.CompareEqual(b, s).AsInt32();
-
-                    m = Avx2.HorizontalAdd(m, m);
-                    m = Avx2.HorizontalAdd(m, m);
-                    m = Avx2.CompareEqual(m, Vector256.Create(-4));
-
-                    Unsafe.Add(ref resultBase, x) = Avx.BlendVariable(s, replacement256, m.AsSingle());
-
-                    x++;
-                    remaining -= 2;
-                }
-            }
-
-            for (int i = remaining; i >= 0; i--)
-            {
-                x = (uint)i;
-                Vector4 b = Unsafe.Add(ref Unsafe.As<Vector256<float>, Vector4>(ref backgroundBase), x);
-                Vector4 s = Unsafe.Add(ref Unsafe.As<Vector256<float>, Vector4>(ref sourceBase), x);
-                ref Vector4 r = ref Unsafe.Add(ref Unsafe.As<Vector256<float>, Vector4>(ref resultBase), x);
-                r = (b == s) ? replacement : s;
-            }
-
-            PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, result, resultFrame.DangerousGetPixelRowMemory(y).Span, PixelConversionModifiers.Scale);
-        }
-    }
-
-    private static Rectangle TrimTransparentPixels(Buffer2D<byte> buffer, int transparencyIndex)
-    {
-        if (transparencyIndex < 0)
-        {
-            return buffer.FullRectangle();
-        }
-
-        byte trimmableIndex = unchecked((byte)transparencyIndex);
-
-        int top = int.MinValue;
-        int bottom = int.MaxValue;
-        int left = int.MaxValue;
-        int right = int.MinValue;
-        int minY = -1;
-        bool isTransparentRow = true;
-
-        // Run through the buffer in a single pass. Use variables to track the min/max values.
-        for (int y = 0; y < buffer.Height; y++)
+        IndexedImageFrame<TPixel> quantized;
+        if (useLocal)
         {
-            isTransparentRow = true;
-            Span<byte> rowSpan = buffer.DangerousGetRowSpan(y);
-            ref byte rowPtr = ref MemoryMarshal.GetReference(rowSpan);
-            nint rowLength = (nint)(uint)rowSpan.Length;
-            nint x = 0;
-
-#if NET7_0_OR_GREATER
-            if (Vector128.IsHardwareAccelerated && rowLength >= Vector128<byte>.Count)
-            {
-                Vector256<byte> trimmableVec256 = Vector256.Create(trimmableIndex);
-
-                if (Vector256.IsHardwareAccelerated && rowLength >= Vector256<byte>.Count)
-                {
-                    do
-                    {
-                        Vector256<byte> vec = Vector256.LoadUnsafe(ref rowPtr, (nuint)x);
-                        Vector256<byte> notEquals = ~Vector256.Equals(vec, trimmableVec256);
-                        uint mask = notEquals.ExtractMostSignificantBits();
-
-                        if (mask != 0)
-                        {
-                            isTransparentRow = false;
-                            nint start = x + (nint)uint.TrailingZeroCount(mask);
-                            nint end = (nint)uint.LeadingZeroCount(mask);
-
-                            // end is from the end, but we need the index from the beginning
-                            end = x + Vector256<byte>.Count - 1 - end;
-
-                            left = Math.Min(left, (int)start);
-                            right = Math.Max(right, (int)end);
-                        }
-
-                        x += Vector256<byte>.Count;
-                    }
-                    while (x <= rowLength - Vector256<byte>.Count);
-                }
-
-                Vector128<byte> trimmableVec = Vector256.IsHardwareAccelerated
-                    ? trimmableVec256.GetLower()
-                    : Vector128.Create(trimmableIndex);
-
-                while (x <= rowLength - Vector128<byte>.Count)
-                {
-                    Vector128<byte> vec = Vector128.LoadUnsafe(ref rowPtr, (nuint)x);
-                    Vector128<byte> notEquals = ~Vector128.Equals(vec, trimmableVec);
-                    uint mask = notEquals.ExtractMostSignificantBits();
-
-                    if (mask != 0)
-                    {
-                        isTransparentRow = false;
-                        nint start = x + (nint)uint.TrailingZeroCount(mask);
-                        nint end = (nint)uint.LeadingZeroCount(mask) - Vector128<byte>.Count;
-
-                        // end is from the end, but we need the index from the beginning
-                        end = x + Vector128<byte>.Count - 1 - end;
-
-                        left = Math.Min(left, (int)start);
-                        right = Math.Max(right, (int)end);
-                    }
-
-                    x += Vector128<byte>.Count;
-                }
-            }
-#else
-            if (Sse41.IsSupported && rowLength >= Vector128<byte>.Count)
+            // Reassign using the current frame and details.
+            if (metadata.LocalColorTable?.Length > 0)
             {
-                Vector256<byte> trimmableVec256 = Vector256.Create(trimmableIndex);
+                // We can use the color data from the decoded metadata here.
+                // We avoid dithering by default to preserve the original colors.
+                ReadOnlyMemory<Color> palette = metadata.LocalColorTable.Value;
 
-                if (Avx2.IsSupported && rowLength >= Vector256<byte>.Count)
+                if (hasDuplicates && !metadata.HasTransparency)
                 {
-                    do
-                    {
-                        Vector256<byte> vec = Unsafe.ReadUnaligned<Vector256<byte>>(ref Unsafe.Add(ref rowPtr, x));
-                        Vector256<byte> notEquals = Avx2.CompareEqual(vec, trimmableVec256);
-                        notEquals = Avx2.Xor(notEquals, Vector256<byte>.AllBitsSet);
-                        int mask = Avx2.MoveMask(notEquals);
-
-                        if (mask != 0)
-                        {
-                            isTransparentRow = false;
-                            nint start = x + (nint)(uint)BitOperations.TrailingZeroCount(mask);
-                            nint end = (nint)(uint)BitOperations.LeadingZeroCount((uint)mask);
-
-                            // end is from the end, but we need the index from the beginning
-                            end = x + Vector256<byte>.Count - 1 - end;
-
-                            left = Math.Min(left, (int)start);
-                            right = Math.Max(right, (int)end);
-                        }
-
-                        x += Vector256<byte>.Count;
-                    }
-                    while (x <= rowLength - Vector256<byte>.Count);
+                    // A difference was captured but the metadata does not have transparency.
+                    metadata.HasTransparency = true;
+                    transparencyIndex = palette.Length;
+                    metadata.TransparencyIndex = ClampIndex(transparencyIndex);
                 }
 
-                Vector128<byte> trimmableVec = Sse41.IsSupported
-                    ? trimmableVec256.GetLower()
-                    : Vector128.Create(trimmableIndex);
-
-                while (x <= rowLength - Vector128<byte>.Count)
-                {
-                    Vector128<byte> vec = Unsafe.ReadUnaligned<Vector128<byte>>(ref Unsafe.Add(ref rowPtr, x));
-                    Vector128<byte> notEquals = Sse2.CompareEqual(vec, trimmableVec);
-                    notEquals = Sse2.Xor(notEquals, Vector128<byte>.AllBitsSet);
-                    int mask = Sse2.MoveMask(notEquals);
-
-                    if (mask != 0)
-                    {
-                        isTransparentRow = false;
-                        nint start = x + (nint)(uint)BitOperations.TrailingZeroCount(mask);
-                        nint end = (nint)(uint)BitOperations.LeadingZeroCount((uint)mask) - Vector128<byte>.Count;
-
-                        // end is from the end, but we need the index from the beginning
-                        end = x + Vector128<byte>.Count - 1 - end;
-
-                        left = Math.Min(left, (int)start);
-                        right = Math.Max(right, (int)end);
-                    }
-
-                    x += Vector128<byte>.Count;
-                }
+                PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex);
+                using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
+                quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
             }
-#endif
-            for (; x < rowLength; ++x)
+            else
             {
-                if (Unsafe.Add(ref rowPtr, x) != trimmableIndex)
-                {
-                    isTransparentRow = false;
-                    left = Math.Min(left, (int)x);
-                    right = Math.Max(right, (int)x);
-                }
-            }
+                // We must quantize the frame to generate a local color table.
+                IQuantizer quantizer = this.hasQuantizer ? this.quantizer! : KnownQuantizers.Octree;
+                using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
+                quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
 
-            if (!isTransparentRow)
-            {
-                if (y == 0)
+                // The transparency index derived by the quantizer might differ from the index
+                // within the metadata. We need to update the metadata to reflect this.
+                int derivedTransparencyIndex = GetTransparentIndex(quantized, null);
+                if (derivedTransparencyIndex < 0)
                 {
-                    // First row is opaque.
-                    // Capture to prevent over assignment when a match is found below.
-                    top = 0;
+                    // If no index is found set to the palette length, this trick allows us to fake transparency without an explicit index.
+                    derivedTransparencyIndex = quantized.Palette.Length;
                 }
 
-                // The minimum top bounds have already been captured.
-                // Increment the bottom to include the current opaque row.
-                if (minY < 0 && top != 0)
-                {
-                    // Increment to the first opaque row.
-                    top++;
-                }
+                metadata.TransparencyIndex = ClampIndex(derivedTransparencyIndex);
 
-                minY = top;
-                bottom = y;
-            }
-            else
-            {
-                // We've yet to hit an opaque row. Capture the top position.
-                if (minY < 0)
+                if (hasDuplicates)
                 {
-                    top = Math.Max(top, y);
+                    metadata.HasTransparency = true;
                 }
-
-                bottom = Math.Min(bottom, y);
             }
         }
-
-        if (left == int.MaxValue)
-        {
-            left = 0;
-        }
-
-        if (right == int.MinValue)
+        else
         {
-            right = buffer.Width;
-        }
+            // Quantize the image using the global palette.
+            // Individual frames, though using the shared palette, can use a different transparent index to represent transparency.
 
-        if (top == bottom || left == right)
-        {
-            // The entire image is transparent.
-            return buffer.FullRectangle();
-        }
+            // A difference was captured but the metadata does not have transparency.
+            if (hasDuplicates && !metadata.HasTransparency)
+            {
+                metadata.HasTransparency = true;
+                transparencyIndex = globalPaletteQuantizer.Palette.Length;
+                metadata.TransparencyIndex = ClampIndex(transparencyIndex);
+            }
 
-        if (!isTransparentRow)
-        {
-            // Last row is opaque.
-            bottom = buffer.Height;
+            globalPaletteQuantizer.SetTransparentIndex(transparencyIndex);
+            quantized = globalPaletteQuantizer.QuantizeFrame(encodingFrame, bounds);
         }
 
-        return Rectangle.FromLTRB(left, top, Math.Min(right + 1, buffer.Width), Math.Min(bottom + 1, buffer.Height));
+        return quantized;
     }
 
+    private static byte ClampIndex(int value) => (byte)Numerics.Clamp(value, byte.MinValue, byte.MaxValue);
+
     /// <summary>
     /// Returns the index of the most transparent color in the palette.
     /// </summary>
@@ -800,30 +630,19 @@ private static void WriteCommentSubBlock(Stream stream, ReadOnlySpan<char> comme
     /// Writes the optional graphics control extension to the stream.
     /// </summary>
     /// <param name="metadata">The metadata of the image or frame.</param>
-    /// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
     /// <param name="stream">The stream to write to.</param>
-    private void WriteGraphicalControlExtension(GifFrameMetadata? metadata, int transparencyIndex, Stream stream)
+    private void WriteGraphicalControlExtension(GifFrameMetadata metadata, Stream stream)
     {
-        GifFrameMetadata? data = metadata;
-        bool hasTransparency;
-        if (metadata is null)
-        {
-            data = new();
-            hasTransparency = transparencyIndex >= 0;
-        }
-        else
-        {
-            hasTransparency = metadata.HasTransparency;
-        }
+        bool hasTransparency = metadata.HasTransparency;
 
         byte packedValue = GifGraphicControlExtension.GetPackedValue(
-            disposalMethod: data!.DisposalMethod,
+            disposalMethod: metadata.DisposalMethod,
             transparencyFlag: hasTransparency);
 
         GifGraphicControlExtension extension = new(
             packed: packedValue,
-            delayTime: (ushort)data.FrameDelay,
-            transparencyIndex: hasTransparency ? unchecked((byte)transparencyIndex) : byte.MinValue);
+            delayTime: (ushort)metadata.FrameDelay,
+            transparencyIndex: hasTransparency ? metadata.TransparencyIndex : byte.MinValue);
 
         this.WriteExtension(extension, stream);
     }
@@ -924,14 +743,11 @@ private void WriteColorTable<TPixel>(IndexedImageFrame<TPixel> image, int bitDep
     /// Writes the image pixel data to the stream.
     /// </summary>
     /// <param name="indices">The <see cref="Buffer2DRegion{Byte}"/> containing indexed pixels.</param>
-    /// <param name="interest">The region of interest.</param>
     /// <param name="stream">The stream to write to.</param>
     /// <param name="paletteLength">The length of the frame color palette.</param>
     /// <param name="transparencyIndex">The index of the color used to represent transparency.</param>
-    private void WriteImageData(Buffer2D<byte> indices, Rectangle interest, Stream stream, int paletteLength, int transparencyIndex)
+    private void WriteImageData(Buffer2D<byte> indices, Stream stream, int paletteLength, int transparencyIndex)
     {
-        Buffer2DRegion<byte> region = indices.GetRegion(interest);
-
         // Pad the bit depth when required for encoding the image data.
         // This is a common trick which allows to use out of range indexes for transparency and avoid allocating a larger color palette
         // as decoders skip indexes that are out of range.
@@ -940,6 +756,6 @@ private void WriteImageData(Buffer2D<byte> indices, Rectangle interest, Stream s
             : 0;
 
         using LzwEncoder encoder = new(this.memoryAllocator, ColorNumerics.GetBitsNeededForColorDepth(paletteLength + padding));
-        encoder.Encode(region, stream);
+        encoder.Encode(indices, stream);
     }
 }
diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
index faabf7dfa8..f8734bb5a3 100644
--- a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
@@ -1,6 +1,7 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
+using System.Numerics;
 using SixLabors.ImageSharp.PixelFormats;
 
 namespace SixLabors.ImageSharp.Formats.Gif;
@@ -76,4 +77,43 @@ private GifFrameMetadata(GifFrameMetadata other)
 
     /// <inheritdoc/>
     public IDeepCloneable DeepClone() => new GifFrameMetadata(this);
+
+    internal static GifFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata)
+    {
+        // TODO: v4 How do I link the parent metadata to the frame metadata to get the global color table?
+        int index = -1;
+        float background = 1f;
+        if (metadata.ColorTable.HasValue)
+        {
+            ReadOnlySpan<Color> colorTable = metadata.ColorTable.Value.Span;
+            for (int i = 0; i < colorTable.Length; i++)
+            {
+                Vector4 vector = (Vector4)colorTable[i];
+                if (vector.W < background)
+                {
+                    index = i;
+                }
+            }
+        }
+
+        bool hasTransparency = index >= 0;
+
+        return new()
+        {
+            LocalColorTable = metadata.ColorTable,
+            ColorTableMode = metadata.ColorTableMode == FrameColorTableMode.Global ? GifColorTableMode.Global : GifColorTableMode.Local,
+            FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10),
+            DisposalMethod = GetMode(metadata.DisposalMode),
+            HasTransparency = hasTransparency,
+            TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue,
+        };
+    }
+
+    private static GifDisposalMethod GetMode(FrameDisposalMode mode) => mode switch
+    {
+        FrameDisposalMode.DoNotDispose => GifDisposalMethod.NotDispose,
+        FrameDisposalMode.RestoreToBackground => GifDisposalMethod.RestoreToBackground,
+        FrameDisposalMode.RestoreToPrevious => GifDisposalMethod.RestoreToPrevious,
+        _ => GifDisposalMethod.Unspecified,
+    };
 }
diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs
index d25e2a5cc2..1331edee89 100644
--- a/src/ImageSharp/Formats/Gif/GifMetadata.cs
+++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs
@@ -71,4 +71,30 @@ private GifMetadata(GifMetadata other)
 
     /// <inheritdoc/>
     public IDeepCloneable DeepClone() => new GifMetadata(this);
+
+    internal static GifMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
+    {
+        int index = 0;
+        Color background = metadata.BackgroundColor;
+        if (metadata.ColorTable.HasValue)
+        {
+            ReadOnlySpan<Color> colorTable = metadata.ColorTable.Value.Span;
+            for (int i = 0; i < colorTable.Length; i++)
+            {
+                if (background == colorTable[i])
+                {
+                    index = i;
+                    break;
+                }
+            }
+        }
+
+        return new()
+        {
+            GlobalColorTable = metadata.ColorTable,
+            ColorTableMode = metadata.ColorTableMode == FrameColorTableMode.Global ? GifColorTableMode.Global : GifColorTableMode.Local,
+            RepeatCount = metadata.RepeatCount,
+            BackgroundColorIndex = (byte)Numerics.Clamp(index, 0, 255),
+        };
+    }
 }
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index 4b40c44e45..d4050810df 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -186,7 +186,7 @@ public LzwEncoder(MemoryAllocator memoryAllocator, int colorDepth)
     /// </summary>
     /// <param name="indexedPixels">The 2D buffer of indexed pixels.</param>
     /// <param name="stream">The stream to write to.</param>
-    public void Encode(Buffer2DRegion<byte> indexedPixels, Stream stream)
+    public void Encode(Buffer2D<byte> indexedPixels, Stream stream)
     {
         // Write "initial code size" byte
         stream.WriteByte((byte)this.initialCodeSize);
@@ -204,7 +204,7 @@ public void Encode(Buffer2DRegion<byte> indexedPixels, Stream stream)
     /// <param name="bitCount">The number of bits</param>
     /// <returns>See <see cref="int"/></returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static int GetMaxcode(int bitCount) => (1 << bitCount) - 1;
+    private static int GetMaxCode(int bitCount) => (1 << bitCount) - 1;
 
     /// <summary>
     /// Add a character to the end of the current packet, and if it is 254 characters,
@@ -249,7 +249,7 @@ private void ClearBlock(Stream stream)
     /// <param name="indexedPixels">The 2D buffer of indexed pixels.</param>
     /// <param name="initialBits">The initial bits.</param>
     /// <param name="stream">The stream to write to.</param>
-    private void Compress(Buffer2DRegion<byte> indexedPixels, int initialBits, Stream stream)
+    private void Compress(Buffer2D<byte> indexedPixels, int initialBits, Stream stream)
     {
         // Set up the globals: globalInitialBits - initial number of bits
         this.globalInitialBits = initialBits;
@@ -257,7 +257,7 @@ private void Compress(Buffer2DRegion<byte> indexedPixels, int initialBits, Strea
         // Set up the necessary values
         this.clearFlag = false;
         this.bitCount = this.globalInitialBits;
-        this.maxCode = GetMaxcode(this.bitCount);
+        this.maxCode = GetMaxCode(this.bitCount);
         this.clearCode = 1 << (initialBits - 1);
         this.eofCode = this.clearCode + 1;
         this.freeEntry = this.clearCode + 2;
@@ -383,7 +383,7 @@ private void Output(int code, Stream outs)
         {
             if (this.clearFlag)
             {
-                this.maxCode = GetMaxcode(this.bitCount = this.globalInitialBits);
+                this.maxCode = GetMaxCode(this.bitCount = this.globalInitialBits);
                 this.clearFlag = false;
             }
             else
@@ -391,7 +391,7 @@ private void Output(int code, Stream outs)
                 ++this.bitCount;
                 this.maxCode = this.bitCount == MaxBits
                     ? MaxMaxCode
-                    : GetMaxcode(this.bitCount);
+                    : GetMaxCode(this.bitCount);
             }
         }
 
diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
index 9ba95952e7..16f788e3db 100644
--- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
@@ -2,6 +2,7 @@
 // Licensed under the Six Labors Split License.
 
 using System.Diagnostics.CodeAnalysis;
+using SixLabors.ImageSharp.Formats;
 using SixLabors.ImageSharp.Formats.Gif;
 using SixLabors.ImageSharp.Metadata;
 
@@ -20,6 +21,21 @@ public static partial class MetadataExtensions
     public static GifMetadata GetGifMetadata(this ImageMetadata source)
         => source.GetFormatMetadata(GifFormat.Instance);
 
+    /// <summary>
+    /// Gets the gif format specific metadata for the image.
+    /// </summary>
+    /// <param name="source">The metadata this method extends.</param>
+    /// <param name="metadata">
+    /// When this method returns, contains the metadata associated with the specified image,
+    /// if found; otherwise, the default value for the type of the metadata parameter.
+    /// This parameter is passed uninitialized.
+    /// </param>
+    /// <returns>
+    /// <see langword="true"/> if the gif metadata exists; otherwise, <see langword="false"/>.
+    /// </returns>
+    public static bool TryGetGifMetadata(this ImageMetadata source, [NotNullWhen(true)] out GifMetadata? metadata)
+        => source.TryGetFormatMetadata(GifFormat.Instance, out metadata);
+
     /// <summary>
     /// Gets the gif format specific metadata for the image frame.
     /// </summary>
@@ -42,4 +58,39 @@ public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source)
     /// </returns>
     public static bool TryGetGifMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out GifFrameMetadata? metadata)
         => source.TryGetFormatMetadata(GifFormat.Instance, out metadata);
+
+    internal static AnimatedImageMetadata ToAnimatedImageMetadata(this GifMetadata source)
+    {
+        Color background = Color.Transparent;
+        if (source.GlobalColorTable != null)
+        {
+            background = source.GlobalColorTable.Value.Span[source.BackgroundColorIndex];
+        }
+
+        return new()
+        {
+            ColorTable = source.GlobalColorTable,
+            ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local,
+            RepeatCount = source.RepeatCount,
+            BackgroundColor = background,
+        };
+    }
+
+    internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this GifFrameMetadata source)
+        => new()
+        {
+            ColorTable = source.LocalColorTable,
+            ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local,
+            Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10),
+            DisposalMode = GetMode(source.DisposalMethod),
+            BlendMode = source.DisposalMethod == GifDisposalMethod.RestoreToBackground ? FrameBlendMode.Source : FrameBlendMode.Over,
+        };
+
+    private static FrameDisposalMode GetMode(GifDisposalMethod method) => method switch
+    {
+        GifDisposalMethod.NotDispose => FrameDisposalMode.DoNotDispose,
+        GifDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground,
+        GifDisposalMethod.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious,
+        _ => FrameDisposalMode.Unspecified,
+    };
 }
diff --git a/src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs b/src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs
index a9f99a9e4a..cd78f80885 100644
--- a/src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs
@@ -9,7 +9,7 @@ internal readonly struct AnimationControl
 {
     public const int Size = 8;
 
-    public AnimationControl(int numberFrames, int numberPlays)
+    public AnimationControl(uint numberFrames, uint numberPlays)
     {
         this.NumberFrames = numberFrames;
         this.NumberPlays = numberPlays;
@@ -18,12 +18,12 @@ public AnimationControl(int numberFrames, int numberPlays)
     /// <summary>
     /// Gets the number of frames
     /// </summary>
-    public int NumberFrames { get; }
+    public uint NumberFrames { get; }
 
     /// <summary>
     /// Gets the number of times to loop this APNG.  0 indicates infinite looping.
     /// </summary>
-    public int NumberPlays { get; }
+    public uint NumberPlays { get; }
 
     /// <summary>
     /// Writes the acTL to the given buffer.
@@ -31,8 +31,8 @@ public AnimationControl(int numberFrames, int numberPlays)
     /// <param name="buffer">The buffer to write to.</param>
     public void WriteTo(Span<byte> buffer)
     {
-        BinaryPrimitives.WriteInt32BigEndian(buffer[..4], this.NumberFrames);
-        BinaryPrimitives.WriteInt32BigEndian(buffer[4..8], this.NumberPlays);
+        BinaryPrimitives.WriteInt32BigEndian(buffer[..4], (int)this.NumberFrames);
+        BinaryPrimitives.WriteInt32BigEndian(buffer[4..8], (int)this.NumberPlays);
     }
 
     /// <summary>
@@ -42,6 +42,6 @@ public void WriteTo(Span<byte> buffer)
     /// <returns>The parsed acTL.</returns>
     public static AnimationControl Parse(ReadOnlySpan<byte> data)
         => new(
-            numberFrames: BinaryPrimitives.ReadInt32BigEndian(data[..4]),
-            numberPlays: BinaryPrimitives.ReadInt32BigEndian(data[4..8]));
+            numberFrames: BinaryPrimitives.ReadUInt32BigEndian(data[..4]),
+            numberPlays: BinaryPrimitives.ReadUInt32BigEndian(data[4..8]));
 }
diff --git a/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs b/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
index 7847882484..8af0ac8ca7 100644
--- a/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
@@ -61,10 +61,10 @@ public static PngPhysical Parse(ReadOnlySpan<byte> data)
     /// <returns>The constructed PngPhysicalChunkData instance.</returns>
     public static PngPhysical FromMetadata(ImageMetadata meta)
     {
-        byte unitSpecifier = 0;
         uint x;
         uint y;
 
+        byte unitSpecifier;
         switch (meta.ResolutionUnits)
         {
             case PixelResolutionUnit.AspectRatio:
diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs
index f24b8d1b5c..b6313bffe0 100644
--- a/src/ImageSharp/Formats/Png/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Png/MetadataExtensions.cs
@@ -2,6 +2,7 @@
 // Licensed under the Six Labors Split License.
 
 using System.Diagnostics.CodeAnalysis;
+using SixLabors.ImageSharp.Formats;
 using SixLabors.ImageSharp.Formats.Png;
 using SixLabors.ImageSharp.Metadata;
 
@@ -20,17 +21,64 @@ public static partial class MetadataExtensions
     public static PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
 
     /// <summary>
-    /// Gets the aPng format specific metadata for the image frame.
+    /// Gets the png format specific metadata for the image.
+    /// </summary>
+    /// <param name="source">The metadata this method extends.</param>
+    /// <param name="metadata">The metadata.</param>
+    /// <returns>
+    /// <see langword="true"/> if the png metadata exists; otherwise, <see langword="false"/>.
+    /// </returns>
+    public static bool TryGetPngMetadata(this ImageMetadata source, [NotNullWhen(true)] out PngMetadata? metadata)
+        => source.TryGetFormatMetadata(PngFormat.Instance, out metadata);
+
+    /// <summary>
+    /// Gets the png format specific metadata for the image frame.
     /// </summary>
     /// <param name="source">The metadata this method extends.</param>
     /// <returns>The <see cref="PngFrameMetadata"/>.</returns>
-    public static PngFrameMetadata GetPngFrameMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
+    public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
 
     /// <summary>
-    /// Gets the aPng format specific metadata for the image frame.
+    /// Gets the png format specific metadata for the image frame.
     /// </summary>
     /// <param name="source">The metadata this method extends.</param>
     /// <param name="metadata">The metadata.</param>
-    /// <returns>The <see cref="PngFrameMetadata"/>.</returns>
-    public static bool TryGetPngFrameMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out PngFrameMetadata? metadata) => source.TryGetFormatMetadata(PngFormat.Instance, out metadata);
+    /// <returns>
+    /// <see langword="true"/> if the png frame metadata exists; otherwise, <see langword="false"/>.
+    /// </returns>
+    public static bool TryGetPngMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out PngFrameMetadata? metadata)
+        => source.TryGetFormatMetadata(PngFormat.Instance, out metadata);
+
+    internal static AnimatedImageMetadata ToAnimatedImageMetadata(this PngMetadata source)
+        => new()
+        {
+            ColorTable = source.ColorTable,
+            ColorTableMode = FrameColorTableMode.Global,
+            RepeatCount = (ushort)Numerics.Clamp(source.RepeatCount, 0, ushort.MaxValue),
+        };
+
+    internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this PngFrameMetadata source)
+    {
+        double delay = source.FrameDelay.ToDouble();
+        if (double.IsNaN(delay))
+        {
+            delay = 0;
+        }
+
+        return new()
+        {
+            ColorTableMode = FrameColorTableMode.Global,
+            Duration = TimeSpan.FromMilliseconds(delay * 1000),
+            DisposalMode = GetMode(source.DisposalMethod),
+            BlendMode = source.BlendMethod == PngBlendMethod.Source ? FrameBlendMode.Source : FrameBlendMode.Over,
+        };
+    }
+
+    private static FrameDisposalMode GetMode(PngDisposalMethod method) => method switch
+    {
+        PngDisposalMethod.DoNotDispose => FrameDisposalMode.DoNotDispose,
+        PngDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground,
+        PngDisposalMethod.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious,
+        _ => FrameDisposalMode.Unspecified,
+    };
 }
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 731cc9b1c1..49490ecd54 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -218,7 +218,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
                                 chunk.Length - 4,
                                 currentFrame,
                                 pngMetadata,
-                                this.ReadNextDataChunkAndSkipSeq,
+                                this.ReadNextFrameDataChunk,
                                 currentFrameControl.Value,
                                 cancellationToken);
 
@@ -582,7 +582,7 @@ private void InitializeImage<TPixel>(ImageMetadata metadata, FrameControl frameC
             this.header.Height,
             metadata);
 
-        PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngFrameMetadata();
+        PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngMetadata();
         frameMetadata.FromChunk(in frameControl);
 
         this.bytesPerPixel = this.CalculateBytesPerPixel();
@@ -622,8 +622,8 @@ private void InitializeFrame<TPixel>(
         frame = image.Frames.AddFrame(previousFrame ?? image.Frames.RootFrame);
 
         // If the first `fcTL` chunk uses a `dispose_op` of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND.
-        if (previousFrameControl.DisposeOperation == PngDisposalMethod.Background
-            || (previousFrame is null && previousFrameControl.DisposeOperation == PngDisposalMethod.Previous))
+        if (previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToBackground
+            || (previousFrame is null && previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToPrevious))
         {
             Rectangle restoreArea = previousFrameControl.Bounds;
             Rectangle interest = Rectangle.Intersect(frame.Bounds(), restoreArea);
@@ -631,7 +631,7 @@ private void InitializeFrame<TPixel>(
             pixelRegion.Clear();
         }
 
-        PngFrameMetadata frameMetadata = frame.Metadata.GetPngFrameMetadata();
+        PngFrameMetadata frameMetadata = frame.Metadata.GetPngMetadata();
         frameMetadata.FromChunk(currentFrameControl);
 
         this.previousScanline?.Dispose();
@@ -765,10 +765,12 @@ private void DecodePixelData<TPixel>(
         {
             cancellationToken.ThrowIfCancellationRequested();
             int bytesPerFrameScanline = this.CalculateScanlineLength((int)frameControl.Width) + 1;
-            Span<byte> scanlineSpan = this.scanline.GetSpan()[..bytesPerFrameScanline];
+            Span<byte> scanSpan = this.scanline.GetSpan()[..bytesPerFrameScanline];
+            Span<byte> prevSpan = this.previousScanline.GetSpan()[..bytesPerFrameScanline];
+
             while (currentRowBytesRead < bytesPerFrameScanline)
             {
-                int bytesRead = compressedStream.Read(scanlineSpan, currentRowBytesRead, bytesPerFrameScanline - currentRowBytesRead);
+                int bytesRead = compressedStream.Read(scanSpan, currentRowBytesRead, bytesPerFrameScanline - currentRowBytesRead);
                 if (bytesRead <= 0)
                 {
                     return;
@@ -779,25 +781,25 @@ private void DecodePixelData<TPixel>(
 
             currentRowBytesRead = 0;
 
-            switch ((FilterType)scanlineSpan[0])
+            switch ((FilterType)scanSpan[0])
             {
                 case FilterType.None:
                     break;
 
                 case FilterType.Sub:
-                    SubFilter.Decode(scanlineSpan, this.bytesPerPixel);
+                    SubFilter.Decode(scanSpan, this.bytesPerPixel);
                     break;
 
                 case FilterType.Up:
-                    UpFilter.Decode(scanlineSpan, this.previousScanline.GetSpan());
+                    UpFilter.Decode(scanSpan, prevSpan);
                     break;
 
                 case FilterType.Average:
-                    AverageFilter.Decode(scanlineSpan, this.previousScanline.GetSpan(), this.bytesPerPixel);
+                    AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel);
                     break;
 
                 case FilterType.Paeth:
-                    PaethFilter.Decode(scanlineSpan, this.previousScanline.GetSpan(), this.bytesPerPixel);
+                    PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel);
                     break;
 
                 default:
@@ -805,7 +807,7 @@ private void DecodePixelData<TPixel>(
                     break;
             }
 
-            this.ProcessDefilteredScanline(frameControl, currentRow, scanlineSpan, imageFrame, pngMetadata, blendRowBuffer);
+            this.ProcessDefilteredScanline(frameControl, currentRow, scanSpan, imageFrame, pngMetadata, blendRowBuffer);
             this.SwapScanlineBuffers();
             currentRow++;
         }
@@ -1720,19 +1722,34 @@ private int ReadNextDataChunk()
     }
 
     /// <summary>
-    /// Reads the next data chunk and skip sequence number.
+    /// Reads the next animated frame data chunk.
     /// </summary>
     /// <returns>Count of bytes in the next data chunk, or 0 if there are no more data chunks left.</returns>
-    private int ReadNextDataChunkAndSkipSeq()
+    private int ReadNextFrameDataChunk()
     {
-        int length = this.ReadNextDataChunk();
-        if (this.ReadNextDataChunk() is 0)
+        if (this.nextChunk != null)
+        {
+            return 0;
+        }
+
+        Span<byte> buffer = stackalloc byte[20];
+
+        _ = this.currentStream.Read(buffer, 0, 4);
+
+        if (this.TryReadChunk(buffer, out PngChunk chunk))
         {
-            return length;
+            if (chunk.Type is PngChunkType.FrameData)
+            {
+                chunk.Data?.Dispose();
+
+                this.currentStream.Position += 4; // Skip sequence number
+                return chunk.Length - 4;
+            }
+
+            this.nextChunk = chunk;
         }
 
-        this.currentStream.Position += 4; // Skip sequence number
-        return length - 4;
+        return 0;
     }
 
     /// <summary>
diff --git a/src/ImageSharp/Formats/Png/PngDisposalMethod.cs b/src/ImageSharp/Formats/Png/PngDisposalMethod.cs
index 17391de95c..1537c5cedf 100644
--- a/src/ImageSharp/Formats/Png/PngDisposalMethod.cs
+++ b/src/ImageSharp/Formats/Png/PngDisposalMethod.cs
@@ -1,4 +1,4 @@
-﻿// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
 namespace SixLabors.ImageSharp.Formats.Png;
@@ -11,15 +11,15 @@ public enum PngDisposalMethod
     /// <summary>
     /// No disposal is done on this frame before rendering the next; the contents of the output buffer are left as is.
     /// </summary>
-    None,
+    DoNotDispose,
 
     /// <summary>
     /// The frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
     /// </summary>
-    Background,
+    RestoreToBackground,
 
     /// <summary>
     /// The frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame.
     /// </summary>
-    Previous
+    RestoreToPrevious
 }
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 04e3b1d840..932916dec2 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -7,8 +7,10 @@
 using System.Runtime.InteropServices;
 using SixLabors.ImageSharp.Common.Helpers;
 using SixLabors.ImageSharp.Compression.Zlib;
+using SixLabors.ImageSharp.Formats.Gif;
 using SixLabors.ImageSharp.Formats.Png.Chunks;
 using SixLabors.ImageSharp.Formats.Png.Filters;
+using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Memory;
 using SixLabors.ImageSharp.Metadata;
 using SixLabors.ImageSharp.PixelFormats;
@@ -116,6 +118,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
     /// </summary>
     private IQuantizer? quantizer;
 
+    /// <summary>
+    /// Any explicit quantized transparent index provided by the background color.
+    /// </summary>
+    private int derivedTransparencyIndex = -1;
+
     /// <summary>
     /// Initializes a new instance of the <see cref="PngEncoderCore" /> class.
     /// </summary>
@@ -137,7 +144,7 @@ public PngEncoderCore(Configuration configuration, PngEncoder encoder)
     /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
     /// <param name="cancellationToken">The token to request cancellation.</param>
     public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
-            where TPixel : unmanaged, IPixel<TPixel>
+        where TPixel : unmanaged, IPixel<TPixel>
     {
         Guard.NotNull(image, nameof(image));
         Guard.NotNull(stream, nameof(stream));
@@ -146,7 +153,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
         this.height = image.Height;
 
         ImageMetadata metadata = image.Metadata;
-        PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
+        PngMetadata pngMetadata = GetPngMetadata(image);
         this.SanitizeAndSetEncoderOptions<TPixel>(this.encoder, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
 
         stream.Write(PngConstants.HeaderBytes);
@@ -162,7 +169,11 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
         }
 
         // Do not move this. We require an accurate bit depth for the header chunk.
-        IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedImageAndUpdateBitDepth(pngMetadata, currentFrame, null);
+        IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedImageAndUpdateBitDepth(
+            pngMetadata,
+            currentFrame,
+            currentFrame.Bounds(),
+            null);
 
         this.WriteHeaderChunk(stream);
         this.WriteGammaChunk(stream);
@@ -176,46 +187,64 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
 
         if (image.Frames.Count > 1)
         {
-            this.WriteAnimationControlChunk(stream, image.Frames.Count, pngMetadata.RepeatCount);
-
-            // TODO: We should attempt to optimize the output by clipping the indexed result to
-            // non-transparent bounds. That way we can assign frame control bounds and encode
-            // less data. See GifEncoder for the implementation there.
+            this.WriteAnimationControlChunk(stream, (uint)image.Frames.Count, pngMetadata.RepeatCount);
 
             // Write the first frame.
-            FrameControl frameControl = this.WriteFrameControlChunk(stream, currentFrame, 0);
-            this.WriteDataChunks(frameControl, currentFrame, quantized, stream, false);
+            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);
 
             // 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++)
             {
+                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;
+
+                frameMetadata = GetPngFrameMetadata(currentFrame);
+                bool blend = frameMetadata.BlendMethod == PngBlendMethod.Over;
+
+                (bool difference, Rectangle bounds) =
+                    AnimationUtilities.DeDuplicatePixels(
+                        image.Configuration,
+                        prev,
+                        currentFrame,
+                        nextFrame,
+                        encodingFrame,
+                        Color.Transparent,
+                        blend);
+
                 if (clearTransparency)
                 {
-                    // Dispose of previous clone and reassign.
-                    clonedFrame?.Dispose();
-                    currentFrame = clonedFrame = currentFrame.Clone();
-                    ClearTransparentPixels(currentFrame);
+                    ClearTransparentPixels(encodingFrame);
                 }
 
-                // Each frame control sequence number must be incremented by the
-                // number of frame data chunks that follow.
-                frameControl = this.WriteFrameControlChunk(stream, currentFrame, (uint)i + increment);
+                // 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);
 
                 // Dispose of previous quantized frame and reassign.
                 quantized?.Dispose();
-                quantized = this.CreateQuantizedImageAndUpdateBitDepth(pngMetadata, currentFrame, previousPalette);
-                increment += this.WriteDataChunks(frameControl, currentFrame, quantized, stream, true);
+                quantized = this.CreateQuantizedImageAndUpdateBitDepth(pngMetadata, encodingFrame, bounds, previousPalette);
+                increment += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true);
+
+                previousFrame = currentFrame;
+                previousDisposal = frameMetadata.DisposalMethod;
             }
         }
         else
         {
             FrameControl frameControl = new((uint)this.width, (uint)this.height);
-            this.WriteDataChunks(frameControl, currentFrame, quantized, stream, false);
+            this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false);
         }
 
         this.WriteEndChunk(stream);
@@ -234,6 +263,54 @@ public void Dispose()
         this.currentScanline?.Dispose();
     }
 
+    private static PngMetadata GetPngMetadata<TPixel>(Image<TPixel> image)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (image.Metadata.TryGetPngMetadata(out PngMetadata? png))
+        {
+            return (PngMetadata)png.DeepClone();
+        }
+
+        if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif))
+        {
+            AnimatedImageMetadata ani = gif.ToAnimatedImageMetadata();
+            return PngMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp))
+        {
+            AnimatedImageMetadata ani = webp.ToAnimatedImageMetadata();
+            return PngMetadata.FromAnimatedMetadata(ani);
+        }
+
+        // Return explicit new instance so we do not mutate the original metadata.
+        return new();
+    }
+
+    private static PngFrameMetadata GetPngFrameMetadata<TPixel>(ImageFrame<TPixel> frame)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png))
+        {
+            return (PngFrameMetadata)png.DeepClone();
+        }
+
+        if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif))
+        {
+            AnimatedImageFrameMetadata ani = gif.ToAnimatedImageFrameMetadata();
+            return PngFrameMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp))
+        {
+            AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata();
+            return PngFrameMetadata.FromAnimatedMetadata(ani);
+        }
+
+        // Return explicit new instance so we do not mutate the original metadata.
+        return new();
+    }
+
     /// <summary>
     /// Convert transparent pixels, to transparent black pixels, which can yield to better compression in some cases.
     /// </summary>
@@ -267,15 +344,17 @@ private static void ClearTransparentPixels<TPixel>(ImageFrame<TPixel> clone)
     /// <typeparam name="TPixel">The type of the pixel.</typeparam>
     /// <param name="metadata">The image metadata.</param>
     /// <param name="frame">The frame to quantize.</param>
+    /// <param name="bounds">The area of interest within the frame.</param>
     /// <param name="previousPalette">Any previously derived palette.</param>
     /// <returns>The quantized image.</returns>
     private IndexedImageFrame<TPixel>? CreateQuantizedImageAndUpdateBitDepth<TPixel>(
         PngMetadata metadata,
         ImageFrame<TPixel> frame,
+        Rectangle bounds,
         ReadOnlyMemory<TPixel>? previousPalette)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedFrame(this.encoder, this.colorType, this.bitDepth, metadata, frame, previousPalette);
+        IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedFrame(this.encoder, this.colorType, this.bitDepth, metadata, frame, bounds, previousPalette);
         this.bitDepth = CalculateBitDepth(this.colorType, this.bitDepth, quantized);
         return quantized;
     }
@@ -621,7 +700,7 @@ private void WriteHeaderChunk(Stream stream)
     /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
     /// <param name="framesCount">The number of frames.</param>
     /// <param name="playsCount">The number of times to loop this APNG.</param>
-    private void WriteAnimationControlChunk(Stream stream, int framesCount, int playsCount)
+    private void WriteAnimationControlChunk(Stream stream, uint framesCount, uint playsCount)
     {
         AnimationControl acTL = new(framesCount, playsCount);
 
@@ -983,19 +1062,17 @@ private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata)
     /// Writes the animation control chunk to the stream.
     /// </summary>
     /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
-    /// <param name="imageFrame">The image frame.</param>
+    /// <param name="frameMetadata">The frame metadata.</param>
+    /// <param name="bounds">The frame area of interest.</param>
     /// <param name="sequenceNumber">The frame sequence number.</param>
-    private FrameControl WriteFrameControlChunk(Stream stream, ImageFrame imageFrame, uint sequenceNumber)
+    private FrameControl WriteFrameControlChunk(Stream stream, PngFrameMetadata frameMetadata, Rectangle bounds, uint sequenceNumber)
     {
-        PngFrameMetadata frameMetadata = imageFrame.Metadata.GetPngFrameMetadata();
-
-        // TODO: If we can clip the indexed frame for transparent bounds we can set properties here.
         FrameControl fcTL = new(
             sequenceNumber: sequenceNumber,
-            width: (uint)imageFrame.Width,
-            height: (uint)imageFrame.Height,
-            xOffset: 0,
-            yOffset: 0,
+            width: (uint)bounds.Width,
+            height: (uint)bounds.Height,
+            xOffset: (uint)bounds.Left,
+            yOffset: (uint)bounds.Top,
             delayNumerator: (ushort)frameMetadata.FrameDelay.Numerator,
             delayDenominator: (ushort)frameMetadata.FrameDelay.Denominator,
             disposeOperation: frameMetadata.DisposalMethod,
@@ -1013,11 +1090,11 @@ private FrameControl WriteFrameControlChunk(Stream stream, ImageFrame imageFrame
     /// </summary>
     /// <typeparam name="TPixel">The pixel format.</typeparam>
     /// <param name="frameControl">The frame control</param>
-    /// <param name="pixels">The frame.</param>
+    /// <param name="frame">The image frame.</param>
     /// <param name="quantized">The quantized pixel data. Can be null.</param>
     /// <param name="stream">The stream.</param>
     /// <param name="isFrame">Is writing fdAT or IDAT.</param>
-    private uint WriteDataChunks<TPixel>(FrameControl frameControl, ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, Stream stream, bool isFrame)
+    private uint WriteDataChunks<TPixel>(FrameControl frameControl, Buffer2DRegion<TPixel> frame, IndexedImageFrame<TPixel>? quantized, Stream stream, bool isFrame)
         where TPixel : unmanaged, IPixel<TPixel>
     {
         byte[] buffer;
@@ -1031,16 +1108,16 @@ private uint WriteDataChunks<TPixel>(FrameControl frameControl, ImageFrame<TPixe
                 {
                     if (quantized is not null)
                     {
-                        this.EncodeAdam7IndexedPixels(frameControl, quantized, deflateStream);
+                        this.EncodeAdam7IndexedPixels(quantized, deflateStream);
                     }
                     else
                     {
-                        this.EncodeAdam7Pixels(frameControl, pixels, deflateStream);
+                        this.EncodeAdam7Pixels(frame, deflateStream);
                     }
                 }
                 else
                 {
-                    this.EncodePixels(frameControl, pixels, quantized, deflateStream);
+                    this.EncodePixels(frame, quantized, deflateStream);
                 }
             }
 
@@ -1105,54 +1182,43 @@ private void AllocateScanlineBuffers(int bytesPerScanline)
     /// Encodes the pixels.
     /// </summary>
     /// <typeparam name="TPixel">The type of the pixel.</typeparam>
-    /// <param name="frameControl">The frame control</param>
-    /// <param name="pixels">The pixels.</param>
-    /// <param name="quantized">The quantized pixels span.</param>
+    /// <param name="pixels">The image frame pixel buffer.</param>
+    /// <param name="quantized">The quantized pixels.</param>
     /// <param name="deflateStream">The deflate stream.</param>
-    private void EncodePixels<TPixel>(FrameControl frameControl, ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, ZlibDeflateStream deflateStream)
+    private void EncodePixels<TPixel>(Buffer2DRegion<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, ZlibDeflateStream deflateStream)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        int width = (int)frameControl.Width;
-        int height = (int)frameControl.Height;
-
-        int bytesPerScanline = this.CalculateScanlineLength(width);
+        int bytesPerScanline = this.CalculateScanlineLength(pixels.Width);
         int filterLength = bytesPerScanline + 1;
         this.AllocateScanlineBuffers(bytesPerScanline);
 
         using IMemoryOwner<byte> filterBuffer = this.memoryAllocator.Allocate<byte>(filterLength, AllocationOptions.Clean);
         using IMemoryOwner<byte> attemptBuffer = this.memoryAllocator.Allocate<byte>(filterLength, AllocationOptions.Clean);
 
-        pixels.ProcessPixelRows(accessor =>
+        Span<byte> filter = filterBuffer.GetSpan();
+        Span<byte> attempt = attemptBuffer.GetSpan();
+        for (int y = 0; y < pixels.Height; y++)
         {
-            Span<byte> filter = filterBuffer.GetSpan();
-            Span<byte> attempt = attemptBuffer.GetSpan();
-            for (int y = (int)frameControl.YOffset; y < frameControl.YMax; y++)
-            {
-                this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y);
-                deflateStream.Write(filter);
-                this.SwapScanlineBuffers();
-            }
-        });
+            this.CollectAndFilterPixelRow(pixels.DangerousGetRowSpan(y), ref filter, ref attempt, quantized, y);
+            deflateStream.Write(filter);
+            this.SwapScanlineBuffers();
+        }
     }
 
     /// <summary>
     /// Interlaced encoding the pixels.
     /// </summary>
     /// <typeparam name="TPixel">The type of the pixel.</typeparam>
-    /// <param name="frameControl">The frame control</param>
-    /// <param name="frame">The image frame.</param>
+    /// <param name="pixels">The image frame pixel buffer.</param>
     /// <param name="deflateStream">The deflate stream.</param>
-    private void EncodeAdam7Pixels<TPixel>(FrameControl frameControl, ImageFrame<TPixel> frame, ZlibDeflateStream deflateStream)
+    private void EncodeAdam7Pixels<TPixel>(Buffer2DRegion<TPixel> pixels, ZlibDeflateStream deflateStream)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        int width = (int)frameControl.XMax;
-        int height = (int)frameControl.YMax;
-        Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer;
         for (int pass = 0; pass < 7; pass++)
         {
-            int startRow = Adam7.FirstRow[pass] + (int)frameControl.YOffset;
-            int startCol = Adam7.FirstColumn[pass] + (int)frameControl.XOffset;
-            int blockWidth = Adam7.ComputeBlockWidth(width, pass);
+            int startRow = Adam7.FirstRow[pass];
+            int startCol = Adam7.FirstColumn[pass];
+            int blockWidth = Adam7.ComputeBlockWidth(pixels.Width, pass);
 
             int bytesPerScanline = this.bytesPerPixel <= 1
                 ? ((blockWidth * this.bitDepth) + 7) / 8
@@ -1169,13 +1235,13 @@ private void EncodeAdam7Pixels<TPixel>(FrameControl frameControl, ImageFrame<TPi
             Span<byte> filter = filterBuffer.GetSpan();
             Span<byte> attempt = attemptBuffer.GetSpan();
 
-            for (int row = startRow; row < height; row += Adam7.RowIncrement[pass])
+            for (int row = startRow; row < pixels.Height; row += Adam7.RowIncrement[pass])
             {
                 // Collect pixel data
-                Span<TPixel> srcRow = pixelBuffer.DangerousGetRowSpan(row);
-                for (int col = startCol, i = 0; col < frameControl.XMax; col += Adam7.ColumnIncrement[pass])
+                Span<TPixel> srcRow = pixels.DangerousGetRowSpan(row);
+                for (int col = startCol, i = 0; col < pixels.Width; col += Adam7.ColumnIncrement[pass], i++)
                 {
-                    block[i++] = srcRow[col];
+                    block[i] = srcRow[col];
                 }
 
                 // Encode data
@@ -1193,19 +1259,16 @@ private void EncodeAdam7Pixels<TPixel>(FrameControl frameControl, ImageFrame<TPi
     /// Interlaced encoding the quantized (indexed, with palette) pixels.
     /// </summary>
     /// <typeparam name="TPixel">The type of the pixel.</typeparam>
-    /// <param name="frameControl">The frame control</param>
     /// <param name="quantized">The quantized.</param>
     /// <param name="deflateStream">The deflate stream.</param>
-    private void EncodeAdam7IndexedPixels<TPixel>(FrameControl frameControl, IndexedImageFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
+    private void EncodeAdam7IndexedPixels<TPixel>(IndexedImageFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        int width = (int)frameControl.Width;
-        int endRow = (int)frameControl.YMax;
         for (int pass = 0; pass < 7; pass++)
         {
-            int startRow = Adam7.FirstRow[pass] + (int)frameControl.YOffset;
-            int startCol = Adam7.FirstColumn[pass] + (int)frameControl.XOffset;
-            int blockWidth = Adam7.ComputeBlockWidth(width, pass);
+            int startRow = Adam7.FirstRow[pass];
+            int startCol = Adam7.FirstColumn[pass];
+            int blockWidth = Adam7.ComputeBlockWidth(quantized.Width, pass);
 
             int bytesPerScanline = this.bytesPerPixel <= 1
                 ? ((blockWidth * this.bitDepth) + 7) / 8
@@ -1223,16 +1286,13 @@ private void EncodeAdam7IndexedPixels<TPixel>(FrameControl frameControl, Indexed
             Span<byte> filter = filterBuffer.GetSpan();
             Span<byte> attempt = attemptBuffer.GetSpan();
 
-            for (int row = startRow; row < endRow; row += Adam7.RowIncrement[pass])
+            for (int row = startRow; row < quantized.Height; row += Adam7.RowIncrement[pass])
             {
                 // Collect data
                 ReadOnlySpan<byte> srcRow = quantized.DangerousGetRowSpan(row);
-                for (int col = startCol, i = 0;
-                    col < frameControl.XMax;
-                    col += Adam7.ColumnIncrement[pass])
+                for (int col = startCol, i = 0; col < quantized.Width; col += Adam7.ColumnIncrement[pass], i++)
                 {
                     block[i] = srcRow[col];
-                    i++;
                 }
 
                 // Encode data
@@ -1404,6 +1464,7 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
     /// <param name="bitDepth">The bits per component.</param>
     /// <param name="metadata">The image metadata.</param>
     /// <param name="frame">The frame to quantize.</param>
+    /// <param name="bounds">The frame area of interest.</param>
     /// <param name="previousPalette">Any previously derived palette.</param>
     private IndexedImageFrame<TPixel>? CreateQuantizedFrame<TPixel>(
         QuantizingImageEncoder encoder,
@@ -1411,6 +1472,7 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
         byte bitDepth,
         PngMetadata metadata,
         ImageFrame<TPixel> frame,
+        Rectangle bounds,
         ReadOnlyMemory<TPixel>? previousPalette)
         where TPixel : unmanaged, IPixel<TPixel>
     {
@@ -1422,9 +1484,13 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
         if (previousPalette is not null)
         {
             // Use the previously derived palette created by quantizing the root frame to quantize the current frame.
-            using PaletteQuantizer<TPixel> paletteQuantizer = new(this.configuration, this.quantizer!.Options, previousPalette.Value, -1);
+            using PaletteQuantizer<TPixel> paletteQuantizer = new(
+                this.configuration,
+                this.quantizer!.Options,
+                previousPalette.Value,
+                this.derivedTransparencyIndex);
             paletteQuantizer.BuildPalette(encoder.PixelSamplingStrategy, frame);
-            return paletteQuantizer.QuantizeFrame(frame, frame.Bounds());
+            return paletteQuantizer.QuantizeFrame(frame, bounds);
         }
 
         // Use the metadata to determine what quantization depth to use if no quantizer has been set.
@@ -1432,8 +1498,10 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
         {
             if (metadata.ColorTable is not null)
             {
-                // Use the provided palette. The caller is responsible for setting values.
-                this.quantizer = new PaletteQuantizer(metadata.ColorTable.Value);
+                // We can use the color data from the decoded metadata here.
+                // We avoid dithering by default to preserve the original colors.
+                this.derivedTransparencyIndex = metadata.ColorTable.Value.Span.IndexOf(Color.Transparent);
+                this.quantizer = new PaletteQuantizer(metadata.ColorTable.Value, new() { Dither = null }, this.derivedTransparencyIndex);
             }
             else
             {
@@ -1445,7 +1513,7 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
         using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(frame.Configuration);
 
         frameQuantizer.BuildPalette(encoder.PixelSamplingStrategy, frame);
-        return frameQuantizer.QuantizeFrame(frame, frame.Bounds());
+        return frameQuantizer.QuantizeFrame(frame, bounds);
     }
 
     /// <summary>
diff --git a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs
index ca4d8c1f45..dbda4d73c9 100644
--- a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs
@@ -34,7 +34,7 @@ private PngFrameMetadata(PngFrameMetadata other)
     /// wait before continuing with the processing of the Data Stream.
     /// The clock starts ticking immediately after the graphic is rendered.
     /// </summary>
-    public Rational FrameDelay { get; set; }
+    public Rational FrameDelay { get; set; } = new(0);
 
     /// <summary>
     /// Gets or sets the type of frame area disposal to be done after rendering this frame
@@ -59,4 +59,20 @@ internal void FromChunk(in FrameControl frameControl)
 
     /// <inheritdoc/>
     public IDeepCloneable DeepClone() => new PngFrameMetadata(this);
+
+    internal static PngFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata)
+        => new()
+        {
+            FrameDelay = new(metadata.Duration.TotalMilliseconds / 1000),
+            DisposalMethod = GetMode(metadata.DisposalMode),
+            BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? PngBlendMethod.Source : PngBlendMethod.Over,
+        };
+
+    private static PngDisposalMethod GetMode(FrameDisposalMode mode) => mode switch
+    {
+        FrameDisposalMode.RestoreToBackground => PngDisposalMethod.RestoreToBackground,
+        FrameDisposalMode.RestoreToPrevious => PngDisposalMethod.RestoreToPrevious,
+        FrameDisposalMode.DoNotDispose => PngDisposalMethod.DoNotDispose,
+        _ => PngDisposalMethod.DoNotDispose,
+    };
 }
diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs
index b113dbfc17..93ddcf2636 100644
--- a/src/ImageSharp/Formats/Png/PngMetadata.cs
+++ b/src/ImageSharp/Formats/Png/PngMetadata.cs
@@ -2,7 +2,6 @@
 // Licensed under the Six Labors Split License.
 
 using SixLabors.ImageSharp.Formats.Png.Chunks;
-using SixLabors.ImageSharp.PixelFormats;
 
 namespace SixLabors.ImageSharp.Formats.Png;
 
@@ -82,8 +81,38 @@ private PngMetadata(PngMetadata other)
     /// <summary>
     /// Gets or sets the number of times to loop this APNG.  0 indicates infinite looping.
     /// </summary>
-    public int RepeatCount { get; set; }
+    public uint RepeatCount { get; set; } = 1;
 
     /// <inheritdoc/>
     public IDeepCloneable DeepClone() => new PngMetadata(this);
+
+    internal static PngMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
+    {
+        // Should the conversion be from a format that uses a 24bit palette entries (gif)
+        // we need to clone and adjust the color table to allow for transparency.
+        Color[]? colorTable = metadata.ColorTable.HasValue ? metadata.ColorTable.Value.ToArray() : null;
+        if (colorTable != null)
+        {
+            for (int i = 0; i < colorTable.Length; i++)
+            {
+                ref Color c = ref colorTable[i];
+                if (c == metadata.BackgroundColor)
+                {
+                    // Png treats background as fully empty
+                    c = Color.Transparent;
+                    break;
+                }
+            }
+        }
+
+        return new()
+        {
+            ColorType = colorTable != null ? PngColorType.Palette : null,
+            BitDepth = colorTable != null
+                        ? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Length), 1, 8)
+                        : null,
+            ColorTable = colorTable,
+            RepeatCount = metadata.RepeatCount,
+        };
+    }
 }
diff --git a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
index cbd2aa8e7f..46030dde32 100644
--- a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
@@ -27,7 +27,7 @@ internal static class AlphaEncoder
     /// <param name="size">The size in bytes of the alpha data.</param>
     /// <returns>The encoded alpha data.</returns>
     public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
-        ImageFrame<TPixel> frame,
+        Buffer2DRegion<TPixel> frame,
         Configuration configuration,
         MemoryAllocator memoryAllocator,
         bool skipMetadata,
@@ -35,8 +35,6 @@ public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
         out int size)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        int width = frame.Width;
-        int height = frame.Height;
         IMemoryOwner<byte> alphaData = ExtractAlphaChannel(frame, configuration, memoryAllocator);
 
         if (compress)
@@ -46,8 +44,8 @@ public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
             using Vp8LEncoder lossLessEncoder = new(
                 memoryAllocator,
                 configuration,
-                width,
-                height,
+                frame.Width,
+                frame.Height,
                 quality,
                 skipMetadata,
                 effort,
@@ -58,14 +56,14 @@ public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
             // The transparency information will be stored in the green channel of the ARGB quadruplet.
             // The green channel is allowed extra transformation steps in the specification -- unlike the other channels,
             // that can improve compression.
-            using ImageFrame<Rgba32> alphaAsFrame = DispatchAlphaToGreen(frame, alphaData.GetSpan());
+            using ImageFrame<Bgra32> alphaAsFrame = DispatchAlphaToGreen(configuration, frame, alphaData.GetSpan());
 
-            size = lossLessEncoder.EncodeAlphaImageData(alphaAsFrame, alphaData);
+            size = lossLessEncoder.EncodeAlphaImageData(alphaAsFrame.PixelBuffer.GetRegion(), alphaData);
 
             return alphaData;
         }
 
-        size = width * height;
+        size = frame.Width * frame.Height;
         return alphaData;
     }
 
@@ -73,25 +71,28 @@ public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
     /// Store the transparency in the green channel.
     /// </summary>
     /// <typeparam name="TPixel">The pixel format.</typeparam>
-    /// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
+    /// <param name="configuration">The configuration.</param>
+    /// <param name="frame">The pixel buffer to encode from.</param>
     /// <param name="alphaData">A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</param>
     /// <returns>The transparency frame.</returns>
-    private static ImageFrame<Rgba32> DispatchAlphaToGreen<TPixel>(ImageFrame<TPixel> frame, Span<byte> alphaData)
+    private static ImageFrame<Bgra32> DispatchAlphaToGreen<TPixel>(Configuration configuration, Buffer2DRegion<TPixel> frame, Span<byte> alphaData)
         where TPixel : unmanaged, IPixel<TPixel>
     {
         int width = frame.Width;
         int height = frame.Height;
-        ImageFrame<Rgba32> alphaAsFrame = new ImageFrame<Rgba32>(Configuration.Default, width, height);
+        ImageFrame<Bgra32> alphaAsFrame = new(configuration, width, height);
 
         for (int y = 0; y < height; y++)
         {
-            Memory<Rgba32> rowBuffer = alphaAsFrame.DangerousGetPixelRowMemory(y);
-            Span<Rgba32> pixelRow = rowBuffer.Span;
+            Memory<Bgra32> rowBuffer = alphaAsFrame.DangerousGetPixelRowMemory(y);
+            Span<Bgra32> pixelRow = rowBuffer.Span;
             Span<byte> alphaRow = alphaData.Slice(y * width, width);
+
+            // TODO: This can be probably simd optimized.
             for (int x = 0; x < width; x++)
             {
                 // Leave A/R/B channels zero'd.
-                pixelRow[x] = new Rgba32(0, alphaRow[x], 0, 0);
+                pixelRow[x] = new Bgra32(0, alphaRow[x], 0, 0);
             }
         }
 
@@ -106,12 +107,12 @@ private static ImageFrame<Rgba32> DispatchAlphaToGreen<TPixel>(ImageFrame<TPixel
     /// <param name="configuration">The global configuration.</param>
     /// <param name="memoryAllocator">The memory manager.</param>
     /// <returns>A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</returns>
-    private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(ImageFrame<TPixel> frame, Configuration configuration, MemoryAllocator memoryAllocator)
+    private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(Buffer2DRegion<TPixel> frame, Configuration configuration, MemoryAllocator memoryAllocator)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        Buffer2D<TPixel> imageBuffer = frame.PixelBuffer;
-        int height = frame.Height;
         int width = frame.Width;
+        int height = frame.Height;
+
         IMemoryOwner<byte> alphaDataBuffer = memoryAllocator.Allocate<byte>(width * height);
         Span<byte> alphaData = alphaDataBuffer.GetSpan();
 
@@ -120,7 +121,7 @@ private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(ImageFrame<TPixel>
 
         for (int y = 0; y < height; y++)
         {
-            Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(y);
+            Span<TPixel> rowSpan = frame.DangerousGetRowSpan(y);
             PixelOperations<TPixel>.Instance.ToRgba32(configuration, rowSpan, rgbaRow);
             int offset = y * width;
             for (int x = 0; x < width; x++)
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
index d502fd6063..9ffda0f51f 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
@@ -1,7 +1,6 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
-using System.Diagnostics;
 using SixLabors.ImageSharp.Common.Helpers;
 using SixLabors.ImageSharp.Formats.Webp.Chunks;
 using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@@ -100,9 +99,7 @@ public static void WriteTrunksBeforeData(
         bool hasAnimation)
     {
         // Write file size later
-        long pos = RiffHelper.BeginWriteRiffFile(stream, WebpConstants.WebpFourCc);
-
-        Debug.Assert(pos is 4, "Stream should be written from position 0.");
+        RiffHelper.BeginWriteRiffFile(stream, WebpConstants.WebpFourCc);
 
         // Write VP8X, header if necessary.
         bool isVp8X = exifProfile != null || xmpProfile != null || iccProfile != null || hasAlpha || hasAnimation;
@@ -160,7 +157,7 @@ public static void WriteTrunksAfterData(
     /// <param name="loopCount">The number of times to loop the animation. If it is 0, this means infinitely.</param>
     public static void WriteAnimationParameter(Stream stream, Color background, ushort loopCount)
     {
-        WebpAnimationParameter chunk = new(background.ToRgba32().Rgba, loopCount);
+        WebpAnimationParameter chunk = new(background.ToBgra32().PackedValue, loopCount);
         chunk.WriteTo(stream);
     }
 
diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs
index f22a3fd540..5ed7aab1ea 100644
--- a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs
+++ b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs
@@ -12,7 +12,7 @@ internal readonly struct WebpFrameData
     /// </summary>
     public const uint HeaderSize = 16;
 
-    public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uint duration, WebpBlendingMethod blendingMethod, WebpDisposalMethod disposalMethod)
+    public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uint duration, WebpBlendMethod blendingMethod, WebpDisposalMethod disposalMethod)
     {
         this.DataSize = dataSize;
         this.X = x;
@@ -32,12 +32,12 @@ public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uin
             width,
             height,
             duration,
-            (flags & 2) != 0 ? WebpBlendingMethod.DoNotBlend : WebpBlendingMethod.AlphaBlending,
-            (flags & 1) == 1 ? WebpDisposalMethod.Dispose : WebpDisposalMethod.DoNotDispose)
+            (flags & 2) == 0 ? WebpBlendMethod.Over : WebpBlendMethod.Source,
+            (flags & 1) == 1 ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose)
     {
     }
 
-    public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, WebpBlendingMethod blendingMethod, WebpDisposalMethod disposalMethod)
+    public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, WebpBlendMethod blendingMethod, WebpDisposalMethod disposalMethod)
         : this(0, x, y, width, height, duration, blendingMethod, disposalMethod)
     {
     }
@@ -76,14 +76,14 @@ public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, Web
     /// <summary>
     /// Gets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
     /// </summary>
-    public WebpBlendingMethod BlendingMethod { get; }
+    public WebpBlendMethod BlendingMethod { get; }
 
     /// <summary>
     /// Gets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas.
     /// </summary>
     public WebpDisposalMethod DisposalMethod { get; }
 
-    public Rectangle Bounds => new((int)this.X * 2, (int)this.Y * 2, (int)this.Width, (int)this.Height);
+    public Rectangle Bounds => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height);
 
     /// <summary>
     /// Writes the animation frame(<see cref="WebpChunkType.FrameData"/>) to the stream.
@@ -93,13 +93,13 @@ public long WriteHeaderTo(Stream stream)
     {
         byte flags = 0;
 
-        if (this.BlendingMethod is WebpBlendingMethod.DoNotBlend)
+        if (this.BlendingMethod is WebpBlendMethod.Source)
         {
             // Set blending flag.
             flags |= 2;
         }
 
-        if (this.DisposalMethod is WebpDisposalMethod.Dispose)
+        if (this.DisposalMethod is WebpDisposalMethod.RestoreToBackground)
         {
             // Set disposal flag.
             flags |= 1;
@@ -107,8 +107,8 @@ public long WriteHeaderTo(Stream stream)
 
         long pos = RiffHelper.BeginWriteChunk(stream, (uint)WebpChunkType.FrameData);
 
-        WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, this.X);
-        WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, this.Y);
+        WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, (uint)Math.Round(this.X / 2f));
+        WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, (uint)Math.Round(this.Y / 2f));
         WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, this.Width - 1);
         WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, this.Height - 1);
         WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, this.Duration);
@@ -128,8 +128,8 @@ public static WebpFrameData Parse(Stream stream)
 
         WebpFrameData data = new(
             dataSize: WebpChunkParsingUtils.ReadChunkSize(stream, buffer),
-            x: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer),
-            y: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer),
+            x: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) * 2,
+            y: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) * 2,
             width: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) + 1,
             height: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) + 1,
             duration: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer),
diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
index 4fdbb31d37..518c09ff4d 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
@@ -240,7 +240,7 @@ public Vp8LEncoder(
     public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAnimation)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        // Write bytes from the bitwriter buffer to the stream.
+        // Write bytes from the bit-writer buffer to the stream.
         ImageMetadata metadata = image.Metadata;
         metadata.SyncProfiles();
 
@@ -259,15 +259,15 @@ public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAni
 
         if (hasAnimation)
         {
-            WebpMetadata webpMetadata = metadata.GetWebpMetadata();
-            BitWriterBase.WriteAnimationParameter(stream, webpMetadata.AnimationBackground, webpMetadata.AnimationLoopCount);
+            WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image);
+            BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount);
         }
     }
 
     public void EncodeFooter<TPixel>(Image<TPixel> image, Stream stream)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        // Write bytes from the bitwriter buffer to the stream.
+        // Write bytes from the bit-writer buffer to the stream.
         ImageMetadata metadata = image.Metadata;
 
         ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile;
@@ -280,26 +280,25 @@ public void EncodeFooter<TPixel>(Image<TPixel> image, Stream stream)
     /// Encodes the image as lossless webp to the specified stream.
     /// </summary>
     /// <typeparam name="TPixel">The pixel format.</typeparam>
-    /// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
+    /// <param name="frame">The image frame to encode from.</param>
+    /// <param name="bounds">The region of interest within the frame to encode.</param>
+    /// <param name="frameMetadata">The frame metadata.</param>
     /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
     /// <param name="hasAnimation">Flag indicating, if an animation parameter is present.</param>
-    public void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAnimation)
+    public void Encode<TPixel>(ImageFrame<TPixel> frame, Rectangle bounds, WebpFrameMetadata frameMetadata, Stream stream, bool hasAnimation)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        int width = frame.Width;
-        int height = frame.Height;
-
         // Convert image pixels to bgra array.
-        bool hasAlpha = this.ConvertPixelsToBgra(frame, width, height);
+        bool hasAlpha = this.ConvertPixelsToBgra(frame.PixelBuffer.GetRegion(bounds));
 
         // Write the image size.
-        this.WriteImageSize(width, height);
+        this.WriteImageSize(bounds.Width, bounds.Height);
 
         // Write the non-trivial Alpha flag and lossless version.
         this.WriteAlphaAndVersion(hasAlpha);
 
         // Encode the main image stream.
-        this.EncodeStream(frame);
+        this.EncodeStream(bounds.Width, bounds.Height);
 
         this.bitWriter.Finish();
 
@@ -307,21 +306,18 @@ public void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAnim
 
         if (hasAnimation)
         {
-            WebpFrameMetadata frameMetadata = frame.Metadata.GetWebpMetadata();
-
-            // TODO: If we can clip the indexed frame for transparent bounds we can set properties here.
             prevPosition = new WebpFrameData(
-                    0,
-                    0,
-                    (uint)frame.Width,
-                    (uint)frame.Height,
+                    (uint)bounds.Left,
+                    (uint)bounds.Top,
+                    (uint)bounds.Width,
+                    (uint)bounds.Height,
                     frameMetadata.FrameDelay,
                     frameMetadata.BlendMethod,
                     frameMetadata.DisposalMethod)
                 .WriteHeaderTo(stream);
         }
 
-        // Write bytes from the bitwriter buffer to the stream.
+        // Write bytes from the bit-writer buffer to the stream.
         this.bitWriter.WriteEncodedImageToStream(stream);
 
         if (hasAnimation)
@@ -334,12 +330,12 @@ public void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAnim
     /// Encodes the alpha image data using the webp lossless compression.
     /// </summary>
     /// <typeparam name="TPixel">The type of the pixel.</typeparam>
-    /// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
+    /// <param name="frame">The alpha-pixel data to encode from.</param>
     /// <param name="alphaData">The destination buffer to write the encoded alpha data to.</param>
     /// <returns>The size of the compressed data in bytes.
     /// If the size of the data is the same as the pixel count, the compression would not yield in smaller data and is left uncompressed.
     /// </returns>
-    public int EncodeAlphaImageData<TPixel>(ImageFrame<TPixel> frame, IMemoryOwner<byte> alphaData)
+    public int EncodeAlphaImageData<TPixel>(Buffer2DRegion<TPixel> frame, IMemoryOwner<byte> alphaData)
         where TPixel : unmanaged, IPixel<TPixel>
     {
         int width = frame.Width;
@@ -347,10 +343,10 @@ public int EncodeAlphaImageData<TPixel>(ImageFrame<TPixel> frame, IMemoryOwner<b
         int pixelCount = width * height;
 
         // Convert image pixels to bgra array.
-        this.ConvertPixelsToBgra(frame, width, height);
+        this.ConvertPixelsToBgra(frame);
 
         // The image-stream will NOT contain any headers describing the image dimension, the dimension is already known.
-        this.EncodeStream(frame);
+        this.EncodeStream(width, height);
         this.bitWriter.Finish();
         int size = this.bitWriter.NumBytes;
         if (size >= pixelCount)
@@ -364,7 +360,7 @@ public int EncodeAlphaImageData<TPixel>(ImageFrame<TPixel> frame, IMemoryOwner<b
     }
 
     /// <summary>
-    /// Writes the image size to the bitwriter buffer.
+    /// Writes the image size to the bit writer buffer.
     /// </summary>
     /// <param name="inputImgWidth">The input image width.</param>
     /// <param name="inputImgHeight">The input image height.</param>
@@ -381,7 +377,7 @@ private void WriteImageSize(int inputImgWidth, int inputImgHeight)
     }
 
     /// <summary>
-    /// Writes a flag indicating if alpha channel is used and the VP8L version to the bitwriter buffer.
+    /// Writes a flag indicating if alpha channel is used and the VP8L version to the bit-writer buffer.
     /// </summary>
     /// <param name="hasAlpha">Indicates if a alpha channel is present.</param>
     private void WriteAlphaAndVersion(bool hasAlpha)
@@ -393,14 +389,10 @@ private void WriteAlphaAndVersion(bool hasAlpha)
     /// <summary>
     /// Encodes the image stream using lossless webp format.
     /// </summary>
-    /// <typeparam name="TPixel">The pixel type.</typeparam>
-    /// <param name="frame">The frame to encode.</param>
-    private void EncodeStream<TPixel>(ImageFrame<TPixel> frame)
-        where TPixel : unmanaged, IPixel<TPixel>
+    /// <param name="width">The image frame width.</param>
+    /// <param name="height">The image frame height.</param>
+    private void EncodeStream(int width, int height)
     {
-        int width = frame.Width;
-        int height = frame.Height;
-
         Span<uint> bgra = this.Bgra.GetSpan();
         Span<uint> encodedData = this.EncodedData.GetSpan();
         bool lowEffort = this.method == 0;
@@ -508,23 +500,20 @@ private void EncodeStream<TPixel>(ImageFrame<TPixel> frame)
     /// Converts the pixels of the image to bgra.
     /// </summary>
     /// <typeparam name="TPixel">The type of the pixels.</typeparam>
-    /// <param name="frame">The frame to convert.</param>
-    /// <param name="width">The width of the image.</param>
-    /// <param name="height">The height of the image.</param>
+    /// <param name="pixels">The frame pixel buffer to convert.</param>
     /// <returns>true, if the image is non opaque.</returns>
-    private bool ConvertPixelsToBgra<TPixel>(ImageFrame<TPixel> frame, int width, int height)
+    private bool ConvertPixelsToBgra<TPixel>(Buffer2DRegion<TPixel> pixels)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        Buffer2D<TPixel> imageBuffer = frame.PixelBuffer;
         bool nonOpaque = false;
         Span<uint> bgra = this.Bgra.GetSpan();
         Span<byte> bgraBytes = MemoryMarshal.Cast<uint, byte>(bgra);
-        int widthBytes = width * 4;
-        for (int y = 0; y < height; y++)
+        int widthBytes = pixels.Width * 4;
+        for (int y = 0; y < pixels.Height; y++)
         {
-            Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(y);
+            Span<TPixel> rowSpan = pixels.DangerousGetRowSpan(y);
             Span<byte> rowBytes = bgraBytes.Slice(y * widthBytes, widthBytes);
-            PixelOperations<TPixel>.Instance.ToBgra32Bytes(this.configuration, rowSpan, rowBytes, width);
+            PixelOperations<TPixel>.Instance.ToBgra32Bytes(this.configuration, rowSpan, rowBytes, pixels.Width);
             if (!nonOpaque)
             {
                 Span<Bgra32> rowBgra = MemoryMarshal.Cast<byte, Bgra32>(rowBytes);
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
index 98e50bb9c2..2b74c300a4 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
@@ -333,8 +333,8 @@ public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAlp
 
         if (hasAnimation)
         {
-            WebpMetadata webpMetadata = metadata.GetWebpMetadata();
-            BitWriterBase.WriteAnimationParameter(stream, webpMetadata.AnimationBackground, webpMetadata.AnimationLoopCount);
+            WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image);
+            BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount);
         }
     }
 
@@ -351,44 +351,53 @@ public void EncodeFooter<TPixel>(Image<TPixel> image, Stream stream)
     }
 
     /// <summary>
-    /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
+    /// Encodes the animated image frame to the specified stream.
     /// </summary>
     /// <typeparam name="TPixel">The pixel format.</typeparam>
-    /// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
-    /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
-    public void EncodeAnimation<TPixel>(ImageFrame<TPixel> frame, Stream stream)
+    /// <param name="frame">The image frame to encode from.</param>
+    /// <param name="stream">The stream to encode the image data to.</param>
+    /// <param name="bounds">The region of interest within the frame to encode.</param>
+    /// <param name="frameMetadata">The frame metadata.</param>
+    public void EncodeAnimation<TPixel>(ImageFrame<TPixel> frame, Stream stream, Rectangle bounds, WebpFrameMetadata frameMetadata)
         where TPixel : unmanaged, IPixel<TPixel> =>
-        this.Encode(frame, stream, true, null);
+        this.Encode(stream, frame, bounds, frameMetadata, true, null);
 
     /// <summary>
-    /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
+    /// Encodes the static image frame to the specified stream.
     /// </summary>
     /// <typeparam name="TPixel">The pixel format.</typeparam>
-    /// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
-    /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
-    public void EncodeStatic<TPixel>(Image<TPixel> image, Stream stream)
-        where TPixel : unmanaged, IPixel<TPixel> =>
-        this.Encode(image.Frames.RootFrame, stream, false, image);
+    /// <param name="stream">The stream to encode the image data to.</param>
+    /// <param name="image">The image to encode from.</param>
+    public void EncodeStatic<TPixel>(Stream stream, Image<TPixel> image)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        ImageFrame<TPixel> frame = image.Frames.RootFrame;
+        this.Encode(stream, frame, image.Bounds, WebpCommonUtils.GetWebpFrameMetadata(frame), false, image);
+    }
 
     /// <summary>
-    /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
+    /// Encodes the image to the specified stream.
     /// </summary>
     /// <typeparam name="TPixel">The pixel format.</typeparam>
-    /// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
-    /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
+    /// <param name="stream">The stream to encode the image data to.</param>
+    /// <param name="frame">The image frame to encode from.</param>
+    /// <param name="bounds">The region of interest within the frame to encode.</param>
+    /// <param name="frameMetadata">The frame metadata.</param>
     /// <param name="hasAnimation">Flag indicating, if an animation parameter is present.</param>
-    /// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
-    private void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAnimation, Image<TPixel> image)
+    /// <param name="image">The image to encode from.</param>
+    private void Encode<TPixel>(Stream stream, ImageFrame<TPixel> frame, Rectangle bounds, WebpFrameMetadata frameMetadata, bool hasAnimation, Image<TPixel> image)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        int width = frame.Width;
-        int height = frame.Height;
+        int width = bounds.Width;
+        int height = bounds.Height;
 
         int pixelCount = width * height;
         Span<byte> y = this.Y.GetSpan();
         Span<byte> u = this.U.GetSpan();
         Span<byte> v = this.V.GetSpan();
-        bool hasAlpha = YuvConversion.ConvertRgbToYuv(frame, this.configuration, this.memoryAllocator, y, u, v);
+
+        Buffer2DRegion<TPixel> pixels = frame.PixelBuffer.GetRegion(bounds);
+        bool hasAlpha = YuvConversion.ConvertRgbToYuv(pixels, this.configuration, this.memoryAllocator, y, u, v);
 
         if (!hasAnimation)
         {
@@ -456,7 +465,7 @@ private void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAni
             {
                 // TODO: This can potentially run in an separate task.
                 encodedAlphaData = AlphaEncoder.EncodeAlpha(
-                    frame,
+                    pixels,
                     this.configuration,
                     this.memoryAllocator,
                     this.skipMetadata,
@@ -477,14 +486,11 @@ private void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAni
 
             if (hasAnimation)
             {
-                WebpFrameMetadata frameMetadata = frame.Metadata.GetWebpMetadata();
-
-                // TODO: If we can clip the indexed frame for transparent bounds we can set properties here.
                 prevPosition = new WebpFrameData(
-                    0,
-                    0,
-                    (uint)frame.Width,
-                    (uint)frame.Height,
+                    (uint)bounds.X,
+                    (uint)bounds.Y,
+                    (uint)bounds.Width,
+                    (uint)bounds.Height,
                     frameMetadata.FrameDelay,
                     frameMetadata.BlendMethod,
                     frameMetadata.DisposalMethod)
diff --git a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
index d669a37b74..f8e664ed03 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
@@ -259,7 +259,7 @@ private static void PackAndStore(Vector128<byte> a, Vector128<byte> b, Vector128
     }
 
     /// <summary>
-    /// Converts the RGB values of the image to YUV.
+    /// Converts the pixel values of the image to YUV.
     /// </summary>
     /// <typeparam name="TPixel">The pixel type of the image.</typeparam>
     /// <param name="frame">The frame to convert.</param>
@@ -269,12 +269,11 @@ private static void PackAndStore(Vector128<byte> a, Vector128<byte> b, Vector128
     /// <param name="u">Span to store the u component of the image.</param>
     /// <param name="v">Span to store the v component of the image.</param>
     /// <returns>true, if the image contains alpha data.</returns>
-    public static bool ConvertRgbToYuv<TPixel>(ImageFrame<TPixel> frame, Configuration configuration, MemoryAllocator memoryAllocator, Span<byte> y, Span<byte> u, Span<byte> v)
+    public static bool ConvertRgbToYuv<TPixel>(Buffer2DRegion<TPixel> frame, Configuration configuration, MemoryAllocator memoryAllocator, Span<byte> y, Span<byte> u, Span<byte> v)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        Buffer2D<TPixel> imageBuffer = frame.PixelBuffer;
-        int width = imageBuffer.Width;
-        int height = imageBuffer.Height;
+        int width = frame.Width;
+        int height = frame.Height;
         int uvWidth = (width + 1) >> 1;
 
         // Temporary storage for accumulated R/G/B values during conversion to U/V.
@@ -289,8 +288,8 @@ public static bool ConvertRgbToYuv<TPixel>(ImageFrame<TPixel> frame, Configurati
         bool hasAlpha = false;
         for (rowIndex = 0; rowIndex < height - 1; rowIndex += 2)
         {
-            Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
-            Span<TPixel> nextRowSpan = imageBuffer.DangerousGetRowSpan(rowIndex + 1);
+            Span<TPixel> rowSpan = frame.DangerousGetRowSpan(rowIndex);
+            Span<TPixel> nextRowSpan = frame.DangerousGetRowSpan(rowIndex + 1);
             PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
             PixelOperations<TPixel>.Instance.ToBgra32(configuration, nextRowSpan, bgraRow1);
 
@@ -320,7 +319,7 @@ public static bool ConvertRgbToYuv<TPixel>(ImageFrame<TPixel> frame, Configurati
         // Extra last row.
         if ((height & 1) != 0)
         {
-            Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
+            Span<TPixel> rowSpan = frame.DangerousGetRowSpan(rowIndex);
             PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
             ConvertRgbaToY(bgraRow0, y[(rowIndex * width)..], width);
 
diff --git a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs b/src/ImageSharp/Formats/Webp/MetadataExtensions.cs
index 7f0920f2dd..731d3f1ff2 100644
--- a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Webp/MetadataExtensions.cs
@@ -1,6 +1,8 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
+using System.Diagnostics.CodeAnalysis;
+using SixLabors.ImageSharp.Formats;
 using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Metadata;
 
@@ -18,10 +20,56 @@ public static partial class MetadataExtensions
     /// <returns>The <see cref="WebpMetadata"/>.</returns>
     public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
 
+    /// <summary>
+    /// Gets the webp format specific metadata for the image.
+    /// </summary>
+    /// <param name="source">The metadata this method extends.</param>
+    /// <param name="metadata">The metadata.</param>
+    /// <returns>
+    /// <see langword="true"/> if the webp metadata exists; otherwise, <see langword="false"/>.
+    /// </returns>
+    public static bool TryGetWebpMetadata(this ImageMetadata source, [NotNullWhen(true)] out WebpMetadata? metadata)
+        => source.TryGetFormatMetadata(WebpFormat.Instance, out metadata);
+
     /// <summary>
     /// Gets the webp format specific metadata for the image frame.
     /// </summary>
     /// <param name="metadata">The metadata this method extends.</param>
     /// <returns>The <see cref="WebpFrameMetadata"/>.</returns>
     public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
+
+    /// <summary>
+    /// Gets the webp format specific metadata for the image frame.
+    /// </summary>
+    /// <param name="source">The metadata this method extends.</param>
+    /// <param name="metadata">The metadata.</param>
+    /// <returns>
+    /// <see langword="true"/> if the webp frame metadata exists; otherwise, <see langword="false"/>.
+    /// </returns>
+    public static bool TryGetWebpFrameMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out WebpFrameMetadata? metadata)
+        => source.TryGetFormatMetadata(WebpFormat.Instance, out metadata);
+
+    internal static AnimatedImageMetadata ToAnimatedImageMetadata(this WebpMetadata source)
+        => new()
+        {
+            ColorTableMode = FrameColorTableMode.Global,
+            RepeatCount = source.RepeatCount,
+            BackgroundColor = source.BackgroundColor
+        };
+
+    internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this WebpFrameMetadata source)
+        => new()
+        {
+            ColorTableMode = FrameColorTableMode.Global,
+            Duration = TimeSpan.FromMilliseconds(source.FrameDelay),
+            DisposalMode = GetMode(source.DisposalMethod),
+            BlendMode = source.BlendMethod == WebpBlendMethod.Over ? FrameBlendMode.Over : FrameBlendMode.Source,
+        };
+
+    private static FrameDisposalMode GetMode(WebpDisposalMethod method) => method switch
+    {
+        WebpDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground,
+        WebpDisposalMethod.DoNotDispose => FrameDisposalMode.DoNotDispose,
+        _ => FrameDisposalMode.DoNotDispose,
+    };
 }
diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
index 66e69d9a43..65f1a4da46 100644
--- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
@@ -89,7 +89,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, WebpFeatures feat
 
         this.metadata = new ImageMetadata();
         this.webpMetadata = this.metadata.GetWebpMetadata();
-        this.webpMetadata.AnimationLoopCount = features.AnimationLoopCount;
+        this.webpMetadata.RepeatCount = features.AnimationLoopCount;
 
         Span<byte> buffer = stackalloc byte[4];
         uint frameCount = 0;
@@ -195,14 +195,14 @@ private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
 
         Rectangle regionRectangle = frameData.Bounds;
 
-        if (frameData.DisposalMethod is WebpDisposalMethod.Dispose)
+        if (frameData.DisposalMethod is WebpDisposalMethod.RestoreToBackground)
         {
             this.RestoreToBackground(imageFrame, backgroundColor);
         }
 
         using Buffer2D<TPixel> decodedImageFrame = this.DecodeImageFrameData<TPixel>(frameData, webpInfo);
 
-        bool blend = previousFrame != null && frameData.BlendingMethod == WebpBlendingMethod.AlphaBlending;
+        bool blend = previousFrame != null && frameData.BlendingMethod == WebpBlendMethod.Over;
         DrawDecodedImageFrameOnCanvas(decodedImageFrame, imageFrame, regionRectangle, blend);
 
         previousFrame = currentFrame ?? image.Frames.RootFrame;
@@ -253,7 +253,7 @@ private byte ReadAlphaData(BufferedReadStream stream)
     private Buffer2D<TPixel> DecodeImageFrameData<TPixel>(WebpFrameData frameData, WebpImageInfo webpInfo)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        ImageFrame<TPixel> decodedFrame = new(Configuration.Default, (int)frameData.Width, (int)frameData.Height);
+        ImageFrame<TPixel> decodedFrame = new(this.configuration, (int)frameData.Width, (int)frameData.Height);
 
         try
         {
diff --git a/src/ImageSharp/Formats/Webp/WebpBlendingMethod.cs b/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs
similarity index 88%
rename from src/ImageSharp/Formats/Webp/WebpBlendingMethod.cs
rename to src/ImageSharp/Formats/Webp/WebpBlendMethod.cs
index cbd0e9a8cc..f16f7650c7 100644
--- a/src/ImageSharp/Formats/Webp/WebpBlendingMethod.cs
+++ b/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+﻿// Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
 namespace SixLabors.ImageSharp.Formats.Webp;
@@ -6,17 +6,17 @@ namespace SixLabors.ImageSharp.Formats.Webp;
 /// <summary>
 /// Indicates how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
 /// </summary>
-public enum WebpBlendingMethod
+public enum WebpBlendMethod
 {
     /// <summary>
-    /// Use alpha blending. After disposing of the previous frame, render the current frame on the canvas using alpha-blending.
-    /// If the current frame does not have an alpha channel, assume alpha value of 255, effectively replacing the rectangle.
+    /// Do not blend. After disposing of the previous frame,
+    /// render the current frame on the canvas by overwriting the rectangle covered by the current frame.
     /// </summary>
-    AlphaBlending = 0,
+    Source = 0,
 
     /// <summary>
-    /// Do not blend. After disposing of the previous frame,
-    /// render the current frame on the canvas by overwriting the rectangle covered by the current frame.
+    /// Use alpha blending. After disposing of the previous frame, render the current frame on the canvas using alpha-blending.
+    /// If the current frame does not have an alpha channel, assume alpha value of 255, effectively replacing the rectangle.
     /// </summary>
-    DoNotBlend = 1
+    Over = 1,
 }
diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs
index 1a8fcbafc9..49482260bb 100644
--- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs
+++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs
@@ -4,6 +4,8 @@
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics.X86;
+using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Formats.Png;
 using SixLabors.ImageSharp.PixelFormats;
 
 namespace SixLabors.ImageSharp.Formats.Webp;
@@ -13,6 +15,54 @@ namespace SixLabors.ImageSharp.Formats.Webp;
 /// </summary>
 internal static class WebpCommonUtils
 {
+    public static WebpMetadata GetWebpMetadata<TPixel>(Image<TPixel> image)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp))
+        {
+            return (WebpMetadata)webp.DeepClone();
+        }
+
+        if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif))
+        {
+            AnimatedImageMetadata ani = gif.ToAnimatedImageMetadata();
+            return WebpMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (image.Metadata.TryGetPngMetadata(out PngMetadata? png))
+        {
+            AnimatedImageMetadata ani = png.ToAnimatedImageMetadata();
+            return WebpMetadata.FromAnimatedMetadata(ani);
+        }
+
+        // Return explicit new instance so we do not mutate the original metadata.
+        return new();
+    }
+
+    public static WebpFrameMetadata GetWebpFrameMetadata<TPixel>(ImageFrame<TPixel> frame)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp))
+        {
+            return (WebpFrameMetadata)webp.DeepClone();
+        }
+
+        if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif))
+        {
+            AnimatedImageFrameMetadata ani = gif.ToAnimatedImageFrameMetadata();
+            return WebpFrameMetadata.FromAnimatedMetadata(ani);
+        }
+
+        if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png))
+        {
+            AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata();
+            return WebpFrameMetadata.FromAnimatedMetadata(ani);
+        }
+
+        // Return explicit new instance so we do not mutate the original metadata.
+        return new();
+    }
+
     /// <summary>
     /// Checks if the pixel row is not opaque.
     /// </summary>
@@ -27,7 +77,7 @@ public static unsafe bool CheckNonOpaque(Span<Bgra32> row)
             int length = (row.Length * 4) - 3;
             fixed (byte* src = rowBytes)
             {
-                var alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
+                Vector256<byte> alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
                 Vector256<byte> all0x80Vector256 = Vector256.Create((byte)0x80).AsByte();
 
                 for (; i + 128 <= length; i += 128)
@@ -124,7 +174,7 @@ public static unsafe bool CheckNonOpaque(Span<Bgra32> row)
 
     private static unsafe bool IsNoneOpaque64Bytes(byte* src, int i)
     {
-        var alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
+        Vector128<byte> alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
 
         Vector128<byte> a0 = Sse2.LoadVector128(src + i).AsByte();
         Vector128<byte> a1 = Sse2.LoadVector128(src + i + 16).AsByte();
@@ -144,7 +194,7 @@ private static unsafe bool IsNoneOpaque64Bytes(byte* src, int i)
 
     private static unsafe bool IsNoneOpaque32Bytes(byte* src, int i)
     {
-        var alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
+        Vector128<byte> alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
 
         Vector128<byte> a0 = Sse2.LoadVector128(src + i).AsByte();
         Vector128<byte> a1 = Sse2.LoadVector128(src + i + 16).AsByte();
diff --git a/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs b/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs
index d409973a99..47cc83951d 100644
--- a/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs
+++ b/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs
@@ -16,5 +16,5 @@ public enum WebpDisposalMethod
     /// <summary>
     /// Dispose to background color. Fill the rectangle on the canvas covered by the current frame with background color specified in the ANIM chunk.
     /// </summary>
-    Dispose = 1
+    RestoreToBackground = 1
 }
diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
index 47712071bf..e37c1d1796 100644
--- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
@@ -123,12 +123,14 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
         }
         else
         {
-            WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata();
+            WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image);
             lossless = webpMetadata.FileFormat == WebpFileFormatType.Lossless;
         }
 
         if (lossless)
         {
+            bool hasAnimation = image.Frames.Count > 1;
+
             using Vp8LEncoder encoder = new(
                 this.memoryAllocator,
                 this.configuration,
@@ -141,17 +143,46 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
                 this.nearLossless,
                 this.nearLosslessQuality);
 
-            bool hasAnimation = image.Frames.Count > 1;
             encoder.EncodeHeader(image, stream, hasAnimation);
+
+            // Encode the first frame.
+            ImageFrame<TPixel> previousFrame = image.Frames.RootFrame;
+            WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame);
+            encoder.Encode(previousFrame, previousFrame.Bounds(), frameMetadata, stream, hasAnimation);
+
             if (hasAnimation)
             {
-                foreach (ImageFrame<TPixel> imageFrame in image.Frames)
+                WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod;
+
+                // Encode additional frames
+                // 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++)
                 {
-                    using Vp8LEncoder enc = new(
+                    ImageFrame<TPixel>? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame;
+                    ImageFrame<TPixel> currentFrame = image.Frames[i];
+                    ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
+
+                    frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(currentFrame);
+                    bool blend = frameMetadata.BlendMethod == WebpBlendMethod.Over;
+
+                    (bool difference, Rectangle bounds) =
+                        AnimationUtilities.DeDuplicatePixels(
+                            image.Configuration,
+                            prev,
+                            currentFrame,
+                            nextFrame,
+                            encodingFrame,
+                            Color.Transparent,
+                            blend,
+                            ClampingMode.Even);
+
+                    using Vp8LEncoder animatedEncoder = new(
                         this.memoryAllocator,
                         this.configuration,
-                        image.Width,
-                        image.Height,
+                        bounds.Width,
+                        bounds.Height,
                         this.quality,
                         this.skipMetadata,
                         this.method,
@@ -159,13 +190,12 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
                         this.nearLossless,
                         this.nearLosslessQuality);
 
-                    enc.Encode(imageFrame, stream, true);
+                    animatedEncoder.Encode(encodingFrame, bounds, frameMetadata, stream, hasAnimation);
+
+                    previousFrame = currentFrame;
+                    previousDisposal = frameMetadata.DisposalMethod;
                 }
             }
-            else
-            {
-                encoder.Encode(image.Frames.RootFrame, stream, false);
-            }
 
             encoder.EncodeFooter(image, stream);
         }
@@ -183,17 +213,48 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
                 this.filterStrength,
                 this.spatialNoiseShaping,
                 this.alphaCompression);
+
             if (image.Frames.Count > 1)
             {
+                // TODO: What about alpha here?
                 encoder.EncodeHeader(image, stream, false, true);
 
-                foreach (ImageFrame<TPixel> imageFrame in image.Frames)
+                // Encode the first frame.
+                ImageFrame<TPixel> previousFrame = image.Frames.RootFrame;
+                WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame);
+                WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod;
+
+                encoder.EncodeAnimation(previousFrame, stream, previousFrame.Bounds(), frameMetadata);
+
+                // Encode additional frames
+                // 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++)
                 {
-                    using Vp8Encoder enc = new(
+                    ImageFrame<TPixel>? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame;
+                    ImageFrame<TPixel> currentFrame = image.Frames[i];
+                    ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
+
+                    frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(currentFrame);
+                    bool blend = frameMetadata.BlendMethod == WebpBlendMethod.Over;
+
+                    (bool difference, Rectangle bounds) =
+                        AnimationUtilities.DeDuplicatePixels(
+                            image.Configuration,
+                            prev,
+                            currentFrame,
+                            nextFrame,
+                            encodingFrame,
+                            Color.Transparent,
+                            blend,
+                            ClampingMode.Even);
+
+                    using Vp8Encoder animatedEncoder = new(
                         this.memoryAllocator,
                         this.configuration,
-                        image.Width,
-                        image.Height,
+                        bounds.Width,
+                        bounds.Height,
                         this.quality,
                         this.skipMetadata,
                         this.method,
@@ -202,12 +263,15 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
                         this.spatialNoiseShaping,
                         this.alphaCompression);
 
-                    enc.EncodeAnimation(imageFrame, stream);
+                    animatedEncoder.EncodeAnimation(encodingFrame, stream, bounds, frameMetadata);
+
+                    previousFrame = currentFrame;
+                    previousDisposal = frameMetadata.DisposalMethod;
                 }
             }
             else
             {
-                encoder.EncodeStatic(image, stream);
+                encoder.EncodeStatic(stream, image);
             }
 
             encoder.EncodeFooter(image, stream);
diff --git a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
index ef21d8b6fe..422ad6bc7c 100644
--- a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
@@ -29,7 +29,7 @@ private WebpFrameMetadata(WebpFrameMetadata other)
     /// <summary>
     /// Gets or sets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
     /// </summary>
-    public WebpBlendingMethod BlendMethod { get; set; }
+    public WebpBlendMethod BlendMethod { get; set; }
 
     /// <summary>
     /// Gets or sets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas.
@@ -44,4 +44,12 @@ private WebpFrameMetadata(WebpFrameMetadata other)
 
     /// <inheritdoc/>
     public IDeepCloneable DeepClone() => new WebpFrameMetadata(this);
+
+    internal static WebpFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata)
+        => new()
+        {
+            FrameDelay = (uint)metadata.Duration.Milliseconds,
+            BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? WebpBlendMethod.Source : WebpBlendMethod.Over,
+            DisposalMethod = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose
+        };
 }
diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs
index a6bb0a7b80..536ea09294 100644
--- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs
+++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs
@@ -22,8 +22,8 @@ public WebpMetadata()
     private WebpMetadata(WebpMetadata other)
     {
         this.FileFormat = other.FileFormat;
-        this.AnimationLoopCount = other.AnimationLoopCount;
-        this.AnimationBackground = other.AnimationBackground;
+        this.RepeatCount = other.RepeatCount;
+        this.BackgroundColor = other.BackgroundColor;
     }
 
     /// <summary>
@@ -34,16 +34,24 @@ private WebpMetadata(WebpMetadata other)
     /// <summary>
     /// Gets or sets the loop count. The number of times to loop the animation. 0 means infinitely.
     /// </summary>
-    public ushort AnimationLoopCount { get; set; } = 1;
+    public ushort RepeatCount { get; set; } = 1;
 
     /// <summary>
-    /// Gets or sets the default background color of the canvas in [Blue, Green, Red, Alpha] byte order.
-    /// This color MAY be used to fill the unused space on the canvas around the frames,
+    /// Gets or sets the default background color of the canvas when animating.
+    /// This color may be used to fill the unused space on the canvas around the frames,
     /// as well as the transparent pixels of the first frame.
-    /// The background color is also used when the Disposal method is 1.
+    /// The background color is also used when the Disposal method is <see cref="WebpDisposalMethod.RestoreToBackground"/>.
     /// </summary>
-    public Color AnimationBackground { get; set; }
+    public Color BackgroundColor { get; set; }
 
     /// <inheritdoc/>
     public IDeepCloneable DeepClone() => new WebpMetadata(this);
+
+    internal static WebpMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
+        => new()
+        {
+            FileFormat = WebpFileFormatType.Lossless,
+            BackgroundColor = metadata.BackgroundColor,
+            RepeatCount = metadata.RepeatCount
+        };
 }
diff --git a/src/ImageSharp/Metadata/FrameDecodingMode.cs b/src/ImageSharp/Metadata/FrameDecodingMode.cs
deleted file mode 100644
index 3a59654897..0000000000
--- a/src/ImageSharp/Metadata/FrameDecodingMode.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-﻿// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Metadata;
-
-/// <summary>
-/// Enumerated frame process modes to apply to multi-frame images.
-/// </summary>
-public enum FrameDecodingMode
-{
-    /// <summary>
-    /// Decodes all the frames of a multi-frame image.
-    /// </summary>
-    All,
-
-    /// <summary>
-    /// Decodes only the first frame of a multi-frame image.
-    /// </summary>
-    First
-}
diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs
index f54fc5c7ae..e1284b50e8 100644
--- a/src/ImageSharp/Metadata/ImageMetadata.cs
+++ b/src/ImageSharp/Metadata/ImageMetadata.cs
@@ -183,6 +183,32 @@ public TFormatMetadata GetFormatMetadata<TFormatMetadata>(IImageFormat<TFormatMe
         return newMeta;
     }
 
+    /// <summary>
+    /// Gets the metadata value associated with the specified key.
+    /// </summary>
+    /// <typeparam name="TFormatMetadata">The type of format metadata.</typeparam>
+    /// <param name="key">The key of the value to get.</param>
+    /// <param name="metadata">
+    /// When this method returns, contains the metadata associated with the specified key,
+    /// if the key is found; otherwise, the default value for the type of the metadata parameter.
+    /// This parameter is passed uninitialized.
+    /// </param>
+    /// <returns>
+    /// <see langword="true"/> if the frame metadata exists for the specified key; otherwise, <see langword="false"/>.
+    /// </returns>
+    public bool TryGetFormatMetadata<TFormatMetadata>(IImageFormat<TFormatMetadata> key, out TFormatMetadata? metadata)
+        where TFormatMetadata : class, IDeepCloneable
+    {
+        if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta))
+        {
+            metadata = (TFormatMetadata)meta;
+            return true;
+        }
+
+        metadata = default;
+        return false;
+    }
+
     /// <inheritdoc/>
     public ImageMetadata DeepClone() => new(this);
 
diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
index 754aac90ea..3c56d0243b 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
@@ -107,15 +107,15 @@ public void ApplyQuantizationDither<TFrameQuantizer, TPixel>(
         float scale = quantizer.Options.DitherScale;
         Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
 
-        for (int y = bounds.Top; y < bounds.Bottom; y++)
+        for (int y = 0; y < destination.Height; y++)
         {
-            ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(sourceBuffer.DangerousGetRowSpan(y));
-            ref byte destinationRowRef = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(y - offsetY));
+            ReadOnlySpan<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y + offsetY);
+            Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y);
 
-            for (int x = bounds.Left; x < bounds.Right; x++)
+            for (int x = 0; x < destinationRow.Length; x++)
             {
-                TPixel sourcePixel = Unsafe.Add(ref sourceRowRef, (uint)x);
-                Unsafe.Add(ref destinationRowRef, (uint)(x - offsetX)) = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed);
+                TPixel sourcePixel = sourceRow[x + offsetX];
+                destinationRow[x] = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed);
                 this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
             }
         }
@@ -200,7 +200,7 @@ internal TPixel Dither<TPixel>(
                 }
 
                 ref TPixel pixel = ref rowSpan[targetX];
-                var result = pixel.ToVector4();
+                Vector4 result = pixel.ToVector4();
 
                 result += error * coefficient;
                 pixel.FromVector4(result);
diff --git a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs
index f75664903d..72148374aa 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs
@@ -2,7 +2,6 @@
 // Licensed under the Six Labors Split License.
 
 using System.Buffers;
-using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using SixLabors.ImageSharp.Memory;
@@ -23,6 +22,7 @@ internal sealed class EuclideanPixelMap<TPixel> : IDisposable
 {
     private Rgba32[] rgbaPalette;
     private int transparentIndex;
+    private readonly TPixel transparentMatch;
 
     /// <summary>
     /// Do not make this readonly! Struct value would be always copied on non-readonly method calls.
@@ -54,8 +54,9 @@ public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory<TPixel> pal
         this.cache = new ColorDistanceCache(configuration.MemoryAllocator);
         PixelOperations<TPixel>.Instance.ToRgba32(configuration, this.Palette.Span, this.rgbaPalette);
 
-        // If the provided transparentIndex is outside of the palette, silently ignore it.
-        this.transparentIndex = transparentIndex < this.Palette.Length ? transparentIndex : -1;
+        this.transparentIndex = transparentIndex;
+        Unsafe.SkipInit(out this.transparentMatch);
+        this.transparentMatch.FromRgba32(default);
     }
 
     /// <summary>
@@ -97,32 +98,40 @@ public void Clear(ReadOnlyMemory<TPixel> palette)
         this.Palette = palette;
         this.rgbaPalette = new Rgba32[palette.Length];
         PixelOperations<TPixel>.Instance.ToRgba32(this.configuration, this.Palette.Span, this.rgbaPalette);
+        this.transparentIndex = -1;
         this.cache.Clear();
     }
 
     /// <summary>
-    /// Allows setting the transparent index after construction. If the provided transparentIndex is outside of the palette, silently ignore it.
+    /// Allows setting the transparent index after construction.
     /// </summary>
     /// <param name="index">An explicit index at which to match transparent pixels.</param>
-    public void SetTransparentIndex(int index) => this.transparentIndex = index < this.Palette.Length ? index : -1;
+    public void SetTransparentIndex(int index)
+    {
+        if (index != this.transparentIndex)
+        {
+            this.cache.Clear();
+        }
+
+        this.transparentIndex = index;
+    }
 
     [MethodImpl(InliningOptions.ShortMethod)]
     private int GetClosestColorSlow(Rgba32 rgba, ref TPixel paletteRef, out TPixel match)
     {
         // Loop through the palette and find the nearest match.
         int index = 0;
-        float leastDistance = float.MaxValue;
 
         if (this.transparentIndex >= 0 && rgba == default)
         {
             // We have explicit instructions. No need to search.
             index = this.transparentIndex;
-            DebugGuard.MustBeLessThan(index, this.Palette.Length, nameof(index));
             this.cache.Add(rgba, (byte)index);
-            match = Unsafe.Add(ref paletteRef, (uint)index);
+            match = this.transparentMatch;
             return index;
         }
 
+        float leastDistance = float.MaxValue;
         for (int i = 0; i < this.rgbaPalette.Length; i++)
         {
             Rgba32 candidate = this.rgbaPalette[i];
@@ -175,18 +184,24 @@ private static float DistanceSquared(Rgba32 a, Rgba32 b)
     /// The granularity of the cache has been determined based upon the current
     /// suite of test images and provides the lowest possible memory usage while
     /// providing enough match accuracy.
-    /// Entry count is currently limited to 2335905 entries (4671810 bytes ~4.45MB).
+    /// Entry count is currently limited to 2335905 entries (4MB).
     /// </para>
     /// </remarks>
     private unsafe struct ColorDistanceCache : IDisposable
     {
-        private const int IndexBits = 5;
-        private const int IndexAlphaBits = 6;
-        private const int IndexCount = (1 << IndexBits) + 1;
-        private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1;
-        private const int RgbShift = 8 - IndexBits;
-        private const int AlphaShift = 8 - IndexAlphaBits;
-        private const int Entries = IndexCount * IndexCount * IndexCount * IndexAlphaCount;
+        private const int IndexRBits = 5;
+        private const int IndexGBits = 5;
+        private const int IndexBBits = 5;
+        private const int IndexABits = 6;
+        private const int IndexRCount = (1 << IndexRBits) + 1;
+        private const int IndexGCount = (1 << IndexGBits) + 1;
+        private const int IndexBCount = (1 << IndexBBits) + 1;
+        private const int IndexACount = (1 << IndexABits) + 1;
+        private const int RShift = 8 - IndexRBits;
+        private const int GShift = 8 - IndexGBits;
+        private const int BShift = 8 - IndexBBits;
+        private const int AShift = 8 - IndexABits;
+        private const int Entries = IndexRCount * IndexGCount * IndexBCount * IndexACount;
         private MemoryHandle tableHandle;
         private readonly IMemoryOwner<short> table;
         private readonly short* tablePointer;
@@ -200,24 +215,16 @@ public ColorDistanceCache(MemoryAllocator allocator)
         }
 
         [MethodImpl(InliningOptions.ShortMethod)]
-        public void Add(Rgba32 rgba, byte index)
+        public readonly void Add(Rgba32 rgba, byte index)
         {
-            int r = rgba.R >> RgbShift;
-            int g = rgba.G >> RgbShift;
-            int b = rgba.B >> RgbShift;
-            int a = rgba.A >> AlphaShift;
-            int idx = GetPaletteIndex(r, g, b, a);
+            int idx = GetPaletteIndex(rgba);
             this.tablePointer[idx] = index;
         }
 
         [MethodImpl(InliningOptions.ShortMethod)]
-        public bool TryGetValue(Rgba32 rgba, out short match)
+        public readonly bool TryGetValue(Rgba32 rgba, out short match)
         {
-            int r = rgba.R >> RgbShift;
-            int g = rgba.G >> RgbShift;
-            int b = rgba.B >> RgbShift;
-            int a = rgba.A >> AlphaShift;
-            int idx = GetPaletteIndex(r, g, b, a);
+            int idx = GetPaletteIndex(rgba);
             match = this.tablePointer[idx];
             return match > -1;
         }
@@ -226,18 +233,20 @@ public bool TryGetValue(Rgba32 rgba, out short match)
         /// Clears the cache resetting each entry to empty.
         /// </summary>
         [MethodImpl(InliningOptions.ShortMethod)]
-        public void Clear() => this.table.GetSpan().Fill(-1);
+        public readonly void Clear() => this.table.GetSpan().Fill(-1);
 
         [MethodImpl(InliningOptions.ShortMethod)]
-        private static int GetPaletteIndex(int r, int g, int b, int a)
-            => (r << ((IndexBits << 1) + IndexAlphaBits))
-            + (r << (IndexBits + IndexAlphaBits + 1))
-            + (g << (IndexBits + IndexAlphaBits))
-            + (r << (IndexBits << 1))
-            + (r << (IndexBits + 1))
-            + (g << IndexBits)
-            + ((r + g + b) << IndexAlphaBits)
-            + r + g + b + a;
+        private static int GetPaletteIndex(Rgba32 rgba)
+        {
+            int rIndex = rgba.R >> RShift;
+            int gIndex = rgba.G >> GShift;
+            int bIndex = rgba.B >> BShift;
+            int aIndex = rgba.A >> AShift;
+
+            return (aIndex * (IndexRCount * IndexGCount * IndexBCount)) +
+                   (rIndex * (IndexGCount * IndexBCount)) +
+                   (gIndex * IndexBCount) + bIndex;
+        }
 
         public void Dispose()
         {
diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
index a7edec662e..10ffe68dca 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
@@ -156,10 +156,10 @@ private static void SecondPass<TFrameQuantizer, TPixel>(
 
             for (int y = 0; y < destination.Height; y++)
             {
-                Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y + offsetY);
+                ReadOnlySpan<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y + offsetY);
                 Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y);
 
-                for (int x = 0; x < destination.Width; x++)
+                for (int x = 0; x < destinationRow.Length; x++)
                 {
                     destinationRow[x] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x + offsetX], out TPixel _);
                 }
diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
index 1d84d6600a..27511f7beb 100644
--- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
+++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
@@ -162,37 +162,37 @@ public void ImageCanConvertFormat()
         foreach (TestFile file in Files)
         {
             using Image<Rgba32> image = file.CreateRgba32Image();
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.bmp")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.bmp")))
             {
                 image.SaveAsBmp(output);
             }
 
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.jpg")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.jpg")))
             {
                 image.SaveAsJpeg(output);
             }
 
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.pbm")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.pbm")))
             {
                 image.SaveAsPbm(output);
             }
 
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.png")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.png")))
             {
                 image.SaveAsPng(output);
             }
 
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.gif")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.gif")))
             {
                 image.SaveAsGif(output);
             }
 
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tga")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.tga")))
             {
                 image.SaveAsTga(output);
             }
 
-            using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff")))
+            using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff")))
             {
                 image.SaveAsTiff(output);
             }
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
index 8b23927418..6399e1193e 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
@@ -101,9 +101,9 @@ public void GifDecoder_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPi
     }
 
     [Theory]
-    [WithFile(TestImages.Gif.Cheers, PixelTypes.Rgba32, 93)]
+    [WithFile(TestImages.Gif.M4nb, PixelTypes.Rgba32, 5)]
     [WithFile(TestImages.Gif.Rings, PixelTypes.Rgba32, 1)]
-    [WithFile(TestImages.Gif.Issues.BadDescriptorWidth, PixelTypes.Rgba32, 36)]
+    [WithFile(TestImages.Gif.MixedDisposal, PixelTypes.Rgba32, 11)]
     public void Decode_VerifyRootFrameAndFrameCount<TPixel>(TestImageProvider<TPixel> provider, int expectedFrameCount)
         where TPixel : unmanaged, IPixel<TPixel>
     {
@@ -133,7 +133,6 @@ public void CanDecodeAllFrames<TPixel>(TestImageProvider<TPixel> provider)
     }
 
     [Theory]
-    [InlineData(TestImages.Gif.Cheers, 8)]
     [InlineData(TestImages.Gif.Giphy, 8)]
     [InlineData(TestImages.Gif.Rings, 8)]
     [InlineData(TestImages.Gif.Trans, 8)]
@@ -194,7 +193,7 @@ public void CanDecodeIntermingledImages()
         }
     }
 
-    // https://github.com/SixLabors/ImageSharp/issues/1503
+    // https://github.com/SixLabors/ImageSharp/issues/1530
     [Theory]
     [WithFile(TestImages.Gif.Issues.Issue1530, PixelTypes.Rgba32)]
     public void Issue1530_BadDescriptorDimensions<TPixel>(TestImageProvider<TPixel> provider)
@@ -212,7 +211,7 @@ public void Issue1530_BadDescriptorDimensions<TPixel>(TestImageProvider<TPixel>
     public void Issue405_BadApplicationExtensionBlockLength<TPixel>(TestImageProvider<TPixel> provider)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        using Image<TPixel> image = provider.GetImage();
+        using Image<TPixel> image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 });
         image.DebugSave(provider);
 
         image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider);
@@ -224,7 +223,7 @@ public void Issue405_BadApplicationExtensionBlockLength<TPixel>(TestImageProvide
     public void Issue1668_InvalidColorIndex<TPixel>(TestImageProvider<TPixel> provider)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        using Image<TPixel> image = provider.GetImage();
+        using Image<TPixel> image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 });
         image.DebugSave(provider);
 
         image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider);
@@ -273,7 +272,7 @@ TestImageProvider<Rgba32> provider
     public void Issue1962<TPixel>(TestImageProvider<TPixel> provider)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        using Image<TPixel> image = provider.GetImage();
+        using Image<TPixel> image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 });
         image.DebugSave(provider);
 
         image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider);
@@ -285,7 +284,7 @@ public void Issue1962<TPixel>(TestImageProvider<TPixel> provider)
     public void Issue2012EmptyXmp<TPixel>(TestImageProvider<TPixel> provider)
         where TPixel : unmanaged, IPixel<TPixel>
     {
-        using Image<TPixel> image = provider.GetImage();
+        using Image<TPixel> image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 });
 
         image.DebugSave(provider);
         image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider);
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
index 31001e31b4..a7e16f7737 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
@@ -2,6 +2,8 @@
 // Licensed under the Six Labors Split License.
 
 using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Metadata;
 using SixLabors.ImageSharp.PixelFormats;
 using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -224,8 +226,6 @@ public void NonMutatingEncodePreservesPaletteCount()
             }
 
             Assert.Equal(iMeta.FrameDelay, cMeta.FrameDelay);
-            Assert.Equal(iMeta.HasTransparency, cMeta.HasTransparency);
-            Assert.Equal(iMeta.TransparencyIndex, cMeta.TransparencyIndex);
         }
 
         image.Dispose();
@@ -269,4 +269,117 @@ public void OptionalExtensionsShouldBeHandledProperly<TPixel>(TestImageProvider<
 
         Assert.Equal(image2.Frames.Count, count);
     }
+
+    [Theory]
+    [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)]
+    public void Encode_AnimatedFormatTransform_FromPng<TPixel>(TestImageProvider<TPixel> provider)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (TestEnvironment.RunsOnCI && !TestEnvironment.IsWindows)
+        {
+            return;
+        }
+
+        using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
+
+        using MemoryStream memStream = new();
+        image.Save(memStream, new GifEncoder());
+        memStream.Position = 0;
+
+        using Image<TPixel> output = Image.Load<TPixel>(memStream);
+
+        // TODO: Find a better way to compare.
+        // The image has been visually checked but the quantization and frame trimming pattern used in the gif encoder
+        // means we cannot use an exact comparison nor replicate using the quantizing processor.
+        ImageComparer.TolerantPercentage(1.51f).VerifySimilarity(output, image);
+
+        PngMetadata png = image.Metadata.GetPngMetadata();
+        GifMetadata gif = output.Metadata.GetGifMetadata();
+
+        Assert.Equal(png.RepeatCount, gif.RepeatCount);
+
+        for (int i = 0; i < image.Frames.Count; i++)
+        {
+            PngFrameMetadata pngF = image.Frames[i].Metadata.GetPngMetadata();
+            GifFrameMetadata gifF = output.Frames[i].Metadata.GetGifMetadata();
+
+            Assert.Equal((int)(pngF.FrameDelay.ToDouble() * 100), gifF.FrameDelay);
+
+            switch (pngF.DisposalMethod)
+            {
+                case PngDisposalMethod.RestoreToBackground:
+                    Assert.Equal(GifDisposalMethod.RestoreToBackground, gifF.DisposalMethod);
+                    break;
+                case PngDisposalMethod.DoNotDispose:
+                default:
+                    Assert.Equal(GifDisposalMethod.NotDispose, gifF.DisposalMethod);
+                    break;
+            }
+        }
+    }
+
+    [Theory]
+    [WithFile(TestImages.Webp.Lossless.Animated, PixelTypes.Rgba32)]
+    public void Encode_AnimatedFormatTransform_FromWebp<TPixel>(TestImageProvider<TPixel> provider)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (TestEnvironment.RunsOnCI && !TestEnvironment.IsWindows)
+        {
+            return;
+        }
+
+        using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
+
+        using MemoryStream memStream = new();
+        image.Save(memStream, new GifEncoder());
+        memStream.Position = 0;
+
+        using Image<TPixel> output = Image.Load<TPixel>(memStream);
+
+        image.Save(provider.Utility.GetTestOutputFileName("gif"), new GifEncoder());
+
+        // TODO: Find a better way to compare.
+        // The image has been visually checked but the quantization and frame trimming pattern used in the gif encoder
+        // means we cannot use an exact comparison nor replicate using the quantizing processor.
+        ImageComparer.TolerantPercentage(0.776f).VerifySimilarity(output, image);
+
+        WebpMetadata webp = image.Metadata.GetWebpMetadata();
+        GifMetadata gif = output.Metadata.GetGifMetadata();
+
+        Assert.Equal(webp.RepeatCount, gif.RepeatCount);
+
+        for (int i = 0; i < image.Frames.Count; i++)
+        {
+            WebpFrameMetadata webpF = image.Frames[i].Metadata.GetWebpMetadata();
+            GifFrameMetadata gifF = output.Frames[i].Metadata.GetGifMetadata();
+
+            Assert.Equal(webpF.FrameDelay, (uint)(gifF.FrameDelay * 10));
+
+            switch (webpF.DisposalMethod)
+            {
+                case WebpDisposalMethod.RestoreToBackground:
+                    Assert.Equal(GifDisposalMethod.RestoreToBackground, gifF.DisposalMethod);
+                    break;
+                case WebpDisposalMethod.DoNotDispose:
+                default:
+                    Assert.Equal(GifDisposalMethod.NotDispose, gifF.DisposalMethod);
+                    break;
+            }
+        }
+    }
+
+    public static string[] Animated => TestImages.Gif.Animated;
+
+    [Theory(Skip = "Enable for visual animated testing")]
+    [WithFileCollection(nameof(Animated), PixelTypes.Rgba32)]
+    public void Encode_Animated_VisualTest<TPixel>(TestImageProvider<TPixel> provider)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        using Image<TPixel> image = provider.GetImage();
+
+        provider.Utility.SaveTestOutputFile(image, "webp", new WebpEncoder() { FileFormat = WebpFileFormatType.Lossless }, "animated");
+        provider.Utility.SaveTestOutputFile(image, "webp", new WebpEncoder() { FileFormat = WebpFileFormatType.Lossy }, "animated-lossy");
+        provider.Utility.SaveTestOutputFile(image, "png", new PngEncoder(), "animated");
+        provider.Utility.SaveTestOutputFile(image, "gif", new GifEncoder(), "animated");
+    }
 }
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
index 92c07a27a6..c81b7eb6cd 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
@@ -3,7 +3,9 @@
 
 // ReSharper disable InconsistentNaming
 using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.Formats.Gif;
 using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Metadata;
 using SixLabors.ImageSharp.PixelFormats;
 using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -467,8 +469,8 @@ public void Encode_APng<TPixel>(TestImageProvider<TPixel> provider)
 
         for (int i = 0; i < image.Frames.Count; i++)
         {
-            PngFrameMetadata originalFrameMetadata = image.Frames[i].Metadata.GetPngFrameMetadata();
-            PngFrameMetadata outputFrameMetadata = output.Frames[i].Metadata.GetPngFrameMetadata();
+            PngFrameMetadata originalFrameMetadata = image.Frames[i].Metadata.GetPngMetadata();
+            PngFrameMetadata outputFrameMetadata = output.Frames[i].Metadata.GetPngMetadata();
 
             Assert.Equal(originalFrameMetadata.FrameDelay, outputFrameMetadata.FrameDelay);
             Assert.Equal(originalFrameMetadata.BlendMethod, outputFrameMetadata.BlendMethod);
@@ -476,6 +478,113 @@ public void Encode_APng<TPixel>(TestImageProvider<TPixel> provider)
         }
     }
 
+    [Theory]
+    [WithFile(TestImages.Gif.Leo, PixelTypes.Rgba32)]
+    public void Encode_AnimatedFormatTransform_FromGif<TPixel>(TestImageProvider<TPixel> provider)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (TestEnvironment.RunsOnCI && !TestEnvironment.IsWindows)
+        {
+            return;
+        }
+
+        using Image<TPixel> image = provider.GetImage(GifDecoder.Instance);
+
+        using MemoryStream memStream = new();
+        image.Save(memStream, PngEncoder);
+        memStream.Position = 0;
+
+        using Image<TPixel> output = Image.Load<TPixel>(memStream);
+
+        // TODO: Find a better way to compare.
+        // The image has been visually checked but the quantization pattern used in the png encoder
+        // means we cannot use an exact comparison nor replicate using the quantizing processor.
+        ImageComparer.TolerantPercentage(0.46f).VerifySimilarity(output, image);
+
+        GifMetadata gif = image.Metadata.GetGifMetadata();
+        PngMetadata png = output.Metadata.GetPngMetadata();
+
+        Assert.Equal(gif.RepeatCount, png.RepeatCount);
+
+        for (int i = 0; i < image.Frames.Count; i++)
+        {
+            GifFrameMetadata gifF = image.Frames[i].Metadata.GetGifMetadata();
+            PngFrameMetadata pngF = output.Frames[i].Metadata.GetPngMetadata();
+
+            Assert.Equal(gifF.FrameDelay, (int)(pngF.FrameDelay.ToDouble() * 100));
+
+            switch (gifF.DisposalMethod)
+            {
+                case GifDisposalMethod.RestoreToBackground:
+                    Assert.Equal(PngDisposalMethod.RestoreToBackground, pngF.DisposalMethod);
+                    break;
+                case GifDisposalMethod.RestoreToPrevious:
+                    Assert.Equal(PngDisposalMethod.RestoreToPrevious, pngF.DisposalMethod);
+                    break;
+                case GifDisposalMethod.Unspecified:
+                case GifDisposalMethod.NotDispose:
+                default:
+                    Assert.Equal(PngDisposalMethod.DoNotDispose, pngF.DisposalMethod);
+                    break;
+            }
+        }
+    }
+
+    [Theory]
+    [WithFile(TestImages.Webp.Lossless.Animated, PixelTypes.Rgba32)]
+    public void Encode_AnimatedFormatTransform_FromWebp<TPixel>(TestImageProvider<TPixel> provider)
+    where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (TestEnvironment.RunsOnCI && !TestEnvironment.IsWindows)
+        {
+            return;
+        }
+
+        using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
+
+        using MemoryStream memStream = new();
+        image.Save(memStream, PngEncoder);
+        memStream.Position = 0;
+
+        using Image<TPixel> output = Image.Load<TPixel>(memStream);
+        ImageComparer.Exact.VerifySimilarity(output, image);
+
+        WebpMetadata webp = image.Metadata.GetWebpMetadata();
+        PngMetadata png = output.Metadata.GetPngMetadata();
+
+        Assert.Equal(webp.RepeatCount, png.RepeatCount);
+
+        for (int i = 0; i < image.Frames.Count; i++)
+        {
+            WebpFrameMetadata webpF = image.Frames[i].Metadata.GetWebpMetadata();
+            PngFrameMetadata pngF = output.Frames[i].Metadata.GetPngMetadata();
+
+            Assert.Equal(webpF.FrameDelay, (uint)(pngF.FrameDelay.ToDouble() * 1000));
+
+            switch (webpF.BlendMethod)
+            {
+                case WebpBlendMethod.Source:
+                    Assert.Equal(PngBlendMethod.Source, pngF.BlendMethod);
+                    break;
+                case WebpBlendMethod.Over:
+                default:
+                    Assert.Equal(PngBlendMethod.Over, pngF.BlendMethod);
+                    break;
+            }
+
+            switch (webpF.DisposalMethod)
+            {
+                case WebpDisposalMethod.RestoreToBackground:
+                    Assert.Equal(PngDisposalMethod.RestoreToBackground, pngF.DisposalMethod);
+                    break;
+                case WebpDisposalMethod.DoNotDispose:
+                default:
+                    Assert.Equal(PngDisposalMethod.DoNotDispose, pngF.DisposalMethod);
+                    break;
+            }
+        }
+    }
+
     [Theory]
     [MemberData(nameof(PngTrnsFiles))]
     public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType)
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs
index e29585c2dc..9ba2617281 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs
@@ -14,7 +14,7 @@ public void CloneIsDeep()
         PngFrameMetadata meta = new()
         {
             FrameDelay = new(1, 0),
-            DisposalMethod = PngDisposalMethod.Background,
+            DisposalMethod = PngDisposalMethod.RestoreToBackground,
             BlendMethod = PngBlendMethod.Over,
         };
 
@@ -25,7 +25,7 @@ public void CloneIsDeep()
         Assert.True(meta.BlendMethod.Equals(clone.BlendMethod));
 
         clone.FrameDelay = new(2, 1);
-        clone.DisposalMethod = PngDisposalMethod.Previous;
+        clone.DisposalMethod = PngDisposalMethod.RestoreToPrevious;
         clone.BlendMethod = PngBlendMethod.Source;
 
         Assert.False(meta.FrameDelay.Equals(clone.FrameDelay));
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
index 4b03671e16..6301f341cc 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
@@ -307,7 +307,7 @@ public void Decode_AnimatedLossless_VerifyAllFrames<TPixel>(TestImageProvider<TP
         image.DebugSaveMultiFrame(provider);
         image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
 
-        Assert.Equal(0, webpMetaData.AnimationLoopCount);
+        Assert.Equal(0, webpMetaData.RepeatCount);
         Assert.Equal(150U, frameMetaData.FrameDelay);
         Assert.Equal(12, image.Frames.Count);
     }
@@ -324,7 +324,7 @@ public void Decode_AnimatedLossy_VerifyAllFrames<TPixel>(TestImageProvider<TPixe
         image.DebugSaveMultiFrame(provider);
         image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
 
-        Assert.Equal(0, webpMetaData.AnimationLoopCount);
+        Assert.Equal(0, webpMetaData.RepeatCount);
         Assert.Equal(150U, frameMetaData.FrameDelay);
         Assert.Equal(12, image.Frames.Count);
     }
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
index 0ad684b277..acca49dcf4 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
@@ -2,6 +2,8 @@
 // Licensed under the Six Labors Split License.
 
 using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Formats.Png;
 using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Metadata;
 using SixLabors.ImageSharp.PixelFormats;
@@ -60,6 +62,111 @@ public void Encode_AnimatedLossy<TPixel>(TestImageProvider<TPixel> provider)
         encoded.CompareToReferenceOutput(ImageComparer.Tolerant(0.01f), provider, null, "webp");
     }
 
+    [Theory]
+    [WithFile(TestImages.Gif.Leo, PixelTypes.Rgba32)]
+    public void Encode_AnimatedFormatTransform_FromGif<TPixel>(TestImageProvider<TPixel> provider)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (TestEnvironment.RunsOnCI && !TestEnvironment.IsWindows)
+        {
+            return;
+        }
+
+        using Image<TPixel> image = provider.GetImage(GifDecoder.Instance);
+        using MemoryStream memStream = new();
+
+        image.Save(memStream, new WebpEncoder());
+        memStream.Position = 0;
+
+        using Image<TPixel> output = Image.Load<TPixel>(memStream);
+
+        ImageComparer.Exact.VerifySimilarity(output, image);
+
+        GifMetadata gif = image.Metadata.GetGifMetadata();
+        WebpMetadata webp = output.Metadata.GetWebpMetadata();
+
+        Assert.Equal(gif.RepeatCount, webp.RepeatCount);
+
+        for (int i = 0; i < image.Frames.Count; i++)
+        {
+            GifFrameMetadata gifF = image.Frames[i].Metadata.GetGifMetadata();
+            WebpFrameMetadata webpF = output.Frames[i].Metadata.GetWebpMetadata();
+
+            Assert.Equal(gifF.FrameDelay, (int)(webpF.FrameDelay / 10));
+
+            switch (gifF.DisposalMethod)
+            {
+                case GifDisposalMethod.RestoreToBackground:
+                    Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod);
+                    break;
+                case GifDisposalMethod.RestoreToPrevious:
+                case GifDisposalMethod.Unspecified:
+                case GifDisposalMethod.NotDispose:
+                default:
+                    Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod);
+                    break;
+            }
+        }
+    }
+
+    [Theory]
+    [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)]
+    public void Encode_AnimatedFormatTransform_FromPng<TPixel>(TestImageProvider<TPixel> provider)
+        where TPixel : unmanaged, IPixel<TPixel>
+    {
+        if (TestEnvironment.RunsOnCI && !TestEnvironment.IsWindows)
+        {
+            return;
+        }
+
+        using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
+
+        using MemoryStream memStream = new();
+        image.Save(memStream, new WebpEncoder());
+        memStream.Position = 0;
+
+        provider.Utility.SaveTestOutputFile(image, "gif", new GifEncoder());
+        provider.Utility.SaveTestOutputFile(image, "png", new PngEncoder());
+        provider.Utility.SaveTestOutputFile(image, "webp", new WebpEncoder());
+
+        using Image<TPixel> output = Image.Load<TPixel>(memStream);
+        ImageComparer.Exact.VerifySimilarity(output, image);
+        PngMetadata png = image.Metadata.GetPngMetadata();
+        WebpMetadata webp = output.Metadata.GetWebpMetadata();
+
+        Assert.Equal(png.RepeatCount, webp.RepeatCount);
+
+        for (int i = 0; i < image.Frames.Count; i++)
+        {
+            PngFrameMetadata pngF = image.Frames[i].Metadata.GetPngMetadata();
+            WebpFrameMetadata webpF = output.Frames[i].Metadata.GetWebpMetadata();
+
+            Assert.Equal((uint)(pngF.FrameDelay.ToDouble() * 1000), webpF.FrameDelay);
+
+            switch (pngF.BlendMethod)
+            {
+                case PngBlendMethod.Source:
+                    Assert.Equal(WebpBlendMethod.Source, webpF.BlendMethod);
+                    break;
+                case PngBlendMethod.Over:
+                default:
+                    Assert.Equal(WebpBlendMethod.Over, webpF.BlendMethod);
+                    break;
+            }
+
+            switch (pngF.DisposalMethod)
+            {
+                case PngDisposalMethod.RestoreToBackground:
+                    Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod);
+                    break;
+                case PngDisposalMethod.DoNotDispose:
+                default:
+                    Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod);
+                    break;
+            }
+        }
+    }
+
     [Theory]
     [WithFile(Flag, PixelTypes.Rgba32, WebpFileFormatType.Lossy)] // If its not a webp input image, it should default to lossy.
     [WithFile(Lossless.NoTransform1, PixelTypes.Rgba32, WebpFileFormatType.Lossless)]
diff --git a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs
index 433b280bc3..3ae6601b18 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs
@@ -1,7 +1,6 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
-using SixLabors.ImageSharp.Advanced;
 using SixLabors.ImageSharp.Formats.Webp;
 using SixLabors.ImageSharp.Formats.Webp.Lossy;
 using SixLabors.ImageSharp.Memory;
@@ -143,7 +142,7 @@ public void ConvertRgbToYuv_Works<TPixel>(TestImageProvider<TPixel> provider)
         };
 
         // act
-        YuvConversion.ConvertRgbToYuv(image.Frames.RootFrame, config, memoryAllocator, y, u, v);
+        YuvConversion.ConvertRgbToYuv(image.Frames.RootFrame.PixelBuffer.GetRegion(), config, memoryAllocator, y, u, v);
 
         // assert
         Assert.True(expectedY.AsSpan().SequenceEqual(y));
@@ -249,7 +248,7 @@ public void ConvertRgbToYuv_WithAlpha_Works<TPixel>(TestImageProvider<TPixel> pr
         };
 
         // act
-        YuvConversion.ConvertRgbToYuv(image.Frames.RootFrame, config, memoryAllocator, y, u, v);
+        YuvConversion.ConvertRgbToYuv(image.Frames.RootFrame.PixelBuffer.GetRegion(), config, memoryAllocator, y, u, v);
 
         // assert
         Assert.True(expectedY.AsSpan().SequenceEqual(y));
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 0d9fc55007..943814ecf0 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -482,6 +482,8 @@ public static class Gif
         public const string Ratio1x4 = "Gif/base_1x4.gif";
         public const string LargeComment = "Gif/large_comment.gif";
         public const string GlobalQuantizationTest = "Gif/GlobalQuantizationTest.gif";
+        public const string MixedDisposal = "Gif/mixed-disposal.gif";
+        public const string M4nb = "Gif/m4nb.gif";
 
         // Test images from https://github.com/robert-ancell/pygif/tree/master/test-suite
         public const string ZeroSize = "Gif/image-zero-size.gif";
@@ -511,7 +513,25 @@ public static class Issues
             public const string Issue2198 = "Gif/issues/issue_2198.gif";
         }
 
-        public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 };
+        public static readonly string[] Animated =
+        {
+            M4nb,
+            Giphy,
+            Cheers,
+            Kumin,
+            Leo,
+            MixedDisposal,
+            GlobalQuantizationTest,
+            Issues.Issue2198,
+            Issues.Issue2288_A,
+            Issues.Issue2288_B,
+            Issues.Issue2288_C,
+            Issues.Issue2288_D,
+            Issues.Issue2450_A,
+            Issues.Issue2450_B,
+            Issues.BadDescriptorWidth,
+            Issues.Issue1530
+        };
     }
 
     public static class Tga
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
index 9b100047f0..2243c852d2 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
@@ -171,7 +171,7 @@ public string SaveTestOutputFile(
 
         encoder ??= TestEnvironment.GetReferenceEncoder(path);
 
-        using (FileStream stream = File.OpenWrite(path))
+        using (FileStream stream = File.Create(path))
         {
             image.Save(stream, encoder);
         }
@@ -227,7 +227,7 @@ public string SaveTestOutputFile(
         {
             using Image<TPixel> frameImage = image.Frames.CloneFrame(file.Index);
             string filePath = file.FileName;
-            using FileStream stream = File.OpenWrite(filePath);
+            using FileStream stream = File.Create(filePath);
             frameImage.Save(stream, encoder);
         }
 
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
index 6c6f300d02..9508de2469 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
@@ -1,7 +1,6 @@
 // Copyright (c) Six Labors.
 // Licensed under the Six Labors Split License.
 
-using System.Diagnostics.CodeAnalysis;
 using SixLabors.ImageSharp.Formats;
 using SixLabors.ImageSharp.Formats.Bmp;
 using SixLabors.ImageSharp.Formats.Gif;
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDiffusionFilterInBox_Rgba32_CalliphoraPartial.png b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDiffusionFilterInBox_Rgba32_CalliphoraPartial.png
index dd2f49f08b..de42d1bfc2 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDiffusionFilterInBox_Rgba32_CalliphoraPartial.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDiffusionFilterInBox_Rgba32_CalliphoraPartial.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:cafc426ac8e8d02a87f67c90e8c1976c5fae0e12b49deae52ad08476f7ed49a4
-size 266391
+oid sha256:681b0e36298cb702683fb9ffb2a82f7dfd9080b268db19a03f413809f69d0e07
+size 273269
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png
index f226b166e4..43e414da6d 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:98115a7087aced0c28cefa32a57bc72be245886cabeefc4ff7faf7984236218c
-size 271226
+oid sha256:a899a84c6af24bfad89f9fde75957c7a979d65bcf096ab667cb976efd71cb560
+size 271171
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png
index daa4b5e437..d8ececb4cd 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0369747820c86bb692fc7b75f3519095c9b2a58a885ebd37c871c103d08405a0
-size 720
+oid sha256:f7c19df70d24948e1a36299705bb030715cf0d01b453d390989d472c0999d46a
+size 728
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png
index daa4b5e437..d8ececb4cd 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0369747820c86bb692fc7b75f3519095c9b2a58a885ebd37c871c103d08405a0
-size 720
+oid sha256:f7c19df70d24948e1a36299705bb030715cf0d01b453d390989d472c0999d46a
+size 728
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png
index daa4b5e437..d8ececb4cd 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0369747820c86bb692fc7b75f3519095c9b2a58a885ebd37c871c103d08405a0
-size 720
+oid sha256:f7c19df70d24948e1a36299705bb030715cf0d01b453d390989d472c0999d46a
+size 728
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png
index d8f9b640dd..5da96d59d2 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f63aebed17504ef50d96ac7e58dc41f5227a83a38810359ed8e9cecda137183b
-size 720
+oid sha256:0e7ece9d70c4fe0771abd43e4dbb33fb95f474ca56633dcb821022ee44e746d4
+size 728
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Atkinson.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Atkinson.png
index 3656e32db6..1656b2e9cb 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Atkinson.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Atkinson.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:471eaf2e532b40592c86dc816709d3ae4bbd64892006e00fd611ef6869d3b934
-size 52070
+oid sha256:38597c6144d61960d25c74d7a465b1cdf69b7c0804a6dec68128a6c953258313
+size 52688
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Burks.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Burks.png
index 7cafd50c17..c6016ae358 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Burks.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Burks.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:91fb9966a4b3eaefd5533ddf0b98ec08fbf8cbc263e4ebd438895e6d4129dd03
-size 61447
+oid sha256:5f9191c71eea1f73aa4c55397ca26f240615c9c4a7fff9a05e6f2e046b5e4d8b
+size 62323
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_FloydSteinberg.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_FloydSteinberg.png
index 5d0c82e058..40243937d3 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_FloydSteinberg.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_FloydSteinberg.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d74faa8d188a2915739de64ba9d71b2132b53c8d154db22510c524ae757578a5
-size 61183
+oid sha256:b63810145832db459bb7a6b37a028a7b778f6b6b4e6eae00e50e6e21c5a06086
+size 62199
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_JarvisJudiceNinke.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_JarvisJudiceNinke.png
index 584e677e20..83f9e067db 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_JarvisJudiceNinke.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_JarvisJudiceNinke.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:080cc89d1d6568a2c9b707bf05428ab5febd2951e37223f96e349cc6646d32aa
-size 56070
+oid sha256:a67c14ef99a943706f050ff1ea0ef101429292d52bc14ed4610f8338736ff87e
+size 56800
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra2.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra2.png
index 641ecaca19..22e4f4b6d6 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra2.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra2.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c7589986c1a762d52fe8ffc252e9938ff0e3a9e00b91ea7f5e36d4335b2b7870
-size 58502
+oid sha256:623dd82d372ba517b0d3357d06cffaf105d407a9090cbcbc6a76ae944ab33d67
+size 59468
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra3.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra3.png
index 61bbf2b155..838863c158 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra3.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Sierra3.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:934042746c3a9b652069da26b479e2be7cbdb17ab20e41c5e271013a76e96e46
-size 58480
+oid sha256:8edceef8e12c4f3d194523437045c5cf4e80c7bb95ff75f38c1f38a21872e3d0
+size 59376
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_SierraLite.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_SierraLite.png
index 42e595b0ab..60513e1992 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_SierraLite.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_SierraLite.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:03d5d5cbf1b2c0be736aa2bf726ad4bb04fca77aff393edb9663a7915a794264
-size 62418
+oid sha256:b1d7019e8cb170ae67496f8250446c4f6b6217378658408c3d51a95c49a4c3bc
+size 63287
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_StevensonArce.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_StevensonArce.png
index 5cd6eca10d..0d1b34d8ce 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_StevensonArce.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_StevensonArce.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:19a0d8667bfd01e18adbfca778e868ea7a6c43d427f9ae40eb4281d438ef509c
-size 54464
+oid sha256:d7c03ede7ab3bd4e57e6a63e53e2e8c771e938fdc7d5dfe5c9339a2c9907c9cf
+size 55550
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Stucki.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Stucki.png
index 5a97796404..f8c998ecbd 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Stucki.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_Bike_Stucki.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:11c1056e013292e0543598f5690625b9bac0420a15fd1f37f6484daa3b8326fa
-size 60074
+oid sha256:79b690b91223d1fe7ddf1b8826b4474b89644822bc8aa9adee3cf819bc095b4c
+size 60979
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png
index d0c3196426..cc2327b23f 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:3fcf9b7e4ee34e80e8811f94940aff09a5392c21019fc86b145d16fd9c6b1cd2
-size 57501
+oid sha256:7e22401dddf6552cd91517c1cdd142d3b9a66a7ad5c80d2e52ae07a7f583708e
+size 57657
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png
index 773ff203ac..e3ae6508e1 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:4f0d9a43d8a47e00f6e5932b57f99565370a7239496fdbe162fb774497c4ef2a
-size 59377
+oid sha256:819a0ce38e27e2adfa454d8c5ad5b24e818bf8954c9f2406f608dcecf506c2c4
+size 59838
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png
index a41b9989f8..2b897a5d6d 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d4a64da29f144d4d4c525ea45e56819e02a46030ae09542be01fdd8ffc85a295
-size 60377
+oid sha256:007ac609ec61b39c7bdd04bc87a698f5cdc76eadd834c1457f41eb9c135c3f7b
+size 60688
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png
index 39fc93541e..10ba90ae86 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:90fc8048141b2182e4851a48ac5a79c96210eab9e56468fe06f90e7e70a7c180
-size 58539
+oid sha256:46892c07e9a93f1df71f0e38b331a437fb9b7c52d8f40cf62780cb6bd35d3b13
+size 58963
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png
index e7bd1c6f36..9608289e84 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:b312bd18eba03a37121bbcfb3b285f97fe22283b51256883ce0235bb8605b757
-size 58616
+oid sha256:1b83345ca3de8d1fc0fbb5d8e68329b94ad79fc29b9f10a1392a97ffe9a0733e
+size 58985
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png
index f3155ba80b..79d2c5eb14 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:750ccd26984a4d5a370c1af6ca5dd1c9c5c6c66e693f7645130fd1669e3b7b4e
-size 58923
+oid sha256:c775a5b19ba09e1b335389e0dc12cb0c3feaff6072e904da750a676fcd6b07dc
+size 59202
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_SierraLite.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_SierraLite.png
index d5cbbd3e04..8d3cf1a564 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_SierraLite.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_SierraLite.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f9d3777a936883a2177a964f24d9ac86c8a106c375583bc9a8fbeb0ec39a7dc6
-size 60610
+oid sha256:6c88740c0553829eaa42ca751b34cc456623a84ccdff4020949a06ef4b4802d1
+size 61137
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_StevensonArce.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_StevensonArce.png
index 5b83ace203..a146f8f668 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_StevensonArce.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_StevensonArce.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f638821c29d852d6fabe4cc4cfe802e386024835ad07ee496a7bec7a930e851b
-size 57886
+oid sha256:0a4a404b0767faac952435f768867cf7bf053848e1e3ef121624f136658a107c
+size 58386
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png
index 46dace67b2..edec46a92a 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c6e86bfc1594ec4cb8f89a1c92a42778c59aa755ce170a97afb8cab3e623aa79
-size 58376
+oid sha256:8cc216ed952216d203836dc559234216614f1ed059651677cc0ea714010bd932
+size 58855
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png
index 909af9b6d3..e2e4147f68 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f7e849620a297e29ba11014c54430db01d851e4192650f6e39e0410591244cb5
-size 865
+oid sha256:a3253003b088c9975725cf321c2fc827547a5feb199f2d1aa515c69bde59deb7
+size 871
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png
index 909af9b6d3..aa0e9a4824 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f7e849620a297e29ba11014c54430db01d851e4192650f6e39e0410591244cb5
-size 865
+oid sha256:bb3e3b9b3001e76505fb0e2db7ad200cad2a016c06f1993c60c3cab42c134863
+size 867
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png
index 909af9b6d3..e2e4147f68 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f7e849620a297e29ba11014c54430db01d851e4192650f6e39e0410591244cb5
-size 865
+oid sha256:a3253003b088c9975725cf321c2fc827547a5feb199f2d1aa515c69bde59deb7
+size 871
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png
index 909af9b6d3..e2e4147f68 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f7e849620a297e29ba11014c54430db01d851e4192650f6e39e0410591244cb5
-size 865
+oid sha256:a3253003b088c9975725cf321c2fc827547a5feb199f2d1aa515c69bde59deb7
+size 871
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png
index 5961b0384d..e899ffb42a 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:58c03e354b108033873e2a4c0b043ce15919c4d0630e6ca72ff70b89cbedb979
-size 44239
+oid sha256:ca70bb0200776efd00c4ef7596d4e1f2f5fbc68e447b395b25ef2b3c732e5156
+size 44189
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png
index a2bbce465e..543640c2e8 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f987f4d270568facefc11eee7f81dd156af56c26b69fe3a6d2d2e9818652befa
-size 43116
+oid sha256:8474b847b7d4a8f3e5c9793ca257ce46efcf49c473c731a9ca9c759851410b94
+size 43066
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png
index 727a45d0b1..fec3c9b2b3 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ebdad83936e50bbb00fd74b7dd7d2f5a480bb7347aa3d151e7827107cd279bac
-size 44441
+oid sha256:20e80e7d9e68fd85bfbc63c61953327354b0634000ec142e01a42618995fd14c
+size 44391
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png
index 5f9b599b7d..68a95a0540 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ccdf5937c30999e3b09071200de2e1db63b606ad9cbf6f7677a7499fb0b52963
-size 44252
+oid sha256:8af98bfcc5edef3f3ff33ee8f76f33ce2906a6677167e2b29e1dbe63b00a78d8
+size 44202
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png
index b50fce4dc1..d67f02dca5 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:baf70b732646d7c6cec60cfbe569ec673418dfb2dd0b5937bccfb91d9821d586
-size 45053
+oid sha256:b149ebbd550808ae46ff05b5ddcdb1fc0eb6ae0eacbe048e9a1ff24368d8f64d
+size 45003
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png
index dfdb4f642f..4175cf40b7 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f6a1eae610ed730e4cec41693829929ba8db674886c2bd558f1b8893d2b76802
-size 51201
+oid sha256:9316cbbcb137ae6ff31646f6a5ba1d0aec100db4512509f7684187e74d16a111
+size 51074
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png
index c37ea9dcaf..11d916bdc3 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ba674e0236c2e146c64a7f3e224c702030769304cd0fd624d1989536da341659
-size 52814
+oid sha256:08c39a43993deadebab21f1d3504027b5910a52adc437c167d77d62e5f5db46e
+size 52762
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png
index d1ccc680b6..a4f91b3301 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:316231c8d837f864cf62dcc79fdce698dc8c45c0327372de42c2b89eac1d9f81
-size 51851
+oid sha256:0c9c47fa755d603f8c148011511ee91f32444e0d94367f9db57593e3bf30f2e0
+size 51808
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png
index 35ab5b75b8..ac56fa9236 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:b58144146585f50960dfd6ac5dc3f52238160287ae5f9b18c6796962cc3d2fd2
-size 51550
+oid sha256:6d2289ed4fa0c679f0f120d260fec8ab40b1599043cc0a1fbebc6b67e238ff87
+size 51428
diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png
index fde570043d..9a7c7b4611 100644
--- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png
+++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:6515be764d7026a87cfeea2d58344c404e4f15908139a25f413d51cc7cc61a0c
-size 52216
+oid sha256:366e84ab8587735455798651096d2af5f965fc325f4852dc68356e94600598b1
+size 52176
diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_cheers.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_cheers.png
deleted file mode 100644
index d5be7b0b25..0000000000
--- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_cheers.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b199a36f9e682a54c5d7c67f3403bba174b37e1a7a8412481f66c6d5eb0349e9
-size 27679
diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_issue403_baddescriptorwidth.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_issue403_baddescriptorwidth.png
deleted file mode 100644
index 5bd551cb16..0000000000
--- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_issue403_baddescriptorwidth.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4bedcf1a0ca0281dd5531d96c74e19c5d5fd379d6e2acb899077299917215705
-size 1013
diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_m4nb.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_m4nb.png
new file mode 100644
index 0000000000..abe2828ec7
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_m4nb.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:04239ee46f0276ba52566bcb2407f4a6fcee35b3f51a6182394f851e2d8df3fc
+size 277
diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_mixed-disposal.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_mixed-disposal.png
new file mode 100644
index 0000000000..c30d822c18
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyRootFrameAndFrameCount_Rgba32_mixed-disposal.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b654f948a26d256ff9e28ada399465bd6a4205aedaf93ea7cdffb70483535ef
+size 2216
diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png
index 48e4261f1e..4c78303750 100644
--- a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png
+++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ef5a85b2adde25b5f343a18420fe787f5e159029a361a15ef2d6322eb7bb81fb
-size 944597
+oid sha256:1af50619f835b4470afac4553445176c121c3c9fa838dff937dcc56ae37941c3
+size 945821
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png
index a7730d4e65..4948c7adee 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f4d36c8f7e5d5c0d798af5fb6bfad28ed0d628b880bea81efe0d54ac1fde86b2
-size 265268
+oid sha256:a51d04953c1c82d99884af62912d2271108c6bc62f18d4b32d0b5290c01fa7f7
+size 247462
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png
index 367db5ea10..922c2bf9b2 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:531594a2e47710545d96d0fd9a8cc86983233420172e0ced28df050df1a5e039
-size 239844
+oid sha256:4f1462733e02d499b0d8c61ab835a27c7fee560fdc7fc521d20ec09bb4ccc80f
+size 216030
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png
index f8b5e61332..dbfab2b508 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:620463c14de12abb4f2cab3ee6259ad8cbb24c688212729535f41ebf492a8836
-size 224490
+oid sha256:c68eba122814b5470e5f2e03e34190ff79e84e4b431ad8227355ce7ffcd4a6a7
+size 220192
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png
index a9e5e18dfa..82d5e5d592 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:274c3e57f186c47bb070dfd2a79b8353032f9d91d03a3ab9ecb3aec13fdd9855
-size 273333
+oid sha256:f4df5b1bc2c291ec1cf599580d198b447278412576ab998e099cc21110e82b3d
+size 263152
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png
index b5cb6c0fa8..ebb9ff6b00 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:fd007786164af8f410bd9738c0a072fc75d1f9b50145e5c191c9e3df345341a5
-size 318778
+oid sha256:f414473561bfa792c2e6342ff5e5dddffbdec5286932781b11a093803593b52a
+size 313787
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png
index 3ae9d369d6..0205626738 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:08ace2edc5089a7742c1c8005126dcce850b1adf9c017b12db6892f4daeef1bb
-size 271721
+oid sha256:3a2aae04edebcaca9b95f30963201794887fa0eac954b64c68bfe529b14fa9be
+size 269397
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png
index 11939c16cd..324bd92539 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:6ac7cdcc2fbee0572a369bed29c51e1c9056a4f09c4e0750ecb34d65daf403d4
-size 287741
+oid sha256:752760327cc1416c171a920f1e0e95e34eae6d78bd0c7393a3be427bf3c8e55c
+size 284481
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png
index 9702a635d8..05be1395ab 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:be7812accadc6347d6df43c308f7293596f92d0d90cf0b6a8e48fac1f4144fc0
-size 320157
+oid sha256:90a2b7b3872c6eb1f1f039558d9f6ace92891c86951c801da01ad55b055fd670
+size 316544
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png
index 4b1e9fed20..82b965123d 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:033bb9e0ce89ffe4abda4d409af5741958d4035f9c9824c28d7598d72c4db96f
-size 13818
+oid sha256:4bed69d43856ebd4b1af4055f8d3aacabd50c361a4e1e1f9cad080d799d6b744
+size 13853
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png
index cdf5f1a963..571b0db4b9 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:9c6617ebf10f2fe1608fbc2a3c75f1a86ff4e3835b5d3fd7fcc2e5d0a4e5bbb1
-size 14380
+oid sha256:4c8c8393708002f06f9d8ed1ff8979db820035585c08b66ae463d94724fa64d3
+size 14330
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png
index 2ad42755c6..a1b3da6816 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:5bb52f047f410e2b0bdcd8d186043f0d3b03835f39007775608fb05365ac9a20
-size 14616
+oid sha256:fda13875f4c762a95001426487cc04c9add39821eb793168fdbe5cc18e705643
+size 14566
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png
index 4b1e9fed20..82b965123d 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:033bb9e0ce89ffe4abda4d409af5741958d4035f9c9824c28d7598d72c4db96f
-size 13818
+oid sha256:4bed69d43856ebd4b1af4055f8d3aacabd50c361a4e1e1f9cad080d799d6b744
+size 13853
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png
index aabb936bd8..e0fc792026 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:47e0924ddbf191dca8932eb46b9c533d0983b9fbce956026b392d5fe589fb90a
-size 14630
+oid sha256:cb826afb127fe4175e6e47253b8a8313b9d10aee193c316731f34e5d327a2591
+size 14580
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png
index 0fe9cd8672..491847e491 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:4ede7ffc5d07a09c7c5706e8e2554897d29acaabf71701191b0f689f2c22ae71
-size 17826
+oid sha256:37018ecc499651833208d846a0f446db94cc11eae002ab6e7ce45b3e7c09e86c
+size 17734
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png
index 1e8cd88ffe..013bb4a3b7 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f91c7f1f687c73dd909b629e40f703740270042507c47aa3834ccd38bf289dc3
-size 19394
+oid sha256:c2f9ed902882f58704b22460bc64a7b27bc6f47fc2c822ee09f52345cc0d6ebf
+size 19255
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png
index a9f7d76f80..31fd7a5445 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:2b836ab4e0f1c95fe5d553bf5bca37eac402ba431268d25e4641e86c952f5fdd
-size 19802
+oid sha256:aad3f26f2939f3679afa2b6165db29885fff40bbb1d171d5ffecc7861b5fac31
+size 19654
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png
index 4b1e9fed20..82b965123d 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:033bb9e0ce89ffe4abda4d409af5741958d4035f9c9824c28d7598d72c4db96f
-size 13818
+oid sha256:4bed69d43856ebd4b1af4055f8d3aacabd50c361a4e1e1f9cad080d799d6b744
+size 13853
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png
index 958eee6ad7..e2a05b9bd5 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:00bcd2ac107f45cb31efee44de275dd597eae6d2d4fda71c397416ff5f2f0914
-size 20175
+oid sha256:6d21029fa22dbe72cdc60b90c758cb9becd9fce03a33580d9466c1aedd323c1c
+size 20000
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.25.png
index 881ee4e58a..9850675bed 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:b04b71b0a4718e5b3f91c33c23ef792fc81a67c9fef7b9e4d80bdb9dce3539dd
-size 9107
+oid sha256:ea836214840a5da2b89dad3cd9e916413d3f9e21f9b855dc8161faa3544edcfc
+size 9266
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.5.png
index 6804454813..f3278c3d2f 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:e0125454d2c3859d8457202856e21591ab96f61e2a29c3f017af29bf03961c48
-size 8883
+oid sha256:346c9e4239d917614525a99f7ae58ed0c0a22dc09d639f3a54dad1975e75ec44
+size 8833
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.75.png
index 52ba79e98b..77821255bb 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:213ebd9fc7a190ad7226b487387edb9452284193cee4c4720448d7e19ef38e76
-size 11149
+oid sha256:717fe46156f3d144f31cfce066dd13532ee8721d7d3a7b8c8425c646f411e8a5
+size 11099
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.png
index fc26d1b766..0615793d57 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:43b21e948795abb52ebbbf94e785542e55488cc7f17996e2b92404ca8ad1a7cb
-size 7844
+oid sha256:9e01c7276f1c4e905b1d8f4c84259f1047c0949f7a6a81f43a790bd1bd3201e3
+size 7932
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_1.png
index 6f67736dd6..c43b5836ec 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_ErrorDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0bb8f730a43a8f58e4269905014461bab8dc8b47387ec86a84c1064b1cdabe14
-size 11813
+oid sha256:ae18d22edc011d576d6a1e9545bc52084ca0bed55a6ce19d391d2a5f97b1843c
+size 11763
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png
index 22ce841b48..e54740610c 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7500be583beb22759dec7e5bbb2a8d2230054366bfbd0e39bf10fdc8af63eb58
-size 8925
+oid sha256:74b3f36e3fbac940d1f3bf90089b6b40234aa2ce3570b094534a4448c1d98aec
+size 8875
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png
index 2457c12e91..b08ba5be19 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c48da7aad9c0aad576293d7085f9ddb2ea90a76d53bea54cbe0cb7aef71c7bb4
-size 9136
+oid sha256:80e60c42fa11e973e1c865ed93448d3af0503e32d7b119bfe7162738efe691db
+size 9086
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png
index 33ec41713e..692c119e4a 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:72e6c327e98d9929a4d850905b1471fcd1191088feb38b028e4240e9f93b4996
-size 9827
+oid sha256:fd5a9c76ee332603877624e219d84f85fe159389e7f9e72d1fb6177289dd1fb7
+size 9777
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.png
index fc26d1b766..0615793d57 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:43b21e948795abb52ebbbf94e785542e55488cc7f17996e2b92404ca8ad1a7cb
-size 7844
+oid sha256:9e01c7276f1c4e905b1d8f4c84259f1047c0949f7a6a81f43a790bd1bd3201e3
+size 7932
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png
index ea35c328c4..17a810448e 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:70f8fc84d6e578822582ebee5888514dcd70c8d0da1a920f35e63a1617d1e92c
-size 9841
+oid sha256:240743d5f742b872c0f66f4033ad065402372605a76cda23f4c506d254a9d127
+size 9791
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.25.png
index e2b742ca45..10b511a1a3 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ff4d6e3a7c8e69db0d5a88b7f1c91621692962bdbcedd6b8af1311506243bcde
-size 11259
+oid sha256:074842dcbdf60690f41da31e12c290045d05ab6dc587f3f5ba29c9496871391c
+size 11209
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.5.png
index 61a28a9cfd..1ed81c0d0a 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:520910b75cae834b9844ce896ad9ab6d0b6c33c44c7bd822ce63752aaa2a8c5a
-size 12122
+oid sha256:29e1ff6d454efca61852a88946e25dcf29708230bfc47c2625c4d1b2407070c6
+size 12072
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.75.png
index bfaf9e30ab..30f75826eb 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:8e1fb4bb5d1591ac6bb8a4fb2504e2e836578c9c42d8286061cf4f57f0b7f97d
-size 12876
+oid sha256:f7838c37c32134f325960312095ed8e1decbb0dd7e14a84e82637258c7ea117e
+size 12826
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.png
index 78576c467e..af9954116a 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:3f5babf5f6ae746a9b9f131ecf990f037c8e8bdb257bdbb7f6127460c9b8f98d
-size 10507
+oid sha256:0166ad5236ecdcc943d839fad092fe3899dcd4e418703846c492edb7700e4726
+size 10682
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_1.png
index 71d771fe36..5b8c5127c0 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_ErrorDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:56a9088fdca14b82fa3ac3ecfb67b6b8e97e8d69b89907c8408e17861e8609e0
-size 14195
+oid sha256:aaeee39c61b86d9ce569ca2288f998b8461a3f2169dac23cf2f750dd475d8b81
+size 14145
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png
index a68c99d4d9..93fa5c1de3 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7d4ef129d8846e6802997c0ace90abdc7412d232e9a31fc53f25bb9f32de08f2
-size 12665
+oid sha256:5908ff88ddaa6eb3faea6174d87b0182e4407b11812ad70ddcd39c6619b6a5c5
+size 12615
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png
index 992ae04257..af2345fe52 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:69b5771a313aa777341d1460db94a7b3cc74b69c44f6c9d1a1b5b3734936e795
-size 12791
+oid sha256:b6852daae665638e38c0b7ff58b2a0de1d5df9dd771c5cbccbbb83ff78e6a1d7
+size 12741
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png
index 7865b70d7a..3f91a9259c 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:a56f72c5a12e6d216a32404c996e72721b4cf350d82071a43ebe44190adef94a
-size 12895
+oid sha256:6d86473ff1024fc53373b1dba49fc14283b8a323d6b85ba3e16f41ebff8288d0
+size 12845
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.png
index 78576c467e..af9954116a 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:3f5babf5f6ae746a9b9f131ecf990f037c8e8bdb257bdbb7f6127460c9b8f98d
-size 10507
+oid sha256:0166ad5236ecdcc943d839fad092fe3899dcd4e418703846c492edb7700e4726
+size 10682
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png
index 140af0ed89..878a36a477 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:39f85cb564a257381656fd969acaeda04016e09380133f772eb029e332aeaa95
-size 13482
+oid sha256:b2bd11fa19fab712b5cd6c2b36d673c7dce904b5032b860d257b00e095e4aadf
+size 13432
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png
index 1a53f430c6..dba9232097 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:9d528ea896a2e56e0b8967d5ce3486078cbd7bf29e5f0d43a58ef21100147915
-size 13818
+oid sha256:faa91657288e6a6797d8459d41d5fecca3c0a2e8e63317ebaf47df28688d13d7
+size 13853
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png
index 90823958c7..ea062d5be6 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:4e96967a2a6362f42026ab2e2cdd82437e573c16a52c5631f7495cb30615441d
-size 13841
+oid sha256:79e48506430f3a9b25f484ef191fd820819c438392a4e588c2ecafb6db9a2210
+size 13775
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png
index f0270cd0ac..ae90ea9b5f 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d5f9e9e4f7f48681d5b240d6df6ae282c6b9e896eacc1ad73f2a097975fa8d29
-size 14060
+oid sha256:f56c884a0e4666cd662d36ec3a0d4e751c899c0122595378154507fffc69fda4
+size 14010
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png
index 1a53f430c6..dba9232097 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:9d528ea896a2e56e0b8967d5ce3486078cbd7bf29e5f0d43a58ef21100147915
-size 13818
+oid sha256:faa91657288e6a6797d8459d41d5fecca3c0a2e8e63317ebaf47df28688d13d7
+size 13853
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png
index 636f8299aa..1e1795063b 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:97d6923c3342d600a9a2ab3fa713136b2649b74364d6e90a905de607838e0cb7
-size 14319
+oid sha256:4095927693b3cd49df58c0c1d7c5430255350c9ae595408a52ad83b1a65614ac
+size 14269
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png
index f287f604e0..29a3ed7ffd 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:66399a4ba262bdacdc5966fa9fc6f8f6bfa3f8c70db889363e9bbd5778dc1ecf
-size 16081
+oid sha256:d062d4b79ee01942776ae13467e9bcbb529a7eeb5ad7c28ff3d0ccd3d88dcde6
+size 15962
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png
index f6a36e2035..50fa46d169 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:5953a12a2fa67f9fe17485c62d156bb7f80a4bcf4124c123db4440c2559e0e69
-size 17002
+oid sha256:47b2265af41ba042904cab387bf1de4715bd4d8a318bc6c1f69bfdbff5eabe2c
+size 16928
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png
index 03519e0322..5d1030e6b8 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:efc08c6969344de404692c2b367130e3f736442c0722067a9105f036a9e4511e
-size 17616
+oid sha256:6679d6d6f7c8b44461956b54654cea71180a2b0d43712d3775e60cbedd90cc82
+size 17520
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png
index 1a53f430c6..dba9232097 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:9d528ea896a2e56e0b8967d5ce3486078cbd7bf29e5f0d43a58ef21100147915
-size 13818
+oid sha256:faa91657288e6a6797d8459d41d5fecca3c0a2e8e63317ebaf47df28688d13d7
+size 13853
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png
index c4c8d7aac3..567e5d6a3b 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:455488369cd943f3c3a0b2c402dbb17d6fc9c384b5f26dbc049634ad4bdab73f
-size 18259
+oid sha256:5af5d16f875172d73f8426928fc8edaa4a6cab321a968b6c29fca32d0fba0df5
+size 18182
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png
index ce6858e2d8..09c471914a 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:2a65ffa4c6b8488f52892306052337e17a4e58d28fa43f18009e6d1f997962de
-size 83060
+oid sha256:a40b319d264f046159722cb57599eda51de9ba3795272b3785901cdc51053fab
+size 83010
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png
index 651ed382fe..3bd7cbabbb 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:1f69909cdc4b65548834e30caf44a31b7e5d41be1db05cb43bd8277613f327ed
-size 55263
+oid sha256:3bc93509a983e20986614f4937f66d5d979bbb433a30a7736150934cf14b452a
+size 55213
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png
index 501074f232..34490e602d 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ff7d35f1ce04d8f2b6cee41c951487bd24e18f923dc84c77a7944ec3aab61540
-size 80844
+oid sha256:b92f3320120d53444cefc79b4684933cfe2b933dc79c2414496785743b5c8f18
+size 80808
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_ErrorDither.png
index 08ddd2deff..40243937d3 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:48ac9eea396e010b1c110413a6861b1708f3928243271ea78f390e64dfe11737
-size 62249
+oid sha256:b63810145832db459bb7a6b37a028a7b778f6b6b4e6eae00e50e6e21c5a06086
+size 62199
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_NoDither.png
index da68bff3ce..5e9fa12332 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:a4786eb2c04be00302996f3ad65987f54fe5d80ded438fdcccf7b9bfe9520dbb
-size 33930
+oid sha256:f90db3ce2153cc9ba4d1d79e5749dc4d49e916dff8a0e121ebce9b00702cfcc8
+size 33880
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png
index 5f9b599b7d..68a95a0540 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ccdf5937c30999e3b09071200de2e1db63b606ad9cbf6f7677a7499fb0b52963
-size 44252
+oid sha256:8af98bfcc5edef3f3ff33ee8f76f33ce2906a6677167e2b29e1dbe63b00a78d8
+size 44202
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_ErrorDither.png
index 60e5f18dc0..96c66aad72 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:898c8524d6fcad8498a22dd97956265302458d43f8d3f93846c2268d7b47fb73
-size 35351
+oid sha256:828b082a1892f0200ef84254637b340b1276e1bee44e01c6b715de8838e4818f
+size 35301
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_NoDither.png
index cf7cba1811..3ff151f6d0 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:5f37abc44b703b6feac3a2a950df7f40a8a67fe64fa25c19af59862e5272f0ff
-size 34311
+oid sha256:f70d1aa2f985dfb7227ea5fd7b4b98effc1a31c89fd05bbee9cfa8f003b9cb4e
+size 34261
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png
index 8ddd2cf001..10daff76b2 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:1cb4518f6b9e1ed1409b0f4e87c17ae990cff2725c650d69941cc76ba90b2f7b
-size 44672
+oid sha256:d8ba00e2948337f77d935d98349958c6a520958671e9ec714ff1bfadfb130e72
+size 44622
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png
index e2fc05dd92..747ca70c1d 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:16a592718ba5aa15f79ef4864cba75fae5a7cc5e13ca56eeab5fd13a0e5347de
-size 102049
+oid sha256:d58c425ce5b1ca56450095a66dea24b379935b0087aec7b4102f15a99f95a017
+size 101999
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png
index fb44b61f9e..de464b94cc 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:86ebb09209d4bfbe266c633df1c7062755cd413f47d0a96a05bcf003a02cb12b
-size 84428
+oid sha256:93a4822e39babba059a88536a965e4f3207e4402d2b92d7d18485fec5e9e69da
+size 84378
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png
index b4ff7af8d8..ce54548279 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:779521e132b4ee6b3ac7a9186fca3250c6d2ace7e55f4f6c9601fe5393d9eb10
-size 99561
+oid sha256:35969c8dc96de4dacc3048ae760a0681278a2011993a0edbceaacc93d6fc3a67
+size 102713
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png
index 8b5956b1ac..5efcaedc94 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:a6d0b12e19b980fd558a4920ec18620a9bdb00bd1379a20719ff0ee92c6887a1
-size 96007
+oid sha256:40d012f4ecb4e36c94d086f8ec7bc199fbfd9fb30a9427a07b35df1b1e430a71
+size 95601
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png
index e1122365e9..916dc37566 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:82c34b115c081a1f3723540b9a273724353930c53006dd6daea576a227271e01
-size 78599
+oid sha256:fa64863f73dfd1c5daef645c54e9275136f66513a87750bee0ec8e13ac357da5
+size 79649
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png
index 30cfcf2dea..f039dd222e 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:6fbe71277a196e5c57fba7caba5c0a1b2bff2da9eadbd7bab1ae1853fab2dd93
-size 92581
+oid sha256:8f5138589c606de20ba193d4279f049ee1ecb3f1801b949d3436995bbf242cbe
+size 92683
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png
index d79f324a77..2b897a5d6d 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:b1061a363744661febdd051ef2f6839bfe288eb83f7ebf281fb06717fbe6703a
-size 60739
+oid sha256:007ac609ec61b39c7bdd04bc87a698f5cdc76eadd834c1457f41eb9c135c3f7b
+size 60688
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png
index 03baa702a7..e40a91cbc4 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:cacd80b681c086310d3be7d90575da357e6ab0a62e24227a1e7e3ae4cab1de2d
-size 46905
+oid sha256:6fc2f82bdbf4b204ad78f3bb54bfdea7452a2d1430814f45262fd309225f2fc0
+size 46727
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png
index 35ab5b75b8..ac56fa9236 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:b58144146585f50960dfd6ac5dc3f52238160287ae5f9b18c6796962cc3d2fd2
-size 51550
+oid sha256:6d2289ed4fa0c679f0f120d260fec8ab40b1599043cc0a1fbebc6b67e238ff87
+size 51428
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png
index dfcc0603dc..8b79a19e05 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d54a2b762a16c76ad52919708d0126ae63958e9d900e3870cced695540e16192
-size 68366
+oid sha256:ef2b6073b75a2de97a78d47d3b3e40c264687c5756f153d3d85bc5b2714cf85a
+size 68226
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png
index c1c66f8409..8d0d2b60db 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:87450152fdccfaebb55e22040206cb246bc59b61461fd8b6ca6e099256fa0f1d
-size 63839
+oid sha256:ac1424c6c4c18feb42106e14da6b161ce3f48276d0aa6603ca60ad5caa0a5338
+size 63764
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png
index f79a145a6e..88cf83a306 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7136f92f3fd1927e98204b1140ca36a901dcd9b67c17f7080413588c2e2dcc28
-size 69579
+oid sha256:513844ed95c2b50e792d3346398256846b8b280dbadf7ef3f4e11d58c1e679c0
+size 69529
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png
index 79cec59e5f..a3eefcba20 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ab5972eaa6b008b9768f3af4c61957db3d6da41cdf52696c05ecd3f2efaf3d5f
-size 113964
+oid sha256:32b269d62d4eebe555d5d9f12b9958b41206848504bb985dcd1ff9c81a5003c6
+size 117073
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png
index 159e284c59..3b0c46ac38 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:46c6b9eb83c6d4ffafc2163b0e0ccd5aa24ac56bb65e8cf5f02b80319cf29e4b
-size 108931
+oid sha256:12f58b00a16913cd85ffa18fcea580a59550dcc201295b060d55a870230f37f7
+size 113995
diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png
index c11f7ac792..328f863307 100644
--- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png
+++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f54b00e54e8786c9c9ff8e8cf7e42111e06e5dc5d2e69ee2c8c8be2353030e43
-size 114680
+oid sha256:867d7b727de278cbc01b7d2b8e968f1fc0d0a81a3e4af636ce4a6598a8709be6
+size 114630
diff --git a/tests/Images/Input/Gif/m4nb.gif b/tests/Images/Input/Gif/m4nb.gif
new file mode 100644
index 0000000000..0c921b2afb
--- /dev/null
+++ b/tests/Images/Input/Gif/m4nb.gif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b5b495caf1eb1f1cf7b15a1998faa33a6f4a49999e5edd435d4ff91265ff1ce5
+size 2100
diff --git a/tests/Images/Input/Gif/mixed-disposal.gif b/tests/Images/Input/Gif/mixed-disposal.gif
new file mode 100644
index 0000000000..07ca32e915
--- /dev/null
+++ b/tests/Images/Input/Gif/mixed-disposal.gif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd7d4093d6d75aa149418936bac73f66c3d81d9e01252993f321ee792514a47a
+size 16636
