diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
index 7cb193e82..8133ebb38 100644
--- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
+++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
@@ -12,6 +12,34 @@ namespace SixLabors.ImageSharp
///
internal static class Vector4Extensions
{
+ ///
+ /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
+ ///
+ /// The to premultiply
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 Premultiply(this Vector4 source)
+ {
+ float w = source.W;
+ Vector4 premultiplied = source * w;
+ premultiplied.W = w;
+ return premultiplied;
+ }
+
+ ///
+ /// Reverses the result of premultiplying a vector via .
+ ///
+ /// The to premultiply
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 UnPremultiply(this Vector4 source)
+ {
+ float w = source.W;
+ Vector4 unpremultiplied = source / w;
+ unpremultiplied.W = w;
+ return unpremultiplied;
+ }
+
///
/// Compresses a linear color signal to its sRGB equivalent.
///
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
index b85432ac5..cf9b7be9c 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
@@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
- var currentColor = sourceOffsetRow[offsetX].ToVector4();
+ Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply();
if (fy < kernelXHeight)
{
@@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
float blue = MathF.Sqrt((bX * bX) + (bY * bY));
ref TPixel pixel = ref targetRow[x];
- pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
+ pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply());
}
});
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
index 362fa5c50..57f434ee0 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
@@ -113,13 +113,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
- var currentColor = row[offsetX].ToVector4();
+ Vector4 currentColor = row[offsetX].ToVector4().Premultiply();
destination += kernel[fy, fx] * currentColor;
}
}
ref TPixel pixel = ref targetRow[x];
- pixel.PackFromVector4(destination);
+ pixel.PackFromVector4(destination.UnPremultiply());
}
});
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
index c0d3fdcfe..96db9a4ce 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
@@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
- var currentColor = sourceOffsetRow[offsetX].ToVector4();
+ Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply();
currentColor *= this.KernelXY[fy, fx];
red += currentColor.X;
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
ref TPixel pixel = ref targetRow[x];
- pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
+ pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply());
}
});
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
index 07c247d73..9d7056b67 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
@@ -204,7 +204,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
var vector = source[i, j].ToVector4();
// Values are first premultiplied to prevent darkening of edge pixels
- var mupltiplied = new Vector4(new Vector3(vector.X, vector.Y, vector.Z) * vector.W, vector.W);
+ Vector4 mupltiplied = vector.Premultiply();
sum += mupltiplied * xWeight * yWeight;
}
}
@@ -212,7 +212,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
ref TPixel dest = ref destRow[x];
// Reverse the premultiplication
- dest.PackFromVector4(new Vector4(new Vector3(sum.X, sum.Y, sum.Z) / sum.W, sum.W));
+ dest.PackFromVector4(sum.UnPremultiply());
}
});
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
index f53585093..463d3717d 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
@@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
var vector = source[i, j].ToVector4();
// Values are first premultiplied to prevent darkening of edge pixels
- var mupltiplied = new Vector4(new Vector3(vector.X, vector.Y, vector.Z) * vector.W, vector.W);
+ Vector4 mupltiplied = vector.Premultiply();
sum += mupltiplied * xWeight * yWeight;
}
}
@@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
ref TPixel dest = ref destRow[x];
// Reverse the premultiplication
- dest.PackFromVector4(new Vector4(new Vector3(sum.X, sum.Y, sum.Z) / sum.W, sum.W));
+ dest.PackFromVector4(sum.UnPremultiply());
}
});
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
index d23ee5a06..b0a530514 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
float weight = Unsafe.Add(ref horizontalValues, i);
Vector4 v = Unsafe.Add(ref vecPtr, i);
- result += v * weight;
+ result += v.Premultiply() * weight;
}
return result;
@@ -114,10 +114,10 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
float weight = Unsafe.Add(ref horizontalValues, i);
Vector4 v = Unsafe.Add(ref vecPtr, i);
- result += v.Expand() * weight;
+ result += v.Premultiply().Expand() * weight;
}
- return result;
+ return result.UnPremultiply();
}
///
@@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
result += firstPassPixels[x, index] * yw;
}
- return result;
+ return result.UnPremultiply();
}
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs
index c2d891638..323842556 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs
@@ -3,7 +3,6 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
-using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
@@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
public class DetectEdgesTest : FileTestBase
{
public static readonly string[] CommonTestImages = { TestImages.Png.Bike };
-
+
public static readonly TheoryData DetectEdgesFilters = new TheoryData
{
EdgeDetection.Kayyali,
@@ -39,9 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
{
image.Mutate(x => x.DetectEdges(detector));
image.DebugSave(provider, detector.ToString());
-
- // TODO: Enable once we have updated the images
- // image.CompareToReferenceOutput(provider, detector.ToString());
+ image.CompareToReferenceOutput(provider, detector.ToString());
}
}
@@ -54,9 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
{
image.Mutate(x => x.DetectEdges());
image.DebugSave(provider);
-
- // TODO: Enable once we have updated the images
- // image.CompareToReferenceOutput(provider);
+ image.CompareToReferenceOutput(provider);
}
}
@@ -83,12 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
image.Mutate(x => x.DetectEdges(bounds));
image.DebugSave(provider);
-
- // TODO: Enable once we have updated the images
- //image.CompareToReferenceOutput(provider);
-
- // TODO: We don't need this any longer after switching to ReferenceImages
- //ImageComparer.Tolerant().EnsureProcessorChangesAreConstrained(source, image, bounds);
+ image.CompareToReferenceOutput(provider);
}
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
index 92f1af58e..8370a802c 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
@@ -82,6 +82,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
}
+ [Theory]
+ [WithFile(TestImages.Png.Kaboom, DefaultPixelType)]
+ public void Resize_DoesNotBleedAlphaPixels(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage())
+ {
+ image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2));
+ image.DebugSave(provider);
+ image.CompareToReferenceOutput(provider);
+ }
+ }
+
[Theory]
[WithFile(TestImages.Gif.Giphy, DefaultPixelType)]
public void Resize_IsAppliedToAllFrames(TestImageProvider provider)
@@ -89,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
using (Image image = provider.GetImage())
{
- image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, true));
+ image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, KnownResamplers.NearestNeighbor));
// Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :(
image.DebugSave(provider, extension: Extensions.Gif);
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index c542fa880..864c96332 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -35,6 +35,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png";
public const string SnakeGame = "Png/SnakeGame.png";
public const string Icon = "Png/icon.png";
+ public const string Kaboom = "Png/kaboom.png";
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png";
diff --git a/tests/Images/External b/tests/Images/External
index ddc404592..376605e05 160000
--- a/tests/Images/External
+++ b/tests/Images/External
@@ -1 +1 @@
-Subproject commit ddc4045926bb0331be8c1785d9adf5f76dc4e52e
+Subproject commit 376605e05bb704d425b2d17bf5b310f5376da22e
diff --git a/tests/Images/Input/Png/kaboom.png b/tests/Images/Input/Png/kaboom.png
new file mode 100644
index 000000000..c2abdf465
Binary files /dev/null and b/tests/Images/Input/Png/kaboom.png differ