Browse Source

Merge pull request #1132 from SixLabors/feature/single-row-value-delegate-optin

Single row value delegate option
pull/1140/head
James Jackson-South 6 years ago
committed by GitHub
parent
commit
a9e10e23c6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/ImageSharp/Advanced/IRowIntervalOperation.cs
  2. 2
      src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
  3. 17
      src/ImageSharp/Advanced/IRowOperation.cs
  4. 22
      src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
  5. 138
      src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
  6. 154
      src/ImageSharp/Advanced/ParallelRowIterator.cs
  7. 2
      src/ImageSharp/ImageFrame{TPixel}.cs
  8. 30
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  9. 108
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  10. 78
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
  11. 79
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  12. 75
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  13. 29
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
  14. 4
      src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
  15. 17
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  16. 2
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
  17. 23
      src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
  18. 21
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
  19. 4
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
  20. 46
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  21. 33
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
  22. 35
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
  23. 37
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
  24. 2
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs
  25. 2
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
  26. 19
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs
  27. 79
      src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
  28. 15
      src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs
  29. 79
      src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
  30. 44
      src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs
  31. 25
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  32. 18
      tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs
  33. 2
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

2
src/ImageSharp/Advanced/IRowIntervalOperation.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced

2
src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs

@ -2,8 +2,6 @@
// 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

17
src/ImageSharp/Advanced/IRowOperation.cs

@ -0,0 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row.
/// </summary>
public interface IRowOperation
{
/// <summary>
/// Invokes the method passing the row y coordinate.
/// </summary>
/// <param name="y">The row y coordinate.</param>
void Invoke(int y);
}
}

22
src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row with a temporary buffer.
/// </summary>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
public interface IRowOperation<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Invokes the method passing the row and a buffer.
/// </summary>
/// <param name="y">The row y coordinate.</param>
/// <param name="span">The contiguous region of memory.</param>
void Invoke(int y, Span<TBuffer> span);
}
}

138
src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs

@ -17,51 +17,130 @@ namespace SixLabors.ImageSharp.Advanced
/// </content>
public static partial class ParallelRowIterator
{
private readonly struct IterationParameters
private readonly struct RowOperationWrapper<T>
where T : struct, IRowOperation
{
public readonly int MinY;
public readonly int MaxY;
public readonly int StepY;
public readonly int Width;
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly T action;
public IterationParameters(int minY, int maxY, int stepY)
: this(minY, maxY, stepY, 0)
[MethodImpl(InliningOptions.ShortMethod)]
public RowOperationWrapper(
int minY,
int maxY,
int stepY,
in T action)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.action = action;
}
public IterationParameters(int minY, int maxY, int stepY, int width)
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
this.MinY = minY;
this.MaxY = maxY;
this.StepY = stepY;
this.Width = width;
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);
}
}
}
private readonly struct RowOperationWrapper<T, TBuffer>
where T : struct, IRowOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly int width;
private readonly MemoryAllocator allocator;
private readonly T action;
[MethodImpl(InliningOptions.ShortMethod)]
public RowOperationWrapper(
int minY,
int maxY,
int stepY,
int width,
MemoryAllocator allocator,
in T action)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.width = width;
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<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
Span<TBuffer> span = buffer.Memory.Span;
for (int y = yMin; y < yMax; y++)
{
Unsafe.AsRef(this.action).Invoke(y, span);
}
}
}
private readonly struct RowIntervalOperationWrapper<T>
where T : struct, IRowIntervalOperation
{
private readonly IterationParameters info;
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(in IterationParameters info, in T operation)
public RowIntervalOperationWrapper(
int minY,
int maxY,
int stepY,
in T operation)
{
this.info = info;
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
int yMin = this.minY + (i * this.stepY);
if (yMin >= this.info.MaxY)
if (yMin >= this.maxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
// Skip the safety copy when invoking a potentially impure method on a readonly field
@ -73,17 +152,26 @@ namespace SixLabors.ImageSharp.Advanced
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly IterationParameters info;
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly int width;
private readonly MemoryAllocator allocator;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(
in IterationParameters info,
int minY,
int maxY,
int stepY,
int width,
MemoryAllocator allocator,
in T operation)
{
this.info = info;
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.width = width;
this.allocator = allocator;
this.operation = operation;
}
@ -91,17 +179,17 @@ namespace SixLabors.ImageSharp.Advanced
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
int yMin = this.minY + (i * this.stepY);
if (yMin >= this.info.MaxY)
if (yMin >= this.maxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.Width);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span);
}

154
src/ImageSharp/Advanced/ParallelRowIterator.cs

@ -17,6 +17,135 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
public static partial class ParallelRowIterator
{
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IterateRows<T>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, in parallelSettings, in operation);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
public static void IterateRows<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowOperation
{
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(operation).Invoke(y);
}
return;
}
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowOperationWrapper<T>(top, bottom, verticalStep, in operation);
Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
public static void IterateRows<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowOperation<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows<T, TBuffer>(rectangle, in parallelSettings, in operation);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
public static void IterateRows<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowOperation<TBuffer>
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<TBuffer> buffer = allocator.Allocate<TBuffer>(width);
Span<TBuffer> span = buffer.Memory.Span;
for (int y = top; y < bottom; y++)
{
Unsafe.AsRef(operation).Invoke(y, span);
}
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, in operation);
Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
@ -25,11 +154,11 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IterateRows<T>(Configuration configuration, Rectangle rectangle, in T operation)
public static void IterateRowIntervals<T>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, in parallelSettings, in operation);
IterateRowIntervals(rectangle, in parallelSettings, in operation);
}
/// <summary>
@ -39,7 +168,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T>(
public static void IterateRowIntervals<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
@ -65,8 +194,7 @@ namespace SixLabors.ImageSharp.Advanced
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var info = new IterationParameters(top, bottom, verticalStep);
var wrappingOperation = new RowIntervalOperationWrapper<T>(in info, in operation);
var wrappingOperation = new RowIntervalOperationWrapper<T>(top, bottom, verticalStep, in operation);
Parallel.For(
0,
@ -84,12 +212,12 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation)
public static void IterateRowIntervals<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows<T, TBuffer>(rectangle, in parallelSettings, in operation);
IterateRowIntervals<T, TBuffer>(rectangle, in parallelSettings, in operation);
}
/// <summary>
@ -101,7 +229,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T, TBuffer>(
public static void IterateRowIntervals<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
@ -123,18 +251,16 @@ namespace SixLabors.ImageSharp.Advanced
if (numOfSteps == 1)
{
var rows = new RowInterval(top, bottom);
using (IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width))
{
Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
}
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width);
Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var info = new IterationParameters(top, bottom, verticalStep, width);
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(in info, allocator, in operation);
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, in operation);
Parallel.For(
0,

2
src/ImageSharp/ImageFrame{TPixel}.cs

@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp
var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone());
var operation = new RowIntervalOperation<TPixel2>(this, target, configuration);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
configuration,
this.Bounds(),
in operation);

30
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs

@ -3,8 +3,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
bool isAlphaOnly = typeof(TPixel) == typeof(A8);
var operation = new RowIntervalOperation(interest, source, upper, lower, threshold, isAlphaOnly);
var operation = new RowOperation(interest, source, upper, lower, threshold, isAlphaOnly);
ParallelRowIterator.IterateRows(
configuration,
interest,
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// <summary>
/// A <see langword="struct"/> implementing the clone logic for <see cref="BinaryThresholdProcessor{TPixel}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation
private readonly struct RowOperation : IRowOperation
{
private readonly ImageFrame<TPixel> source;
private readonly TPixel upper;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
private readonly bool isAlphaOnly;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Rectangle bounds,
ImageFrame<TPixel> source,
TPixel upper,
@ -84,22 +84,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
Rgba32 rgba = default;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> row = this.source.GetPixelRowSpan(y);
Span<TPixel> row = this.source.GetPixelRowSpan(y);
ref TPixel rowRef = ref MemoryMarshal.GetReference(row);
for (int x = this.minX; x < this.maxX; x++)
{
ref TPixel color = ref row[x];
color.ToRgba32(ref rgba);
for (int x = this.minX; x < this.maxX; x++)
{
ref TPixel color = ref Unsafe.Add(ref rowRef, x);
color.ToRgba32(ref rgba);
// Convert to grayscale using ITU-R Recommendation BT.709 if required
byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
color = luminance >= this.threshold ? this.upper : this.lower;
}
// Convert to grayscale using ITU-R Recommendation BT.709 if required
byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
color = luminance >= this.threshold ? this.upper : this.lower;
}
}
}

108
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -72,8 +72,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
// Preliminary gamma highlight pass
var gammaOperation = new ApplyGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
ParallelRowIterator.IterateRows<ApplyGammaExposureRowIntervalOperation, Vector4>(
var gammaOperation = new ApplyGammaExposureRowOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
ParallelRowIterator.IterateRows<ApplyGammaExposureRowOperation, Vector4>(
this.Configuration,
this.SourceRectangle,
in gammaOperation);
@ -87,7 +87,7 @@ 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);
var operation = new ApplyInverseGammaExposureRowOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma);
ParallelRowIterator.IterateRows(
this.Configuration,
this.SourceRectangle,
@ -120,14 +120,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Vector4 parameters = Unsafe.Add(ref paramsRef, i);
// Compute the vertical 1D convolution
var verticalOperation = new ApplyVerticalConvolutionRowIntervalOperation(sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel);
var verticalOperation = new ApplyVerticalConvolutionRowOperation(sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel);
ParallelRowIterator.IterateRows(
configuration,
sourceRectangle,
in verticalOperation);
// Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer
var horizontalOperation = new ApplyHorizontalConvolutionRowIntervalOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
var horizontalOperation = new ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
ParallelRowIterator.IterateRows(
configuration,
sourceRectangle,
@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the vertical convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyVerticalConvolutionRowIntervalOperation : IRowIntervalOperation
private readonly struct ApplyVerticalConvolutionRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D<ComplexVector4> targetValues;
@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyVerticalConvolutionRowIntervalOperation(
public ApplyVerticalConvolutionRowOperation(
Rectangle bounds,
Buffer2D<ComplexVector4> targetValues,
Buffer2D<TPixel> sourcePixels,
@ -164,16 +164,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<ComplexVector4> targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
Span<ComplexVector4> targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
for (int x = 0; x < this.bounds.Width; x++)
{
Buffer2DUtils.Convolve4(this.kernel, this.sourcePixels, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX);
}
for (int x = 0; x < this.bounds.Width; x++)
{
Buffer2DUtils.Convolve4(this.kernel, this.sourcePixels, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX);
}
}
}
@ -181,7 +178,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the horizontal convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyHorizontalConvolutionRowIntervalOperation : IRowIntervalOperation
private readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D<Vector4> targetValues;
@ -193,7 +190,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyHorizontalConvolutionRowIntervalOperation(
public ApplyHorizontalConvolutionRowOperation(
Rectangle bounds,
Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues,
@ -213,16 +210,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<Vector4> targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
Span<Vector4> targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
for (int x = 0; x < this.bounds.Width; x++)
{
Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
}
for (int x = 0; x < this.bounds.Width; x++)
{
Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
}
}
}
@ -230,7 +224,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyGammaExposureRowIntervalOperation : IRowIntervalOperation<Vector4>
private readonly struct ApplyGammaExposureRowOperation : IRowOperation<Vector4>
{
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
@ -238,7 +232,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly float gamma;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyGammaExposureRowIntervalOperation(
public ApplyGammaExposureRowOperation(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Configuration configuration,
@ -252,31 +246,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
for (int y = rows.Min; y < rows.Max; y++)
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply);
ref Vector4 baseRef = ref MemoryMarshal.GetReference(span);
for (int x = 0; x < this.bounds.Width; x++)
{
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply);
ref Vector4 baseRef = ref MemoryMarshal.GetReference(span);
for (int x = 0; x < this.bounds.Width; x++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, x);
v.X = MathF.Pow(v.X, this.gamma);
v.Y = MathF.Pow(v.Y, this.gamma);
v.Z = MathF.Pow(v.Z, this.gamma);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
ref Vector4 v = ref Unsafe.Add(ref baseRef, x);
v.X = MathF.Pow(v.X, this.gamma);
v.Y = MathF.Pow(v.Y, this.gamma);
v.Z = MathF.Pow(v.Z, this.gamma);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
/// <summary>
/// A <see langword="struct"/> implementing the inverse gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyInverseGammaExposureRowIntervalOperation : IRowIntervalOperation
private readonly struct ApplyInverseGammaExposureRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
@ -285,7 +276,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly float inverseGamma;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyInverseGammaExposureRowIntervalOperation(
public ApplyInverseGammaExposureRowOperation(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<Vector4> sourceValues,
@ -301,28 +292,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
Vector4 low = Vector4.Zero;
var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
for (int y = rows.Min; y < rows.Max; y++)
Span<TPixel> targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
Span<Vector4> sourceRowSpan = this.sourceValues.GetRowSpan(y).Slice(this.bounds.X);
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
for (int x = 0; x < this.bounds.Width; x++)
{
Span<TPixel> targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
Span<Vector4> sourceRowSpan = this.sourceValues.GetRowSpan(y).Slice(this.bounds.X);
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
for (int x = 0; x < this.bounds.Width; x++)
{
ref Vector4 v = ref Unsafe.Add(ref sourceRef, x);
var clamp = Vector4.Clamp(v, low, high);
v.X = MathF.Pow(clamp.X, this.inverseGamma);
v.Y = MathF.Pow(clamp.Y, this.inverseGamma);
v.Z = MathF.Pow(clamp.Z, this.inverseGamma);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply);
ref Vector4 v = ref Unsafe.Add(ref sourceRef, x);
var clamp = Vector4.Clamp(v, low, high);
v.X = MathF.Pow(clamp.X, this.inverseGamma);
v.Y = MathF.Pow(clamp.Y, this.inverseGamma);
v.Z = MathF.Pow(clamp.Z, this.inverseGamma);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply);
}
}
}

78
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs

@ -65,9 +65,9 @@ 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);
var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowIntervalOperation, Vector4>(
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in operation);
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="Convolution2DProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation<Vector4>
private readonly struct RowOperation : IRowOperation<Vector4>
{
private readonly Rectangle bounds;
private readonly int maxY;
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly bool preserveAlpha;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
@ -113,52 +113,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
for (int y = rows.Min; y < rows.Max; y++)
if (this.preserveAlpha)
{
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
if (this.preserveAlpha)
for (int x = 0; x < this.bounds.Width; x++)
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve2D3(
in this.kernelY,
in this.kernelX,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
DenseMatrixUtils.Convolve2D3(
in this.kernelY,
in this.kernelX,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
else
}
else
{
for (int x = 0; x < this.bounds.Width; x++)
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve2D4(
in this.kernelY,
in this.kernelX,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
DenseMatrixUtils.Convolve2D4(
in this.kernelY,
in this.kernelX,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

79
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs

@ -64,15 +64,15 @@ 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<RowIntervalOperation, Vector4>(
var horizontalOperation = new RowOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in horizontalOperation);
// Vertical convolution
var verticalOperation = new RowIntervalOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowIntervalOperation, Vector4>(
var verticalOperation = new RowOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in verticalOperation);
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="Convolution2PassProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation<Vector4>
private readonly struct RowOperation : IRowOperation<Vector4>
{
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly bool preserveAlpha;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
@ -109,53 +109,50 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
int maxY = this.bounds.Bottom - 1;
int maxX = this.bounds.Right - 1;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
if (this.preserveAlpha)
if (this.preserveAlpha)
{
for (int x = 0; x < this.bounds.Width; x++)
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve3(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
maxY,
this.bounds.X,
maxX);
}
DenseMatrixUtils.Convolve3(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
maxY,
this.bounds.X,
maxX);
}
else
}
else
{
for (int x = 0; x < this.bounds.Width; x++)
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
maxY,
this.bounds.X,
maxX);
}
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
maxY,
this.bounds.X,
maxX);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

75
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs

@ -56,8 +56,8 @@ 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<RowIntervalOperation, Vector4>(
var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in operation);
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="ConvolutionProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation<Vector4>
private readonly struct RowOperation : IRowOperation<Vector4>
{
private readonly Rectangle bounds;
private readonly int maxY;
@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly bool preserveAlpha;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
@ -100,50 +100,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
if (this.preserveAlpha)
if (this.preserveAlpha)
{
for (int x = 0; x < this.bounds.Width; x++)
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve3(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
DenseMatrixUtils.Convolve3(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
else
}
else
{
for (int x = 0; x < this.bounds.Width; x++)
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

29
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
processor.Apply(pass);
}
var operation = new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, interest);
var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="EdgeDetectorCompassProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation
private readonly struct RowOperation : IRowOperation
{
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> passPixels;
@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> passPixels,
Rectangle bounds)
@ -110,23 +110,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(y));
ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(y));
ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(y));
ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(y));
for (int x = this.minX; x < this.maxX; x++)
{
// Grab the max components of the two pixels
ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x);
ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x);
for (int x = this.minX; x < this.maxX; x++)
{
// Grab the max components of the two pixels
ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x);
ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x);
var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4());
var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4());
currentTargetPixel.FromVector4(pixelValue);
}
currentTargetPixel.FromVector4(pixelValue);
}
}
}

4
src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs

@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
palette,
ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length));
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
quantizer.Configuration,
bounds,
in ditherOperation);
@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
scale,
ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length));
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
configuration,
bounds,
in ditherOperation);

17
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -99,7 +99,7 @@ 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);
var operation = new RowOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity);
ParallelRowIterator.IterateRows(
configuration,
workingRect,
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary>
/// A <see langword="struct"/> implementing the draw logic for <see cref="DrawImageProcessor{TPixelBg,TPixelFg}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation
private readonly struct RowOperation : IRowOperation
{
private readonly ImageFrame<TPixelBg> sourceFrame;
private readonly Image<TPixelFg> targetImage;
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
private readonly float opacity;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
ImageFrame<TPixelBg> sourceFrame,
Image<TPixelFg> targetImage,
PixelBlender<TPixelBg> blender,
@ -146,14 +146,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixelBg> background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width);
Span<TPixelFg> foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width);
this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity);
}
Span<TPixelBg> background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width);
Span<TPixelFg> foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width);
this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity);
}
}
}

2
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs

@ -48,7 +48,7 @@ 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(
ParallelRowIterator.IterateRowIntervals(
this.Configuration,
this.SourceRectangle,
in operation);

23
src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs

@ -50,9 +50,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
var operation = new RowIntervalOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate);
var operation = new RowOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate);
ParallelRowIterator.IterateRows<RowIntervalOperation, Vector4>(
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in operation);
@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="PixelRowDelegateProcessor{TPixel,TDelegate}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation<Vector4>
private readonly struct RowOperation : IRowOperation<Vector4>
{
private readonly int startX;
private readonly ImageFrame<TPixel> source;
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private readonly TDelegate rowProcessor;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
int startX,
ImageFrame<TPixel> source,
Configuration configuration,
@ -86,18 +86,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers);
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers);
// Run the user defined pixel shader to the current row of pixels
Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y));
// Run the user defined pixel shader to the current row of pixels
Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y));
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers);
}
}
}

21
src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs

@ -36,9 +36,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
var operation = new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration);
var operation = new RowOperation(interest.X, source, this.definition.Matrix, this.Configuration);
ParallelRowIterator.IterateRows<RowIntervalOperation, Vector4>(
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in operation);
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="FilterProcessor{TPixel}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation<Vector4>
private readonly struct RowOperation : IRowOperation<Vector4>
{
private readonly int startX;
private readonly ImageFrame<TPixel> source;
@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
int startX,
ImageFrame<TPixel> source,
ColorMatrix matrix,
@ -69,17 +69,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span);
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span);
Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix));
Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix));
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan);
}
}
}

4
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs

@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
this.Configuration,
new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count),
in operation);
@ -522,7 +522,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
this.luminanceLevels,
source);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
this.configuration,
new Rectangle(0, 0, this.sourceWidth, this.tileYStartPositions.Count),
in operation);

46
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels
var grayscaleOperation = new GrayscaleLevelsRowIntervalOperation(interest, histogramBuffer, source, this.LuminanceLevels);
var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source, this.LuminanceLevels);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image
var cdfOperation = new CdfApplicationRowIntervalOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// A <see langword="struct"/> implementing the grayscale levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary>
private readonly struct GrayscaleLevelsRowIntervalOperation : IRowIntervalOperation
private readonly struct GrayscaleLevelsRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer;
@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly int luminanceLevels;
[MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowIntervalOperation(
public GrayscaleLevelsRowOperation(
Rectangle bounds,
IMemoryOwner<int> histogramBuffer,
ImageFrame<TPixel> source,
@ -107,18 +107,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan());
for (int y = rows.Min; y < rows.Max; y++)
{
ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
for (int x = 0; x < this.bounds.Width; x++)
{
int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels);
Unsafe.Add(ref histogramBase, luminance)++;
}
for (int x = 0; x < this.bounds.Width; x++)
{
int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels);
Unsafe.Add(ref histogramBase, luminance)++;
}
}
}
@ -126,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary>
private readonly struct CdfApplicationRowIntervalOperation : IRowIntervalOperation
private readonly struct CdfApplicationRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer;
@ -135,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly float numberOfPixelsMinusCdfMin;
[MethodImpl(InliningOptions.ShortMethod)]
public CdfApplicationRowIntervalOperation(
public CdfApplicationRowOperation(
Rectangle bounds,
IMemoryOwner<int> cdfBuffer,
ImageFrame<TPixel> source,
@ -151,20 +148,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
for (int y = rows.Min; y < rows.Max; y++)
ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
for (int x = 0; x < this.bounds.Width; x++)
{
ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
for (int x = 0; x < this.bounds.Width; x++)
{
ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
int luminance = GetLuminance(pixel, this.luminanceLevels);
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / this.numberOfPixelsMinusCdfMin;
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W));
}
ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
int luminance = GetLuminance(pixel, this.luminanceLevels);
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / this.numberOfPixelsMinusCdfMin;
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W));
}
}
}

33
src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs

@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(graphicsOptions);
var operation = new RowIntervalOperation(configuration, interest, blender, amount, colors, source);
var operation = new RowOperation(configuration, interest, blender, amount, colors, source);
ParallelRowIterator.IterateRows(
configuration,
interest,
in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation
private readonly struct RowOperation : IRowOperation
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Configuration configuration,
Rectangle bounds,
PixelBlender<TPixel> blender,
@ -83,23 +83,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destination =
this.source.GetPixelRowSpan(y)
.Slice(this.bounds.X, this.bounds.Width);
Span<TPixel> destination =
this.source.GetPixelRowSpan(y)
.Slice(this.bounds.X, this.bounds.Width);
// Switch color & destination in the 2nd and 3rd places because we are
// applying the target color under the current one.
this.blender.Blend(
this.configuration,
destination,
this.colors.GetSpan(),
destination,
this.amount.GetSpan());
}
// Switch color & destination in the 2nd and 3rd places because we are
// applying the target color under the current one.
this.blender.Blend(
this.configuration,
destination,
this.colors.GetSpan(),
destination,
this.amount.GetSpan());
}
}
}

35
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs

@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner<TPixel> rowColors = allocator.Allocate<TPixel>(interest.Width);
rowColors.GetSpan().Fill(glowColor);
var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
ParallelRowIterator.IterateRows<RowIntervalOperation, float>(
var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
ParallelRowIterator.IterateRows<RowOperation, float>(
configuration,
interest,
in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation<float>
private readonly struct RowOperation : IRowOperation<float>
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner<TPixel> colors,
@ -95,27 +95,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<float> span)
public void Invoke(int y, Span<float> span)
{
Span<TPixel> colorSpan = this.colors.GetSpan();
for (int y = rows.Min; y < rows.Max; y++)
for (int i = 0; i < this.bounds.Width; i++)
{
for (int i = 0; i < this.bounds.Width; i++)
{
float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y));
span[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1);
}
float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y));
span[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1);
}
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
this.blender.Blend(
this.configuration,
destination,
destination,
colorSpan,
span);
}
this.blender.Blend(
this.configuration,
destination,
destination,
colorSpan,
span);
}
}
}

37
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs

@ -63,14 +63,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner<TPixel> rowColors = allocator.Allocate<TPixel>(interest.Width);
rowColors.GetSpan().Fill(vignetteColor);
var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
ParallelRowIterator.IterateRows<RowIntervalOperation, float>(
var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
ParallelRowIterator.IterateRows<RowOperation, float>(
configuration,
interest,
in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation<float>
private readonly struct RowOperation : IRowOperation<float>
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(
public RowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner<TPixel> colors,
@ -103,27 +103,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<float> span)
public void Invoke(int y, Span<float> span)
{
Span<TPixel> colorSpan = this.colors.GetSpan();
for (int y = rows.Min; y < rows.Max; y++)
for (int i = 0; i < this.bounds.Width; i++)
{
for (int i = 0; i < this.bounds.Width; i++)
{
float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y));
span[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1);
}
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
this.blender.Blend(
this.configuration,
destination,
destination,
colorSpan,
span);
float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y));
span[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1);
}
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
this.blender.Blend(
this.configuration,
destination,
destination,
colorSpan,
span);
}
}
}

2
src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs

@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (dither is null)
{
var operation = new RowIntervalOperation<TFrameQuantizer, TPixel>(quantizer, source, output, bounds, palette);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
quantizer.Configuration,
bounds,
in operation);

2
src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs

@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
using QuantizedFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(source, interest);
var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
configuration,
interest,
in operation);

19
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ParallelExecutionSettings parallelSettings =
ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
var operation = new RowIntervalOperation(bounds, source, destination);
var operation = new RowOperation(bounds, source, destination);
ParallelRowIterator.IterateRows(
bounds,
@ -62,20 +62,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary>
/// A <see langword="struct"/> implementing the processor logic for <see cref="CropProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalOperation : IRowIntervalOperation
private readonly struct RowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
/// <summary>
/// Initializes a new instance of the <see cref="RowIntervalOperation"/> struct.
/// Initializes a new instance of the <see cref="RowOperation"/> struct.
/// </summary>
/// <param name="bounds">The target processing bounds for the current instance.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current instance.</param>
/// <param name="destination">The destination <see cref="Image{TPixel}"/> for the current instance.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
public RowOperation(Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
this.bounds = bounds;
this.source = source;
@ -84,14 +84,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top);
sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow);
}
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top);
sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow);
}
}
}

79
src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs

@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
in operation);
}
private readonly struct NNAffineOperation : IRowIntervalOperation
private readonly struct NNAffineOperation : IRowOperation
{
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
@ -133,28 +133,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
for (int x = 0; x < this.maxX; x++)
if (this.bounds.Contains(px, py))
{
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
destRow[x] = this.source[px, py];
}
destRow[x] = this.source[px, py];
}
}
}
}
private readonly struct AffineOperation<TResampler> : IRowIntervalOperation<Vector4>
private readonly struct AffineOperation<TResampler> : IRowOperation<Vector4>
where TResampler : struct, IResampler
{
private readonly Configuration configuration;
@ -193,41 +190,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
LinearTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,
span,
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
LinearTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,
span,
this.destination.GetPixelRowSpan(y));
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
span,
this.destination.GetPixelRowSpan(y));
}
}
}

15
src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -76,28 +75,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param>
private void FlipY(ImageFrame<TPixel> source, Configuration configuration)
{
var operation = new RowIntervalOperation(source);
var operation = new RowOperation(source);
ParallelRowIterator.IterateRows(
configuration,
source.Bounds(),
in operation);
}
private readonly struct RowIntervalOperation : IRowIntervalOperation
private readonly struct RowOperation : IRowOperation
{
private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation(ImageFrame<TPixel> source) => this.source = source;
public RowOperation(ImageFrame<TPixel> source) => this.source = source;
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
this.source.GetPixelRowSpan(y).Reverse();
}
}
public void Invoke(int y) => this.source.GetPixelRowSpan(y).Reverse();
}
}
}

79
src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs

@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
in operation);
}
private readonly struct NNProjectiveOperation : IRowIntervalOperation
private readonly struct NNProjectiveOperation : IRowOperation
{
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
@ -133,28 +133,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
for (int x = 0; x < this.maxX; x++)
if (this.bounds.Contains(px, py))
{
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
destRow[x] = this.source[px, py];
}
destRow[x] = this.source[px, py];
}
}
}
}
private readonly struct ProjectiveOperation<TResampler> : IRowIntervalOperation<Vector4>
private readonly struct ProjectiveOperation<TResampler> : IRowOperation<Vector4>
where TResampler : struct, IResampler
{
private readonly Configuration configuration;
@ -193,41 +190,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
public void Invoke(int y, Span<Vector4> span)
{
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
LinearTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,
span,
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
LinearTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,
span,
this.destination.GetPixelRowSpan(y));
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
span,
this.destination.GetPixelRowSpan(y));
}
}
}

44
src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs

@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param>
private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{
var operation = new Rotate180RowIntervalOperation(source.Width, source.Height, source, destination);
var operation = new Rotate180RowOperation(source.Width, source.Height, source, destination);
ParallelRowIterator.IterateRows(
configuration,
source.Bounds(),
@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{
var operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
configuration,
source.Bounds(),
in operation);
@ -161,14 +161,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param>
private void Rotate90(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{
var operation = new Rotate90RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination);
var operation = new Rotate90RowOperation(destination.Bounds(), source.Width, source.Height, source, destination);
ParallelRowIterator.IterateRows(
configuration,
source.Bounds(),
in operation);
}
private readonly struct Rotate180RowIntervalOperation : IRowIntervalOperation
private readonly struct Rotate180RowOperation : IRowOperation
{
private readonly int width;
private readonly int height;
@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public Rotate180RowIntervalOperation(
public Rotate180RowOperation(
int width,
int height,
ImageFrame<TPixel> source,
@ -189,17 +189,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(this.height - y - 1);
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(this.height - y - 1);
for (int x = 0; x < this.width; x++)
{
targetRow[this.width - x - 1] = sourceRow[x];
}
for (int x = 0; x < this.width; x++)
{
targetRow[this.width - x - 1] = sourceRow[x];
}
}
}
@ -248,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
}
private readonly struct Rotate90RowIntervalOperation : IRowIntervalOperation
private readonly struct Rotate90RowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly int width;
@ -257,7 +254,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public Rotate90RowIntervalOperation(
public Rotate90RowOperation(
Rectangle bounds,
int width,
int height,
@ -272,18 +269,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
for (int y = rows.Min; y < rows.Max; y++)
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
int newX = this.height - y - 1;
for (int x = 0; x < this.width; x++)
{
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
int newX = this.height - y - 1;
for (int x = 0; x < this.width; x++)
if (this.bounds.Contains(newX, x))
{
if (this.bounds.Contains(newX, x))
{
this.destination[newX, x] = sourceRow[x];
}
this.destination[newX, x] = sourceRow[x];
}
}
}

25
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height;
var operation = new NNRowIntervalOperation(
var operation = new NNRowOperation(
sourceRectangle,
destinationRectangle,
widthFactor,
@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
}
private readonly struct NNRowIntervalOperation : IRowIntervalOperation
private readonly struct NNRowOperation : IRowOperation
{
private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds;
@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public NNRowIntervalOperation(
public NNRowOperation(
Rectangle sourceBounds,
Rectangle destinationBounds,
float widthFactor,
@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
int sourceX = this.sourceBounds.X;
int sourceY = this.sourceBounds.Y;
@ -229,17 +229,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int destLeft = this.destinationBounds.Left;
int destRight = this.destinationBounds.Right;
for (int y = rows.Min; y < rows.Max; y++)
// Y coordinates of source points
Span<TPixel> sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY));
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y);
for (int x = destLeft; x < destRight; x++)
{
// Y coordinates of source points
Span<TPixel> sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY));
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y);
for (int x = destLeft; x < destRight; x++)
{
// X coordinates of source points
targetRow[x] = sourceRow[(int)(((x - destX) * this.widthFactor) + sourceX)];
}
// X coordinates of source points
targetRow[x] = sourceRow[(int)(((x - destX) * this.widthFactor) + sourceX)];
}
}
}

18
tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation(RowAction);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
rectangle,
in parallelSettings,
in operation);
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation(RowAction);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
rectangle,
in parallelSettings,
in operation);
@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation<Vector4>(RowAction);
ParallelRowIterator.IterateRows<TestRowIntervalOperation<Vector4>, Vector4>(
ParallelRowIterator.IterateRowIntervals<TestRowIntervalOperation<Vector4>, Vector4>(
rectangle,
in parallelSettings,
in operation);
@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation<Vector4>(RowAction);
ParallelRowIterator.IterateRows<TestRowIntervalOperation<Vector4>, Vector4>(
ParallelRowIterator.IterateRowIntervals<TestRowIntervalOperation<Vector4>, Vector4>(
rectangle,
in parallelSettings,
in operation);
@ -249,7 +249,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation(RowAction);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
rectangle,
in parallelSettings,
in operation);
@ -291,7 +291,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation<Vector4>(RowAction);
ParallelRowIterator.IterateRows<TestRowIntervalOperation<Vector4>, Vector4>(
ParallelRowIterator.IterateRowIntervals<TestRowIntervalOperation<Vector4>, Vector4>(
rectangle,
in parallelSettings,
in operation);
@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation(RowAction);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRowIntervals(
rect,
settings,
in operation);
@ -383,7 +383,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation(RowAction);
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(
() => ParallelRowIterator.IterateRows(rect, in parallelSettings, in operation));
() => ParallelRowIterator.IterateRowIntervals(rect, in parallelSettings, in operation));
Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message);
}
@ -406,7 +406,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var operation = new TestRowIntervalOperation<Rgba32>(RowAction);
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(
() => ParallelRowIterator.IterateRows<TestRowIntervalOperation<Rgba32>, Rgba32>(rect, in parallelSettings, in operation));
() => ParallelRowIterator.IterateRowIntervals<TestRowIntervalOperation<Rgba32>, Rgba32>(rect, in parallelSettings, in operation));
Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message);
}

2
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -713,7 +713,7 @@ namespace SixLabors.ImageSharp.Tests
var operation = new RowOperation(configuration, sourceRectangle, source);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
ParallelRowIterator.IterateRowIntervals<RowOperation, Vector4>(
configuration,
sourceRectangle,
in operation);

Loading…
Cancel
Save