diff --git a/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs b/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs
new file mode 100644
index 000000000..4bf0d1fe4
--- /dev/null
+++ b/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Advanced
+{
+ ///
+ /// Defines the contract for an action that operates on a row with a temporary buffer.
+ ///
+ /// The type of buffer elements.
+ public interface IRowAction
+ where TBuffer : unmanaged
+ {
+ ///
+ /// Invokes the method passing the row and a buffer.
+ ///
+ /// The row y coordinate.
+ /// The contiguous region of memory.
+ void Invoke(int y, Span span);
+ }
+
+ internal readonly struct WrappingRowAction
+ where T : struct, IRowAction
+ where TBuffer : unmanaged
+ {
+ public readonly int MinY;
+ public readonly int MaxY;
+ public readonly int StepY;
+ public readonly int MaxX;
+
+ private readonly MemoryAllocator allocator;
+ private readonly T action;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public WrappingRowAction(
+ int minY,
+ int maxY,
+ int stepY,
+ MemoryAllocator allocator,
+ in T action)
+ : this(minY, maxY, stepY, 0, allocator, action)
+ {
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public WrappingRowAction(
+ int minY,
+ int maxY,
+ int stepY,
+ int maxX,
+ MemoryAllocator allocator,
+ in T action)
+ {
+ this.MinY = minY;
+ this.MaxY = maxY;
+ this.StepY = stepY;
+ this.MaxX = maxX;
+ this.allocator = allocator;
+ 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);
+
+ using IMemoryOwner buffer = this.allocator.Allocate(this.MaxX);
+
+ Span span = buffer.Memory.Span;
+
+ for (int y = yMin; y < yMax; y++)
+ {
+ Unsafe.AsRef(this.action).Invoke(y, span);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs
index 7802d9653..8bfda431a 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs
@@ -141,6 +141,72 @@ namespace SixLabors.ImageSharp.Advanced
rowAction.Invoke);
}
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches defined by -s
+ /// instantiating a temporary buffer for each invocation.
+ ///
+ /// The type of row action 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 IterateRows2(Rectangle rectangle, Configuration configuration, in T body)
+ where T : struct, IRowAction
+ where TBuffer : unmanaged
+ {
+ var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
+ IterateRows2(rectangle, in parallelSettings, in body);
+ }
+
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches defined by -s
+ /// instantiating a temporary buffer for each invocation.
+ ///
+ internal static void IterateRows2(
+ Rectangle rectangle,
+ in ParallelExecutionSettings parallelSettings,
+ in T body)
+ where T : struct, IRowAction
+ 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)
+ {
+ using (IMemoryOwner buffer = allocator.Allocate(width))
+ {
+ Span span = buffer.Memory.Span;
+
+ for (int y = top; y < bottom; y++)
+ {
+ Unsafe.AsRef(body).Invoke(y, span);
+ }
+ }
+
+ return;
+ }
+
+ int verticalStep = DivideCeil(height, numOfSteps);
+ var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
+ var rowAction = new WrappingRowAction(top, bottom, verticalStep, width, allocator, in body);
+
+ Parallel.For(
+ 0,
+ numOfSteps,
+ parallelOptions,
+ rowAction.Invoke);
+ }
+
///
/// Iterate through the rows of a rectangle in optimized batches defined by -s.
///