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());
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
this.Bounds(),
configuration,
new RowIntervalAction<TPixel2>(this, target, configuration));
new RowAction<TPixel2>(this, target, configuration));
return target;
}
@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// A <see langword="struct"/> implementing the clone logic for <see cref="ImageFrame{TPixel}"/>.
/// </summary>
private readonly struct RowIntervalAction<TPixel2> : IRowIntervalAction
private readonly struct RowAction<TPixel2> : IRowAction
where TPixel2 : struct, IPixel<TPixel2>
{
private readonly ImageFrame<TPixel> source;
@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp
private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
ImageFrame<TPixel> source,
ImageFrame<TPixel2> target,
Configuration configuration)
@ -309,14 +309,11 @@ namespace SixLabors.ImageSharp
/// <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);
Span<TPixel2> targetRow = this.target.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.To(this.configuration, sourceRow, targetRow);
}
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization
@ -51,16 +50,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
workingRect,
configuration,
new RowIntervalAction(source, upper, lower, threshold, startX, endX, isAlphaOnly));
new RowAction(source, upper, lower, threshold, startX, endX, isAlphaOnly));
}
/// <summary>
/// A <see langword="struct"/> implementing the clone logic for <see cref="BinaryThresholdProcessor{TPixel}"/>.
/// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction
private readonly struct RowAction : IRowAction
{
private readonly ImageFrame<TPixel> source;
private readonly TPixel upper;
@ -71,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
private readonly bool isAlphaOnly;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
ImageFrame<TPixel> source,
TPixel upper,
TPixel lower,
@ -91,22 +90,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);
for (int x = this.startX; x < this.endX; x++)
{
ref TPixel color = ref row[x];
color.ToRgba32(ref rgba);
Span<TPixel> row = this.source.GetPixelRowSpan(y);
for (int x = this.startX; x < this.endX; x++)
{
ref TPixel color = ref row[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;
}
}
}

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

@ -282,10 +282,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float inverseGamma = 1 / this.gamma;
// Apply the inverse gamma exposure pass, and write the final pixel data
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
this.SourceRectangle,
this.Configuration,
new ApplyInverseGammaExposureRowIntervalAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma));
new ApplyInverseGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma));
}
/// <summary>
@ -314,23 +314,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Vector4 parameters = Unsafe.Add(ref paramsRef, i);
// Compute the vertical 1D convolution
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
sourceRectangle,
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
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
sourceRectangle,
configuration,
new ApplyHorizontalConvolutionRowIntervalAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W));
new ApplyHorizontalConvolutionRowAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W));
}
}
/// <summary>
/// A <see langword="struct"/> implementing the vertical convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyVerticalConvolutionRowIntervalAction : IRowIntervalAction
private readonly struct ApplyVerticalConvolutionRowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly Buffer2D<ComplexVector4> targetValues;
@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyVerticalConvolutionRowIntervalAction(
public ApplyVerticalConvolutionRowAction(
ref Rectangle bounds,
Buffer2D<ComplexVector4> targetValues,
Buffer2D<TPixel> sourcePixels,
@ -356,16 +356,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);
}
}
}
@ -373,7 +370,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 ApplyHorizontalConvolutionRowIntervalAction : IRowIntervalAction
private readonly struct ApplyHorizontalConvolutionRowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly Buffer2D<Vector4> targetValues;
@ -385,7 +382,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyHorizontalConvolutionRowIntervalAction(
public ApplyHorizontalConvolutionRowAction(
ref Rectangle bounds,
Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues,
@ -405,16 +402,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);
}
}
}
@ -471,7 +465,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// A <see langword="struct"/> implementing the inverse gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction
private readonly struct ApplyInverseGammaExposureRowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
@ -480,7 +474,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly float inverseGamma;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyInverseGammaExposureRowIntervalAction(
public ApplyInverseGammaExposureRowAction(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<Vector4> sourceValues,
@ -496,28 +490,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++)
{
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);
}
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);
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);
}
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
Rectangle.FromLTRB(minX, minY, maxX, maxY),
this.Configuration,
new RowIntervalAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX));
new RowAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX));
}
}
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="EdgeDetectorCompassProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction
private readonly struct RowAction : IRowAction
{
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> passPixels;
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int shiftX;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> passPixels,
int minX,
@ -140,29 +140,26 @@ 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++)
{
int offsetY = y - this.shiftY;
int offsetY = y - this.shiftY;
ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(offsetY));
ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(offsetY));
ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(offsetY));
ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(offsetY));
for (int x = this.minX; x < this.maxX; x++)
{
int offsetX = x - this.shiftX;
for (int x = this.minX; x < this.maxX; x++)
{
int offsetX = x - this.shiftX;
// Grab the max components of the two pixels
ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX);
ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX);
// Grab the max components of the two pixels
ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX);
ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX);
var pixelValue = Vector4.Max(
currentPassPixel.ToVector4(),
currentTargetPixel.ToVector4());
var pixelValue = Vector4.Max(
currentPassPixel.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.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
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.");
}
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
workingRect,
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>
/// A <see langword="struct"/> implementing the draw logic for <see cref="DrawImageProcessor{TPixelBg,TPixelFg}"/>.
/// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction
private readonly struct RowAction : IRowAction
{
private readonly ImageFrame<TPixelBg> sourceFrame;
private readonly Image<TPixelFg> targetImage;
@ -121,7 +120,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
private readonly float opacity;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
ImageFrame<TPixelBg> sourceFrame,
Image<TPixelFg> targetImage,
PixelBlender<TPixelBg> blender,
@ -145,14 +144,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);
}
}
}

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

@ -47,10 +47,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
source.CopyTo(targetPixels);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
this.SourceRectangle,
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);
}
@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="OilPaintingProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction
private readonly struct RowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private readonly int levels;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
ImageFrame<TPixel> source,
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
public void Invoke(int y)
{
int maxY = this.bounds.Bottom - 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 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++)
{
int maxIntensity = 0;
int maxIndex = 0;
for (int x = this.bounds.X; x < this.bounds.Right; x++)
{
int maxIntensity = 0;
int maxIndex = 0;
// Clear the current shared buffer before processing each target pixel
bins.Memory.Span.Clear();
// Clear the current shared buffer before processing each target pixel
bins.Memory.Span.Clear();
for (int fy = 0; fy <= this.radius; fy++)
{
int fyr = fy - this.radius;
int offsetY = y + fyr;
for (int fy = 0; fy <= this.radius; fy++)
{
int fyr = fy - this.radius;
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++)
{
int fxr = fx - this.radius;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
for (int fx = 0; fx <= this.radius; fx++)
{
int fxr = fx - this.radius;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
var vector = sourceOffsetRow[offsetX].ToVector4();
var vector = sourceOffsetRow[offsetX].ToVector4();
float sourceRed = vector.X;
float sourceBlue = vector.Z;
float sourceGreen = vector.Y;
float sourceRed = vector.X;
float sourceBlue = vector.Z;
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 redBinRef, currentIntensity) += sourceRed;
Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue;
Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen;
Unsafe.Add(ref intensityBinRef, currentIntensity)++;
Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed;
Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue;
Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen;
if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity)
{
maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity);
maxIndex = currentIntensity;
}
if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity)
{
maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity);
maxIndex = currentIntensity;
}
}
float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity);
float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity);
float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity);
float alpha = sourceRowVector4Span[x].W;
float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity);
float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity);
float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity);
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);
// Build the histogram of the grayscale levels
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
workingRect,
this.Configuration,
new GrayscaleLevelsRowIntervalAction(workingRect, histogramBuffer, source, this.LuminanceLevels));
new GrayscaleLevelsRowAction(workingRect, histogramBuffer, source, this.LuminanceLevels));
Span<int> histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled)
@ -74,16 +74,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
workingRect,
this.Configuration,
new CdfApplicationRowIntervalAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin));
new CdfApplicationRowAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin));
}
/// <summary>
/// A <see langword="struct"/> implementing the grayscale levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary>
private readonly struct GrayscaleLevelsRowIntervalAction : IRowIntervalAction
private readonly struct GrayscaleLevelsRowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer;
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly int luminanceLevels;
[MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowIntervalAction(
public GrayscaleLevelsRowAction(
in Rectangle bounds,
IMemoryOwner<int> histogramBuffer,
ImageFrame<TPixel> source,
@ -105,18 +105,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)++;
}
}
}
@ -124,7 +121,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 CdfApplicationRowIntervalAction : IRowIntervalAction
private readonly struct CdfApplicationRowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer;
@ -133,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly float numberOfPixelsMinusCdfMin;
[MethodImpl(InliningOptions.ShortMethod)]
public CdfApplicationRowIntervalAction(
public CdfApplicationRowAction(
in Rectangle bounds,
IMemoryOwner<int> cdfBuffer,
ImageFrame<TPixel> source,
@ -149,20 +146,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,13 +49,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(graphicsOptions);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
interest,
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 Rectangle bounds;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
Configuration configuration,
Rectangle bounds,
PixelBlender<TPixel> blender,
@ -82,23 +82,18 @@ 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());
}
}
}

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

@ -58,10 +58,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.resampler is NearestNeighborResampler)
{
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
targetBounds,
configuration,
new NearestNeighborRowIntervalAction(this.SourceRectangle, ref matrix, width, source, destination));
new NearestNeighborRowAction(this.SourceRectangle, ref matrix, width, source, destination));
return;
}
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary>
/// A <see langword="struct"/> implementing the nearest neighbor resampler logic for <see cref="AffineTransformProcessor{T}"/>.
/// </summary>
private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction
private readonly struct NearestNeighborRowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly Matrix3x2 matrix;
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public NearestNeighborRowIntervalAction(
public NearestNeighborRowAction(
Rectangle bounds,
ref Matrix3x2 matrix,
int maxX,
@ -103,19 +103,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
/// <param name="rows"></param>
[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);
if (this.bounds.Contains(point.X, point.Y))
{
destRow[x] = this.source[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.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -48,34 +47,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle bounds = this.cropRectangle;
// Copying is cheap, we should process more pixels per task:
ParallelExecutionSettings parallelSettings =
ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
var rowAction = new RowIntervalAction(ref bounds, source, destination);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
bounds,
in parallelSettings,
in rowAction);
new RowAction(ref bounds, source, destination));
}
/// <summary>
/// A <see langword="struct"/> implementing the processor logic for <see cref="CropProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction
private readonly struct RowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source;
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)]
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.source = source;
@ -84,14 +74,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);
}
}
}

19
src/ImageSharp/Processing/Processors/Transforms/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,26 +75,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param>
private void FlipY(ImageFrame<TPixel> source, Configuration configuration)
{
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
source.Bounds(),
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;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(ImageFrame<TPixel> source) => this.source = source;
public RowAction(ImageFrame<TPixel> source) => this.source = source;
[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;
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
targetBounds,
configuration,
new NearestNeighborRowIntervalAction(ref sourceBounds, ref matrix, width, source, destination));
new NearestNeighborRowAction(ref sourceBounds, ref matrix, width, source, destination));
return;
}
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
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 Matrix4x4 matrix;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public NearestNeighborRowIntervalAction(
public NearestNeighborRowAction(
ref Rectangle bounds,
ref Matrix4x4 matrix,
int maxX,
@ -100,22 +100,19 @@ 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 = 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);
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];
}
}
}

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 heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height;
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows2(
interest,
configuration,
new RowIntervalAction(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination));
new RowAction(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination));
return;
}
@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
base.Dispose(disposing);
}
private readonly struct RowIntervalAction : IRowIntervalAction
private readonly struct RowAction : IRowAction
{
private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds;
@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
public RowAction(
Rectangle sourceBounds,
Rectangle destinationBounds,
float widthFactor,
@ -174,7 +174,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;
@ -183,17 +183,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)];
}
}
}

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

Loading…
Cancel
Save