diff --git a/src/ImageSharp/Advanced/IRowIntervalOperation.cs b/src/ImageSharp/Advanced/IRowIntervalOperation.cs index 3e1b086218..980ed91a78 100644 --- a/src/ImageSharp/Advanced/IRowIntervalOperation.cs +++ b/src/ImageSharp/Advanced/IRowIntervalOperation.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Advanced diff --git a/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs b/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs index c18842a92c..47fcf253eb 100644 --- a/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs +++ b/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Advanced diff --git a/src/ImageSharp/Advanced/IRowOperation.cs b/src/ImageSharp/Advanced/IRowOperation.cs new file mode 100644 index 0000000000..0a6065e4b1 --- /dev/null +++ b/src/ImageSharp/Advanced/IRowOperation.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Defines the contract for an action that operates on a row. + /// + public interface IRowOperation + { + /// + /// Invokes the method passing the row y coordinate. + /// + /// The row y coordinate. + void Invoke(int y); + } +} diff --git a/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs b/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs new file mode 100644 index 0000000000..7a13930fad --- /dev/null +++ b/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Defines the contract for an action that operates on a row with a temporary buffer. + /// + /// The type of buffer elements. + public interface IRowOperation + 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); + } +} diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs index adbad0d662..3f0f77ca39 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs @@ -17,51 +17,130 @@ namespace SixLabors.ImageSharp.Advanced /// public static partial class ParallelRowIterator { - private readonly struct IterationParameters + private readonly struct RowOperationWrapper + where T : struct, IRowOperation { - public readonly int MinY; - public readonly int MaxY; - public readonly int StepY; - public readonly int Width; + private readonly int minY; + private readonly int maxY; + private readonly int stepY; + private readonly T action; - public IterationParameters(int minY, int maxY, int stepY) - : this(minY, maxY, stepY, 0) + [MethodImpl(InliningOptions.ShortMethod)] + public RowOperationWrapper( + int minY, + int maxY, + int stepY, + in T action) { + this.minY = minY; + this.maxY = maxY; + this.stepY = stepY; + this.action = action; } - public IterationParameters(int minY, int maxY, int stepY, int width) + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int i) { - this.MinY = minY; - this.MaxY = maxY; - this.StepY = stepY; - this.Width = width; + int yMin = this.minY + (i * this.stepY); + + if (yMin >= this.maxY) + { + return; + } + + int yMax = Math.Min(yMin + this.stepY, this.maxY); + + for (int y = yMin; y < yMax; y++) + { + // Skip the safety copy when invoking a potentially impure method on a readonly field + Unsafe.AsRef(this.action).Invoke(y); + } + } + } + + private readonly struct RowOperationWrapper + where T : struct, IRowOperation + where TBuffer : unmanaged + { + private readonly int minY; + private readonly int maxY; + private readonly int stepY; + private readonly int width; + private readonly MemoryAllocator allocator; + private readonly T action; + + [MethodImpl(InliningOptions.ShortMethod)] + public RowOperationWrapper( + int minY, + int maxY, + int stepY, + int width, + MemoryAllocator allocator, + in T action) + { + this.minY = minY; + this.maxY = maxY; + this.stepY = stepY; + this.width = width; + this.allocator = allocator; + this.action = action; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int i) + { + int yMin = this.minY + (i * this.stepY); + + if (yMin >= this.maxY) + { + return; + } + + int yMax = Math.Min(yMin + this.stepY, this.maxY); + + using IMemoryOwner buffer = this.allocator.Allocate(this.width); + + Span span = buffer.Memory.Span; + + for (int y = yMin; y < yMax; y++) + { + Unsafe.AsRef(this.action).Invoke(y, span); + } } } private readonly struct RowIntervalOperationWrapper where T : struct, IRowIntervalOperation { - private readonly IterationParameters info; + private readonly int minY; + private readonly int maxY; + private readonly int stepY; private readonly T operation; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperationWrapper(in IterationParameters info, in T operation) + public RowIntervalOperationWrapper( + int minY, + int maxY, + int stepY, + in T operation) { - this.info = info; + this.minY = minY; + this.maxY = maxY; + this.stepY = stepY; this.operation = operation; } [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int i) { - int yMin = this.info.MinY + (i * this.info.StepY); + int yMin = this.minY + (i * this.stepY); - if (yMin >= this.info.MaxY) + if (yMin >= this.maxY) { return; } - int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY); + int yMax = Math.Min(yMin + this.stepY, this.maxY); var rows = new RowInterval(yMin, yMax); // Skip the safety copy when invoking a potentially impure method on a readonly field @@ -73,17 +152,26 @@ namespace SixLabors.ImageSharp.Advanced where T : struct, IRowIntervalOperation where TBuffer : unmanaged { - private readonly IterationParameters info; + private readonly int minY; + private readonly int maxY; + private readonly int stepY; + private readonly int width; private readonly MemoryAllocator allocator; private readonly T operation; [MethodImpl(InliningOptions.ShortMethod)] public RowIntervalOperationWrapper( - in IterationParameters info, + int minY, + int maxY, + int stepY, + int width, MemoryAllocator allocator, in T operation) { - this.info = info; + this.minY = minY; + this.maxY = maxY; + this.stepY = stepY; + this.width = width; this.allocator = allocator; this.operation = operation; } @@ -91,17 +179,17 @@ namespace SixLabors.ImageSharp.Advanced [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int i) { - int yMin = this.info.MinY + (i * this.info.StepY); + int yMin = this.minY + (i * this.stepY); - if (yMin >= this.info.MaxY) + if (yMin >= this.maxY) { return; } - int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY); + int yMax = Math.Min(yMin + this.stepY, this.maxY); var rows = new RowInterval(yMin, yMax); - using IMemoryOwner buffer = this.allocator.Allocate(this.info.Width); + using IMemoryOwner buffer = this.allocator.Allocate(this.width); Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span); } diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 123784c57f..fb85de9863 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -17,6 +17,135 @@ namespace SixLabors.ImageSharp.Advanced /// public static partial class ParallelRowIterator { + /// + /// Iterate through the rows of a rectangle in optimized batches. + /// + /// The type of row operation to perform. + /// The to get the parallel settings from. + /// The . + /// The operation defining the iteration logic on a single row. + [MethodImpl(InliningOptions.ShortMethod)] + public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation) + where T : struct, IRowOperation + { + var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); + IterateRows(rectangle, in parallelSettings, in operation); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches. + /// + /// The type of row operation to perform. + /// The . + /// The . + /// The operation defining the iteration logic on a single row. + public static void IterateRows( + Rectangle rectangle, + in ParallelExecutionSettings parallelSettings, + in T operation) + where T : struct, IRowOperation + { + ValidateRectangle(rectangle); + + int top = rectangle.Top; + int bottom = rectangle.Bottom; + int width = rectangle.Width; + int height = rectangle.Height; + + int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); + int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); + + // Avoid TPL overhead in this trivial case: + if (numOfSteps == 1) + { + for (int y = top; y < bottom; y++) + { + Unsafe.AsRef(operation).Invoke(y); + } + + return; + } + + int verticalStep = DivideCeil(rectangle.Height, numOfSteps); + var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; + var wrappingOperation = new RowOperationWrapper(top, bottom, verticalStep, in operation); + + Parallel.For( + 0, + numOfSteps, + parallelOptions, + wrappingOperation.Invoke); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches. + /// instantiating a temporary buffer for each invocation. + /// + /// The type of row operation to perform. + /// The type of buffer elements. + /// The to get the parallel settings from. + /// The . + /// The operation defining the iteration logic on a single row. + public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation) + where T : struct, IRowOperation + where TBuffer : unmanaged + { + var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); + IterateRows(rectangle, in parallelSettings, in operation); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches. + /// instantiating a temporary buffer for each invocation. + /// + /// The type of row operation to perform. + /// The type of buffer elements. + /// The . + /// The . + /// The operation defining the iteration logic on a single row. + public static void IterateRows( + Rectangle rectangle, + in ParallelExecutionSettings parallelSettings, + in T operation) + where T : struct, IRowOperation + 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(operation).Invoke(y, span); + } + + return; + } + + int verticalStep = DivideCeil(height, numOfSteps); + var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; + var wrappingOperation = new RowOperationWrapper(top, bottom, verticalStep, width, allocator, in operation); + + Parallel.For( + 0, + numOfSteps, + parallelOptions, + wrappingOperation.Invoke); + } + /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// @@ -25,11 +154,11 @@ namespace SixLabors.ImageSharp.Advanced /// The . /// The operation defining the iteration logic on a single . [MethodImpl(InliningOptions.ShortMethod)] - public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation) + public static void IterateRowIntervals(Configuration configuration, Rectangle rectangle, in T operation) where T : struct, IRowIntervalOperation { var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); - IterateRows(rectangle, in parallelSettings, in operation); + IterateRowIntervals(rectangle, in parallelSettings, in operation); } /// @@ -39,7 +168,7 @@ namespace SixLabors.ImageSharp.Advanced /// The . /// The . /// The operation defining the iteration logic on a single . - public static void IterateRows( + public static void IterateRowIntervals( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T operation) @@ -65,8 +194,7 @@ namespace SixLabors.ImageSharp.Advanced int verticalStep = DivideCeil(rectangle.Height, numOfSteps); var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; - var info = new IterationParameters(top, bottom, verticalStep); - var wrappingOperation = new RowIntervalOperationWrapper(in info, in operation); + var wrappingOperation = new RowIntervalOperationWrapper(top, bottom, verticalStep, in operation); Parallel.For( 0, @@ -84,12 +212,12 @@ namespace SixLabors.ImageSharp.Advanced /// The to get the parallel settings from. /// The . /// The operation defining the iteration logic on a single . - public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation) + public static void IterateRowIntervals(Configuration configuration, Rectangle rectangle, in T operation) where T : struct, IRowIntervalOperation where TBuffer : unmanaged { var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); - IterateRows(rectangle, in parallelSettings, in operation); + IterateRowIntervals(rectangle, in parallelSettings, in operation); } /// @@ -101,7 +229,7 @@ namespace SixLabors.ImageSharp.Advanced /// The . /// The . /// The operation defining the iteration logic on a single . - public static void IterateRows( + public static void IterateRowIntervals( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T operation) @@ -123,18 +251,16 @@ namespace SixLabors.ImageSharp.Advanced if (numOfSteps == 1) { var rows = new RowInterval(top, bottom); - using (IMemoryOwner buffer = allocator.Allocate(width)) - { - Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span); - } + using IMemoryOwner buffer = allocator.Allocate(width); + + Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span); return; } int verticalStep = DivideCeil(height, numOfSteps); var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; - var info = new IterationParameters(top, bottom, verticalStep, width); - var wrappingOperation = new RowIntervalOperationWrapper(in info, allocator, in operation); + var wrappingOperation = new RowIntervalOperationWrapper(top, bottom, verticalStep, width, allocator, in operation); Parallel.For( 0, diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 321f2938c5..a35443ec9d 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone()); var operation = new RowIntervalOperation(this, target, configuration); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( configuration, this.Bounds(), in operation); diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 18b3471441..0d363689dd 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -3,8 +3,8 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Binarization @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); bool isAlphaOnly = typeof(TPixel) == typeof(A8); - var operation = new RowIntervalOperation(interest, source, upper, lower, threshold, isAlphaOnly); + var operation = new RowOperation(interest, source, upper, lower, threshold, isAlphaOnly); ParallelRowIterator.IterateRows( configuration, interest, @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// A implementing the clone logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly ImageFrame source; private readonly TPixel upper; @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization private readonly bool isAlphaOnly; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Rectangle bounds, ImageFrame source, TPixel upper, @@ -84,22 +84,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// [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); + Span row = this.source.GetPixelRowSpan(y); + ref TPixel rowRef = ref MemoryMarshal.GetReference(row); - for (int x = this.minX; x < this.maxX; x++) - { - ref TPixel color = ref row[x]; - color.ToRgba32(ref rgba); + for (int x = this.minX; x < this.maxX; x++) + { + ref TPixel color = ref Unsafe.Add(ref rowRef, x); + color.ToRgba32(ref rgba); - // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); - color = luminance >= this.threshold ? this.upper : this.lower; - } + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + color = luminance >= this.threshold ? this.upper : this.lower; } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 17f60ebefc..493218cde5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -72,8 +72,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution protected override void OnFrameApply(ImageFrame source) { // Preliminary gamma highlight pass - var gammaOperation = new ApplyGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma); - ParallelRowIterator.IterateRows( + var gammaOperation = new ApplyGammaExposureRowOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma); + ParallelRowIterator.IterateRows( this.Configuration, this.SourceRectangle, in gammaOperation); @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution float inverseGamma = 1 / this.gamma; // Apply the inverse gamma exposure pass, and write the final pixel data - var operation = new ApplyInverseGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma); + var operation = new ApplyInverseGammaExposureRowOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma); ParallelRowIterator.IterateRows( this.Configuration, this.SourceRectangle, @@ -120,14 +120,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution Vector4 parameters = Unsafe.Add(ref paramsRef, i); // Compute the vertical 1D convolution - var verticalOperation = new ApplyVerticalConvolutionRowIntervalOperation(sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel); + var verticalOperation = new ApplyVerticalConvolutionRowOperation(sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel); ParallelRowIterator.IterateRows( configuration, sourceRectangle, in verticalOperation); // Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer - var horizontalOperation = new ApplyHorizontalConvolutionRowIntervalOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W); + var horizontalOperation = new ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W); ParallelRowIterator.IterateRows( configuration, sourceRectangle, @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the vertical convolution logic for . /// - private readonly struct ApplyVerticalConvolutionRowIntervalOperation : IRowIntervalOperation + private readonly struct ApplyVerticalConvolutionRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetValues; @@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyVerticalConvolutionRowIntervalOperation( + public ApplyVerticalConvolutionRowOperation( Rectangle bounds, Buffer2D targetValues, Buffer2D sourcePixels, @@ -164,16 +164,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [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); } } } @@ -181,7 +178,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the horizontal convolution logic for . /// - private readonly struct ApplyHorizontalConvolutionRowIntervalOperation : IRowIntervalOperation + private readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetValues; @@ -193,7 +190,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyHorizontalConvolutionRowIntervalOperation( + public ApplyHorizontalConvolutionRowOperation( Rectangle bounds, Buffer2D targetValues, Buffer2D sourceValues, @@ -213,16 +210,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [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); } } } @@ -230,7 +224,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the gamma exposure logic for . /// - private readonly struct ApplyGammaExposureRowIntervalOperation : IRowIntervalOperation + private readonly struct ApplyGammaExposureRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -238,7 +232,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly float gamma; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyGammaExposureRowIntervalOperation( + public ApplyGammaExposureRowOperation( Rectangle bounds, Buffer2D targetPixels, Configuration configuration, @@ -252,31 +246,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span 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, span.Length), span, PixelConversionModifiers.Premultiply); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); + + for (int x = 0; x < this.bounds.Width; x++) { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); - - for (int x = 0; x < this.bounds.Width; x++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = MathF.Pow(v.X, this.gamma); - v.Y = MathF.Pow(v.Y, this.gamma); - v.Z = MathF.Pow(v.Z, this.gamma); - } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.X = MathF.Pow(v.X, this.gamma); + v.Y = MathF.Pow(v.Y, this.gamma); + v.Z = MathF.Pow(v.Z, this.gamma); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); } } /// /// A implementing the inverse gamma exposure logic for . /// - private readonly struct ApplyInverseGammaExposureRowIntervalOperation : IRowIntervalOperation + private readonly struct ApplyInverseGammaExposureRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -285,7 +276,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly float inverseGamma; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyInverseGammaExposureRowIntervalOperation( + public ApplyInverseGammaExposureRowOperation( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourceValues, @@ -301,28 +292,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [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++) { - 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); - } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply); + ref Vector4 v = ref Unsafe.Add(ref sourceRef, x); + var clamp = Vector4.Clamp(v, low, high); + v.X = MathF.Pow(clamp.X, this.inverseGamma); + v.Y = MathF.Pow(clamp.Y, this.inverseGamma); + v.Z = MathF.Pow(clamp.Z, this.inverseGamma); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index b4902fbd7e..f7439879e4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -65,9 +65,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution source.CopyTo(targetPixels); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - var operation = new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha); + var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( this.Configuration, interest, in operation); @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly int maxY; @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -113,52 +113,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { ref Vector4 spanRef = ref MemoryMarshal.GetReference(span); + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span); - for (int y = rows.Min; y < rows.Max; y++) + if (this.preserveAlpha) { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span); - - if (this.preserveAlpha) + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve2D3( - in this.kernelY, - in this.kernelX, - this.sourcePixels, - ref spanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve2D3( + in this.kernelY, + in this.kernelX, + this.sourcePixels, + ref spanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - else + } + else + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve2D4( - in this.kernelY, - in this.kernelX, - this.sourcePixels, - ref spanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve2D4( + in this.kernelY, + in this.kernelX, + this.sourcePixels, + ref spanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, 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 30f2c6b4e6..4bbb15cbad 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -64,15 +64,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); // Horizontal convolution - var horizontalOperation = new RowIntervalOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha); - ParallelRowIterator.IterateRows( + var horizontalOperation = new RowOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha); + ParallelRowIterator.IterateRows( this.Configuration, interest, in horizontalOperation); // Vertical convolution - var verticalOperation = new RowIntervalOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha); - ParallelRowIterator.IterateRows( + var verticalOperation = new RowOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha); + ParallelRowIterator.IterateRows( this.Configuration, interest, in verticalOperation); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -109,53 +109,50 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { ref Vector4 spanRef = ref MemoryMarshal.GetReference(span); int maxY = this.bounds.Bottom - 1; int maxX = this.bounds.Right - 1; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span); + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span); - if (this.preserveAlpha) + if (this.preserveAlpha) + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve3( - in this.kernel, - this.sourcePixels, - ref spanRef, - y, - x, - this.bounds.Y, - maxY, - this.bounds.X, - maxX); - } + DenseMatrixUtils.Convolve3( + in this.kernel, + this.sourcePixels, + ref spanRef, + y, + x, + this.bounds.Y, + maxY, + this.bounds.X, + maxX); } - else + } + else + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve4( - in this.kernel, - this.sourcePixels, - ref spanRef, - y, - x, - this.bounds.Y, - maxY, - this.bounds.X, - maxX); - } + DenseMatrixUtils.Convolve4( + in this.kernel, + this.sourcePixels, + ref spanRef, + y, + x, + this.bounds.Y, + maxY, + this.bounds.X, + maxX); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, 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 b6f9bb319e..a2c8fc1fbe 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution source.CopyTo(targetPixels); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - var operation = new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha); - ParallelRowIterator.IterateRows( + var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha); + ParallelRowIterator.IterateRows( this.Configuration, interest, in operation); @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly int maxY; @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -100,50 +100,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { ref Vector4 spanRef = 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, span.Length), span); + Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span); - if (this.preserveAlpha) + if (this.preserveAlpha) + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve3( - in this.kernel, - this.sourcePixels, - ref spanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve3( + in this.kernel, + this.sourcePixels, + ref spanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - else + } + else + { + for (int x = 0; x < this.bounds.Width; x++) { - for (int x = 0; x < this.bounds.Width; x++) - { - DenseMatrixUtils.Convolve4( - in this.kernel, - this.sourcePixels, - ref spanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); - } + DenseMatrixUtils.Convolve4( + in this.kernel, + this.sourcePixels, + ref spanRef, + y, + x, + this.bounds.Y, + this.maxY, + this.bounds.X, + this.maxX); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index f93fe20b14..159c67b5cf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution processor.Apply(pass); } - var operation = new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, interest); + var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Buffer2D targetPixels; private readonly Buffer2D passPixels; @@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Buffer2D targetPixels, Buffer2D passPixels, Rectangle bounds) @@ -110,23 +110,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(y)); - ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(y)); + ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(y)); + ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(y)); - for (int x = this.minX; x < this.maxX; x++) - { - // Grab the max components of the two pixels - ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x); - ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x); + for (int x = this.minX; x < this.maxX; x++) + { + // Grab the max components of the two pixels + ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x); + ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x); - var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4()); + var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4()); - currentTargetPixel.FromVector4(pixelValue); - } + currentTargetPixel.FromVector4(pixelValue); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs index 7803f92b43..6efb84be99 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering palette, ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length)); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( quantizer.Configuration, bounds, in ditherOperation); @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering scale, ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length)); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( configuration, bounds, in ditherOperation); diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index ae938aec1b..fca896929f 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing "Cannot draw image because the source image does not overlap the target image."); } - var operation = new RowIntervalOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity); + var operation = new RowOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity); ParallelRowIterator.IterateRows( configuration, workingRect, @@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// /// A implementing the draw logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly ImageFrame sourceFrame; private readonly Image targetImage; @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing private readonly float opacity; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( ImageFrame sourceFrame, Image targetImage, PixelBlender blender, @@ -146,14 +146,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// [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 270eb1343c..5ee1e40de0 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects source.CopyTo(targetPixels); var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( this.Configuration, this.SourceRectangle, in operation); diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index 1907228c96..71259a6188 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -50,9 +50,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects protected override void OnFrameApply(ImageFrame source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - var operation = new RowIntervalOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate); + var operation = new RowOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( this.Configuration, interest, in operation); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly int startX; private readonly ImageFrame source; @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects private readonly TDelegate rowProcessor; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( int startX, ImageFrame source, Configuration configuration, @@ -86,18 +86,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers); + Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.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(span, new Point(this.startX, y)); + // Run the user defined pixel shader to the current row of pixels + Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y)); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, 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 545c53915a..7da4eb1b10 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -36,9 +36,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters protected override void OnFrameApply(ImageFrame source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - var operation = new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration); + var operation = new RowOperation(interest.X, source, this.definition.Matrix, this.Configuration); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( this.Configuration, interest, in operation); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// A implementing the convolution logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly int startX; private readonly ImageFrame source; @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters private readonly Configuration configuration; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( int startX, ImageFrame source, ColorMatrix matrix, @@ -69,17 +69,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span); + Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); + PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span); - Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix)); + Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix)); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan); - } + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 5fdedad6ed..1547de8acf 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( this.Configuration, new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), in operation); @@ -522,7 +522,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization this.luminanceLevels, source); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( this.configuration, new Rectangle(0, 0, this.sourceWidth, this.tileYStartPositions.Count), in operation); diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index a99fb405e8..209135debb 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); // Build the histogram of the grayscale levels - var grayscaleOperation = new GrayscaleLevelsRowIntervalOperation(interest, histogramBuffer, source, this.LuminanceLevels); + var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source, this.LuminanceLevels); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; // Apply the cdf to each pixel of the image - var cdfOperation = new CdfApplicationRowIntervalOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin); + var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// A implementing the grayscale levels logic for . /// - private readonly struct GrayscaleLevelsRowIntervalOperation : IRowIntervalOperation + private readonly struct GrayscaleLevelsRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly IMemoryOwner histogramBuffer; @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private readonly int luminanceLevels; [MethodImpl(InliningOptions.ShortMethod)] - public GrayscaleLevelsRowIntervalOperation( + public GrayscaleLevelsRowOperation( Rectangle bounds, IMemoryOwner histogramBuffer, ImageFrame source, @@ -107,18 +107,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); + ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); - for (int x = 0; x < this.bounds.Width; x++) - { - int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; - } + for (int x = 0; x < this.bounds.Width; x++) + { + int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; } } } @@ -126,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// A implementing the cdf application levels logic for . /// - private readonly struct CdfApplicationRowIntervalOperation : IRowIntervalOperation + private readonly struct CdfApplicationRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; @@ -135,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private readonly float numberOfPixelsMinusCdfMin; [MethodImpl(InliningOptions.ShortMethod)] - public CdfApplicationRowIntervalOperation( + public CdfApplicationRowOperation( Rectangle bounds, IMemoryOwner cdfBuffer, ImageFrame source, @@ -151,20 +148,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// [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 a1e9d1c4ef..727e724698 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays PixelBlender blender = PixelOperations.Instance.GetPixelBlender(graphicsOptions); - var operation = new RowIntervalOperation(configuration, interest, blender, amount, colors, source); + var operation = new RowOperation(configuration, interest, blender, amount, colors, source); ParallelRowIterator.IterateRows( configuration, interest, in operation); } - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Configuration configuration, Rectangle bounds, PixelBlender blender, @@ -83,23 +83,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span 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/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 33c4123e99..fbecbc37ce 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(glowColor); - var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); - ParallelRowIterator.IterateRows( + var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); + ParallelRowIterator.IterateRows( configuration, interest, in operation); } - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Configuration configuration, Rectangle bounds, IMemoryOwner colors, @@ -95,27 +95,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span 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)); - span[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1); - } + float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y)); + span[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1); + } - Span 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, - span); - } + 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 0618873f25..378009c400 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -63,14 +63,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(vignetteColor); - var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); - ParallelRowIterator.IterateRows( + var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); + ParallelRowIterator.IterateRows( configuration, interest, in operation); } - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( + public RowOperation( Configuration configuration, Rectangle bounds, IMemoryOwner colors, @@ -103,27 +103,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span 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)); - 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); + 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/Quantization/FrameQuantizerExtensions.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs index a88d2b1e21..306e9d36ef 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (dither is null) { var operation = new RowIntervalOperation(quantizer, source, output, bounds, palette); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( quantizer.Configuration, bounds, in operation); diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index 5a0116a03f..cbef193005 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization using QuantizedFrame quantized = frameQuantizer.QuantizeFrame(source, interest); var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( configuration, interest, in operation); diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index a4bcb3d719..a366fd51de 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4); - var operation = new RowIntervalOperation(bounds, source, destination); + var operation = new RowOperation(bounds, source, destination); ParallelRowIterator.IterateRows( bounds, @@ -62,20 +62,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// A implementing the processor logic for . /// - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly ImageFrame source; private readonly ImageFrame destination; /// - /// Initializes a new instance of the struct. + /// 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 RowIntervalOperation(Rectangle bounds, ImageFrame source, ImageFrame destination) + public RowOperation(Rectangle bounds, ImageFrame source, ImageFrame destination) { this.bounds = bounds; this.source = source; @@ -84,14 +84,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span 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/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index ac18c67cd6..a3c8f71082 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms in operation); } - private readonly struct NNAffineOperation : IRowIntervalOperation + private readonly struct NNAffineOperation : IRowOperation { private readonly ImageFrame source; private readonly ImageFrame destination; @@ -133,28 +133,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) + Span destRow = this.destination.GetPixelRowSpan(y); + + for (int x = 0; x < this.maxX; x++) { - Span destRow = this.destination.GetPixelRowSpan(y); + var point = Vector2.Transform(new Vector2(x, y), this.matrix); + int px = (int)MathF.Round(point.X); + int py = (int)MathF.Round(point.Y); - for (int x = 0; x < this.maxX; x++) + if (this.bounds.Contains(px, py)) { - var point = Vector2.Transform(new Vector2(x, y), this.matrix); - int px = (int)MathF.Round(point.X); - int py = (int)MathF.Round(point.Y); - - if (this.bounds.Contains(px, py)) - { - destRow[x] = this.source[px, py]; - } + destRow[x] = this.source[px, py]; } } } } - private readonly struct AffineOperation : IRowIntervalOperation + private readonly struct AffineOperation : IRowOperation where TResampler : struct, IResampler { private readonly Configuration configuration; @@ -193,41 +190,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { Buffer2D sourceBuffer = this.source.PixelBuffer; - for (int y = rows.Min; y < rows.Max; y++) - { - PixelOperations.Instance.ToVector4( - this.configuration, - this.destination.GetPixelRowSpan(y), - span); - ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y)); - ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y)); + PixelOperations.Instance.ToVector4( + this.configuration, + this.destination.GetPixelRowSpan(y), + span); - for (int x = 0; x < this.maxX; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - var point = Vector2.Transform(new Vector2(x, y), this.matrix); - LinearTransformUtilities.Convolve( - in this.sampler, - point, - sourceBuffer, - span, - x, - ref yKernelSpanRef, - ref xKernelSpanRef, - this.radialExtents, - this.maxSourceExtents); - } + ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y)); + ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y)); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, + for (int x = 0; x < this.maxX; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var point = Vector2.Transform(new Vector2(x, y), this.matrix); + LinearTransformUtilities.Convolve( + in this.sampler, + point, + sourceBuffer, span, - this.destination.GetPixelRowSpan(y)); + x, + ref yKernelSpanRef, + ref xKernelSpanRef, + this.radialExtents, + this.maxSourceExtents); } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + span, + this.destination.GetPixelRowSpan(y)); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs index 8db2d42c61..470eafcd8d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -76,28 +75,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void FlipY(ImageFrame source, Configuration configuration) { - var operation = new RowIntervalOperation(source); + var operation = new RowOperation(source); ParallelRowIterator.IterateRows( configuration, source.Bounds(), in operation); } - private readonly struct RowIntervalOperation : IRowIntervalOperation + private readonly struct RowOperation : IRowOperation { private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation(ImageFrame source) => this.source = source; + public RowOperation(ImageFrame source) => this.source = source; [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) - { - for (int y = rows.Min; y < rows.Max; y++) - { - this.source.GetPixelRowSpan(y).Reverse(); - } - } + public void Invoke(int y) => this.source.GetPixelRowSpan(y).Reverse(); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index 89ebb50f24..f348721d7a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms in operation); } - private readonly struct NNProjectiveOperation : IRowIntervalOperation + private readonly struct NNProjectiveOperation : IRowOperation { private readonly ImageFrame source; private readonly ImageFrame destination; @@ -133,28 +133,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) + Span destRow = this.destination.GetPixelRowSpan(y); + + for (int x = 0; x < this.maxX; x++) { - Span destRow = this.destination.GetPixelRowSpan(y); + Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix); + int px = (int)MathF.Round(point.X); + int py = (int)MathF.Round(point.Y); - for (int x = 0; x < this.maxX; x++) + if (this.bounds.Contains(px, py)) { - Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix); - int px = (int)MathF.Round(point.X); - int py = (int)MathF.Round(point.Y); - - if (this.bounds.Contains(px, py)) - { - destRow[x] = this.source[px, py]; - } + destRow[x] = this.source[px, py]; } } } } - private readonly struct ProjectiveOperation : IRowIntervalOperation + private readonly struct ProjectiveOperation : IRowOperation where TResampler : struct, IResampler { private readonly Configuration configuration; @@ -193,41 +190,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows, Span span) + public void Invoke(int y, Span span) { Buffer2D sourceBuffer = this.source.PixelBuffer; - for (int y = rows.Min; y < rows.Max; y++) - { - PixelOperations.Instance.ToVector4( - this.configuration, - this.destination.GetPixelRowSpan(y), - span); - ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y)); - ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y)); + PixelOperations.Instance.ToVector4( + this.configuration, + this.destination.GetPixelRowSpan(y), + span); - for (int x = 0; x < this.maxX; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix); - LinearTransformUtilities.Convolve( - in this.sampler, - point, - sourceBuffer, - span, - x, - ref yKernelSpanRef, - ref xKernelSpanRef, - this.radialExtents, - this.maxSourceExtents); - } + ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y)); + ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y)); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, + for (int x = 0; x < this.maxX; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix); + LinearTransformUtilities.Convolve( + in this.sampler, + point, + sourceBuffer, span, - this.destination.GetPixelRowSpan(y)); + x, + ref yKernelSpanRef, + ref xKernelSpanRef, + this.radialExtents, + this.maxSourceExtents); } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + span, + this.destination.GetPixelRowSpan(y)); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs index 6ce5c71f95..43f67f7914 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs @@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { - var operation = new Rotate180RowIntervalOperation(source.Width, source.Height, source, destination); + var operation = new Rotate180RowOperation(source.Width, source.Height, source, destination); ParallelRowIterator.IterateRows( configuration, source.Bounds(), @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { var operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( configuration, source.Bounds(), in operation); @@ -161,14 +161,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) { - var operation = new Rotate90RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination); + var operation = new Rotate90RowOperation(destination.Bounds(), source.Width, source.Height, source, destination); ParallelRowIterator.IterateRows( configuration, source.Bounds(), in operation); } - private readonly struct Rotate180RowIntervalOperation : IRowIntervalOperation + private readonly struct Rotate180RowOperation : IRowOperation { private readonly int width; private readonly int height; @@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate180RowIntervalOperation( + public Rotate180RowOperation( int width, int height, ImageFrame source, @@ -189,17 +189,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span 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]; } } } @@ -248,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - private readonly struct Rotate90RowIntervalOperation : IRowIntervalOperation + private readonly struct Rotate90RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly int width; @@ -257,7 +254,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate90RowIntervalOperation( + public Rotate90RowOperation( Rectangle bounds, int width, int height, @@ -272,18 +269,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { - for (int y = rows.Min; y < rows.Max; y++) + Span 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]; } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 89bd94e06d..6eafbda891 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width; float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height; - var operation = new NNRowIntervalOperation( + var operation = new NNRowOperation( sourceRectangle, destinationRectangle, widthFactor, @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - private readonly struct NNRowIntervalOperation : IRowIntervalOperation + private readonly struct NNRowOperation : IRowOperation { private readonly Rectangle sourceBounds; private readonly Rectangle destinationBounds; @@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public NNRowIntervalOperation( + public NNRowOperation( Rectangle sourceBounds, Rectangle destinationBounds, float widthFactor, @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(in RowInterval rows) + public void Invoke(int y) { int sourceX = this.sourceBounds.X; int sourceY = this.sourceBounds.Y; @@ -229,17 +229,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int destLeft = this.destinationBounds.Left; int destRight = this.destinationBounds.Right; - for (int y = rows.Min; y < rows.Max; y++) + // Y coordinates of source points + Span 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/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs index 332a141e9d..08d64a738d 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( rectangle, in parallelSettings, in operation); @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( rectangle, in parallelSettings, in operation); @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows, Vector4>( + ParallelRowIterator.IterateRowIntervals, Vector4>( rectangle, in parallelSettings, in operation); @@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows, Vector4>( + ParallelRowIterator.IterateRowIntervals, Vector4>( rectangle, in parallelSettings, in operation); @@ -249,7 +249,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( rectangle, in parallelSettings, in operation); @@ -291,7 +291,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows, Vector4>( + ParallelRowIterator.IterateRowIntervals, Vector4>( rectangle, in parallelSettings, in operation); @@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( rect, settings, in operation); @@ -383,7 +383,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); ArgumentOutOfRangeException ex = Assert.Throws( - () => ParallelRowIterator.IterateRows(rect, in parallelSettings, in operation)); + () => ParallelRowIterator.IterateRowIntervals(rect, in parallelSettings, in operation)); Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message); } @@ -406,7 +406,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers var operation = new TestRowIntervalOperation(RowAction); ArgumentOutOfRangeException ex = Assert.Throws( - () => ParallelRowIterator.IterateRows, Rgba32>(rect, in parallelSettings, in operation)); + () => ParallelRowIterator.IterateRowIntervals, Rgba32>(rect, in parallelSettings, in operation)); Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message); } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 0b78138ac4..8ecb2ee6ce 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -713,7 +713,7 @@ namespace SixLabors.ImageSharp.Tests var operation = new RowOperation(configuration, sourceRectangle, source); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRowIntervals( configuration, sourceRectangle, in operation);