diff --git a/src/ImageSharp/Common/Helpers/ParallelFor.cs b/src/ImageSharp/Common/Helpers/ParallelFor.cs
deleted file mode 100644
index 191875a95..000000000
--- a/src/ImageSharp/Common/Helpers/ParallelFor.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Buffers;
-using System.Threading.Tasks;
-
-using SixLabors.Memory;
-
-namespace SixLabors.ImageSharp
-{
- ///
- /// Utility methods for Parallel.For() execution. Use this instead of raw calls!
- ///
- internal static class ParallelFor
- {
- ///
- /// Helper method to execute Parallel.For using the settings in
- ///
- public static void WithConfiguration(int fromInclusive, int toExclusive, Configuration configuration, Action body)
- {
- Parallel.For(fromInclusive, toExclusive, configuration.GetParallelOptions(), body);
- }
-
- ///
- /// Helper method to execute Parallel.For with temporary worker buffer shared between executing tasks.
- /// The buffer is not guaranteed to be clean!
- ///
- /// The value type of the buffer
- /// The start index, inclusive.
- /// The end index, exclusive.
- /// The used for getting the and
- /// The length of the requested parallel buffer
- /// The delegate that is invoked once per iteration.
- public static void WithTemporaryBuffer(
- int fromInclusive,
- int toExclusive,
- Configuration configuration,
- int bufferLength,
- Action> body)
- where T : struct
- {
- MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
- ParallelOptions parallelOptions = configuration.GetParallelOptions();
-
- IMemoryOwner InitBuffer()
- {
- return memoryAllocator.Allocate(bufferLength);
- }
-
- void CleanUpBuffer(IMemoryOwner buffer)
- {
- buffer.Dispose();
- }
-
- IMemoryOwner BodyFunc(int i, ParallelLoopState state, IMemoryOwner buffer)
- {
- body(i, buffer);
- return buffer;
- }
-
- Parallel.For(fromInclusive, toExclusive, parallelOptions, InitBuffer, BodyFunc, CleanUpBuffer);
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index 132ab598e..511e8ad68 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
@@ -262,20 +263,24 @@ namespace SixLabors.ImageSharp
var target = new ImageFrame(this.configuration, this.Width, this.Height, this.MetaData.Clone());
- ParallelFor.WithTemporaryBuffer(
- 0,
- this.Height,
+ ParallelHelper.IterateRowsWithTempBuffer(
+ this.Bounds(),
this.configuration,
- this.Width,
- (int y, IMemoryOwner tempRowBuffer) =>
- {
- Span sourceRow = this.GetPixelRowSpan(y);
- Span targetRow = target.GetPixelRowSpan(y);
- Span tempRowSpan = tempRowBuffer.GetSpan();
-
- PixelOperations.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length);
- PixelOperations.Instance.PackFromScaledVector4(tempRowSpan, targetRow, targetRow.Length);
- });
+ (rows, tempRowBuffer) =>
+ {
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ Span sourceRow = this.GetPixelRowSpan(y);
+ Span targetRow = target.GetPixelRowSpan(y);
+ Span tempRowSpan = tempRowBuffer.Span;
+
+ PixelOperations.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length);
+ PixelOperations.Instance.PackFromScaledVector4(
+ tempRowSpan,
+ targetRow,
+ targetRow.Length);
+ }
+ });
return target;
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs
deleted file mode 100644
index d55c231a7..000000000
--- a/tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Threading.Tasks;
-
-using BenchmarkDotNet.Attributes;
-
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp.Benchmarks.Codecs
-{
- public class CopyPixels : BenchmarkBase
- {
- [Benchmark(Baseline = true, Description = "PixelAccessor Copy by indexer")]
- public Rgba32 CopyByPixelAccesor()
- {
- using (var source = new Image(1024, 768))
- using (var target = new Image(1024, 768))
- {
- Buffer2D sourcePixels = source.GetRootFramePixelBuffer();
- Buffer2D targetPixels = target.GetRootFramePixelBuffer();
- ParallelFor.WithConfiguration(
- 0,
- source.Height,
- Configuration.Default,
- y =>
- {
- for (int x = 0; x < source.Width; x++)
- {
- targetPixels[x, y] = sourcePixels[x, y];
- }
- });
-
- return targetPixels[0, 0];
- }
- }
-
- [Benchmark(Description = "PixelAccessor Copy by Span")]
- public Rgba32 CopyByPixelAccesorSpan()
- {
- using (var source = new Image(1024, 768))
- using (var target = new Image(1024, 768))
- {
- Buffer2D sourcePixels = source.GetRootFramePixelBuffer();
- Buffer2D targetPixels = target.GetRootFramePixelBuffer();
- ParallelFor.WithConfiguration(
- 0,
- source.Height,
- Configuration.Default,
- y =>
- {
- Span sourceRow = sourcePixels.GetRowSpan(y);
- Span targetRow = targetPixels.GetRowSpan(y);
-
- for (int x = 0; x < source.Width; x++)
- {
- targetRow[x] = sourceRow[x];
- }
- });
-
- return targetPixels[0, 0];
- }
- }
-
- [Benchmark(Description = "Copy by indexer")]
- public Rgba32 Copy()
- {
- using (var source = new Image(1024, 768))
- using (var target = new Image(1024, 768))
- {
- ParallelFor.WithConfiguration(
- 0,
- source.Height,
- Configuration.Default,
- y =>
- {
- for (int x = 0; x < source.Width; x++)
- {
- target[x, y] = source[x, y];
- }
- });
-
- return target[0, 0];
- }
- }
-
- [Benchmark(Description = "Copy by Span")]
- public Rgba32 CopySpan()
- {
- using (var source = new Image(1024, 768))
- using (var target = new Image(1024, 768))
- {
- ParallelFor.WithConfiguration(
- 0,
- source.Height,
- Configuration.Default,
- y =>
- {
- Span sourceRow = source.Frames.RootFrame.GetPixelRowSpan(y);
- Span targetRow = target.Frames.RootFrame.GetPixelRowSpan(y);
-
- for (int x = 0; x < source.Width; x++)
- {
- targetRow[x] = sourceRow[x];
- }
- });
-
- return target[0, 0];
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs
index fe1d4221d..ff2e57b97 100644
--- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs
+++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Processing.Processors.Overlays;
@@ -112,26 +113,29 @@ namespace SixLabors.ImageSharp.Benchmarks
Buffer2D sourcePixels = source.PixelBuffer;
rowColors.GetSpan().Fill(glowColor);
- ParallelFor.WithConfiguration(
- minY,
- maxY,
+ var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
+ ParallelHelper.IterateRows(
+ workingRect,
configuration,
- y =>
+ rows =>
{
- int offsetY = y - startY;
-
- for (int x = minX; x < maxX; x++)
+ for (int y = rows.Min; y < rows.Max; y++)
{
- int offsetX = x - startX;
- float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
- Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
- TPixel packed = default(TPixel);
- packed.PackFromVector4(
- PremultipliedLerp(
- sourceColor,
- glowColor.ToVector4(),
- 1 - (.95F * (distance / maxDistance))));
- sourcePixels[offsetX, offsetY] = packed;
+ int offsetY = y - startY;
+
+ for (int x = minX; x < maxX; x++)
+ {
+ int offsetX = x - startX;
+ float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
+ Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
+ TPixel packed = default(TPixel);
+ packed.PackFromVector4(
+ PremultipliedLerp(
+ sourceColor,
+ glowColor.ToVector4(),
+ 1 - (.95F * (distance / maxDistance))));
+ sourcePixels[offsetX, offsetY] = packed;
+ }
}
});
}