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/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs
index 06cfdf560..69dff78c1 100644
--- a/src/ImageSharp/Memory/Buffer2D{T}.cs
+++ b/src/ImageSharp/Memory/Buffer2D{T}.cs
@@ -69,23 +69,6 @@ namespace SixLabors.ImageSharp.Memory
}
}
- ///
- /// Creates a new instance that maps to a target rows interval from the current instance.
- ///
- /// The target vertical offset for the rows interval to retrieve.
- /// The desired number of rows to extract.
- /// The new instance with the requested rows interval.
- public Buffer2D Slice(int y, int h)
- {
- DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y));
- DebugGuard.MustBeGreaterThan(h, 0, nameof(h));
- DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h));
-
- Memory slice = this.GetMemory().Slice(y * this.Width, h * this.Width);
- var memory = new MemorySource(slice);
- return new Buffer2D(memory, this.Width, h);
- }
-
///
/// Disposes the instance
///
diff --git a/src/ImageSharp/Processing/BokehBlurExecutionMode.cs b/src/ImageSharp/Processing/BokehBlurExecutionMode.cs
deleted file mode 100644
index bc44dca03..000000000
--- a/src/ImageSharp/Processing/BokehBlurExecutionMode.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using SixLabors.ImageSharp.Processing.Processors.Convolution;
-
-namespace SixLabors.ImageSharp.Processing
-{
- ///
- /// An that indicates execution options for the .
- ///
- public enum BokehBlurExecutionMode
- {
- ///
- /// Indicates that the maximum performance should be prioritized over memory usage.
- ///
- PreferMaximumPerformance,
-
- ///
- /// Indicates that the memory usage should be prioritized over raw performance.
- ///
- PreferLowMemoryUsage
- }
-}
diff --git a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs
index ef20f940a..2bbdd03b0 100644
--- a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs
@@ -19,15 +19,6 @@ namespace SixLabors.ImageSharp.Processing
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BokehBlurProcessor());
- ///
- /// Applies a bokeh blur to the image.
- ///
- /// The image this method extends.
- /// The execution mode to use when applying the processor.
- /// The to allow chaining of operations.
- public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, BokehBlurExecutionMode executionMode)
- => source.ApplyProcessor(new BokehBlurProcessor(executionMode));
-
///
/// Applies a bokeh blur to the image.
///
@@ -39,18 +30,6 @@ namespace SixLabors.ImageSharp.Processing
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma));
- ///
- /// Applies a bokeh blur to the image.
- ///
- /// The image this method extends.
- /// The 'radius' value representing the size of the area to sample.
- /// The 'components' value representing the number of kernels to use to approximate the bokeh effect.
- /// The gamma highlight factor to use to emphasize bright spots in the source image
- /// The execution mode to use when applying the processor.
- /// The to allow chaining of operations.
- public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode)
- => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode));
-
///
/// Applies a bokeh blur to the image.
///
@@ -62,18 +41,6 @@ namespace SixLabors.ImageSharp.Processing
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(), rectangle);
- ///
- /// Applies a bokeh blur to the image.
- ///
- /// The image this method extends.
- ///
- /// The structure that specifies the portion of the image object to alter.
- ///
- /// The execution mode to use when applying the processor.
- /// The to allow chaining of operations.
- public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle, BokehBlurExecutionMode executionMode)
- => source.ApplyProcessor(new BokehBlurProcessor(executionMode), rectangle);
-
///
/// Applies a bokeh blur to the image.
///
@@ -87,20 +54,5 @@ namespace SixLabors.ImageSharp.Processing
/// The to allow chaining of operations.
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle);
-
- ///
- /// Applies a bokeh blur to the image.
- ///
- /// The image this method extends.
- /// The 'radius' value representing the size of the area to sample.
- /// The 'components' value representing the number of kernels to use to approximate the bokeh effect.
- /// The gamma highlight factor to use to emphasize bright spots in the source image
- /// The execution mode to use when applying the processor.
- ///
- /// The structure that specifies the portion of the image object to alter.
- ///
- /// The to allow chaining of operations.
- public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode, Rectangle rectangle)
- => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode), rectangle);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
index b7e102deb..1812884b8 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
@@ -26,27 +26,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public const float DefaultGamma = 3F;
- ///
- /// The default execution mode used by the parameterless constructor.
- ///
- public const BokehBlurExecutionMode DefaultExecutionMode = BokehBlurExecutionMode.PreferLowMemoryUsage;
-
///
/// Initializes a new instance of the class.
///
public BokehBlurProcessor()
- : this(DefaultRadius, DefaultComponents, DefaultGamma, DefaultExecutionMode)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The execution mode to use when applying the processor.
- ///
- public BokehBlurProcessor(BokehBlurExecutionMode executionMode)
- : this(DefaultRadius, DefaultComponents, DefaultGamma, executionMode)
+ : this(DefaultRadius, DefaultComponents, DefaultGamma)
{
}
@@ -63,33 +47,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The gamma highlight factor to use to further process the image.
///
public BokehBlurProcessor(int radius, int components, float gamma)
- : this(radius, components, gamma, DefaultExecutionMode)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The 'radius' value representing the size of the area to sample.
- ///
- ///
- /// The number of components to use to approximate the original 2D bokeh blur convolution kernel.
- ///
- ///
- /// The gamma highlight factor to use to further process the image.
- ///
- ///
- /// The execution mode to use when applying the processor.
- ///
- public BokehBlurProcessor(int radius, int components, float gamma, BokehBlurExecutionMode executionMode)
{
Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma));
this.Radius = radius;
this.Components = components;
this.Gamma = gamma;
- this.ExecutionMode = executionMode;
}
///
@@ -107,11 +70,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public float Gamma { get; }
- ///
- /// Gets the execution mode to use when applying the effect.
- ///
- public BokehBlurExecutionMode ExecutionMode { get; }
-
///
public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle)
where TPixel : struct, IPixel
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
index f8fb3f796..efd18dafb 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
@@ -36,11 +36,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
private readonly float gamma;
- ///
- /// The execution mode to use when applying the effect
- ///
- private readonly BokehBlurExecutionMode executionMode;
-
///
/// The maximum size of the kernel in either direction
///
@@ -84,7 +79,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.kernelSize = (this.radius * 2) + 1;
this.componentsCount = definition.Components;
this.gamma = definition.Gamma;
- this.executionMode = definition.ExecutionMode;
// Reuse the initialized values from the cache, if possible
var parameters = new BokehBlurParameters(this.radius, this.componentsCount);
@@ -280,30 +274,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.ApplyGammaExposure(source.PixelBuffer, this.SourceRectangle, this.Configuration);
// 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 processingBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean))
{
- 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, processingBuffer);
// Apply the inverse gamma exposure pass, and write the final pixel data
- this.ApplyInverseGammaExposure(source.PixelBuffer, processing, this.SourceRectangle, this.Configuration);
+ this.ApplyInverseGammaExposure(source.PixelBuffer, processingBuffer, this.SourceRectangle, this.Configuration);
}
}
@@ -314,29 +291,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The structure that specifies the portion of the image object to draw.
/// 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 processingBuffer)
{
- // Perform two 1D convolutions for each component in the current instance
- ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan());
- for (int i = 0; i < this.kernels.Length; i++)
+ using (Buffer2D firstPassBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size()))
{
- // 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);
+ // Perform two 1D convolutions for each component in the current instance
+ ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan());
+ ref Vector4 paramsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan());
+ for (int i = 0; i < this.kernels.Length; i++)
+ {
+ // Compute the resulting complex buffer for the current component
+ var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
+ Complex64[] kernel = Unsafe.Add(ref baseRef, i);
+ Vector4 parameters = Unsafe.Add(ref paramsRef, i);
+
+ // 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 +365,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;
@@ -413,12 +391,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
int width = workingRectangle.Width;
- if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage)
- {
- configuration = configuration.Clone();
- configuration.MaxDegreeOfParallelism = 1;
- }
-
ParallelHelper.IterateRows(
workingRectangle,
configuration,
@@ -426,11 +398,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 +508,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);
- }
- }
- });
- }
}
}