diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
index 7fb5fd8ee3..8dc9c96a06 100644
--- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
+++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
@@ -43,6 +43,42 @@ namespace SixLabors.ImageSharp
return unpremultiplied;
}
+ ///
+ /// Bulk variant of
+ ///
+ /// 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;
+ }
+ }
+
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ public static void UnPremultiply(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(1 / v.W);
+ s.W = 1;
+ v *= s;
+ }
+ }
+
///
/// Compresses a linear color signal to its sRGB equivalent.
///
@@ -71,6 +107,40 @@ namespace SixLabors.ImageSharp
return new Vector4(Expand(gamma.X), Expand(gamma.Y), Expand(gamma.Z), gamma.W);
}
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ public static void Compress(Span vectors)
+ {
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+
+ for (int i = 0; i < vectors.Length; i++)
+ {
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
+ v.X = Compress(v.X);
+ v.Y = Compress(v.Y);
+ v.Z = Compress(v.Z);
+ }
+ }
+
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ public static void Expand(Span vectors)
+ {
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+
+ for (int i = 0; i < vectors.Length; i++)
+ {
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
+ v.X = Expand(v.X);
+ v.Y = Expand(v.Y);
+ v.Z = Expand(v.Z);
+ }
+ }
+
///
/// Gets the compressed sRGB value from an linear signal.
///
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index 6accad43fc..8cd34f5402 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -278,44 +278,5 @@ 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;
- }
- }
-
- ///
- /// Revers
- ///
- ///
- /// The span of vectors
- public static void UnPremultiply(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(1 / v.W);
- s.W = 1;
- v *= s;
- }
- }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
index 3c13d781e0..9481be48b9 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
@@ -229,11 +229,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return;
}
+ int sourceHeight = source.Height;
+
// Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle.
- using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(source.Height, width))
+ using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(sourceHeight, width))
{
firstPassPixelsTransposed.MemorySource.Clear();
@@ -250,14 +252,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Span tempRowSpan = tempRowBuffer.Span;
PixelOperations.Instance.ToVector4(sourceRow, tempRowSpan, sourceRow.Length);
- ImageMaths.Premultiply(tempRowSpan);
+ Vector4Extensions.Premultiply(tempRowSpan);
+
+ ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];
if (this.Compand)
{
for (int x = minX; x < maxX; x++)
{
ResizeKernel window = this.horizontalKernelMap.Kernels[x - startX];
- firstPassPixelsTransposed[y, x] = window.ConvolveExpand(tempRowSpan, sourceX).UnPremultiply();
+
+ Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) =
+ window.ConvolveExpand(tempRowSpan, sourceX).UnPremultiply();
}
}
else
@@ -265,7 +271,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int x = minX; x < maxX; x++)
{
ResizeKernel window = this.horizontalKernelMap.Kernels[x - startX];
- firstPassPixelsTransposed[y, x] =
+ Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) =
window.Convolve(tempRowSpan, sourceX);
}
}
diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
index 3f41a9955e..61f06da9f0 100644
--- a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
@@ -1,10 +1,6 @@
// 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
@@ -39,35 +35,6 @@ 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));
- }
-
- [Theory]
- [InlineData(0)]
- [InlineData(1)]
- [InlineData(30)]
- public void UnPremultiply_VectorSpan(int length)
- {
- var rnd = new Random(42);
- Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
- Vector4[] expected = source.Select(v => v.UnPremultiply()).ToArray();
-
- ImageMaths.UnPremultiply(source);
-
- Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
- }
// TODO: We need to test all ImageMaths methods!
}
diff --git a/tests/ImageSharp.Tests/Helpers/Vector4ExtensionsTests.cs b/tests/ImageSharp.Tests/Helpers/Vector4ExtensionsTests.cs
new file mode 100644
index 0000000000..68f71d88f8
--- /dev/null
+++ b/tests/ImageSharp.Tests/Helpers/Vector4ExtensionsTests.cs
@@ -0,0 +1,76 @@
+// 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
+{
+ public class Vector4ExtensionsTests
+ {
+ [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();
+
+ Vector4Extensions.Premultiply(source);
+
+ Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(30)]
+ public void UnPremultiply_VectorSpan(int length)
+ {
+ var rnd = new Random(42);
+ Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
+ Vector4[] expected = source.Select(v => v.UnPremultiply()).ToArray();
+
+ Vector4Extensions.UnPremultiply(source);
+
+ Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(30)]
+ public void Expand_VectorSpan(int length)
+ {
+ var rnd = new Random(42);
+ Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
+ Vector4[] expected = source.Select(v => v.Expand()).ToArray();
+
+ Vector4Extensions.Expand(source);
+
+ Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(30)]
+ public void Compress_VectorSpan(int length)
+ {
+ var rnd = new Random(42);
+ Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
+ Vector4[] expected = source.Select(v => v.Compress()).ToArray();
+
+ Vector4Extensions.Compress(source);
+
+ Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ }
+
+
+ }
+}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
index 1e0f86dcb8..c74b40622a 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
@@ -144,6 +144,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
}
+ [Theory]
+ [WithFile(TestImages.Png.Kaboom, DefaultPixelType)]
+ public void Resize_Compand_DoesNotBleedAlphaPixels(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage())
+ {
+ image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, true));
+ image.DebugSave(provider);
+ }
+ }
+
[Theory]
[WithFile(TestImages.Gif.Giphy, DefaultPixelType)]
public void Resize_IsAppliedToAllFrames(TestImageProvider provider)