diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Extensions/SimdUtils.ExtendedIntrinsics.cs
index ec52b90eff..97f364a109 100644
--- a/src/ImageSharp/Common/Extensions/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Extensions/SimdUtils.ExtendedIntrinsics.cs
@@ -24,6 +24,9 @@ namespace SixLabors.ImageSharp
false;
#endif
+ ///
+ /// A variant of , which is faster on new .NET runtime.
+ ///
// ReSharper disable once MemberHidesStaticFromOuterClass
internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan source, Span dest)
{
@@ -37,7 +40,7 @@ namespace SixLabors.ImageSharp
ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
- var scale = new Vector(1f / 255f);
+ const float Scale = 1f / 255f;
for (int i = 0; i < n; i++)
{
@@ -47,10 +50,10 @@ namespace SixLabors.ImageSharp
Vector.Widen(s0, out Vector w0, out Vector w1);
Vector.Widen(s1, out Vector w2, out Vector w3);
- Vector f0 = Vector.ConvertToSingle(w0) * scale;
- Vector f1 = Vector.ConvertToSingle(w1) * scale;
- Vector f2 = Vector.ConvertToSingle(w2) * scale;
- Vector f3 = Vector.ConvertToSingle(w3) * scale;
+ Vector f0 = Vector.ConvertToSingle(w0) * Scale;
+ Vector f1 = Vector.ConvertToSingle(w1) * Scale;
+ Vector f2 = Vector.ConvertToSingle(w2) * Scale;
+ Vector f3 = Vector.ConvertToSingle(w3) * Scale;
ref Vector d = ref Unsafe.Add(ref destBase, i * 4);
d = f0;
@@ -59,6 +62,59 @@ namespace SixLabors.ImageSharp
Unsafe.Add(ref d, 3) = f3;
}
}
+
+ ///
+ /// A variant of , which is faster on new .NET runtime.
+ ///
+ // ReSharper disable once MemberHidesStaticFromOuterClass
+ internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest)
+ {
+ Guard.IsTrue(
+ dest.Length % Vector.Count == 0,
+ nameof(source),
+ "dest.Length should be divisable by Vector.Count!");
+
+ int n = dest.Length / Vector.Count;
+
+ ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
+ ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
+
+ for (int i = 0; i < n; i++)
+ {
+ ref Vector s = ref Unsafe.Add(ref sourceBase, i * 4);
+
+ Vector f0 = s;
+ f0 = Clamp(f0);
+
+ Vector f1 = Unsafe.Add(ref s, 1);
+ f1 = Clamp(f1);
+
+ Vector f2 = Unsafe.Add(ref s, 2);
+ f2 = Clamp(f2);
+
+ Vector f3 = Unsafe.Add(ref s, 3);
+ f3 = Clamp(f3);
+
+ Vector w0 = Vector.ConvertToUInt32(f0 * 255f);
+ Vector w1 = Vector.ConvertToUInt32(f1 * 255f);
+ Vector w2 = Vector.ConvertToUInt32(f2 * 255f);
+ Vector w3 = Vector.ConvertToUInt32(f3 * 255f);
+
+ Vector u0 = Vector.Narrow(w0, w1);
+ Vector u1 = Vector.Narrow(w2, w3);
+
+ Vector b = Vector.Narrow(u0, u1);
+
+ Unsafe.Add(ref destBase, i) = b;
+ }
+
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static Vector Clamp(Vector x)
+ {
+ return Vector.Min(Vector.Max(x, Vector.Zero), Vector.One);
+ }
}
}
}
diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
index 0488dd5e15..4b23ca30f1 100644
--- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
+++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
@@ -226,6 +226,24 @@ namespace SixLabors.ImageSharp.Tests.Common
Assert.Equal(expected, result, new ApproximateFloatComparer(1e-5f));
}
+ [Theory]
+ [InlineData(1, 0)]
+ [InlineData(2, 32)]
+ [InlineData(3, 128)]
+ public void ExtendedIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int seed, int count)
+ {
+ float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, -50, 444);
+ float[] normalized = orig.Select(f => f / 255f).ToArray();
+
+ byte[] dest = new byte[count];
+
+ SimdUtils.ExtendedIntrinsics.BulkConvertNormalizedFloatToByteClampOverflows(normalized, dest);
+
+ byte[] expected = orig.Select(f => (byte)Clamp255(f)).ToArray();
+
+ Assert.Equal(expected, dest);
+ }
+
[Theory]
[InlineData(0)]
[InlineData(7)]