diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs
index beef99c1c..d7939478b 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs
@@ -21,15 +21,15 @@ namespace SixLabors.ImageSharp.Advanced
/// Iterate through the rows of a rectangle in optimized batches defined by -s.
///
/// The type of row operation to perform.
- /// The .
/// The to get the parallel settings from.
- /// The method body defining the iteration logic on a single .
+ /// The .
+ /// The operation defining the iteration logic on a single .
[MethodImpl(InliningOptions.ShortMethod)]
- public static void IterateRows(Rectangle rectangle, Configuration configuration, in T body)
+ public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
- IterateRows(rectangle, in parallelSettings, in body);
+ IterateRows(rectangle, in parallelSettings, in operation);
}
///
@@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Advanced
/// The type of row operation to perform.
/// The .
/// The .
- /// The method body defining the iteration logic on a single .
+ /// The operation defining the iteration logic on a single .
public static void IterateRows(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
@@ -81,10 +81,10 @@ namespace SixLabors.ImageSharp.Advanced
///
/// The type of row operation to perform.
/// The type of buffer elements.
- /// The .
/// The to get the parallel settings from.
- /// The method body defining the iteration logic on a single .
- public static void IterateRows(Rectangle rectangle, Configuration configuration, in T operation)
+ /// The .
+ /// The operation defining the iteration logic on a single .
+ public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
where TBuffer : unmanaged
{
@@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Advanced
/// The type of buffer elements.
/// The .
/// The .
- /// The method body defining the iteration logic on a single .
+ /// The operation defining the iteration logic on a single .
public static void IterateRows(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
@@ -143,118 +143,6 @@ namespace SixLabors.ImageSharp.Advanced
rowOperation.Invoke);
}
- ///
- /// Iterate through the rows of a rectangle in optimized batches defined by -s.
- ///
- /// The .
- /// The to get the parallel settings from.
- /// The method body defining the iteration logic on a single .
- internal static void IterateRows(Rectangle rectangle, Configuration configuration, Action body)
- {
- var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
- IterateRows(rectangle, in parallelSettings, body);
- }
-
- ///
- /// Iterate through the rows of a rectangle in optimized batches defined by -s.
- ///
- /// The .
- /// The .
- /// The method body defining the iteration logic on a single .
- internal static void IterateRows(
- Rectangle rectangle,
- in ParallelExecutionSettings parallelSettings,
- Action body)
- {
- ValidateRectangle(rectangle);
-
- int top = rectangle.Top;
- int bottom = rectangle.Bottom;
- int width = rectangle.Width;
- int height = rectangle.Height;
-
- int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
- int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
-
- // Avoid TPL overhead in this trivial case:
- if (numOfSteps == 1)
- {
- var rows = new RowInterval(top, bottom);
- body(rows);
- return;
- }
-
- int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
- var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
- var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep);
- var rowAction = new WrappingRowIntervalOperation(in rowInfo, body);
-
- Parallel.For(
- 0,
- numOfSteps,
- parallelOptions,
- rowAction.Invoke);
- }
-
- ///
- /// Iterate through the rows of a rectangle in optimized batches defined by -s
- /// instantiating a temporary buffer for each invocation.
- ///
- internal static void IterateRows(
- Rectangle rectangle,
- Configuration configuration,
- Action> body)
- where TBuffer : unmanaged
- {
- var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
- IterateRows(rectangle, in parallelSettings, body);
- }
-
- ///
- /// Iterate through the rows of a rectangle in optimized batches defined by -s
- /// instantiating a temporary buffer for each invocation.
- ///
- internal static void IterateRows(
- Rectangle rectangle,
- in ParallelExecutionSettings parallelSettings,
- Action> body)
- where TBuffer : unmanaged
- {
- ValidateRectangle(rectangle);
-
- int top = rectangle.Top;
- int bottom = rectangle.Bottom;
- int width = rectangle.Width;
- int height = rectangle.Height;
-
- int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
- int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
- MemoryAllocator allocator = parallelSettings.MemoryAllocator;
-
- // Avoid TPL overhead in this trivial case:
- if (numOfSteps == 1)
- {
- var rows = new RowInterval(top, bottom);
- using (IMemoryOwner buffer = allocator.Allocate(width))
- {
- body(rows, buffer.Memory);
- }
-
- return;
- }
-
- int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
- var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
- var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep, width);
- var rowAction = new WrappingRowIntervalBufferOperation(in rowInfo, allocator, body);
-
- Parallel.For(
- 0,
- numOfSteps,
- parallelOptions,
- rowAction.Invoke);
- }
-
[MethodImpl(InliningOptions.ShortMethod)]
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor);
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index e3dbad505..a2de8d671 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -259,11 +259,12 @@ namespace SixLabors.ImageSharp
}
var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone());
+ var operation = new RowIntervalOperation(this, target, configuration);
ParallelRowIterator.IterateRows(
- this.Bounds(),
configuration,
- new RowIntervalOperation(this, target, configuration));
+ this.Bounds(),
+ in operation);
return target;
}
diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
index 129edbb03..52be6abe2 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
@@ -50,11 +50,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
bool isAlphaOnly = typeof(TPixel) == typeof(A8);
var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);
-
+ var operation = new RowIntervalOperation(source, upper, lower, threshold, startX, endX, isAlphaOnly);
ParallelRowIterator.IterateRows(
- workingRect,
configuration,
- new RowIntervalOperation(source, upper, lower, threshold, startX, endX, isAlphaOnly));
+ workingRect,
+ in operation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
index 3fb62ed19..16acc2407 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
@@ -268,10 +268,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
protected override void OnFrameApply(ImageFrame source)
{
// Preliminary gamma highlight pass
+ var gammaOperation = new ApplyGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
ParallelRowIterator.IterateRows(
- this.SourceRectangle,
this.Configuration,
- new ApplyGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma));
+ this.SourceRectangle,
+ in gammaOperation);
// Create a 0-filled buffer to use to store the result of the component convolutions
using Buffer2D processingBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean);
@@ -282,10 +283,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float inverseGamma = 1 / this.gamma;
// Apply the inverse gamma exposure pass, and write the final pixel data
+ var operation = new ApplyInverseGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma);
ParallelRowIterator.IterateRows(
- this.SourceRectangle,
this.Configuration,
- new ApplyInverseGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma));
+ this.SourceRectangle,
+ in operation);
}
///
@@ -314,16 +316,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Vector4 parameters = Unsafe.Add(ref paramsRef, i);
// Compute the vertical 1D convolution
+ var verticalOperation = new ApplyVerticalConvolutionRowIntervalOperation(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel);
ParallelRowIterator.IterateRows(
- sourceRectangle,
configuration,
- new ApplyVerticalConvolutionRowIntervalOperation(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel));
+ sourceRectangle,
+ in verticalOperation);
// Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer
+ var horizontalOperation = new ApplyHorizontalConvolutionRowIntervalOperation(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
ParallelRowIterator.IterateRows(
- sourceRectangle,
configuration,
- new ApplyHorizontalConvolutionRowIntervalOperation(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W));
+ sourceRectangle,
+ in horizontalOperation);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
index 0340482fe..d8179c6d5 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
@@ -65,11 +65,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ var operation = new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows(
- interest,
this.Configuration,
- new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha));
+ interest,
+ in operation);
Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index 3cbbf8c46..fb477e2d6 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
@@ -64,16 +64,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
// Horizontal convolution
+ var horizontalOperation = new RowIntervalOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows(
- interest,
this.Configuration,
- new RowIntervalOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha));
+ interest,
+ in horizontalOperation);
// Vertical convolution
+ var verticalOperation = new RowIntervalOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows(
- interest,
this.Configuration,
- new RowIntervalOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha));
+ interest,
+ in verticalOperation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
index 2774a2f88..feb27ac62 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
@@ -56,11 +56,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
-
+ var operation = new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows(
- interest,
this.Configuration,
- new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha));
+ interest,
+ in operation);
Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
index f390ee1ff..fde669ea8 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
@@ -102,10 +102,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
processor.Apply(pass);
}
+ var operation = new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX);
ParallelRowIterator.IterateRows(
- Rectangle.FromLTRB(minX, minY, maxX, maxY),
this.Configuration,
- new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX));
+ Rectangle.FromLTRB(minX, minY, maxX, maxY),
+ in operation);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
index 88fa6bec3..c1ce30cae 100644
--- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
+++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
@@ -99,10 +99,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
"Cannot draw image because the source image does not overlap the target image.");
}
+ var operation = new RowIntervalOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity);
ParallelRowIterator.IterateRows(
- workingRect,
configuration,
- new RowIntervalOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity));
+ workingRect,
+ in operation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
index 1c3be4e63..50c0a22d3 100644
--- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
@@ -47,10 +47,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
source.CopyTo(targetPixels);
+ var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels);
ParallelRowIterator.IterateRows(
- this.SourceRectangle,
this.Configuration,
- new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels));
+ this.SourceRectangle,
+ in operation);
Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}
diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
index 3f8dcd8d9..a22af8ce2 100644
--- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
@@ -50,11 +50,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
protected override void OnFrameApply(ImageFrame source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ var operation = new RowIntervalOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate);
ParallelRowIterator.IterateRows(
- interest,
this.Configuration,
- new RowIntervalOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate));
+ interest,
+ in operation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
index 159a1f981..cae8b14b8 100644
--- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
@@ -36,11 +36,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
protected override void OnFrameApply(ImageFrame source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ var operation = new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration);
ParallelRowIterator.IterateRows(
- interest,
this.Configuration,
- new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration));
+ interest,
+ in operation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
index aa97e3a7e..eb666a4f1 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
@@ -80,10 +80,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart += tileHeight;
}
+ var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source);
ParallelRowIterator.IterateRows(
- new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count),
this.Configuration,
- new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source));
+ new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count),
+ in operation);
ref TPixel pixelsBase = ref source.GetPixelReference(0, 0);
@@ -510,7 +511,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor)
{
- var rowOperation = new RowIntervalOperation(
+ var operation = new RowIntervalOperation(
processor,
this.memoryAllocator,
this.cdfMinBuffer2D,
@@ -522,9 +523,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
source);
ParallelRowIterator.IterateRows(
- new Rectangle(0, 0, this.sourceWidth, this.tileYStartPositions.Count),
this.configuration,
- in rowOperation);
+ new Rectangle(0, 0, this.sourceWidth, this.tileYStartPositions.Count),
+ in operation);
}
[MethodImpl(InliningOptions.ShortMethod)]
diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
index 38da9c8d4..d7ea80737 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
@@ -52,10 +52,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels
+ var grayscaleOperation = new GrayscaleLevelsRowIntervalOperation(workingRect, histogramBuffer, source, this.LuminanceLevels);
ParallelRowIterator.IterateRows(
- workingRect,
this.Configuration,
- new GrayscaleLevelsRowIntervalOperation(workingRect, histogramBuffer, source, this.LuminanceLevels));
+ workingRect,
+ in grayscaleOperation);
Span histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled)
@@ -74,10 +75,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image
+ var cdfOperation = new CdfApplicationRowIntervalOperation(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
- workingRect,
this.Configuration,
- new CdfApplicationRowIntervalOperation(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin));
+ workingRect,
+ in cdfOperation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
index f26e30e52..b796016d1 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
@@ -49,10 +49,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender blender = PixelOperations.Instance.GetPixelBlender(graphicsOptions);
+ var operation = new RowIntervalOperation(configuration, interest, blender, amount, colors, source);
ParallelRowIterator.IterateRows(
- interest,
configuration,
- new RowIntervalOperation(configuration, interest, blender, amount, colors, source));
+ interest,
+ in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation
diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
index ccb2eed60..83d9bd1ef 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
@@ -55,10 +55,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner rowColors = allocator.Allocate(interest.Width);
rowColors.GetSpan().Fill(glowColor);
+ var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
ParallelRowIterator.IterateRows(
- interest,
configuration,
- new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source));
+ interest,
+ in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation
diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
index fd842766d..b36e6b534 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
@@ -63,10 +63,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner rowColors = allocator.Allocate(interest.Width);
rowColors.GetSpan().Fill(vignetteColor);
+ var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
ParallelRowIterator.IterateRows(
- interest,
configuration,
- new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source));
+ interest,
+ in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
index 85ac8dba5..2b579541c 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
@@ -58,20 +58,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.resampler is NearestNeighborResampler)
{
+ var nnOperation = new NearestNeighborRowIntervalOperation(this.SourceRectangle, ref matrix, width, source, destination);
ParallelRowIterator.IterateRows(
- targetBounds,
configuration,
- new NearestNeighborRowIntervalOperation(this.SourceRectangle, ref matrix, width, source, destination));
+ targetBounds,
+ in nnOperation);
return;
}
using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler);
+ var operation = new RowIntervalOperation(configuration, kernelMap, ref matrix, width, source, destination);
ParallelRowIterator.IterateRows(
- targetBounds,
configuration,
- new RowIntervalOperation(configuration, kernelMap, ref matrix, width, source, destination));
+ targetBounds,
+ in operation);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
index 58be1ef0c..877fa074e 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
@@ -76,10 +76,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The configuration.
private void FlipY(ImageFrame source, Configuration configuration)
{
+ var operation = new RowIntervalOperation(source);
ParallelRowIterator.IterateRows(
- source.Bounds(),
configuration,
- new RowIntervalOperation(source));
+ source.Bounds(),
+ in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
index 4e5b87b84..3969a8c3e 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
@@ -60,20 +60,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
Rectangle sourceBounds = this.SourceRectangle;
+ var nnOperation = new NearestNeighborRowIntervalOperation(ref sourceBounds, ref matrix, width, source, destination);
ParallelRowIterator.IterateRows(
- targetBounds,
configuration,
- new NearestNeighborRowIntervalOperation(ref sourceBounds, ref matrix, width, source, destination));
+ targetBounds,
+ in nnOperation);
return;
}
using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler);
+ var operation = new RowIntervalOperation(configuration, kernelMap, ref matrix, width, source, destination);
ParallelRowIterator.IterateRows(
- targetBounds,
configuration,
- new RowIntervalOperation(configuration, kernelMap, ref matrix, width, source, destination));
+ targetBounds,
+ in operation);
}
private readonly struct NearestNeighborRowIntervalOperation : IRowIntervalOperation
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
index 27a2cca51..53810a5cc 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
@@ -95,10 +95,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height;
+ var operation = new RowIntervalOperation(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination);
ParallelRowIterator.IterateRows(
- interest,
configuration,
- new RowIntervalOperation(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination));
+ interest,
+ in operation);
return;
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs
index 59cdf4f10..086314a26 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal class RotateProcessor : AffineTransformProcessor
where TPixel : struct, IPixel
{
- private float degrees;
+ private readonly float degrees;
///
/// Initializes a new instance of the class.
@@ -131,10 +131,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The configuration.
private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration)
{
+ var operation = new Rotate180RowIntervalOperation(source.Width, source.Height, source, destination);
ParallelRowIterator.IterateRows(
- source.Bounds(),
configuration,
- new Rotate180RowIntervalOperation(source.Width, source.Height, source, destination));
+ source.Bounds(),
+ in operation);
}
///
@@ -145,10 +146,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The configuration.
private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration)
{
+ var operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination);
ParallelRowIterator.IterateRows(
- source.Bounds(),
configuration,
- new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination));
+ source.Bounds(),
+ in operation);
}
///
@@ -159,10 +161,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The configuration.
private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration)
{
+ var operation = new Rotate90RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination);
ParallelRowIterator.IterateRows(
- source.Bounds(),
configuration,
- new Rotate90RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination));
+ source.Bounds(),
+ in operation);
}
private readonly struct Rotate180RowIntervalOperation : IRowIntervalOperation
diff --git a/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs
index 243ffe220..80ac384fd 100644
--- a/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs
@@ -10,7 +10,6 @@ using System.Threading;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
-
using Xunit;
using Xunit.Abstractions;
@@ -30,20 +29,20 @@ namespace SixLabors.ImageSharp.Tests.Helpers
///
public static TheoryData IterateRows_OverMinimumPixelsLimit_Data =
new TheoryData
- {
- { 1, 0, 100, -1, 100, 1 },
- { 2, 0, 9, 5, 4, 2 },
- { 4, 0, 19, 5, 4, 4 },
- { 2, 10, 19, 5, 4, 2 },
- { 4, 0, 200, 50, 50, 4 },
- { 4, 123, 323, 50, 50, 4 },
- { 4, 0, 1201, 301, 298, 4 },
- { 8, 10, 236, 29, 23, 8 },
- { 16, 0, 209, 14, 13, 15 },
- { 24, 0, 209, 9, 2, 24 },
- { 32, 0, 209, 7, 6, 30 },
- { 64, 0, 209, 4, 1, 53 },
- };
+ {
+ { 1, 0, 100, -1, 100, 1 },
+ { 2, 0, 9, 5, 4, 2 },
+ { 4, 0, 19, 5, 4, 4 },
+ { 2, 10, 19, 5, 4, 2 },
+ { 4, 0, 200, 50, 50, 4 },
+ { 4, 123, 323, 50, 50, 4 },
+ { 4, 0, 1201, 301, 298, 4 },
+ { 8, 10, 236, 29, 23, 8 },
+ { 16, 0, 209, 14, 13, 15 },
+ { 24, 0, 209, 9, 2, 24 },
+ { 32, 0, 209, 7, 6, 30 },
+ { 64, 0, 209, 4, 1, 53 },
+ };
[Theory]
[MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))]
@@ -64,20 +63,24 @@ namespace SixLabors.ImageSharp.Tests.Helpers
int actualNumberOfSteps = 0;
- ParallelRowIterator.IterateRows(
- rectangle,
- parallelSettings,
- rows =>
- {
- Assert.True(rows.Min >= minY);
- Assert.True(rows.Max <= maxY);
+ void RowAction(RowInterval rows)
+ {
+ Assert.True(rows.Min >= minY);
+ Assert.True(rows.Max <= maxY);
+
+ int step = rows.Max - rows.Min;
+ int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength;
- int step = rows.Max - rows.Min;
- int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength;
+ Interlocked.Increment(ref actualNumberOfSteps);
+ Assert.Equal(expected, step);
+ }
- Interlocked.Increment(ref actualNumberOfSteps);
- Assert.Equal(expected, step);
- });
+ var operation = new TestRowIntervalOperation(RowAction);
+
+ ParallelRowIterator.IterateRows(
+ rectangle,
+ in parallelSettings,
+ in operation);
Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
}
@@ -102,16 +105,20 @@ namespace SixLabors.ImageSharp.Tests.Helpers
int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray();
var actualData = new int[maxY];
+ void RowAction(RowInterval rows)
+ {
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ actualData[y] = y;
+ }
+ }
+
+ var operation = new TestRowIntervalOperation(RowAction);
+
ParallelRowIterator.IterateRows(
rectangle,
- parallelSettings,
- rows =>
- {
- for (int y = rows.Min; y < rows.Max; y++)
- {
- actualData[y] = y;
- }
- });
+ in parallelSettings,
+ in operation);
Assert.Equal(expectedData, actualData);
}
@@ -136,22 +143,27 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var bufferHashes = new ConcurrentBag();
int actualNumberOfSteps = 0;
- ParallelRowIterator.IterateRows(
- rectangle,
- parallelSettings,
- (RowInterval rows, Memory buffer) =>
- {
- Assert.True(rows.Min >= minY);
- Assert.True(rows.Max <= maxY);
- bufferHashes.Add(buffer.GetHashCode());
+ void RowAction(RowInterval rows, Memory buffer)
+ {
+ Assert.True(rows.Min >= minY);
+ Assert.True(rows.Max <= maxY);
+
+ bufferHashes.Add(buffer.GetHashCode());
+
+ int step = rows.Max - rows.Min;
+ int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength;
+
+ Interlocked.Increment(ref actualNumberOfSteps);
+ Assert.Equal(expected, step);
+ }
- int step = rows.Max - rows.Min;
- int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength;
+ var operation = new TestRowIntervalOperation(RowAction);
- Interlocked.Increment(ref actualNumberOfSteps);
- Assert.Equal(expected, step);
- });
+ ParallelRowIterator.IterateRows, Vector4>(
+ rectangle,
+ in parallelSettings,
+ in operation);
Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
@@ -179,31 +191,35 @@ namespace SixLabors.ImageSharp.Tests.Helpers
int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray();
var actualData = new int[maxY];
- ParallelRowIterator.IterateRows(
+ void RowAction(RowInterval rows, Memory buffer)
+ {
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ actualData[y] = y;
+ }
+ }
+
+ var operation = new TestRowIntervalOperation(RowAction);
+
+ ParallelRowIterator.IterateRows, Vector4>(
rectangle,
- parallelSettings,
- (RowInterval rows, Memory buffer) =>
- {
- for (int y = rows.Min; y < rows.Max; y++)
- {
- actualData[y] = y;
- }
- });
+ in parallelSettings,
+ in operation);
Assert.Equal(expectedData, actualData);
}
public static TheoryData IterateRows_WithEffectiveMinimumPixelsLimit_Data =
new TheoryData
- {
- { 2, 200, 50, 2, 1, -1, 2 },
- { 2, 200, 200, 1, 1, -1, 1 },
- { 4, 200, 100, 4, 2, 2, 2 },
- { 4, 300, 100, 8, 3, 3, 2 },
- { 2, 5000, 1, 4500, 1, -1, 4500 },
- { 2, 5000, 1, 5000, 1, -1, 5000 },
- { 2, 5000, 1, 5001, 2, 2501, 2500 },
- };
+ {
+ { 2, 200, 50, 2, 1, -1, 2 },
+ { 2, 200, 200, 1, 1, -1, 1 },
+ { 4, 200, 100, 4, 2, 2, 2 },
+ { 4, 300, 100, 8, 3, 3, 2 },
+ { 2, 5000, 1, 4500, 1, -1, 4500 },
+ { 2, 5000, 1, 5000, 1, -1, 5000 },
+ { 2, 5000, 1, 5001, 2, 2501, 2500 },
+ };
[Theory]
[MemberData(nameof(IterateRows_WithEffectiveMinimumPixelsLimit_Data))]
@@ -225,20 +241,24 @@ namespace SixLabors.ImageSharp.Tests.Helpers
int actualNumberOfSteps = 0;
- ParallelRowIterator.IterateRows(
- rectangle,
- parallelSettings,
- rows =>
- {
- Assert.True(rows.Min >= 0);
- Assert.True(rows.Max <= height);
+ void RowAction(RowInterval rows)
+ {
+ Assert.True(rows.Min >= 0);
+ Assert.True(rows.Max <= height);
+
+ int step = rows.Max - rows.Min;
+ int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength;
+
+ Interlocked.Increment(ref actualNumberOfSteps);
+ Assert.Equal(expected, step);
+ }
- int step = rows.Max - rows.Min;
- int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength;
+ var operation = new TestRowIntervalOperation(RowAction);
- Interlocked.Increment(ref actualNumberOfSteps);
- Assert.Equal(expected, step);
- });
+ ParallelRowIterator.IterateRows(
+ rectangle,
+ in parallelSettings,
+ in operation);
Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
}
@@ -262,33 +282,38 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var rectangle = new Rectangle(0, 0, width, height);
int actualNumberOfSteps = 0;
- ParallelRowIterator.IterateRows(
- rectangle,
- parallelSettings,
- (RowInterval rows, Memory buffer) =>
- {
- Assert.True(rows.Min >= 0);
- Assert.True(rows.Max <= height);
- int step = rows.Max - rows.Min;
- int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength;
+ void RowAction(RowInterval rows, Memory buffer)
+ {
+ Assert.True(rows.Min >= 0);
+ Assert.True(rows.Max <= height);
- Interlocked.Increment(ref actualNumberOfSteps);
- Assert.Equal(expected, step);
- });
+ int step = rows.Max - rows.Min;
+ int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength;
+
+ Interlocked.Increment(ref actualNumberOfSteps);
+ Assert.Equal(expected, step);
+ }
+
+ var operation = new TestRowIntervalOperation(RowAction);
+
+ ParallelRowIterator.IterateRows, Vector4>(
+ rectangle,
+ in parallelSettings,
+ in operation);
Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
}
public static readonly TheoryData IterateRectangularBuffer_Data =
new TheoryData
- {
- { 8, 582, 453, 10, 10, 291, 226 }, // boundary data from DetectEdgesTest.DetectEdges_InBox
- { 2, 582, 453, 10, 10, 291, 226 },
- { 16, 582, 453, 10, 10, 291, 226 },
- { 16, 582, 453, 10, 10, 1, 226 },
- { 16, 1, 453, 0, 10, 1, 226 },
- };
+ {
+ { 8, 582, 453, 10, 10, 291, 226 }, // boundary data from DetectEdgesTest.DetectEdges_InBox
+ { 2, 582, 453, 10, 10, 291, 226 },
+ { 16, 582, 453, 10, 10, 291, 226 },
+ { 16, 582, 453, 10, 10, 1, 226 },
+ { 16, 1, 453, 0, 10, 1, 226 },
+ };
[Theory]
[MemberData(nameof(IterateRectangularBuffer_Data))]
@@ -325,17 +350,21 @@ namespace SixLabors.ImageSharp.Tests.Helpers
// Fill actual data using IterateRows:
var settings = new ParallelExecutionSettings(maxDegreeOfParallelism, memoryAllocator);
+ void RowAction(RowInterval rows)
+ {
+ this.output.WriteLine(rows.ToString());
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ FillRow(y, actual);
+ }
+ }
+
+ var operation = new TestRowIntervalOperation(RowAction);
+
ParallelRowIterator.IterateRows(
rect,
settings,
- rows =>
- {
- this.output.WriteLine(rows.ToString());
- for (int y = rows.Min; y < rows.Max; y++)
- {
- FillRow(y, actual);
- }
- });
+ in operation);
// Assert:
TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan());
@@ -353,8 +382,14 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var rect = new Rectangle(0, 0, width, height);
+ void RowAction(RowInterval rows)
+ {
+ }
+
+ var operation = new TestRowIntervalOperation(RowAction);
+
ArgumentOutOfRangeException ex = Assert.Throws(
- () => ParallelRowIterator.IterateRows(rect, parallelSettings, rows => { }));
+ () => ParallelRowIterator.IterateRows(rect, in parallelSettings, in operation));
Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message);
}
@@ -370,10 +405,38 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var rect = new Rectangle(0, 0, width, height);
+ void RowAction(RowInterval rows, Memory memory)
+ {
+ }
+
+ var operation = new TestRowIntervalOperation(RowAction);
+
ArgumentOutOfRangeException ex = Assert.Throws(
- () => ParallelRowIterator.IterateRows(rect, parallelSettings, (rows, memory) => { }));
+ () => ParallelRowIterator.IterateRows, Rgba32>(rect, in parallelSettings, in operation));
Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message);
}
+
+ private readonly struct TestRowIntervalOperation : IRowIntervalOperation
+ {
+ private readonly Action action;
+
+ public TestRowIntervalOperation(Action action)
+ => this.action = action;
+
+ public void Invoke(in RowInterval rows) => this.action(rows);
+ }
+
+ private readonly struct TestRowIntervalOperation : IRowIntervalOperation
+ where TBuffer : unmanaged
+ {
+ private readonly Action> action;
+
+ public TestRowIntervalOperation(Action> action)
+ => this.action = action;
+
+ public void Invoke(in RowInterval rows, Memory memory)
+ => this.action(rows, memory);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
index 9aaca3e3f..d570b4d05 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
@@ -702,25 +702,44 @@ namespace SixLabors.ImageSharp.Tests
{
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
- ParallelRowIterator.IterateRows(
- sourceRectangle,
+
+ var operation = new RowOperation(configuration, sourceRectangle, source);
+
+ ParallelRowIterator.IterateRows(
configuration,
- (rows, temp) =>
+ sourceRectangle,
+ in operation);
+ }
+
+ private readonly struct RowOperation : IRowIntervalOperation
+ {
+ private readonly Configuration configuration;
+ private readonly Rectangle bounds;
+ private readonly ImageFrame source;
+
+ public RowOperation(Configuration configuration, Rectangle bounds, ImageFrame source)
+ {
+ this.configuration = configuration;
+ this.bounds = bounds;
+ this.source = source;
+ }
+
+ public void Invoke(in RowInterval rows, Memory memory)
+ {
+ Span tempSpan = memory.Span;
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left, this.bounds.Width);
+ PixelOperations.Instance.ToVector4(this.configuration, rowSpan, tempSpan, PixelConversionModifiers.Scale);
+ for (int i = 0; i < tempSpan.Length; i++)
{
- Span tempSpan = temp.Span;
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span rowSpan = source.GetPixelRowSpan(y).Slice(sourceRectangle.Left, sourceRectangle.Width);
- PixelOperations.Instance.ToVector4(configuration, rowSpan, tempSpan, PixelConversionModifiers.Scale);
- for (int i = 0; i < tempSpan.Length; i++)
- {
- ref Vector4 v = ref tempSpan[i];
- v.W = 1F;
- }
-
- PixelOperations.Instance.FromVector4Destructive(configuration, tempSpan, rowSpan, PixelConversionModifiers.Scale);
- }
- });
+ ref Vector4 v = ref tempSpan[i];
+ v.W = 1F;
+ }
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, tempSpan, rowSpan, PixelConversionModifiers.Scale);
+ }
+ }
}
}
}