diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
index b88c229c5d..7fb5fd8ee3 100644
--- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
+++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
@@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index b3a1b4ba39..a318d1941c 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -275,5 +278,25 @@ namespace SixLabors.ImageSharp
return GetBoundingRectangle(topLeft, bottomRight);
}
+
+ ///
+ /// Pre-multiply all vectors.
+ /// "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
+ ///
+ ///
+ /// The span of vectors
+ public static void Premultiply(Span vectors)
+ {
+ // TODO: This method can be AVX2 optimized using Vector
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+
+ for (int i = 0; i < vectors.Length; i++)
+ {
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
+ var s = new Vector4(v.W);
+ s.W = 1;
+ v *= s;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
index 41e6b65c56..51b407f86a 100644
--- a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
@@ -1,6 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
+using System.Linq;
+using System.Numerics;
+
using Xunit;
namespace SixLabors.ImageSharp.Tests.Helpers
@@ -35,6 +39,21 @@ namespace SixLabors.ImageSharp.Tests.Helpers
Assert.Equal(expected, actual);
}
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(30)]
+ public void Premultiply_VectorSpan(int length)
+ {
+ var rnd = new Random(42);
+ Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
+ Vector4[] expected = source.Select(v => v.Premultiply()).ToArray();
+
+ ImageMaths.Premultiply(source);
+
+ Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ }
+
// TODO: We need to test all ImageMaths methods!
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs
index 9eb051e7a7..0b1b89cc00 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Numerics;
namespace SixLabors.ImageSharp.Tests
{
@@ -10,7 +11,23 @@ namespace SixLabors.ImageSharp.Tests
for (int i = 0; i < length; i++)
{
- values[i] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal;
+ values[i] = GetRandomFloat(rnd, minVal, maxVal);
+ }
+
+ return values;
+ }
+
+ public static Vector4[] GenerateRandomVectorArray(this Random rnd, int length, float minVal, float maxVal)
+ {
+ var values = new Vector4[length];
+
+ for (int i = 0; i < length; i++)
+ {
+ ref Vector4 v = ref values[i];
+ v.X = GetRandomFloat(rnd, minVal, maxVal);
+ v.Y = GetRandomFloat(rnd, minVal, maxVal);
+ v.Z = GetRandomFloat(rnd, minVal, maxVal);
+ v.W = GetRandomFloat(rnd, minVal, maxVal);
}
return values;
@@ -28,5 +45,10 @@ namespace SixLabors.ImageSharp.Tests
return values;
}
+
+ private static float GetRandomFloat(Random rnd, float minVal, float maxVal)
+ {
+ return (float)rnd.NextDouble() * (maxVal - minVal) + minVal;
+ }
}
}
\ No newline at end of file