Skip to content

Commit 5da17f3

Browse files
Enable Sse2, simplify
1 parent e35e9a8 commit 5da17f3

File tree

5 files changed

+86
-14
lines changed

5 files changed

+86
-14
lines changed

src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using System.Numerics;
45
using System.Runtime.CompilerServices;
56
using System.Runtime.InteropServices;
67
using System.Runtime.Intrinsics;
@@ -656,6 +657,36 @@ public static Vector128<byte> BlendVariable(Vector128<byte> left, Vector128<byte
656657
return AdvSimd.BitwiseSelect(signedMask, right.AsInt16(), left.AsInt16()).AsByte();
657658
}
658659

660+
/// <summary>
661+
/// Blend packed 32-bit unsigned integers from <paramref name="left"/> and <paramref name="right"/> using <paramref name="mask"/>.
662+
/// The high bit of each corresponding <paramref name="mask"/> byte determines the selection.
663+
/// If the high bit is set the element of <paramref name="left"/> is selected.
664+
/// The element of <paramref name="right"/> is selected otherwise.
665+
/// </summary>
666+
/// <param name="left">The left vector.</param>
667+
/// <param name="right">The right vector.</param>
668+
/// <param name="mask">The mask vector.</param>
669+
/// <returns>The <see cref="Vector256{T}"/>.</returns>
670+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
671+
public static Vector128<uint> BlendVariable(Vector128<uint> left, Vector128<uint> right, Vector128<uint> mask)
672+
=> BlendVariable(left.AsByte(), right.AsByte(), mask.AsByte()).AsUInt32();
673+
674+
/// <summary>
675+
/// Count the number of leading zero bits in a mask.
676+
/// Similar in behavior to the x86 instruction LZCNT.
677+
/// </summary>
678+
/// <param name="value">The value.</param>
679+
public static ushort LeadingZeroCount(ushort value)
680+
=> (ushort)(BitOperations.LeadingZeroCount(value) - 16);
681+
682+
/// <summary>
683+
/// Count the number of trailing zero bits in an integer value.
684+
/// Similar in behavior to the x86 instruction TZCNT.
685+
/// </summary>
686+
/// <param name="value">The value.</param>
687+
public static ushort TrailingZeroCount(ushort value)
688+
=> (ushort)(BitOperations.TrailingZeroCount(value << 16) - 16);
689+
659690
/// <summary>
660691
/// <see cref="ByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
661692
/// </summary>

src/ImageSharp/Formats/AnimationUtilities.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,52 @@ public static (bool Difference, Rectangle Bounds) DeDuplicatePixels<TPixel>(
128128
}
129129
}
130130

131+
if (Sse2.IsSupported && remaining >= 4)
132+
{
133+
Vector128<uint> r128 = previousFrame != null ? Vector128.Create(bg.PackedValue) : Vector128<uint>.Zero;
134+
Vector128<uint> vmb128 = Vector128<uint>.Zero;
135+
if (blend)
136+
{
137+
vmb128 = Sse2.CompareEqual(vmb128, vmb128);
138+
}
139+
140+
while (remaining >= 4)
141+
{
142+
Vector128<uint> p = Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref previousBase), x);
143+
Vector128<uint> c = Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref currentBase), x);
144+
145+
Vector128<uint> eq = Sse2.CompareEqual(p, c);
146+
Vector128<uint> r = SimdUtils.HwIntrinsics.BlendVariable(c, r128, Sse2.And(eq, vmb128));
147+
148+
if (nextFrame != null)
149+
{
150+
Vector128<int> n = Sse2.ShiftRightLogical(Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref nextBase), x), 24).AsInt32();
151+
eq = Sse2.AndNot(Sse2.CompareGreaterThan(Sse2.ShiftRightLogical(c, 24).AsInt32(), n).AsUInt32(), eq);
152+
}
153+
154+
Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<byte>>(ref resultBase), x) = r.AsByte();
155+
156+
ushort msk = (ushort)(uint)Sse2.MoveMask(eq.AsByte());
157+
msk = (ushort)~msk;
158+
if (msk != 0)
159+
{
160+
// If is diff is found, the left side is marked by the min of previously found left side and the start position.
161+
// The right is the max of the previously found right side and the end position.
162+
int start = i + (SimdUtils.HwIntrinsics.TrailingZeroCount(msk) / sizeof(uint));
163+
int end = i + (4 - (SimdUtils.HwIntrinsics.LeadingZeroCount(msk) / sizeof(uint)));
164+
left = Math.Min(left, start);
165+
right = Math.Max(right, end);
166+
hasRowDiff = true;
167+
hasDiff = true;
168+
}
169+
170+
x++;
171+
i += 4;
172+
remaining -= 4;
173+
}
174+
}
175+
176+
// TODO: AdvSimd ??
131177
for (i = remaining; i > 0; i--)
132178
{
133179
x = (uint)(length - i);

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
168168

169169
stream.WriteByte(GifConstants.EndIntroducer);
170170

171-
quantized.Dispose();
171+
quantized?.Dispose();
172172
}
173173

174174
private static GifMetadata GetGifMetadata<TPixel>(Image<TPixel> image)
@@ -251,6 +251,7 @@ private void EncodeAdditionalFrames<TPixel>(
251251
{
252252
// Gather the metadata for this frame.
253253
ImageFrame<TPixel> currentFrame = image.Frames[i];
254+
ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
254255
GifFrameMetadata gifMetadata = GetGifFrameMetadata(currentFrame, globalTransparencyIndex);
255256
bool useLocal = this.colorTableMode == GifColorTableMode.Local || (gifMetadata.ColorTableMode == GifColorTableMode.Local);
256257

@@ -264,8 +265,6 @@ private void EncodeAdditionalFrames<TPixel>(
264265
hasPaletteQuantizer = true;
265266
}
266267

267-
ImageFrame<TPixel>? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
268-
269268
this.EncodeAdditionalFrame(
270269
stream,
271270
previousFrame,

src/ImageSharp/Formats/Png/PngMetadata.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,29 +90,26 @@ internal static PngMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
9090
{
9191
// Should the conversion be from a format that uses a 24bit palette entries (gif)
9292
// we need to clone and adjust the color table to allow for transparency.
93-
ReadOnlyMemory<Color>? colorTable = metadata.ColorTable;
94-
if (metadata.ColorTable.HasValue)
93+
Color[]? colorTable = metadata.ColorTable.HasValue ? metadata.ColorTable.Value.ToArray() : null;
94+
if (colorTable != null)
9595
{
96-
Color[] clone = metadata.ColorTable.Value.ToArray();
97-
for (int i = 0; i < clone.Length; i++)
96+
for (int i = 0; i < colorTable.Length; i++)
9897
{
99-
ref Color c = ref clone[i];
98+
ref Color c = ref colorTable[i];
10099
if (c == metadata.BackgroundColor)
101100
{
102101
// Png treats background as fully empty
103102
c = Color.Transparent;
104103
break;
105104
}
106105
}
107-
108-
colorTable = clone;
109106
}
110107

111108
return new()
112109
{
113-
ColorType = colorTable.HasValue ? PngColorType.Palette : null,
114-
BitDepth = colorTable.HasValue
115-
? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Value.Length), 1, 8)
110+
ColorType = colorTable != null ? PngColorType.Palette : null,
111+
BitDepth = colorTable != null
112+
? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Length), 1, 8)
116113
: null,
117114
ColorTable = colorTable,
118115
RepeatCount = metadata.RepeatCount,

tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4-
using System.Diagnostics.CodeAnalysis;
54
using SixLabors.ImageSharp.Formats;
65
using SixLabors.ImageSharp.Formats.Bmp;
76
using SixLabors.ImageSharp.Formats.Gif;

0 commit comments

Comments
 (0)