diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
index 33b80688ca..95e5107c76 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
@@ -3,6 +3,7 @@
using System;
using System.Numerics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@@ -50,76 +51,109 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
protected override void OnFrameApply(ImageFrame source)
{
- DenseMatrix matrix = this.KernelXY;
- bool preserveAlpha = this.PreserveAlpha;
+ using Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size());
+
+ source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- int startY = interest.Y;
- int endY = interest.Bottom;
- int startX = interest.X;
- int endX = interest.Right;
- int maxY = endY - 1;
- int maxX = endX - 1;
-
- using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size()))
+
+ ParallelRowIterator.IterateRows(
+ interest,
+ this.Configuration,
+ new RowIntervalAction(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha));
+
+ Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels);
+ }
+
+ ///
+ /// A implementing the convolution logic for .
+ ///
+ private readonly struct RowIntervalAction : IRowIntervalAction
+ {
+ private readonly Rectangle bounds;
+ private readonly Buffer2D targetPixels;
+ private readonly Buffer2D sourcePixels;
+ private readonly DenseMatrix kernel;
+ private readonly Configuration configuration;
+ private readonly bool preserveAlpha;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The target processing bounds for the current instance.
+ /// The target pixel buffer to adjust.
+ /// The source pixels. Cannot be null.
+ /// The kernel operator.
+ /// The
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public RowIntervalAction(
+ Rectangle bounds,
+ Buffer2D targetPixels,
+ Buffer2D sourcePixels,
+ DenseMatrix kernel,
+ Configuration configuration,
+ bool preserveAlpha)
+ {
+ this.bounds = bounds;
+ this.targetPixels = targetPixels;
+ this.sourcePixels = sourcePixels;
+ this.kernel = kernel;
+ this.configuration = configuration;
+ this.preserveAlpha = preserveAlpha;
+ }
+
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void Invoke(in RowInterval rows, Memory memory)
{
- source.CopyTo(targetPixels);
+ Span vectorSpan = memory.Span;
+ int length = vectorSpan.Length;
+ ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
- var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
- int width = workingRectangle.Width;
+ int maxY = this.bounds.Bottom - 1;
+ int maxX = this.bounds.Right - 1;
- ParallelRowIterator.IterateRows(
- workingRectangle,
- this.Configuration,
- (rows, vectorBuffer) =>
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
+ PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan);
+
+ if (this.preserveAlpha)
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
+ {
+ DenseMatrixUtils.Convolve3(
+ in this.kernel,
+ this.sourcePixels,
+ ref vectorSpanRef,
+ y,
+ x,
+ this.bounds.Y,
+ maxY,
+ this.bounds.X,
+ maxX);
+ }
+ }
+ else
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
{
- Span vectorSpan = vectorBuffer.Span;
- int length = vectorSpan.Length;
- ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
-
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX);
- PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan.Slice(0, length), vectorSpan);
-
- if (preserveAlpha)
- {
- for (int x = 0; x < width; x++)
- {
- DenseMatrixUtils.Convolve3(
- in matrix,
- source.PixelBuffer,
- ref vectorSpanRef,
- y,
- x,
- startY,
- maxY,
- startX,
- maxX);
- }
- }
- else
- {
- for (int x = 0; x < width; x++)
- {
- DenseMatrixUtils.Convolve4(
- in matrix,
- source.PixelBuffer,
- ref vectorSpanRef,
- y,
- x,
- startY,
- maxY,
- startX,
- maxX);
- }
- }
-
- PixelOperations.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan);
- }
- });
-
- Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels);
+ DenseMatrixUtils.Convolve4(
+ in this.kernel,
+ this.sourcePixels,
+ ref vectorSpanRef,
+ y,
+ x,
+ this.bounds.Y,
+ maxY,
+ this.bounds.X,
+ maxX);
+ }
+ }
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan);
+ }
}
}
}