diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
index def1897530..215e0d7298 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
@@ -434,7 +434,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
///
- /// A implementing the convolution logic for .
+ /// A implementing the gamma exposure logic for .
///
private readonly struct ApplyGammaExposureRowIntervalAction : IRowIntervalAction
{
@@ -490,7 +490,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
///
- /// A implementing the convolution logic for .
+ /// A implementing the inverse gamma exposure logic for .
///
private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction
{
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index 3f12b2a283..65a0ace369 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{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;
@@ -58,95 +59,113 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
protected override void OnFrameApply(ImageFrame source)
{
- using (Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size()))
- {
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, this.Configuration);
- this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, this.Configuration);
- }
+ using Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size());
+
+ var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+
+ // Horizontal convolution
+ ParallelRowIterator.IterateRows(
+ interest,
+ this.Configuration,
+ new RowIntervalAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha));
+
+ // Vertical convolution
+ ParallelRowIterator.IterateRows(
+ interest,
+ this.Configuration,
+ new RowIntervalAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha));
}
///
- /// Applies the process to the specified portion of the specified at the specified location
- /// and with the specified size.
+ /// A implementing the convolution logic for .
///
- /// The target pixels to apply the process to.
- /// The source pixels. Cannot be null.
- ///
- /// The structure that specifies the portion of the image object to draw.
- ///
- /// The kernel operator.
- /// The
- private void ApplyConvolution(
- Buffer2D targetPixels,
- Buffer2D sourcePixels,
- Rectangle sourceRectangle,
- in DenseMatrix kernel,
- Configuration configuration)
+ private readonly struct RowIntervalAction : IRowIntervalAction
{
- DenseMatrix matrix = kernel;
- bool preserveAlpha = this.PreserveAlpha;
-
- int startY = sourceRectangle.Y;
- int endY = sourceRectangle.Bottom;
- int startX = sourceRectangle.X;
- int endX = sourceRectangle.Right;
- int maxY = endY - 1;
- int maxX = endX - 1;
-
- var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
- int width = workingRectangle.Width;
-
- ParallelRowIterator.IterateRows(
- workingRectangle,
- configuration,
- (rows, vectorBuffer) =>
- {
- Span vectorSpan = vectorBuffer.Span;
- int length = vectorSpan.Length;
- ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
+ private readonly Rectangle bounds;
+ private readonly Buffer2D targetPixels;
+ private readonly Buffer2D sourcePixels;
+ private readonly DenseMatrix kernel;
+ private readonly Configuration configuration;
+ private readonly bool preserveAlpha;
- for (int y = rows.Min; y < rows.Max; y++)
+ ///
+ /// 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)
+ {
+ Span vectorSpan = memory.Span;
+ int length = vectorSpan.Length;
+ ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
+
+ int maxY = this.bounds.Bottom - 1;
+ int maxX = this.bounds.Right - 1;
+
+ 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++)
{
- Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX);
- PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan);
-
- if (preserveAlpha)
- {
- for (int x = 0; x < width; x++)
- {
- DenseMatrixUtils.Convolve3(
- in matrix,
- sourcePixels,
- ref vectorSpanRef,
- y,
- x,
- startY,
- maxY,
- startX,
- maxX);
- }
- }
- else
- {
- for (int x = 0; x < width; x++)
- {
- DenseMatrixUtils.Convolve4(
- in matrix,
- sourcePixels,
- ref vectorSpanRef,
- y,
- x,
- startY,
- maxY,
- startX,
- maxX);
- }
- }
-
- PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
+ 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++)
+ {
+ 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);
+ }
+ }
}
}
}