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 @@ internal static partial class SimdUtils
return AdvSimd.BitwiseSelect(signedMask, right.AsInt16(), left.AsInt16()).AsByte();
}
+ ///
+ /// Blend packed 32-bit unsigned integers from and using .
+ /// The high bit of each corresponding byte determines the selection.
+ /// If the high bit is set the element of is selected.
+ /// The element of is selected otherwise.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// The mask vector.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector128 BlendVariable(Vector128 left, Vector128 right, Vector128 mask)
+ => BlendVariable(left.AsByte(), right.AsByte(), mask.AsByte()).AsUInt32();
+
+ ///
+ /// Count the number of leading zero bits in a mask.
+ /// Similar in behavior to the x86 instruction LZCNT.
+ ///
+ /// The value.
+ public static ushort LeadingZeroCount(ushort value)
+ => (ushort)(BitOperations.LeadingZeroCount(value) - 16);
+
+ ///
+ /// Count the number of trailing zero bits in an integer value.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ ///
+ /// The value.
+ public static ushort TrailingZeroCount(ushort value)
+ => (ushort)(BitOperations.TrailingZeroCount(value << 16) - 16);
+
///
/// as many elements as possible, slicing them down (keeping the remainder).
///
diff --git a/src/ImageSharp/Formats/AnimationUtilities.cs b/src/ImageSharp/Formats/AnimationUtilities.cs
index 23fc40cdfe..3c731422ad 100644
--- a/src/ImageSharp/Formats/AnimationUtilities.cs
+++ b/src/ImageSharp/Formats/AnimationUtilities.cs
@@ -128,6 +128,52 @@ internal static class AnimationUtilities
}
}
+ if (Sse2.IsSupported && remaining >= 4)
+ {
+ Vector128 r128 = previousFrame != null ? Vector128.Create(bg.PackedValue) : Vector128.Zero;
+ Vector128 vmb128 = Vector128.Zero;
+ if (blend)
+ {
+ vmb128 = Sse2.CompareEqual(vmb128, vmb128);
+ }
+
+ while (remaining >= 4)
+ {
+ Vector128 p = Unsafe.Add(ref Unsafe.As, Vector128>(ref previousBase), x);
+ Vector128 c = Unsafe.Add(ref Unsafe.As, Vector128>(ref currentBase), x);
+
+ Vector128 eq = Sse2.CompareEqual(p, c);
+ Vector128 r = SimdUtils.HwIntrinsics.BlendVariable(c, r128, Sse2.And(eq, vmb128));
+
+ if (nextFrame != null)
+ {
+ Vector128 n = Sse2.ShiftRightLogical(Unsafe.Add(ref Unsafe.As, Vector128>(ref nextBase), x), 24).AsInt32();
+ eq = Sse2.AndNot(Sse2.CompareGreaterThan(Sse2.ShiftRightLogical(c, 24).AsInt32(), n).AsUInt32(), eq);
+ }
+
+ Unsafe.Add(ref Unsafe.As, Vector128>(ref resultBase), x) = r.AsByte();
+
+ 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: AdvSimd ??
for (i = remaining; i > 0; i--)
{
x = (uint)(length - i);
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 2186cc2e45..f0e1aafd7d 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -168,7 +168,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
stream.WriteByte(GifConstants.EndIntroducer);
- quantized.Dispose();
+ quantized?.Dispose();
}
private static GifMetadata GetGifMetadata(Image image)
@@ -251,6 +251,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
{
// Gather the metadata for this frame.
ImageFrame currentFrame = image.Frames[i];
+ ImageFrame? 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);
@@ -264,8 +265,6 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
hasPaletteQuantizer = true;
}
- ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
-
this.EncodeAdditionalFrame(
stream,
previousFrame,
diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs
index 128237684c..93ddcf2636 100644
--- a/src/ImageSharp/Formats/Png/PngMetadata.cs
+++ b/src/ImageSharp/Formats/Png/PngMetadata.cs
@@ -90,13 +90,12 @@ public class PngMetadata : IDeepCloneable
{
// 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.
- ReadOnlyMemory? colorTable = metadata.ColorTable;
- if (metadata.ColorTable.HasValue)
+ Color[]? colorTable = metadata.ColorTable.HasValue ? metadata.ColorTable.Value.ToArray() : null;
+ if (colorTable != null)
{
- Color[] clone = metadata.ColorTable.Value.ToArray();
- for (int i = 0; i < clone.Length; i++)
+ for (int i = 0; i < colorTable.Length; i++)
{
- ref Color c = ref clone[i];
+ ref Color c = ref colorTable[i];
if (c == metadata.BackgroundColor)
{
// Png treats background as fully empty
@@ -104,15 +103,13 @@ public class PngMetadata : IDeepCloneable
break;
}
}
-
- colorTable = clone;
}
return new()
{
- ColorType = colorTable.HasValue ? PngColorType.Palette : null,
- BitDepth = colorTable.HasValue
- ? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Value.Length), 1, 8)
+ 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/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;