Browse Source

Refactor processors to single row logic

af/octree-no-pixelmap
Sergio Pedri 6 years ago
parent
commit
d6c53a02be
  1. 19
      src/ImageSharp/ImageFrame{TPixel}.cs
  2. 31
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  3. 79
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  4. 39
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
  5. 20
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  6. 97
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
  7. 50
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  8. 33
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
  9. 25
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  10. 31
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs
  11. 19
      src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
  12. 29
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
  13. 27
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  14. 75
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs

19
src/ImageSharp/ImageFrame{TPixel}.cs

@ -260,10 +260,10 @@ namespace SixLabors.ImageSharp
var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone()); var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone());
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
this.Bounds(), this.Bounds(),
configuration, configuration,
new RowIntervalAction<TPixel2>(this, target, configuration)); new RowAction<TPixel2>(this, target, configuration));
return target; return target;
} }
@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the clone logic for <see cref="ImageFrame{TPixel}"/>. /// A <see langword="struct"/> implementing the clone logic for <see cref="ImageFrame{TPixel}"/>.
/// </summary> /// </summary>
private readonly struct RowIntervalAction<TPixel2> : IRowIntervalAction private readonly struct RowAction<TPixel2> : IRowAction
where TPixel2 : struct, IPixel<TPixel2> where TPixel2 : struct, IPixel<TPixel2>
{ {
private readonly ImageFrame<TPixel> source; private readonly ImageFrame<TPixel> source;
@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp
private readonly Configuration configuration; private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
ImageFrame<TPixel2> target, ImageFrame<TPixel2> target,
Configuration configuration) Configuration configuration)
@ -309,14 +309,11 @@ namespace SixLabors.ImageSharp
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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<TPixel2> targetRow = this.target.GetPixelRowSpan(y);
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y); PixelOperations<TPixel>.Instance.To(this.configuration, sourceRow, targetRow);
Span<TPixel2> targetRow = this.target.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.To(this.configuration, sourceRow, targetRow);
}
} }
} }
} }

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

@ -4,7 +4,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization namespace SixLabors.ImageSharp.Processing.Processors.Binarization
@ -51,16 +50,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
workingRect, workingRect,
configuration, configuration,
new RowIntervalAction(source, upper, lower, threshold, startX, endX, isAlphaOnly)); new RowAction(source, upper, lower, threshold, startX, endX, isAlphaOnly));
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the clone logic for <see cref="BinaryThresholdProcessor{TPixel}"/>. /// A <see langword="struct"/> implementing the clone logic for <see cref="BinaryThresholdProcessor{TPixel}"/>.
/// </summary> /// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly ImageFrame<TPixel> source; private readonly ImageFrame<TPixel> source;
private readonly TPixel upper; private readonly TPixel upper;
@ -71,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
private readonly bool isAlphaOnly; private readonly bool isAlphaOnly;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
TPixel upper, TPixel upper,
TPixel lower, TPixel lower,
@ -91,22 +90,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
Rgba32 rgba = default; Rgba32 rgba = default;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> row = this.source.GetPixelRowSpan(y);
for (int x = this.startX; x < this.endX; x++) Span<TPixel> row = this.source.GetPixelRowSpan(y);
{
ref TPixel color = ref row[x]; for (int x = this.startX; x < this.endX; x++)
color.ToRgba32(ref rgba); {
ref TPixel color = ref row[x];
color.ToRgba32(ref rgba);
// Convert to grayscale using ITU-R Recommendation BT.709 if required // 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); byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
color = luminance >= this.threshold ? this.upper : this.lower; color = luminance >= this.threshold ? this.upper : this.lower;
}
} }
} }
} }

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

@ -282,10 +282,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float inverseGamma = 1 / this.gamma; float inverseGamma = 1 / this.gamma;
// Apply the inverse gamma exposure pass, and write the final pixel data // Apply the inverse gamma exposure pass, and write the final pixel data
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
this.SourceRectangle, this.SourceRectangle,
this.Configuration, this.Configuration,
new ApplyInverseGammaExposureRowIntervalAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma)); new ApplyInverseGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma));
} }
/// <summary> /// <summary>
@ -314,23 +314,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Vector4 parameters = Unsafe.Add(ref paramsRef, i); Vector4 parameters = Unsafe.Add(ref paramsRef, i);
// Compute the vertical 1D convolution // Compute the vertical 1D convolution
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
sourceRectangle, sourceRectangle,
configuration, configuration,
new ApplyVerticalConvolutionRowIntervalAction(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel)); new ApplyVerticalConvolutionRowAction(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel));
// Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer // Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
sourceRectangle, sourceRectangle,
configuration, configuration,
new ApplyHorizontalConvolutionRowIntervalAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W)); new ApplyHorizontalConvolutionRowAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W));
} }
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the vertical convolution logic for <see cref="BokehBlurProcessor{T}"/>. /// A <see langword="struct"/> implementing the vertical convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct ApplyVerticalConvolutionRowIntervalAction : IRowIntervalAction private readonly struct ApplyVerticalConvolutionRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Buffer2D<ComplexVector4> targetValues; private readonly Buffer2D<ComplexVector4> targetValues;
@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX; private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public ApplyVerticalConvolutionRowIntervalAction( public ApplyVerticalConvolutionRowAction(
ref Rectangle bounds, ref Rectangle bounds,
Buffer2D<ComplexVector4> targetValues, Buffer2D<ComplexVector4> targetValues,
Buffer2D<TPixel> sourcePixels, Buffer2D<TPixel> sourcePixels,
@ -356,16 +356,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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++) 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); Buffer2DUtils.Convolve4(this.kernel, this.sourcePixels, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX);
}
} }
} }
} }
@ -373,7 +370,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the horizontal convolution logic for <see cref="BokehBlurProcessor{T}"/>. /// A <see langword="struct"/> implementing the horizontal convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct ApplyHorizontalConvolutionRowIntervalAction : IRowIntervalAction private readonly struct ApplyHorizontalConvolutionRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Buffer2D<Vector4> targetValues; private readonly Buffer2D<Vector4> targetValues;
@ -385,7 +382,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX; private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public ApplyHorizontalConvolutionRowIntervalAction( public ApplyHorizontalConvolutionRowAction(
ref Rectangle bounds, ref Rectangle bounds,
Buffer2D<Vector4> targetValues, Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues, Buffer2D<ComplexVector4> sourceValues,
@ -405,16 +402,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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++) 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); Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
}
} }
} }
} }
@ -471,7 +465,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the inverse gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>. /// A <see langword="struct"/> implementing the inverse gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction private readonly struct ApplyInverseGammaExposureRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels; private readonly Buffer2D<TPixel> targetPixels;
@ -480,7 +474,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly float inverseGamma; private readonly float inverseGamma;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public ApplyInverseGammaExposureRowIntervalAction( public ApplyInverseGammaExposureRowAction(
Rectangle bounds, Rectangle bounds,
Buffer2D<TPixel> targetPixels, Buffer2D<TPixel> targetPixels,
Buffer2D<Vector4> sourceValues, Buffer2D<Vector4> sourceValues,
@ -496,28 +490,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
Vector4 low = Vector4.Zero; Vector4 low = Vector4.Zero;
var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); 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);
Span<TPixel> targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
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); 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);
} }
} }
} }

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

@ -102,17 +102,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
processor.Apply(pass); processor.Apply(pass);
} }
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
Rectangle.FromLTRB(minX, minY, maxX, maxY), Rectangle.FromLTRB(minX, minY, maxX, maxY),
this.Configuration, this.Configuration,
new RowIntervalAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX)); new RowAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX));
} }
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="EdgeDetectorCompassProcessor{T}"/>. /// A <see langword="struct"/> implementing the convolution logic for <see cref="EdgeDetectorCompassProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly Buffer2D<TPixel> targetPixels; private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> passPixels; private readonly Buffer2D<TPixel> passPixels;
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int shiftX; private readonly int shiftX;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
Buffer2D<TPixel> targetPixels, Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> passPixels, Buffer2D<TPixel> passPixels,
int minX, int minX,
@ -140,29 +140,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
for (int y = rows.Min; y < rows.Max; y++) int offsetY = y - this.shiftY;
{
int offsetY = y - this.shiftY;
ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(offsetY)); ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(offsetY));
ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(offsetY)); ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(offsetY));
for (int x = this.minX; x < this.maxX; x++) for (int x = this.minX; x < this.maxX; x++)
{ {
int offsetX = x - this.shiftX; int offsetX = x - this.shiftX;
// Grab the max components of the two pixels // Grab the max components of the two pixels
ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX); ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX);
ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX); ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX);
var pixelValue = Vector4.Max( var pixelValue = Vector4.Max(
currentPassPixel.ToVector4(), currentPassPixel.ToVector4(),
currentTargetPixel.ToVector4()); currentTargetPixel.ToVector4());
currentTargetPixel.FromVector4(pixelValue); currentTargetPixel.FromVector4(pixelValue);
}
} }
} }
} }

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

@ -4,7 +4,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing namespace SixLabors.ImageSharp.Processing.Processors.Drawing
@ -99,16 +98,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
"Cannot draw image because the source image does not overlap the target image."); "Cannot draw image because the source image does not overlap the target image.");
} }
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
workingRect, workingRect,
configuration, configuration,
new RowIntervalAction(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity)); new RowAction(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity));
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the draw logic for <see cref="DrawImageProcessor{TPixelBg,TPixelFg}"/>. /// A <see langword="struct"/> implementing the draw logic for <see cref="DrawImageProcessor{TPixelBg,TPixelFg}"/>.
/// </summary> /// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly ImageFrame<TPixelBg> sourceFrame; private readonly ImageFrame<TPixelBg> sourceFrame;
private readonly Image<TPixelFg> targetImage; private readonly Image<TPixelFg> targetImage;
@ -121,7 +120,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
private readonly float opacity; private readonly float opacity;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
ImageFrame<TPixelBg> sourceFrame, ImageFrame<TPixelBg> sourceFrame,
Image<TPixelFg> targetImage, Image<TPixelFg> targetImage,
PixelBlender<TPixelBg> blender, PixelBlender<TPixelBg> blender,
@ -145,14 +144,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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);
Span<TPixelBg> background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width); this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity);
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);
}
} }
} }
} }

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

@ -47,10 +47,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
source.CopyTo(targetPixels); source.CopyTo(targetPixels);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
this.SourceRectangle, this.SourceRectangle,
this.Configuration, this.Configuration,
new RowIntervalAction(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels)); new RowAction(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels));
Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels); Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
} }
@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="OilPaintingProcessor{T}"/>. /// A <see langword="struct"/> implementing the convolution logic for <see cref="OilPaintingProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels; private readonly Buffer2D<TPixel> targetPixels;
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private readonly int levels; private readonly int levels;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
Rectangle bounds, Rectangle bounds,
Buffer2D<TPixel> targetPixels, Buffer2D<TPixel> targetPixels,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
int maxY = this.bounds.Bottom - 1; int maxY = this.bounds.Bottom - 1;
int maxX = this.bounds.Right - 1; int maxX = this.bounds.Right - 1;
@ -117,69 +117,66 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
ref float blueBinRef = ref Unsafe.Add(ref redBinRef, this.levels); ref float blueBinRef = ref Unsafe.Add(ref redBinRef, this.levels);
ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, this.levels); ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, this.levels);
for (int y = rows.Min; y < rows.Max; y++) Span<TPixel> sourceRowPixelSpan = this.source.GetPixelRowSpan(y);
{ Span<TPixel> sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width);
Span<TPixel> sourceRowPixelSpan = this.source.GetPixelRowSpan(y);
Span<TPixel> sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span);
for (int x = this.bounds.X; x < this.bounds.Right; x++) for (int x = this.bounds.X; x < this.bounds.Right; x++)
{ {
int maxIntensity = 0; int maxIntensity = 0;
int maxIndex = 0; int maxIndex = 0;
// Clear the current shared buffer before processing each target pixel // Clear the current shared buffer before processing each target pixel
bins.Memory.Span.Clear(); bins.Memory.Span.Clear();
for (int fy = 0; fy <= this.radius; fy++) for (int fy = 0; fy <= this.radius; fy++)
{ {
int fyr = fy - this.radius; int fyr = fy - this.radius;
int offsetY = y + fyr; int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY); offsetY = offsetY.Clamp(0, maxY);
Span<TPixel> sourceOffsetRow = this.source.GetPixelRowSpan(offsetY); Span<TPixel> sourceOffsetRow = this.source.GetPixelRowSpan(offsetY);
for (int fx = 0; fx <= this.radius; fx++) for (int fx = 0; fx <= this.radius; fx++)
{ {
int fxr = fx - this.radius; int fxr = fx - this.radius;
int offsetX = x + fxr; int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
var vector = sourceOffsetRow[offsetX].ToVector4(); var vector = sourceOffsetRow[offsetX].ToVector4();
float sourceRed = vector.X; float sourceRed = vector.X;
float sourceBlue = vector.Z; float sourceBlue = vector.Z;
float sourceGreen = vector.Y; float sourceGreen = vector.Y;
int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (this.levels - 1)); int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (this.levels - 1));
Unsafe.Add(ref intensityBinRef, currentIntensity)++; Unsafe.Add(ref intensityBinRef, currentIntensity)++;
Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed;
Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue;
Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen;
if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity)
{ {
maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity);
maxIndex = currentIntensity; maxIndex = currentIntensity;
}
} }
}
float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity);
float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity);
float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity);
float alpha = sourceRowVector4Span[x].W; float alpha = sourceRowVector4Span[x].W;
targetRowVector4Span[x] = new Vector4(red, green, blue, alpha); targetRowVector4Span[x] = new Vector4(red, green, blue, alpha);
}
} }
}
Span<TPixel> targetRowAreaPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); Span<TPixel> targetRowAreaPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan);
}
} }
} }
} }

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

@ -52,10 +52,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean); using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels // Build the histogram of the grayscale levels
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
workingRect, workingRect,
this.Configuration, this.Configuration,
new GrayscaleLevelsRowIntervalAction(workingRect, histogramBuffer, source, this.LuminanceLevels)); new GrayscaleLevelsRowAction(workingRect, histogramBuffer, source, this.LuminanceLevels));
Span<int> histogram = histogramBuffer.GetSpan(); Span<int> histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled) if (this.ClipHistogramEnabled)
@ -74,16 +74,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image // Apply the cdf to each pixel of the image
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
workingRect, workingRect,
this.Configuration, this.Configuration,
new CdfApplicationRowIntervalAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin)); new CdfApplicationRowAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin));
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the grayscale levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>. /// A <see langword="struct"/> implementing the grayscale levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary> /// </summary>
private readonly struct GrayscaleLevelsRowIntervalAction : IRowIntervalAction private readonly struct GrayscaleLevelsRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer; private readonly IMemoryOwner<int> histogramBuffer;
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly int luminanceLevels; private readonly int luminanceLevels;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowIntervalAction( public GrayscaleLevelsRowAction(
in Rectangle bounds, in Rectangle bounds,
IMemoryOwner<int> histogramBuffer, IMemoryOwner<int> histogramBuffer,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
@ -105,18 +105,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); 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++) for (int x = 0; x < this.bounds.Width; x++)
{ {
int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels); int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels);
Unsafe.Add(ref histogramBase, luminance)++; Unsafe.Add(ref histogramBase, luminance)++;
}
} }
} }
} }
@ -124,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>. /// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary> /// </summary>
private readonly struct CdfApplicationRowIntervalAction : IRowIntervalAction private readonly struct CdfApplicationRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer; private readonly IMemoryOwner<int> cdfBuffer;
@ -133,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly float numberOfPixelsMinusCdfMin; private readonly float numberOfPixelsMinusCdfMin;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CdfApplicationRowIntervalAction( public CdfApplicationRowAction(
in Rectangle bounds, in Rectangle bounds,
IMemoryOwner<int> cdfBuffer, IMemoryOwner<int> cdfBuffer,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
@ -149,20 +146,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); 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)); ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
int luminance = GetLuminance(pixel, this.luminanceLevels);
for (int x = 0; x < this.bounds.Width; x++) 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,13 +49,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(graphicsOptions); PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(graphicsOptions);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
interest, interest,
configuration, configuration,
new RowIntervalAction(configuration, interest, blender, amount, colors, source)); new RowAction(configuration, interest, blender, amount, colors, source));
} }
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly Rectangle bounds; private readonly Rectangle bounds;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame<TPixel> source; private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
Configuration configuration, Configuration configuration,
Rectangle bounds, Rectangle bounds,
PixelBlender<TPixel> blender, PixelBlender<TPixel> blender,
@ -82,23 +82,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
} }
[MethodImpl(InliningOptions.ShortMethod)] [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 // Switch color & destination in the 2nd and 3rd places because we are
// applying the target color under the current one. // applying the target color under the current one.
this.blender.Blend( this.blender.Blend(
this.configuration, this.configuration,
destination, destination,
this.colors.GetSpan(), this.colors.GetSpan(),
destination, destination,
this.amount.GetSpan()); this.amount.GetSpan());
}
} }
} }
} }

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

@ -58,10 +58,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.resampler is NearestNeighborResampler) if (this.resampler is NearestNeighborResampler)
{ {
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
targetBounds, targetBounds,
configuration, configuration,
new NearestNeighborRowIntervalAction(this.SourceRectangle, ref matrix, width, source, destination)); new NearestNeighborRowAction(this.SourceRectangle, ref matrix, width, source, destination));
return; return;
} }
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the nearest neighbor resampler logic for <see cref="AffineTransformProcessor{T}"/>. /// A <see langword="struct"/> implementing the nearest neighbor resampler logic for <see cref="AffineTransformProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction private readonly struct NearestNeighborRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Matrix3x2 matrix; private readonly Matrix3x2 matrix;
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public NearestNeighborRowIntervalAction( public NearestNeighborRowAction(
Rectangle bounds, Rectangle bounds,
ref Matrix3x2 matrix, ref Matrix3x2 matrix,
int maxX, int maxX,
@ -103,19 +103,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
/// <param name="rows"></param> /// <param name="rows"></param>
[MethodImpl(InliningOptions.ShortMethod)] [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);
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++) for (int x = 0; x < this.maxX; x++)
{
var point = Point.Transform(new Point(x, y), this.matrix);
if (this.bounds.Contains(point.X, point.Y))
{ {
var point = Point.Transform(new Point(x, y), this.matrix); destRow[x] = this.source[point.X, point.Y];
if (this.bounds.Contains(point.X, point.Y))
{
destRow[x] = this.source[point.X, point.Y];
}
} }
} }
} }

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

@ -4,7 +4,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -48,34 +47,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle bounds = this.cropRectangle; Rectangle bounds = this.cropRectangle;
// Copying is cheap, we should process more pixels per task: // Copying is cheap, we should process more pixels per task:
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
var rowAction = new RowIntervalAction(ref bounds, source, destination); ParallelRowIterator.IterateRows2(
ParallelRowIterator.IterateRows(
bounds, bounds,
in parallelSettings, in parallelSettings,
in rowAction); new RowAction(ref bounds, source, destination));
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the processor logic for <see cref="CropProcessor{T}"/>. /// A <see langword="struct"/> implementing the processor logic for <see cref="CropProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source; private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
/// <summary>
/// Initializes a new instance of the <see cref="RowIntervalAction"/> 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)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(ref Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination) public RowAction(ref Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{ {
this.bounds = bounds; this.bounds = bounds;
this.source = source; this.source = source;
@ -84,14 +74,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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);
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left); sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top);
sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow);
}
} }
} }
} }

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

@ -5,7 +5,6 @@ using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -76,26 +75,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void FlipY(ImageFrame<TPixel> source, Configuration configuration) private void FlipY(ImageFrame<TPixel> source, Configuration configuration)
{ {
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
source.Bounds(), source.Bounds(),
configuration, configuration,
new RowIntervalAction(source)); new RowAction(source));
} }
private readonly struct RowIntervalAction : IRowIntervalAction /// <summary>
/// A <see langword="struct"/> implementing the reverse logic for <see cref="FlipProcessor{T}"/>.
/// </summary>
private readonly struct RowAction : IRowAction
{ {
private readonly ImageFrame<TPixel> source; private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(ImageFrame<TPixel> source) => this.source = source; public RowAction(ImageFrame<TPixel> source) => this.source = source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
for (int y = rows.Min; y < rows.Max; y++) this.source.GetPixelRowSpan(y).Reverse();
{
this.source.GetPixelRowSpan(y).Reverse();
}
} }
} }
} }

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

@ -60,10 +60,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
Rectangle sourceBounds = this.SourceRectangle; Rectangle sourceBounds = this.SourceRectangle;
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
targetBounds, targetBounds,
configuration, configuration,
new NearestNeighborRowIntervalAction(ref sourceBounds, ref matrix, width, source, destination)); new NearestNeighborRowAction(ref sourceBounds, ref matrix, width, source, destination));
return; return;
} }
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination)); new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination));
} }
private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction private readonly struct NearestNeighborRowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Matrix4x4 matrix; private readonly Matrix4x4 matrix;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public NearestNeighborRowIntervalAction( public NearestNeighborRowAction(
ref Rectangle bounds, ref Rectangle bounds,
ref Matrix4x4 matrix, ref Matrix4x4 matrix,
int maxX, int maxX,
@ -100,22 +100,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
[MethodImpl(InliningOptions.ShortMethod)] [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 = TransformUtils.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 = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); destRow[x] = this.source[px, py];
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];
}
} }
} }
} }

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

@ -95,10 +95,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width; float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height; float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height;
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
interest, interest,
configuration, configuration,
new RowIntervalAction(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination)); new RowAction(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination));
return; return;
} }
@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
base.Dispose(disposing); base.Dispose(disposing);
} }
private readonly struct RowIntervalAction : IRowIntervalAction private readonly struct RowAction : IRowAction
{ {
private readonly Rectangle sourceBounds; private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds; private readonly Rectangle destinationBounds;
@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction( public RowAction(
Rectangle sourceBounds, Rectangle sourceBounds,
Rectangle destinationBounds, Rectangle destinationBounds,
float widthFactor, float widthFactor,
@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows) public void Invoke(int y)
{ {
int sourceX = this.sourceBounds.X; int sourceX = this.sourceBounds.X;
int sourceY = this.sourceBounds.Y; int sourceY = this.sourceBounds.Y;
@ -183,17 +183,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int destLeft = this.destinationBounds.Left; int destLeft = this.destinationBounds.Left;
int destRight = this.destinationBounds.Right; 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 // X coordinates of source points
Span<TPixel> sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY)); targetRow[x] = sourceRow[(int)(((x - destX) * this.widthFactor) + sourceX)];
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)];
}
} }
} }
} }

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

@ -131,10 +131,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration) private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
source.Bounds(), source.Bounds(),
configuration, configuration,
new Rotate180RowIntervalAction(source.Width, source.Height, source, destination)); new Rotate180RowAction(source.Width, source.Height, source, destination));
} }
/// <summary> /// <summary>
@ -145,10 +145,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration) private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
source.Bounds(), source.Bounds(),
configuration, configuration,
new Rotate270RowIntervalAction(destination.Bounds(), source.Width, source.Height, source, destination)); new Rotate270RowAction(destination.Bounds(), source.Width, source.Height, source, destination));
} }
/// <summary> /// <summary>
@ -159,13 +159,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate90(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration) private void Rotate90(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows2(
source.Bounds(), source.Bounds(),
configuration, configuration,
new Rotate90RowIntervalAction(destination.Bounds(), source.Width, source.Height, source, destination)); new Rotate90RowAction(destination.Bounds(), source.Width, source.Height, source, destination));
} }
private readonly struct Rotate180RowIntervalAction : IRowIntervalAction private readonly struct Rotate180RowAction : IRowAction
{ {
private readonly int width; private readonly int width;
private readonly int height; private readonly int height;
@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rotate180RowIntervalAction( public Rotate180RowAction(
int width, int width,
int height, int height,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
@ -186,22 +186,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
[MethodImpl(InliningOptions.ShortMethod)] [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++) for (int x = 0; x < this.width; x++)
{ {
targetRow[this.width - x - 1] = sourceRow[x]; targetRow[this.width - x - 1] = sourceRow[x];
}
} }
} }
} }
private readonly struct Rotate270RowIntervalAction : IRowIntervalAction private readonly struct Rotate270RowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly int width; private readonly int width;
@ -210,7 +207,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rotate270RowIntervalAction( public Rotate270RowAction(
Rectangle bounds, Rectangle bounds,
int width, int width,
int height, int height,
@ -225,27 +222,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
[MethodImpl(InliningOptions.ShortMethod)] [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);
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++) newX = this.height - newX - 1;
int newY = this.width - x - 1;
if (this.bounds.Contains(newX, newY))
{ {
int newX = this.height - y - 1; this.destination[newX, newY] = sourceRow[x];
newX = this.height - newX - 1;
int newY = this.width - x - 1;
if (this.bounds.Contains(newX, newY))
{
this.destination[newX, newY] = sourceRow[x];
}
} }
} }
} }
} }
private readonly struct Rotate90RowIntervalAction : IRowIntervalAction private readonly struct Rotate90RowAction : IRowAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly int width; private readonly int width;
@ -254,7 +248,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rotate90RowIntervalAction( public Rotate90RowAction(
Rectangle bounds, Rectangle bounds,
int width, int width,
int height, int height,
@ -269,18 +263,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
[MethodImpl(InliningOptions.ShortMethod)] [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); if (this.bounds.Contains(newX, x))
int newX = this.height - y - 1;
for (int x = 0; x < this.width; x++)
{ {
if (this.bounds.Contains(newX, x)) this.destination[newX, x] = sourceRow[x];
{
this.destination[newX, x] = sourceRow[x];
}
} }
} }
} }

Loading…
Cancel
Save