From b77dcfacf7b8df4ffe0a2e5d78685593439cb151 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 17:20:08 +0100 Subject: [PATCH 1/6] Add single row RowAction value delegate --- src/ImageSharp/Advanced/IRowAction.cs | 70 +++++++++++++++++++ .../Advanced/ParallelRowIterator.cs | 60 ++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/ImageSharp/Advanced/IRowAction.cs diff --git a/src/ImageSharp/Advanced/IRowAction.cs b/src/ImageSharp/Advanced/IRowAction.cs new file mode 100644 index 0000000000..74498eb0b1 --- /dev/null +++ b/src/ImageSharp/Advanced/IRowAction.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Defines the contract for an action that operates on a row. + /// + public interface IRowAction + { + /// + /// Invokes the method passing the row y coordinate. + /// + /// The row y coordinate. + void Invoke(int y); + } + + /// + /// A that wraps a value delegate of a specified type, and info on the memory areas to process + /// + /// The type of value delegate to invoke + internal readonly struct WrappingRowAction + where T : struct, IRowAction + { + public readonly int MinY; + public readonly int MaxY; + public readonly int StepY; + public readonly int MaxX; + + private readonly T action; + + [MethodImpl(InliningOptions.ShortMethod)] + public WrappingRowAction(int minY, int maxY, int stepY, in T action) + : this(minY, maxY, stepY, 0, action) + { + } + + [MethodImpl(InliningOptions.ShortMethod)] + public WrappingRowAction(int minY, int maxY, int stepY, int maxX, in T action) + { + this.MinY = minY; + this.MaxY = maxY; + this.StepY = stepY; + this.MaxX = maxX; + this.action = action; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int i) + { + int yMin = this.MinY + (i * this.StepY); + + if (yMin >= this.MaxY) + { + return; + } + + int yMax = Math.Min(yMin + this.StepY, this.MaxY); + + for (int y = yMin; y < yMax; y++) + { + // Skip the safety copy when invoking a potentially impure method on a readonly field + Unsafe.AsRef(this.action).Invoke(y); + } + } + } +} diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 5c8a30f68b..275c3d10eb 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -76,6 +76,66 @@ public static void IterateRows( rowAction.Invoke); } + /// + /// Iterate through the rows of a rectangle in optimized batches. + /// + /// The type of row action to perform. + /// The . + /// The to get the parallel settings from. + /// The method body defining the iteration logic on a single row. + [MethodImpl(InliningOptions.ShortMethod)] + public static void IterateRows2(Rectangle rectangle, Configuration configuration, in T body) + where T : struct, IRowAction + { + var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); + IterateRows2(rectangle, in parallelSettings, in body); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches. + /// + /// The type of row action to perform. + /// The . + /// The . + /// The method body defining the iteration logic on a single row. + public static void IterateRows2( + Rectangle rectangle, + in ParallelExecutionSettings parallelSettings, + in T body) + where T : struct, IRowAction + { + ValidateRectangle(rectangle); + + int top = rectangle.Top; + int bottom = rectangle.Bottom; + int width = rectangle.Width; + int height = rectangle.Height; + + int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); + int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); + + // Avoid TPL overhead in this trivial case: + if (numOfSteps == 1) + { + for (int y = top; y < bottom; y++) + { + Unsafe.AsRef(body).Invoke(y); + } + + return; + } + + int verticalStep = DivideCeil(rectangle.Height, numOfSteps); + var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; + var rowAction = new WrappingRowAction(top, bottom, verticalStep, in body); + + Parallel.For( + 0, + numOfSteps, + parallelOptions, + rowAction.Invoke); + } + /// /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. From a0ad4ce3c6fc13abab1b8e6ed404302ec1d4c607 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 17:20:21 +0100 Subject: [PATCH 2/6] Refactor processors to single row logic --- src/ImageSharp/ImageFrame{TPixel}.cs | 19 ++-- .../BinaryThresholdProcessor{TPixel}.cs | 31 +++--- .../Convolution/BokehBlurProcessor{TPixel}.cs | 79 +++++++-------- .../EdgeDetectorCompassProcessor{TPixel}.cs | 39 ++++---- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 20 ++-- .../Effects/OilPaintingProcessor{TPixel}.cs | 97 +++++++++---------- ...lHistogramEqualizationProcessor{TPixel}.cs | 50 +++++----- .../BackgroundColorProcessor{TPixel}.cs | 33 +++---- .../AffineTransformProcessor{TPixel}.cs | 25 +++-- .../Transforms/CropProcessor{TPixel}.cs | 31 ++---- .../Transforms/FlipProcessor{TPixel}.cs | 19 ++-- .../ProjectiveTransformProcessor{TPixel}.cs | 29 +++--- .../Resize/ResizeProcessor{TPixel}.cs | 27 +++--- .../Transforms/RotateProcessor{TPixel}.cs | 75 +++++++------- 14 files changed, 253 insertions(+), 321 deletions(-) diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index c3bab8e658..0a345db7c8 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -260,10 +260,10 @@ internal ImageFrame CloneAs(Configuration configuration) var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( this.Bounds(), configuration, - new RowIntervalAction(this, target, configuration)); + new RowAction(this, target, configuration)); return target; } @@ -289,7 +289,7 @@ internal void Clear(TPixel value) /// /// A implementing the clone logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction where TPixel2 : struct, IPixel { private readonly ImageFrame source; @@ -297,7 +297,7 @@ internal void Clear(TPixel value) private readonly Configuration configuration; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( ImageFrame source, ImageFrame target, Configuration configuration) @@ -309,14 +309,11 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = this.source.GetPixelRowSpan(y); - Span targetRow = this.target.GetPixelRowSpan(y); - PixelOperations.Instance.To(this.configuration, sourceRow, targetRow); - } + Span sourceRow = this.source.GetPixelRowSpan(y); + Span targetRow = this.target.GetPixelRowSpan(y); + PixelOperations.Instance.To(this.configuration, sourceRow, targetRow); } } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 9e9dccc464..26685d75be 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/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 @@ protected override void OnFrameApply(ImageFrame source) 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)); } /// /// A implementing the clone logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly ImageFrame source; private readonly TPixel upper; @@ -71,7 +70,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly bool isAlphaOnly; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( ImageFrame source, TPixel upper, TPixel lower, @@ -91,22 +90,20 @@ public RowIntervalAction( /// [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 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 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; } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 834120f84a..71647234b5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -282,10 +282,10 @@ protected override void OnFrameApply(ImageFrame source) 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)); } /// @@ -314,23 +314,23 @@ private void OnFrameApplyCore( 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)); } } /// /// A implementing the vertical convolution logic for . /// - private readonly struct ApplyVerticalConvolutionRowIntervalAction : IRowIntervalAction + private readonly struct ApplyVerticalConvolutionRowAction : IRowAction { private readonly Rectangle bounds; private readonly Buffer2D targetValues; @@ -340,7 +340,7 @@ private void OnFrameApplyCore( private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyVerticalConvolutionRowIntervalAction( + public ApplyVerticalConvolutionRowAction( ref Rectangle bounds, Buffer2D targetValues, Buffer2D sourcePixels, @@ -356,16 +356,13 @@ public ApplyVerticalConvolutionRowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X); + Span 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 @@ public void Invoke(in RowInterval rows) /// /// A implementing the horizontal convolution logic for . /// - private readonly struct ApplyHorizontalConvolutionRowIntervalAction : IRowIntervalAction + private readonly struct ApplyHorizontalConvolutionRowAction : IRowAction { private readonly Rectangle bounds; private readonly Buffer2D targetValues; @@ -385,7 +382,7 @@ public void Invoke(in RowInterval rows) private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyHorizontalConvolutionRowIntervalAction( + public ApplyHorizontalConvolutionRowAction( ref Rectangle bounds, Buffer2D targetValues, Buffer2D sourceValues, @@ -405,16 +402,13 @@ public ApplyHorizontalConvolutionRowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X); + Span 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 @@ public void Invoke(in RowInterval rows, Memory memory) /// /// A implementing the inverse gamma exposure logic for . /// - private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction + private readonly struct ApplyInverseGammaExposureRowAction : IRowAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -480,7 +474,7 @@ public void Invoke(in RowInterval rows, Memory memory) private readonly float inverseGamma; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyInverseGammaExposureRowIntervalAction( + public ApplyInverseGammaExposureRowAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourceValues, @@ -496,28 +490,25 @@ public ApplyInverseGammaExposureRowIntervalAction( /// [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 targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - Span 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 targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + Span sourceRowSpan = this.sourceValues.GetRowSpan(y).Slice(this.bounds.X); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); - PixelOperations.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.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index e2480957ea..6ccd9914bb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -102,17 +102,17 @@ protected override void OnFrameApply(ImageFrame source) 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)); } } /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Buffer2D targetPixels; private readonly Buffer2D passPixels; @@ -122,7 +122,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly int shiftX; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Buffer2D targetPixels, Buffer2D passPixels, int minX, @@ -140,29 +140,26 @@ public RowIntervalAction( /// [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); } } } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 2a181174c7..55399c5fde 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/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 @@ protected override void OnFrameApply(ImageFrame source) "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)); } /// /// A implementing the draw logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly ImageFrame sourceFrame; private readonly Image targetImage; @@ -121,7 +120,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly float opacity; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( ImageFrame sourceFrame, Image targetImage, PixelBlender blender, @@ -145,14 +144,11 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width); - Span foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width); - this.blender.Blend(this.configuration, background, background, foreground, this.opacity); - } + Span background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width); + Span foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width); + this.blender.Blend(this.configuration, background, background, foreground, this.opacity); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 4abaf7ac42..728e4850d4 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -47,10 +47,10 @@ protected override void OnFrameApply(ImageFrame source) 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.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -58,7 +58,7 @@ protected override void OnFrameApply(ImageFrame source) /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -68,7 +68,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly int levels; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Rectangle bounds, Buffer2D targetPixels, ImageFrame source, @@ -86,7 +86,7 @@ public RowIntervalAction( /// [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 @@ public void Invoke(in RowInterval rows) 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 sourceRowPixelSpan = this.source.GetPixelRowSpan(y); - Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width); + Span sourceRowPixelSpan = this.source.GetPixelRowSpan(y); + Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width); - PixelOperations.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); + PixelOperations.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 sourceOffsetRow = this.source.GetPixelRowSpan(offsetY); + Span 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 targetRowAreaPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + Span targetRowAreaPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); - } + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 5d25bae821..b6ae981fc1 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -52,10 +52,10 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(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 histogram = histogramBuffer.GetSpan(); if (this.ClipHistogramEnabled) @@ -74,16 +74,16 @@ ref MemoryMarshal.GetReference(histogram), 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)); } /// /// A implementing the grayscale levels logic for . /// - private readonly struct GrayscaleLevelsRowIntervalAction : IRowIntervalAction + private readonly struct GrayscaleLevelsRowAction : IRowAction { private readonly Rectangle bounds; private readonly IMemoryOwner histogramBuffer; @@ -91,7 +91,7 @@ ref MemoryMarshal.GetReference(histogram), private readonly int luminanceLevels; [MethodImpl(InliningOptions.ShortMethod)] - public GrayscaleLevelsRowIntervalAction( + public GrayscaleLevelsRowAction( in Rectangle bounds, IMemoryOwner histogramBuffer, ImageFrame source, @@ -105,18 +105,15 @@ public GrayscaleLevelsRowIntervalAction( /// [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 @@ public void Invoke(in RowInterval rows) /// /// A implementing the cdf application levels logic for . /// - private readonly struct CdfApplicationRowIntervalAction : IRowIntervalAction + private readonly struct CdfApplicationRowAction : IRowAction { private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; @@ -133,7 +130,7 @@ public void Invoke(in RowInterval rows) private readonly float numberOfPixelsMinusCdfMin; [MethodImpl(InliningOptions.ShortMethod)] - public CdfApplicationRowIntervalAction( + public CdfApplicationRowAction( in Rectangle bounds, IMemoryOwner cdfBuffer, ImageFrame source, @@ -149,20 +146,17 @@ public CdfApplicationRowIntervalAction( /// [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)); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index a9b91e837f..cb19211c29 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -49,13 +49,13 @@ protected override void OnFrameApply(ImageFrame source) PixelBlender blender = PixelOperations.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 @@ protected override void OnFrameApply(ImageFrame source) private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Configuration configuration, Rectangle bounds, PixelBlender blender, @@ -82,23 +82,18 @@ public RowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destination = - this.source.GetPixelRowSpan(y) - .Slice(this.bounds.X, this.bounds.Width); + Span 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()); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 402a052496..9bf9dc5076 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -58,10 +58,10 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame source, ImageFrame /// A implementing the nearest neighbor resampler logic for . /// - private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction + private readonly struct NearestNeighborRowAction : IRowAction { private readonly Rectangle bounds; private readonly Matrix3x2 matrix; @@ -86,7 +86,7 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public NearestNeighborRowIntervalAction( + public NearestNeighborRowAction( Rectangle bounds, ref Matrix3x2 matrix, int maxX, @@ -103,19 +103,16 @@ public NearestNeighborRowIntervalAction( /// /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destRow = this.destination.GetPixelRowSpan(y); + Span 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]; } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 103c5d3ffc..55b1534675 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/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 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame /// A implementing the processor logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Rectangle bounds; private readonly ImageFrame source; private readonly ImageFrame destination; - /// - /// Initializes a new instance of the struct. - /// - /// The target processing bounds for the current instance. - /// The source for the current instance. - /// The destination for the current instance. [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction(ref Rectangle bounds, ImageFrame source, ImageFrame destination) + public RowAction(ref Rectangle bounds, ImageFrame source, ImageFrame destination) { this.bounds = bounds; this.source = source; @@ -84,14 +74,11 @@ public RowIntervalAction(ref Rectangle bounds, ImageFrame source, ImageF /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left); - Span targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top); - sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow); - } + Span sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left); + Span targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top); + sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs index 041f602a55..b88ead08fa 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -5,7 +5,6 @@ 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 @@ private void FlipX(ImageFrame source, Configuration configuration) /// The configuration. private void FlipY(ImageFrame source, Configuration configuration) { - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( source.Bounds(), configuration, - new RowIntervalAction(source)); + new RowAction(source)); } - private readonly struct RowIntervalAction : IRowIntervalAction + /// + /// A implementing the reverse logic for . + /// + private readonly struct RowAction : IRowAction { private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction(ImageFrame source) => this.source = source; + public RowAction(ImageFrame 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(); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 5034b072f5..5a13619c55 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -60,10 +60,10 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame source, ImageFrame source, ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public NearestNeighborRowIntervalAction( + public NearestNeighborRowAction( ref Rectangle bounds, ref Matrix4x4 matrix, int maxX, @@ -100,22 +100,19 @@ public NearestNeighborRowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) + Span destRow = this.destination.GetPixelRowSpan(y); + + for (int x = 0; x < this.maxX; x++) { - Span 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]; } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 02622622d7..027846fc4e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -95,10 +95,10 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Rectangle sourceBounds, Rectangle destinationBounds, float widthFactor, @@ -174,7 +174,7 @@ public RowIntervalAction( } [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 @@ public void Invoke(in RowInterval rows) 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 sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY)); + Span targetRow = this.destination.GetPixelRowSpan(y); + + for (int x = destLeft; x < destRight; x++) { - // Y coordinates of source points - Span sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY)); - Span 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)]; } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index bf03ce319b..6107c8ef9c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -131,10 +131,10 @@ private bool OptimizedApply( /// The configuration. private void Rotate180(ImageFrame source, ImageFrame 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)); } /// @@ -145,10 +145,10 @@ private void Rotate180(ImageFrame source, ImageFrame destination /// The configuration. private void Rotate270(ImageFrame source, ImageFrame 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)); } /// @@ -159,13 +159,13 @@ private void Rotate270(ImageFrame source, ImageFrame destination /// The configuration. private void Rotate90(ImageFrame source, ImageFrame 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 @@ private void Rotate90(ImageFrame source, ImageFrame destination, private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate180RowIntervalAction( + public Rotate180RowAction( int width, int height, ImageFrame source, @@ -186,22 +186,19 @@ public Rotate180RowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = this.source.GetPixelRowSpan(y); - Span targetRow = this.destination.GetPixelRowSpan(this.height - y - 1); + Span sourceRow = this.source.GetPixelRowSpan(y); + Span 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 @@ public void Invoke(in RowInterval rows) private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate270RowIntervalAction( + public Rotate270RowAction( Rectangle bounds, int width, int height, @@ -225,27 +222,24 @@ public Rotate270RowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) + Span sourceRow = this.source.GetPixelRowSpan(y); + for (int x = 0; x < this.width; x++) { - Span 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 @@ public void Invoke(in RowInterval rows) private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate90RowIntervalAction( + public Rotate90RowAction( Rectangle bounds, int width, int height, @@ -269,18 +263,15 @@ public Rotate90RowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) + Span sourceRow = this.source.GetPixelRowSpan(y); + int newX = this.height - y - 1; + for (int x = 0; x < this.width; x++) { - Span 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]; } } } From d6fb30eb3ed0ab1be18d30e7561f10e1334482d8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 17:21:43 +0100 Subject: [PATCH 3/6] Refactor single row APIs --- .../Advanced/ParallelRowIterator.cs | 64 +------------------ src/ImageSharp/ImageFrame{TPixel}.cs | 2 +- .../BinaryThresholdProcessor{TPixel}.cs | 2 +- .../Convolution/BokehBlurProcessor{TPixel}.cs | 6 +- .../EdgeDetectorCompassProcessor{TPixel}.cs | 2 +- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 2 +- .../Effects/OilPaintingProcessor{TPixel}.cs | 2 +- ...lHistogramEqualizationProcessor{TPixel}.cs | 4 +- .../BackgroundColorProcessor{TPixel}.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 2 +- .../Transforms/CropProcessor{TPixel}.cs | 2 +- .../Transforms/FlipProcessor{TPixel}.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 2 +- .../Resize/ResizeProcessor{TPixel}.cs | 2 +- .../Transforms/RotateProcessor{TPixel}.cs | 6 +- 15 files changed, 22 insertions(+), 80 deletions(-) diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 275c3d10eb..7802d96533 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -18,64 +18,6 @@ namespace SixLabors.ImageSharp.Advanced /// public static class ParallelRowIterator { - /// - /// Iterate through the rows of a rectangle in optimized batches defined by -s. - /// - /// The type of row action to perform. - /// The . - /// The to get the parallel settings from. - /// The method body defining the iteration logic on a single . - [MethodImpl(InliningOptions.ShortMethod)] - public static void IterateRows(Rectangle rectangle, Configuration configuration, in T body) - where T : struct, IRowIntervalAction - { - var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); - IterateRows(rectangle, in parallelSettings, in body); - } - - /// - /// Iterate through the rows of a rectangle in optimized batches defined by -s. - /// - /// The type of row action to perform. - /// The . - /// The . - /// The method body defining the iteration logic on a single . - public static void IterateRows( - Rectangle rectangle, - in ParallelExecutionSettings parallelSettings, - in T body) - where T : struct, IRowIntervalAction - { - ValidateRectangle(rectangle); - - int top = rectangle.Top; - int bottom = rectangle.Bottom; - int width = rectangle.Width; - int height = rectangle.Height; - - int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); - int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); - - // Avoid TPL overhead in this trivial case: - if (numOfSteps == 1) - { - var rows = new RowInterval(top, bottom); - Unsafe.AsRef(body).Invoke(in rows); - return; - } - - int verticalStep = DivideCeil(rectangle.Height, numOfSteps); - var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; - var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep); - var rowAction = new WrappingRowIntervalAction(in rowInfo, in body); - - Parallel.For( - 0, - numOfSteps, - parallelOptions, - rowAction.Invoke); - } - /// /// Iterate through the rows of a rectangle in optimized batches. /// @@ -84,11 +26,11 @@ public static void IterateRows( /// The to get the parallel settings from. /// The method body defining the iteration logic on a single row. [MethodImpl(InliningOptions.ShortMethod)] - public static void IterateRows2(Rectangle rectangle, Configuration configuration, in T body) + public static void IterateRows(Rectangle rectangle, Configuration configuration, in T body) where T : struct, IRowAction { var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); - IterateRows2(rectangle, in parallelSettings, in body); + IterateRows(rectangle, in parallelSettings, in body); } /// @@ -98,7 +40,7 @@ public static void IterateRows2(Rectangle rectangle, Configuration configurat /// The . /// The . /// The method body defining the iteration logic on a single row. - public static void IterateRows2( + public static void IterateRows( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T body) diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 0a345db7c8..57b8a953a0 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -260,7 +260,7 @@ internal ImageFrame CloneAs(Configuration configuration) var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone()); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( this.Bounds(), configuration, new RowAction(this, target, configuration)); diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 26685d75be..4db2b938ff 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -50,7 +50,7 @@ protected override void OnFrameApply(ImageFrame source) var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( workingRect, configuration, new RowAction(source, upper, lower, threshold, startX, endX, isAlphaOnly)); diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 71647234b5..3265229963 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -282,7 +282,7 @@ protected override void OnFrameApply(ImageFrame source) float inverseGamma = 1 / this.gamma; // Apply the inverse gamma exposure pass, and write the final pixel data - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( this.SourceRectangle, this.Configuration, new ApplyInverseGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma)); @@ -314,13 +314,13 @@ private void OnFrameApplyCore( Vector4 parameters = Unsafe.Add(ref paramsRef, i); // Compute the vertical 1D convolution - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( sourceRectangle, configuration, new ApplyVerticalConvolutionRowAction(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel)); // Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( sourceRectangle, configuration, new ApplyHorizontalConvolutionRowAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W)); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 6ccd9914bb..85736cbd9a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -102,7 +102,7 @@ protected override void OnFrameApply(ImageFrame source) processor.Apply(pass); } - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( Rectangle.FromLTRB(minX, minY, maxX, maxY), this.Configuration, new RowAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX)); diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 55399c5fde..a12bcb1fdc 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -98,7 +98,7 @@ protected override void OnFrameApply(ImageFrame source) "Cannot draw image because the source image does not overlap the target image."); } - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( workingRect, configuration, new RowAction(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity)); diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 728e4850d4..45f221c937 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -47,7 +47,7 @@ protected override void OnFrameApply(ImageFrame source) source.CopyTo(targetPixels); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( this.SourceRectangle, this.Configuration, new RowAction(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels)); diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index b6ae981fc1..a4a643425f 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -52,7 +52,7 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); // Build the histogram of the grayscale levels - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( workingRect, this.Configuration, new GrayscaleLevelsRowAction(workingRect, histogramBuffer, source, this.LuminanceLevels)); @@ -74,7 +74,7 @@ ref MemoryMarshal.GetReference(histogram), float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; // Apply the cdf to each pixel of the image - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( workingRect, this.Configuration, new CdfApplicationRowAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin)); diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index cb19211c29..2c17d71c6c 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -49,7 +49,7 @@ protected override void OnFrameApply(ImageFrame source) PixelBlender blender = PixelOperations.Instance.GetPixelBlender(graphicsOptions); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, configuration, new RowAction(configuration, interest, blender, amount, colors, source)); diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 9bf9dc5076..8a44dcd9bb 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -58,7 +58,7 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame source, ImageFrame source, Configuration configuration) /// The configuration. private void FlipY(ImageFrame source, Configuration configuration) { - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( source.Bounds(), configuration, new RowAction(source)); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 5a13619c55..6619ea8927 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -60,7 +60,7 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame source, ImageFrameThe configuration. private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( source.Bounds(), configuration, new Rotate180RowAction(source.Width, source.Height, source, destination)); @@ -145,7 +145,7 @@ private void Rotate180(ImageFrame source, ImageFrame destination /// The configuration. private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( source.Bounds(), configuration, new Rotate270RowAction(destination.Bounds(), source.Width, source.Height, source, destination)); @@ -159,7 +159,7 @@ private void Rotate270(ImageFrame source, ImageFrame destination /// The configuration. private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) { - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( source.Bounds(), configuration, new Rotate90RowAction(destination.Bounds(), source.Width, source.Height, source, destination)); From dd4285ded4248b2053321e41bef75ddb73ef1376 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 17:32:09 +0100 Subject: [PATCH 4/6] Add single row value delegate with buffer --- .../Advanced/IRowAction{TBuffer}.cs | 88 +++++++++++++++++++ .../Advanced/ParallelRowIterator.cs | 66 ++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/ImageSharp/Advanced/IRowAction{TBuffer}.cs diff --git a/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs b/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs new file mode 100644 index 0000000000..4bf0d1fe44 --- /dev/null +++ b/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs @@ -0,0 +1,88 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Defines the contract for an action that operates on a row with a temporary buffer. + /// + /// The type of buffer elements. + public interface IRowAction + where TBuffer : unmanaged + { + /// + /// Invokes the method passing the row and a buffer. + /// + /// The row y coordinate. + /// The contiguous region of memory. + void Invoke(int y, Span span); + } + + internal readonly struct WrappingRowAction + where T : struct, IRowAction + where TBuffer : unmanaged + { + public readonly int MinY; + public readonly int MaxY; + public readonly int StepY; + public readonly int MaxX; + + private readonly MemoryAllocator allocator; + private readonly T action; + + [MethodImpl(InliningOptions.ShortMethod)] + public WrappingRowAction( + int minY, + int maxY, + int stepY, + MemoryAllocator allocator, + in T action) + : this(minY, maxY, stepY, 0, allocator, action) + { + } + + [MethodImpl(InliningOptions.ShortMethod)] + public WrappingRowAction( + int minY, + int maxY, + int stepY, + int maxX, + MemoryAllocator allocator, + in T action) + { + this.MinY = minY; + this.MaxY = maxY; + this.StepY = stepY; + this.MaxX = maxX; + this.allocator = allocator; + this.action = action; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int i) + { + int yMin = this.MinY + (i * this.StepY); + + if (yMin >= this.MaxY) + { + return; + } + + int yMax = Math.Min(yMin + this.StepY, this.MaxY); + + using IMemoryOwner buffer = this.allocator.Allocate(this.MaxX); + + Span span = buffer.Memory.Span; + + for (int y = yMin; y < yMax; y++) + { + Unsafe.AsRef(this.action).Invoke(y, span); + } + } + } +} diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 7802d96533..8bfda431a0 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -141,6 +141,72 @@ internal static void IterateRows( rowAction.Invoke); } + /// + /// Iterate through the rows of a rectangle in optimized batches defined by -s + /// instantiating a temporary buffer for each invocation. + /// + /// The type of row action to perform. + /// The type of buffer elements. + /// The . + /// The to get the parallel settings from. + /// The method body defining the iteration logic on a single . + public static void IterateRows2(Rectangle rectangle, Configuration configuration, in T body) + where T : struct, IRowAction + where TBuffer : unmanaged + { + var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); + IterateRows2(rectangle, in parallelSettings, in body); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches defined by -s + /// instantiating a temporary buffer for each invocation. + /// + internal static void IterateRows2( + Rectangle rectangle, + in ParallelExecutionSettings parallelSettings, + in T body) + where T : struct, IRowAction + where TBuffer : unmanaged + { + ValidateRectangle(rectangle); + + int top = rectangle.Top; + int bottom = rectangle.Bottom; + int width = rectangle.Width; + int height = rectangle.Height; + + int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); + int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); + MemoryAllocator allocator = parallelSettings.MemoryAllocator; + + // Avoid TPL overhead in this trivial case: + if (numOfSteps == 1) + { + using (IMemoryOwner buffer = allocator.Allocate(width)) + { + Span span = buffer.Memory.Span; + + for (int y = top; y < bottom; y++) + { + Unsafe.AsRef(body).Invoke(y, span); + } + } + + return; + } + + int verticalStep = DivideCeil(height, numOfSteps); + var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; + var rowAction = new WrappingRowAction(top, bottom, verticalStep, width, allocator, in body); + + Parallel.For( + 0, + numOfSteps, + parallelOptions, + rowAction.Invoke); + } + /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// From 3da200d02abb3db61aaca03204912d5ca0ee844c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 17:47:34 +0100 Subject: [PATCH 5/6] Refactor processors to single row with buffer APIs --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 38 ++++----- .../Convolution2DProcessor{TPixel}.cs | 84 +++++++++---------- .../Convolution2PassProcessor{TPixel}.cs | 84 +++++++++---------- .../ConvolutionProcessor{TPixel}.cs | 80 +++++++++--------- ...lRowDelegateProcessor{TPixel,TDelegate}.cs | 27 +++--- .../Filters/FilterProcessor{TPixel}.cs | 25 +++--- .../Overlays/GlowProcessor{TPixel}.cs | 36 ++++---- .../Overlays/VignetteProcessor{TPixel}.cs | 38 ++++----- .../AffineTransformProcessor{TPixel}.cs | 58 ++++++------- .../ProjectiveTransformProcessor{TPixel}.cs | 69 +++++++-------- 10 files changed, 251 insertions(+), 288 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 3265229963..7eb91b68d1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -268,10 +268,10 @@ private void NormalizeKernels() protected override void OnFrameApply(ImageFrame source) { // Preliminary gamma highlight pass - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( this.SourceRectangle, this.Configuration, - new ApplyGammaExposureRowIntervalAction(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma)); + new ApplyGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma)); // Create a 0-filled buffer to use to store the result of the component convolutions using Buffer2D processingBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean); @@ -416,7 +416,7 @@ public void Invoke(int y) /// /// A implementing the gamma exposure logic for . /// - private readonly struct ApplyGammaExposureRowIntervalAction : IRowIntervalAction + private readonly struct ApplyGammaExposureRowAction : IRowAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -424,7 +424,7 @@ public void Invoke(int y) private readonly float gamma; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyGammaExposureRowIntervalAction( + public ApplyGammaExposureRowAction( Rectangle bounds, Buffer2D targetPixels, Configuration configuration, @@ -438,27 +438,23 @@ public ApplyGammaExposureRowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span vectorSpan = memory.Span; - int length = vectorSpan.Length; + int length = span.Length; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan, PixelConversionModifiers.Premultiply); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan); - - for (int x = 0; x < this.bounds.Width; x++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = MathF.Pow(v.X, this.gamma); - v.Y = MathF.Pow(v.Y, this.gamma); - v.Z = MathF.Pow(v.Z, this.gamma); - } + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span, PixelConversionModifiers.Premultiply); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan.Slice(0, length), targetRowSpan); + for (int x = 0; x < this.bounds.Width; x++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.X = MathF.Pow(v.X, this.gamma); + v.Y = MathF.Pow(v.Y, this.gamma); + v.Z = MathF.Pow(v.Z, this.gamma); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span.Slice(0, length), targetRowSpan); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index cd550a3355..c169ef38d7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -66,10 +66,10 @@ protected override void OnFrameApply(ImageFrame source) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, this.Configuration, - new RowIntervalAction(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha)); + new RowAction(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha)); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -77,7 +77,7 @@ protected override void OnFrameApply(ImageFrame source) /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Rectangle bounds; private readonly int maxY; @@ -90,7 +90,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -112,54 +112,50 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span vectorSpan = memory.Span; - int length = vectorSpan.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); + int length = span.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(span); - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan); + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span); - if (this.preserveAlpha) + if (this.preserveAlpha) + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve2D3( - in this.kernelY, - in this.kernelX, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve2D3( + in this.kernelY, + in this.kernelX, + this.sourcePixels, + ref vectorSpanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - else + } + else + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve2D4( - in this.kernelY, - in this.kernelX, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve2D4( + in this.kernelY, + in this.kernelX, + this.sourcePixels, + ref vectorSpanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index 1be97a4f83..156c20d386 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -64,22 +64,22 @@ protected override void OnFrameApply(ImageFrame source) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); // Horizontal convolution - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, this.Configuration, - new RowIntervalAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha)); + new RowAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha)); // Vertical convolution - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, this.Configuration, - new RowIntervalAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha)); + new RowAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha)); } /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -89,7 +89,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -107,55 +107,51 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span vectorSpan = memory.Span; - int length = vectorSpan.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); + int length = span.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(span); int maxY = this.bounds.Bottom - 1; int maxX = this.bounds.Right - 1; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan); + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span); - if (this.preserveAlpha) + if (this.preserveAlpha) + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve3( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - maxY, - this.bounds.X, - maxX); - } + DenseMatrixUtils.Convolve3( + in this.kernel, + this.sourcePixels, + ref vectorSpanRef, + y, + x, + this.bounds.Y, + maxY, + this.bounds.X, + maxX); } - else + } + else + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve4( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - maxY, - this.bounds.X, - maxX); - } + DenseMatrixUtils.Convolve4( + in this.kernel, + this.sourcePixels, + ref vectorSpanRef, + y, + x, + this.bounds.Y, + maxY, + this.bounds.X, + maxX); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index b68dc56e05..6c56af6bb4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -57,10 +57,10 @@ protected override void OnFrameApply(ImageFrame source) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, this.Configuration, - new RowIntervalAction(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha)); + new RowAction(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha)); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -68,7 +68,7 @@ protected override void OnFrameApply(ImageFrame source) /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Rectangle bounds; private readonly int maxY; @@ -80,7 +80,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -100,52 +100,48 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span vectorSpan = memory.Span; - int length = vectorSpan.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); + int length = span.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(span); - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan); + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span); - if (this.preserveAlpha) + if (this.preserveAlpha) + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve3( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve3( + in this.kernel, + this.sourcePixels, + ref vectorSpanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - else + } + else + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve4( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve4( + in this.kernel, + this.sourcePixels, + ref vectorSpanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index fd725d3ba0..99e0341b4e 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Effects @@ -51,16 +50,16 @@ protected override void OnFrameApply(ImageFrame source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, this.Configuration, - new RowIntervalAction(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate)); + new RowAction(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate)); } /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly int startX; private readonly ImageFrame source; @@ -69,7 +68,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly TDelegate rowProcessor; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( int startX, ImageFrame source, Configuration configuration, @@ -85,20 +84,16 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span vectorSpan = memory.Span; - int length = vectorSpan.Length; - Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, vectorSpan, this.modifiers); + int length = span.Length; + Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length); + PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers); - // Run the user defined pixel shader to the current row of pixels - Unsafe.AsRef(this.rowProcessor).Invoke(vectorSpan, new Point(this.startX, y)); + // Run the user defined pixel shader to the current row of pixels + Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y)); - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan, this.modifiers); - } + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers); } } } diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index cdb67e48b2..2426765f8d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Filters @@ -37,16 +36,16 @@ protected override void OnFrameApply(ImageFrame source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, this.Configuration, - new RowIntervalAction(interest.X, source, this.definition.Matrix, this.Configuration)); + new RowAction(interest.X, source, this.definition.Matrix, this.Configuration)); } /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly int startX; private readonly ImageFrame source; @@ -54,7 +53,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly Configuration configuration; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( int startX, ImageFrame source, ColorMatrix matrix, @@ -68,19 +67,15 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span vectorSpan = memory.Span; - int length = vectorSpan.Length; - Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, vectorSpan); + int length = span.Length; + Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length); + PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span); - Vector4Utils.Transform(vectorSpan, ref Unsafe.AsRef(this.matrix)); + Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix)); - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan); - } + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 65a87fbf01..1b321310dc 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -55,13 +55,13 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(glowColor); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, configuration, - new RowIntervalAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); + new RowAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); } - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -73,7 +73,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Configuration configuration, Rectangle bounds, IMemoryOwner colors, @@ -94,28 +94,24 @@ public RowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span amountsSpan = memory.Span; Span colorSpan = this.colors.GetSpan(); - for (int y = rows.Min; y < rows.Max; y++) + for (int i = 0; i < this.bounds.Width; i++) { - for (int i = 0; i < this.bounds.Width; i++) - { - float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y)); - amountsSpan[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1); - } + float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y)); + span[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1); + } - Span destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + Span destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - this.blender.Blend( - this.configuration, - destination, - destination, - colorSpan, - amountsSpan); - } + this.blender.Blend( + this.configuration, + destination, + destination, + colorSpan, + span); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 11887433c0..1ca83190c6 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -63,13 +63,13 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(vignetteColor); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows2( interest, configuration, - new RowIntervalAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); + new RowAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); } - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -81,7 +81,7 @@ protected override void OnFrameApply(ImageFrame source) private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Configuration configuration, Rectangle bounds, IMemoryOwner colors, @@ -102,28 +102,24 @@ public RowIntervalAction( } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span amountsSpan = memory.Span; Span colorSpan = this.colors.GetSpan(); - for (int y = rows.Min; y < rows.Max; y++) + for (int i = 0; i < this.bounds.Width; i++) { - for (int i = 0; i < this.bounds.Width; i++) - { - float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y)); - amountsSpan[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1); - } - - Span destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - - this.blender.Blend( - this.configuration, - destination, - destination, - colorSpan, - amountsSpan); + float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y)); + span[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1); } + + Span destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + + this.blender.Blend( + this.configuration, + destination, + destination, + colorSpan, + span); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 8a44dcd9bb..e7f6f429c4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -68,10 +67,10 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame( + ParallelRowIterator.IterateRows2( targetBounds, configuration, - new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination)); + new RowAction(configuration, kernelMap, ref matrix, width, source, destination)); } /// @@ -101,7 +100,6 @@ public NearestNeighborRowAction( } /// - /// [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { @@ -121,7 +119,7 @@ public void Invoke(int y) /// /// A implementing the transformation logic for . /// - private readonly struct RowIntervalAction : IRowIntervalAction + private readonly struct RowAction : IRowAction { private readonly Configuration configuration; private readonly TransformKernelMap kernelMap; @@ -131,7 +129,7 @@ public void Invoke(int y) private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Configuration configuration, TransformKernelMap kernelMap, ref Matrix3x2 matrix, @@ -149,35 +147,31 @@ public RowIntervalAction( /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span vectorSpan = memory.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan, vectorSpan); - ref float ySpanRef = ref this.kernelMap.GetYStartReference(y); - ref float xSpanRef = ref this.kernelMap.GetXStartReference(y); - - for (int x = 0; x < this.maxX; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - var point = Vector2.Transform(new Vector2(x, y), this.matrix); - this.kernelMap.Convolve( - point, - x, - ref ySpanRef, - ref xSpanRef, - this.source.PixelBuffer, - vectorSpan); - } + Span targetRowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan, span); + ref float ySpanRef = ref this.kernelMap.GetYStartReference(y); + ref float xSpanRef = ref this.kernelMap.GetXStartReference(y); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - vectorSpan, - targetRowSpan); + for (int x = 0; x < this.maxX; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var point = Vector2.Transform(new Vector2(x, y), this.matrix); + this.kernelMap.Convolve( + point, + x, + ref ySpanRef, + ref xSpanRef, + this.source.PixelBuffer, + span); } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + span, + targetRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 6619ea8927..8e219f7ccf 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -17,9 +16,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ProjectiveTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - private Size targetSize; + private readonly Size targetSize; private readonly IResampler resampler; - private Matrix4x4 transformMatrix; + private readonly Matrix4x4 transformMatrix; /// /// Initializes a new instance of the class. @@ -70,12 +69,15 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame( + ParallelRowIterator.IterateRows2( targetBounds, configuration, - new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination)); + new RowAction(configuration, kernelMap, ref matrix, width, source, destination)); } + /// + /// A implementing the nearest neighbor interpolation logic for . + /// private readonly struct NearestNeighborRowAction : IRowAction { private readonly Rectangle bounds; @@ -99,6 +101,7 @@ public NearestNeighborRowAction( this.destination = destination; } + /// [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { @@ -118,7 +121,10 @@ public void Invoke(int y) } } - private readonly struct RowIntervalAction : IRowIntervalAction + /// + /// A implementing the convolution logic for . + /// + private readonly struct RowAction : IRowAction { private readonly Configuration configuration; private readonly TransformKernelMap kernelMap; @@ -128,7 +134,7 @@ public void Invoke(int y) private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalAction( + public RowAction( Configuration configuration, TransformKernelMap kernelMap, ref Matrix4x4 matrix, @@ -144,36 +150,33 @@ public RowIntervalAction( this.destination = destination; } + /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Memory memory) + public void Invoke(int y, Span span) { - Span vectorSpan = memory.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan, vectorSpan); - ref float ySpanRef = ref this.kernelMap.GetYStartReference(y); - ref float xSpanRef = ref this.kernelMap.GetXStartReference(y); - - for (int x = 0; x < this.maxX; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); - this.kernelMap.Convolve( - point, - x, - ref ySpanRef, - ref xSpanRef, - this.source.PixelBuffer, - vectorSpan); - } + Span targetRowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan, span); + ref float ySpanRef = ref this.kernelMap.GetYStartReference(y); + ref float xSpanRef = ref this.kernelMap.GetXStartReference(y); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - vectorSpan, - targetRowSpan); + for (int x = 0; x < this.maxX; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); + this.kernelMap.Convolve( + point, + x, + ref ySpanRef, + ref xSpanRef, + this.source.PixelBuffer, + span); } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + span, + targetRowSpan); } } } From b545ea0979495b7ea1117bc6b7517b2c5091e18d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 17:48:12 +0100 Subject: [PATCH 6/6] Rename APIs --- .../Advanced/ParallelRowIterator.cs | 65 +------------------ .../Convolution/BokehBlurProcessor{TPixel}.cs | 2 +- .../Convolution2DProcessor{TPixel}.cs | 2 +- .../Convolution2PassProcessor{TPixel}.cs | 4 +- .../ConvolutionProcessor{TPixel}.cs | 2 +- ...lRowDelegateProcessor{TPixel,TDelegate}.cs | 2 +- .../Filters/FilterProcessor{TPixel}.cs | 2 +- .../Overlays/GlowProcessor{TPixel}.cs | 2 +- .../Overlays/VignetteProcessor{TPixel}.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 2 +- 11 files changed, 12 insertions(+), 75 deletions(-) diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 8bfda431a0..d57e673c0e 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -88,7 +88,7 @@ public static void IterateRows( /// The to get the parallel settings from. /// The method body defining the iteration logic on a single . public static void IterateRows(Rectangle rectangle, Configuration configuration, in T body) - where T : struct, IRowIntervalAction + where T : struct, IRowAction where TBuffer : unmanaged { var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); @@ -100,69 +100,6 @@ public static void IterateRows(Rectangle rectangle, Configuration co /// instantiating a temporary buffer for each invocation. /// internal static void IterateRows( - Rectangle rectangle, - in ParallelExecutionSettings parallelSettings, - in T body) - where T : struct, IRowIntervalAction - where TBuffer : unmanaged - { - ValidateRectangle(rectangle); - - int top = rectangle.Top; - int bottom = rectangle.Bottom; - int width = rectangle.Width; - int height = rectangle.Height; - - int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); - int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); - MemoryAllocator allocator = parallelSettings.MemoryAllocator; - - // Avoid TPL overhead in this trivial case: - if (numOfSteps == 1) - { - var rows = new RowInterval(top, bottom); - using (IMemoryOwner buffer = allocator.Allocate(width)) - { - Unsafe.AsRef(body).Invoke(rows, buffer.Memory); - } - - return; - } - - int verticalStep = DivideCeil(height, numOfSteps); - var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; - var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep, width); - var rowAction = new WrappingRowIntervalBufferAction(in rowInfo, allocator, in body); - - Parallel.For( - 0, - numOfSteps, - parallelOptions, - rowAction.Invoke); - } - - /// - /// Iterate through the rows of a rectangle in optimized batches defined by -s - /// instantiating a temporary buffer for each invocation. - /// - /// The type of row action to perform. - /// The type of buffer elements. - /// The . - /// The to get the parallel settings from. - /// The method body defining the iteration logic on a single . - public static void IterateRows2(Rectangle rectangle, Configuration configuration, in T body) - where T : struct, IRowAction - where TBuffer : unmanaged - { - var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); - IterateRows2(rectangle, in parallelSettings, in body); - } - - /// - /// Iterate through the rows of a rectangle in optimized batches defined by -s - /// instantiating a temporary buffer for each invocation. - /// - internal static void IterateRows2( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T body) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 7eb91b68d1..05508c90fb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -268,7 +268,7 @@ private void NormalizeKernels() protected override void OnFrameApply(ImageFrame source) { // Preliminary gamma highlight pass - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( this.SourceRectangle, this.Configuration, new ApplyGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma)); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index c169ef38d7..a6d47fa587 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -66,7 +66,7 @@ protected override void OnFrameApply(ImageFrame source) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, this.Configuration, new RowAction(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha)); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index 156c20d386..1c6ca8b921 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -64,13 +64,13 @@ protected override void OnFrameApply(ImageFrame source) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); // Horizontal convolution - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, this.Configuration, new RowAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha)); // Vertical convolution - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, this.Configuration, new RowAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha)); diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 6c56af6bb4..6a2acf7707 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -57,7 +57,7 @@ protected override void OnFrameApply(ImageFrame source) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, this.Configuration, new RowAction(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha)); diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index 99e0341b4e..c0f4797560 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -50,7 +50,7 @@ protected override void OnFrameApply(ImageFrame source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, this.Configuration, new RowAction(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate)); diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 2426765f8d..c797c13587 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -36,7 +36,7 @@ protected override void OnFrameApply(ImageFrame source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, this.Configuration, new RowAction(interest.X, source, this.definition.Matrix, this.Configuration)); diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 1b321310dc..6777e32345 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -55,7 +55,7 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(glowColor); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, configuration, new RowAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 1ca83190c6..6e2c3c4427 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -63,7 +63,7 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(vignetteColor); - ParallelRowIterator.IterateRows2( + ParallelRowIterator.IterateRows( interest, configuration, new RowAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index e7f6f429c4..447a99eeca 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -67,7 +67,7 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame( + ParallelRowIterator.IterateRows( targetBounds, configuration, new RowAction(configuration, kernelMap, ref matrix, width, source, destination)); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 8e219f7ccf..5989f23893 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -69,7 +69,7 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame( + ParallelRowIterator.IterateRows( targetBounds, configuration, new RowAction(configuration, kernelMap, ref matrix, width, source, destination));