diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
index 6611af742b..11a7fc6a7a 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
@@ -55,9 +55,11 @@ public static IImageProcessingContext BoxBlur(this IImageProcessingContext sourc
/// The to use when mapping the pixels outside of the border, in Y direction.
///
/// The .
- public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
- {
- var processor = new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY);
- return source.ApplyProcessor(processor, rectangle);
- }
+ public static IImageProcessingContext BoxBlur(
+ this IImageProcessingContext source,
+ int radius,
+ Rectangle rectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs
new file mode 100644
index 0000000000..2980ff44f0
--- /dev/null
+++ b/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Processing.Processors.Convolution;
+
+namespace SixLabors.ImageSharp.Processing.Extensions.Convolution;
+
+///
+/// Defines general convolution extensions to apply on an
+/// using Mutate/Clone.
+///
+public static class ConvolutionExtensions
+{
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The convolution kernel to apply.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix kernelXY)
+ => Convolve(source, kernelXY, false);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix kernelXY, bool preserveAlpha)
+ => Convolve(source, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ /// The .
+ public static IImageProcessingContext Convolve(
+ this IImageProcessingContext source,
+ DenseMatrix kernelXY,
+ bool preserveAlpha,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY));
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The rectangle structure that specifies the portion of the image object to alter.
+ /// The convolution kernel to apply.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix kernelXY)
+ => Convolve(source, rectangle, kernelXY, false);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The rectangle structure that specifies the portion of the image object to alter.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix kernelXY, bool preserveAlpha)
+ => Convolve(source, rectangle, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The rectangle structure that specifies the portion of the image object to alter.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ /// The .
+ public static IImageProcessingContext Convolve(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ DenseMatrix kernelXY,
+ bool preserveAlpha,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY), rectangle);
+}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
index b851482008..64e0be3eac 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
@@ -57,7 +57,7 @@ public static IImageProcessingContext GaussianBlur(this IImageProcessingContext
/// The .
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
- var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY);
+ GaussianBlurProcessor processor = new(sigma, borderWrapModeX, borderWrapModeY);
return source.ApplyProcessor(processor, rectangle);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
index 8a7c424815..8f5ddd1690 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
@@ -62,14 +62,14 @@ protected override void OnFrameApply(ImageFrame source)
source.CopyTo(targetPixels);
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- using (var map = new KernelSamplingMap(allocator))
+ using (KernelSamplingMap map = new(allocator))
{
// Since the kernel sizes are identical we can use a single map.
map.BuildSamplingOffsetMap(this.KernelY, interest);
- var operation = new Convolution2DRowOperation(
+ Convolution2DRowOperation operation = new(
interest,
targetPixels,
source.PixelBuffer,
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index 10780a21e2..cdd8ff8ae6 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
@@ -35,18 +35,48 @@ public Convolution2PassProcessor(
Rectangle sourceRectangle,
BorderWrappingMode borderWrapModeX,
BorderWrappingMode borderWrapModeY)
+ : this(configuration, kernel, kernel, preserveAlpha, source, sourceRectangle, borderWrapModeX, borderWrapModeY)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The 1D convolution kernel. X Direction
+ /// The 1D convolution kernel. Y Direction
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public Convolution2PassProcessor(
+ Configuration configuration,
+ float[] kernelX,
+ float[] kernelY,
+ bool preserveAlpha,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
- this.Kernel = kernel;
+ this.KernelX = kernelX;
+ this.KernelY = kernelY;
this.PreserveAlpha = preserveAlpha;
this.BorderWrapModeX = borderWrapModeX;
this.BorderWrapModeY = borderWrapModeY;
}
///
- /// Gets the convolution kernel.
+ /// Gets the convolution kernel. X direction.
+ ///
+ public float[] KernelX { get; }
+
+ ///
+ /// Gets the convolution kernel. Y direction.
///
- public float[] Kernel { get; }
+ public float[] KernelY { get; }
///
/// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
@@ -68,21 +98,21 @@ protected override void OnFrameApply(ImageFrame source)
{
using Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size);
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
// We can create a single sampling map with the size as if we were using the non separated 2D kernel
// the two 1D kernels represent, and reuse it across both convolution steps, like in the bokeh blur.
- using var mapXY = new KernelSamplingMap(this.Configuration.MemoryAllocator);
+ using KernelSamplingMap mapXY = new(this.Configuration.MemoryAllocator);
- mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
+ mapXY.BuildSamplingOffsetMap(this.KernelX.Length, this.KernelX.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
// Horizontal convolution
- var horizontalOperation = new HorizontalConvolutionRowOperation(
+ HorizontalConvolutionRowOperation horizontalOperation = new(
interest,
firstPassPixels,
source.PixelBuffer,
mapXY,
- this.Kernel,
+ this.KernelX,
this.Configuration,
this.PreserveAlpha);
@@ -92,12 +122,12 @@ protected override void OnFrameApply(ImageFrame source)
in horizontalOperation);
// Vertical convolution
- var verticalOperation = new VerticalConvolutionRowOperation(
+ VerticalConvolutionRowOperation verticalOperation = new(
interest,
source.PixelBuffer,
firstPassPixels,
mapXY,
- this.Kernel,
+ this.KernelY,
this.Configuration,
this.PreserveAlpha);
@@ -140,7 +170,7 @@ public HorizontalConvolutionRowOperation(
}
///
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRequiredBufferLength(Rectangle bounds)
=> 2 * bounds.Width;
@@ -306,7 +336,7 @@ public VerticalConvolutionRowOperation(
}
///
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRequiredBufferLength(Rectangle bounds)
=> 2 * bounds.Width;
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
new file mode 100644
index 0000000000..995a5164d9
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Convolution;
+
+///
+/// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image.
+///
+public class ConvolutionProcessor : IImageProcessor
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The 2d gradient operator.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public ConvolutionProcessor(
+ in DenseMatrix kernelXY,
+ bool preserveAlpha,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ {
+ this.KernelXY = kernelXY;
+ this.PreserveAlpha = preserveAlpha;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
+ }
+
+ ///
+ /// Gets the 2d convolution kernel.
+ ///
+ public DenseMatrix KernelXY { get; }
+
+ ///
+ /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
+ ///
+ public bool PreserveAlpha { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
+ ///
+ public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
+ where TPixel : unmanaged,
+ IPixel
+ {
+ if (this.KernelXY.TryGetLinearlySeparableComponents(out float[]? kernelX, out float[]? kernelY))
+ {
+ return new Convolution2PassProcessor(
+ configuration,
+ kernelX,
+ kernelY,
+ this.PreserveAlpha,
+ source,
+ sourceRectangle,
+ this.BorderWrapModeX,
+ this.BorderWrapModeY);
+ }
+
+ return new ConvolutionProcessor(
+ configuration,
+ this.KernelXY,
+ this.PreserveAlpha,
+ source,
+ sourceRectangle,
+ this.BorderWrapModeX,
+ this.BorderWrapModeY);
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
index ae79f2c31d..9b4659929b 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
@@ -31,10 +31,34 @@ public ConvolutionProcessor(
bool preserveAlpha,
Image source,
Rectangle sourceRectangle)
+ : this(configuration, kernelXY, preserveAlpha, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The 2d gradient operator.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public ConvolutionProcessor(
+ Configuration configuration,
+ in DenseMatrix kernelXY,
+ bool preserveAlpha,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
this.KernelXY = kernelXY;
this.PreserveAlpha = preserveAlpha;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
}
///
@@ -47,6 +71,16 @@ public ConvolutionProcessor(
///
public bool PreserveAlpha { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
protected override void OnFrameApply(ImageFrame source)
{
@@ -55,13 +89,13 @@ protected override void OnFrameApply(ImageFrame source)
source.CopyTo(targetPixels);
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- using (var map = new KernelSamplingMap(allocator))
+ using (KernelSamplingMap map = new(allocator))
{
- map.BuildSamplingOffsetMap(this.KernelXY, interest);
+ map.BuildSamplingOffsetMap(this.KernelXY.Rows, this.KernelXY.Columns, interest, this.BorderWrapModeX, this.BorderWrapModeY);
- var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha);
+ RowOperation operation = new(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@@ -121,7 +155,7 @@ public void Invoke(int y, Span span)
ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span);
Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth);
- var state = new ConvolutionState(in this.kernel, this.map);
+ ConvolutionState state = new(in this.kernel, this.map);
int row = y - this.bounds.Y;
ref int sampleRowBase = ref state.GetSampleRow((uint)row);
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
index ae891f3507..a1fa4db973 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
@@ -58,12 +58,12 @@ protected override void BeforeImageApply()
///
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
// We need a clean copy for each pass to start from
using ImageFrame cleanCopy = source.Clone();
- using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[0], true, this.Source, interest))
+ using (ConvolutionProcessor processor = new(this.Configuration, in this.kernels[0], true, this.Source, interest))
{
processor.Apply(source);
}
@@ -78,12 +78,12 @@ protected override void OnFrameApply(ImageFrame source)
{
using ImageFrame pass = cleanCopy.Clone();
- using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[i], true, this.Source, interest))
+ using (ConvolutionProcessor processor = new(this.Configuration, in this.kernels[i], true, this.Source, interest))
{
processor.Apply(pass);
}
- var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest);
+ RowOperation operation = new(source.PixelBuffer, pass.PixelBuffer, interest);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
index c353f46b5f..3139d24bb4 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
@@ -53,7 +53,7 @@ protected override void BeforeImageApply()
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new ConvolutionProcessor(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle);
+ using ConvolutionProcessor processor = new(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle);
processor.Apply(source);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
index 6518375b9e..d762dd336b 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
@@ -12,24 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution;
internal class GaussianBlurProcessor : ImageProcessor
where TPixel : unmanaged, IPixel
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The configuration which allows altering default behaviour or extending the library.
- /// The defining the processor parameters.
- /// The source for the current processor instance.
- /// The source area to process for the current processor instance.
- public GaussianBlurProcessor(
- Configuration configuration,
- GaussianBlurProcessor definition,
- Image source,
- Rectangle sourceRectangle)
- : base(configuration, source, sourceRectangle)
- {
- int kernelSize = (definition.Radius * 2) + 1;
- this.Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
- }
-
///
/// Initializes a new instance of the class.
///
@@ -72,7 +54,7 @@ public GaussianBlurProcessor(
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
+ using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
index a286201dff..bdb3a4b380 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
@@ -12,22 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution;
internal class GaussianSharpenProcessor : ImageProcessor
where TPixel : unmanaged, IPixel
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The configuration which allows altering default behaviour or extending the library.
- /// The defining the processor parameters.
- /// The source for the current processor instance.
- /// The source area to process for the current processor instance.
- public GaussianSharpenProcessor(
- Configuration configuration,
- GaussianSharpenProcessor definition,
- Image source,
- Rectangle sourceRectangle)
- : this(configuration, definition, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
- {
- }
-
///
/// Initializes a new instance of the class.
///
@@ -70,7 +54,7 @@ public GaussianSharpenProcessor(
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
+ using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionTests.cs
new file mode 100644
index 0000000000..0cb56b732d
--- /dev/null
+++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionTests.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Extensions.Convolution;
+using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
+
+namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution;
+
+[GroupOutput("Convolution")]
+public class ConvolutionTests
+{
+ private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F);
+
+ public static readonly TheoryData> Values = new TheoryData>
+ {
+ // Sharpening kernel.
+ new float[,]
+ {
+ { -1, -1, -1 },
+ { -1, 16, -1 },
+ { -1, -1, -1 }
+ }
+ };
+
+ public static readonly string[] InputImages =
+ [
+ TestImages.Bmp.Car,
+ TestImages.Png.CalliphoraPartial,
+ TestImages.Png.Blur
+ ];
+
+ [Theory]
+ [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)]
+ public void OnFullImage(TestImageProvider provider, DenseMatrix value)
+ where TPixel : unmanaged, IPixel
+ => provider.RunValidatingProcessorTest(
+ x => x.Convolve(value),
+ string.Join('_', value.Data),
+ ValidatorComparer);
+
+ [Theory]
+ [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)]
+ public void InBox(TestImageProvider provider, DenseMatrix value)
+ where TPixel : unmanaged, IPixel
+ => provider.RunRectangleConstrainedValidatingProcessorTest(
+ (x, rect) => x.Convolve(rect, value),
+ string.Join('_', value.Data),
+ ValidatorComparer);
+}
diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png
new file mode 100644
index 0000000000..ffa8e7cd8a
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b311f117167e17b4611d525aef57146a6757db08f2a36fa093f45f237df9e1a2
+size 326809
diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png
new file mode 100644
index 0000000000..682fd8b49b
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0eabd5f7dd2f258d04d3f1db8ee4d958754541e0009ae183d6e512f408e3201
+size 249497
diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png
new file mode 100644
index 0000000000..55d592a60f
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:048b4a1679852d34d6f74f01b750461ec9db2d3e7e3aa3d2d89d48e5725aa560
+size 142367
diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png
new file mode 100644
index 0000000000..f01496ad76
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6f63d117433a93e30b9b41f68f676bb53eb1761f3c665f178ef07ec2c9626c3
+size 338527
diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png
new file mode 100644
index 0000000000..99e9737b51
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d72a3994fc6dcc461da3af5176d5bf62c95b7abeffcbf5547172377db3b0d9ac
+size 287158
diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png
new file mode 100644
index 0000000000..ecc09e70ff
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b3fd7906f11e87a2b0043cde179317f53e9a2bf02d9e64763be8f9923d60f057
+size 257492