diff --git a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
index 0c22aa68f..b678e798f 100644
--- a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
+++ b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp
}
///
- /// Computes the sum of vectors in weighted by the kernel weight values.
+ /// Computes the sum of vectors in weighted by the kernel weight values and accumulates the partial results.
///
/// The 1D convolution kernel.
/// The source frame.
@@ -73,16 +74,20 @@ namespace SixLabors.ImageSharp
/// The maximum working area row.
/// The minimum working area column.
/// The maximum working area column.
- public static void Convolve4(
+ /// The weight factor for the real component of the complex pixel values.
+ /// The weight factor for the imaginary component of the complex pixel values.
+ public static void Convolve4AndAccumulatePartials(
Span kernel,
Buffer2D sourceValues,
- Span targetRow,
+ Span targetRow,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
- int maxColumn)
+ int maxColumn,
+ float z,
+ float w)
{
ComplexVector4 vector = default;
int kernelLength = kernel.Length;
@@ -99,7 +104,7 @@ namespace SixLabors.ImageSharp
vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX));
}
- targetRow[column] = vector;
+ targetRow[column] += vector.WeightedSum(z, w);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
index f8fb3f796..9c784099c 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
@@ -281,26 +281,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Create a 0-filled buffer to use to store the result of the component convolutions
using (Buffer2D processing = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean))
+ using (Buffer2D firstPassValues = this.Configuration.MemoryAllocator.Allocate2D(source.Size()))
{
- if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage)
- {
- // Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode
- using (Buffer2D buffer = this.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius))
- using (Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height))
- using (Buffer2D secondPassBuffer = buffer.Slice(0, source.Height))
- {
- this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassBuffer, secondPassBuffer);
- }
- }
- else
- {
- // Performance priority: allocate two independent buffers and execute both convolutions in parallel mode
- using (Buffer2D firstPassValues = this.Configuration.MemoryAllocator.Allocate2D(source.Size()))
- using (Buffer2D secondPassBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size()))
- {
- this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues, secondPassBuffer);
- }
- }
+ // Perform the 1D convolutions on all the kernel components and accumulate the results
+ this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues);
// Apply the inverse gamma exposure pass, and write the final pixel data
this.ApplyInverseGammaExposure(source.PixelBuffer, processing, this.SourceRectangle, this.Configuration);
@@ -315,14 +299,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The configuration.
/// The buffer with the raw pixel data to use to aggregate the results of each convolution.
/// The complex buffer to use for the first 1D convolution pass for each kernel.
- /// The complex buffer to use for the second 1D convolution pass for each kernel.
private void OnFrameApplyCore(
ImageFrame source,
Rectangle sourceRectangle,
Configuration configuration,
Buffer2D processingBuffer,
- Buffer2D firstPassBuffer,
- Buffer2D secondPassBuffer)
+ Buffer2D firstPassBuffer)
{
// Perform two 1D convolutions for each component in the current instance
ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan());
@@ -331,12 +313,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Compute the resulting complex buffer for the current component
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
Complex64[] kernel = Unsafe.Add(ref baseRef, i);
- this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration);
- this.ApplyConvolution(secondPassBuffer, firstPassBuffer, interest, kernel, configuration);
-
- // Add the results of the convolution with the current kernel
Vector4 parameters = this.kernelParameters[i];
- this.SumProcessingPartials(processingBuffer, secondPassBuffer, sourceRectangle, configuration, parameters.Z, parameters.W);
+
+ // Compute the two 1D convolutions and accumulate the partial results on the target buffer
+ this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration);
+ this.ApplyConvolution(processingBuffer, firstPassBuffer, interest, kernel, configuration, parameters.Z, parameters.W);
}
}
@@ -389,19 +370,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Applies the process to the specified portion of the specified buffer at the specified location
/// and with the specified size.
///
- /// The target values to use to store the results.
+ /// The target values to use to store the results.
/// The source complex values. Cannot be null.
- ///
- /// The structure that specifies the portion of the image object to draw.
- ///
+ /// The structure that specifies the portion of the image object to draw.
/// The 1D kernel.
/// The
+ /// The weight factor for the real component of the complex pixel values.
+ /// The weight factor for the imaginary component of the complex pixel values.
private void ApplyConvolution(
- Buffer2D targetValues,
+ Buffer2D targetValues,
Buffer2D sourceValues,
Rectangle sourceRectangle,
Complex64[] kernel,
- Configuration configuration)
+ Configuration configuration,
+ float z,
+ float w)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@@ -426,11 +409,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
for (int y = rows.Min; y < rows.Max; y++)
{
- Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX);
+ Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX);
for (int x = 0; x < width; x++)
{
- Buffer2DUtils.Convolve4(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX);
+ Buffer2DUtils.Convolve4AndAccumulatePartials(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX, z, w);
}
}
});
@@ -536,53 +519,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
});
}
-
- ///
- /// Applies the process to the specified portion of the specified at the specified location
- /// and with the specified size.
- ///
- /// The target instance to use to store the results.
- /// The source complex pixels. Cannot be null.
- ///
- /// The structure that specifies the portion of the image object to draw.
- ///
- /// The
- /// The weight factor for the real component of the complex pixel values.
- /// The weight factor for the imaginary component of the complex pixel values.
- private void SumProcessingPartials(
- Buffer2D targetValues,
- Buffer2D sourceValues,
- Rectangle sourceRectangle,
- Configuration configuration,
- float z,
- float w)
- {
- int startY = sourceRectangle.Y;
- int endY = sourceRectangle.Bottom;
- int startX = sourceRectangle.X;
- int endX = sourceRectangle.Right;
-
- var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
- int width = workingRectangle.Width;
-
- ParallelHelper.IterateRows(
- workingRectangle,
- configuration,
- rows =>
- {
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX);
- Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX);
- ref Vector4 baseTargetRef = ref MemoryMarshal.GetReference(targetRowSpan);
- ref ComplexVector4 baseSourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
-
- for (int x = 0; x < width; x++)
- {
- Unsafe.Add(ref baseTargetRef, x) += Unsafe.Add(ref baseSourceRef, x).WeightedSum(z, w);
- }
- }
- });
- }
}
}