diff --git a/src/ImageSharp/Advanced/IRowAction.cs b/src/ImageSharp/Advanced/IRowAction.cs
new file mode 100644
index 000000000..74498eb0b
--- /dev/null
+++ b/src/ImageSharp/Advanced/IRowAction.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Advanced
+{
+ ///
+ /// Defines the contract for an action that operates on a row.
+ ///
+ public interface IRowAction
+ {
+ ///
+ /// Invokes the method passing the row y coordinate.
+ ///
+ /// The row y coordinate.
+ void Invoke(int y);
+ }
+
+ ///
+ /// A that wraps a value delegate of a specified type, and info on the memory areas to process
+ ///
+ /// The type of value delegate to invoke
+ internal readonly struct WrappingRowAction
+ where T : struct, IRowAction
+ {
+ public readonly int MinY;
+ public readonly int MaxY;
+ public readonly int StepY;
+ public readonly int MaxX;
+
+ private readonly T action;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public WrappingRowAction(int minY, int maxY, int stepY, in T action)
+ : this(minY, maxY, stepY, 0, action)
+ {
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public WrappingRowAction(int minY, int maxY, int stepY, int maxX, in T action)
+ {
+ this.MinY = minY;
+ this.MaxY = maxY;
+ this.StepY = stepY;
+ this.MaxX = maxX;
+ this.action = action;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void Invoke(int i)
+ {
+ int yMin = this.MinY + (i * this.StepY);
+
+ if (yMin >= this.MaxY)
+ {
+ return;
+ }
+
+ int yMax = Math.Min(yMin + this.StepY, this.MaxY);
+
+ for (int y = yMin; y < yMax; y++)
+ {
+ // Skip the safety copy when invoking a potentially impure method on a readonly field
+ Unsafe.AsRef(this.action).Invoke(y);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs
index 5c8a30f68..275c3d10e 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs
@@ -76,6 +76,66 @@ namespace SixLabors.ImageSharp.Advanced
rowAction.Invoke);
}
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches.
+ ///
+ /// The type of row action to perform.
+ /// The .
+ /// The to get the parallel settings from.
+ /// The method body defining the iteration logic on a single row.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void IterateRows2(Rectangle rectangle, Configuration configuration, in T body)
+ where T : struct, IRowAction
+ {
+ var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
+ IterateRows2(rectangle, in parallelSettings, in body);
+ }
+
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches.
+ ///
+ /// The type of row action to perform.
+ /// The .
+ /// The .
+ /// The method body defining the iteration logic on a single row.
+ public static void IterateRows2(
+ Rectangle rectangle,
+ in ParallelExecutionSettings parallelSettings,
+ in T body)
+ where T : struct, IRowAction
+ {
+ 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)
+ {
+ for (int y = top; y < bottom; y++)
+ {
+ Unsafe.AsRef(body).Invoke(y);
+ }
+
+ return;
+ }
+
+ int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
+ var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
+ var rowAction = new WrappingRowAction(top, bottom, verticalStep, in 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.