From 0952bc8f8d7c24feed74c58367f555944d3b5405 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 8 Feb 2020 22:13:17 +1100 Subject: [PATCH] Revert "Merge pull request #1108 from SixLabors/sp/single-row-parallel-value-delegate" This reverts commit 1835475ad8cd3e2353e7c15065eb9c832394d535, reversing changes made to dd7bd7b27919db9dd0432472f0e7aea3e1c53e48. --- src/ImageSharp/Advanced/IRowAction.cs | 70 ----------- .../Advanced/IRowAction{TBuffer}.cs | 88 -------------- .../Advanced/ParallelRowIterator.cs | 37 +++--- src/ImageSharp/ImageFrame{TPixel}.cs | 17 +-- .../BinaryThresholdProcessor{TPixel}.cs | 29 +++-- .../Convolution/BokehBlurProcessor{TPixel}.cs | 111 ++++++++++-------- .../Convolution2DProcessor{TPixel}.cs | 84 ++++++------- .../Convolution2PassProcessor{TPixel}.cs | 84 ++++++------- .../ConvolutionProcessor{TPixel}.cs | 80 +++++++------ .../EdgeDetectorCompassProcessor{TPixel}.cs | 37 +++--- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 18 +-- .../Effects/OilPaintingProcessor{TPixel}.cs | 95 +++++++-------- ...lRowDelegateProcessor{TPixel,TDelegate}.cs | 27 +++-- .../Filters/FilterProcessor{TPixel}.cs | 25 ++-- ...lHistogramEqualizationProcessor{TPixel}.cs | 46 ++++---- .../BackgroundColorProcessor{TPixel}.cs | 31 +++-- .../Overlays/GlowProcessor{TPixel}.cs | 36 +++--- .../Overlays/VignetteProcessor{TPixel}.cs | 38 +++--- .../AffineTransformProcessor{TPixel}.cs | 81 +++++++------ .../Transforms/CropProcessor{TPixel}.cs | 29 +++-- .../Transforms/FlipProcessor{TPixel}.cs | 17 +-- .../ProjectiveTransformProcessor{TPixel}.cs | 96 +++++++-------- .../Resize/ResizeProcessor{TPixel}.cs | 25 ++-- .../Transforms/RotateProcessor{TPixel}.cs | 69 ++++++----- 24 files changed, 606 insertions(+), 664 deletions(-) delete mode 100644 src/ImageSharp/Advanced/IRowAction.cs delete mode 100644 src/ImageSharp/Advanced/IRowAction{TBuffer}.cs diff --git a/src/ImageSharp/Advanced/IRowAction.cs b/src/ImageSharp/Advanced/IRowAction.cs deleted file mode 100644 index 74498eb0b1..0000000000 --- a/src/ImageSharp/Advanced/IRowAction.cs +++ /dev/null @@ -1,70 +0,0 @@ -// 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/IRowAction{TBuffer}.cs b/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs deleted file mode 100644 index 4bf0d1fe44..0000000000 --- a/src/ImageSharp/Advanced/IRowAction{TBuffer}.cs +++ /dev/null @@ -1,88 +0,0 @@ -// 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 d57e673c0e..5c8a30f68b 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -19,32 +19,32 @@ namespace SixLabors.ImageSharp.Advanced public static class ParallelRowIterator { /// - /// Iterate through the rows of a rectangle in optimized batches. + /// 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 row. + /// 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, IRowAction + 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. + /// 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 row. + /// 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, IRowAction + where T : struct, IRowIntervalAction { ValidateRectangle(rectangle); @@ -59,17 +59,15 @@ namespace SixLabors.ImageSharp.Advanced // Avoid TPL overhead in this trivial case: if (numOfSteps == 1) { - for (int y = top; y < bottom; y++) - { - Unsafe.AsRef(body).Invoke(y); - } - + 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 rowAction = new WrappingRowAction(top, bottom, verticalStep, in body); + var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep); + var rowAction = new WrappingRowIntervalAction(in rowInfo, in body); Parallel.For( 0, @@ -88,7 +86,7 @@ namespace SixLabors.ImageSharp.Advanced /// 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, IRowAction + where T : struct, IRowIntervalAction where TBuffer : unmanaged { var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); @@ -103,7 +101,7 @@ namespace SixLabors.ImageSharp.Advanced Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T body) - where T : struct, IRowAction + where T : struct, IRowIntervalAction where TBuffer : unmanaged { ValidateRectangle(rectangle); @@ -120,14 +118,10 @@ namespace SixLabors.ImageSharp.Advanced // Avoid TPL overhead in this trivial case: if (numOfSteps == 1) { + var rows = new RowInterval(top, bottom); using (IMemoryOwner buffer = allocator.Allocate(width)) { - Span span = buffer.Memory.Span; - - for (int y = top; y < bottom; y++) - { - Unsafe.AsRef(body).Invoke(y, span); - } + Unsafe.AsRef(body).Invoke(rows, buffer.Memory); } return; @@ -135,7 +129,8 @@ namespace SixLabors.ImageSharp.Advanced int verticalStep = DivideCeil(height, numOfSteps); var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; - var rowAction = new WrappingRowAction(top, bottom, verticalStep, width, allocator, in body); + var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep, width); + var rowAction = new WrappingRowIntervalBufferAction(in rowInfo, allocator, in body); Parallel.For( 0, diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 57b8a953a0..c3bab8e658 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp ParallelRowIterator.IterateRows( this.Bounds(), configuration, - new RowAction(this, target, configuration)); + new RowIntervalAction(this, target, configuration)); return target; } @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp /// /// A implementing the clone logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction where TPixel2 : struct, IPixel { private readonly ImageFrame source; @@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp private readonly Configuration configuration; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( ImageFrame source, ImageFrame target, Configuration configuration) @@ -309,11 +309,14 @@ namespace SixLabors.ImageSharp /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span sourceRow = this.source.GetPixelRowSpan(y); - Span targetRow = this.target.GetPixelRowSpan(y); - PixelOperations.Instance.To(this.configuration, sourceRow, targetRow); + 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); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 4db2b938ff..9e9dccc464 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Binarization @@ -53,13 +54,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization ParallelRowIterator.IterateRows( workingRect, configuration, - new RowAction(source, upper, lower, threshold, startX, endX, isAlphaOnly)); + new RowIntervalAction(source, upper, lower, threshold, startX, endX, isAlphaOnly)); } /// /// A implementing the clone logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly ImageFrame source; private readonly TPixel upper; @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization private readonly bool isAlphaOnly; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( ImageFrame source, TPixel upper, TPixel lower, @@ -90,20 +91,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { Rgba32 rgba = default; - - Span row = this.source.GetPixelRowSpan(y); - - for (int x = this.startX; x < this.endX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - 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 05508c90fb..834120f84a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -268,10 +268,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution protected override void OnFrameApply(ImageFrame source) { // Preliminary gamma highlight pass - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( this.SourceRectangle, this.Configuration, - new ApplyGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma)); + new ApplyGammaExposureRowIntervalAction(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); @@ -285,7 +285,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ParallelRowIterator.IterateRows( this.SourceRectangle, this.Configuration, - new ApplyInverseGammaExposureRowAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma)); + new ApplyInverseGammaExposureRowIntervalAction(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma)); } /// @@ -317,20 +317,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ParallelRowIterator.IterateRows( sourceRectangle, configuration, - new ApplyVerticalConvolutionRowAction(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel)); + new ApplyVerticalConvolutionRowIntervalAction(ref sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel)); // Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer ParallelRowIterator.IterateRows( sourceRectangle, configuration, - new ApplyHorizontalConvolutionRowAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W)); + new ApplyHorizontalConvolutionRowIntervalAction(ref sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W)); } } /// /// A implementing the vertical convolution logic for . /// - private readonly struct ApplyVerticalConvolutionRowAction : IRowAction + private readonly struct ApplyVerticalConvolutionRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Buffer2D targetValues; @@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyVerticalConvolutionRowAction( + public ApplyVerticalConvolutionRowIntervalAction( ref Rectangle bounds, Buffer2D targetValues, Buffer2D sourcePixels, @@ -356,13 +356,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X); - - for (int x = 0; x < this.bounds.Width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - Buffer2DUtils.Convolve4(this.kernel, this.sourcePixels, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX); + 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); + } } } } @@ -370,7 +373,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the horizontal convolution logic for . /// - private readonly struct ApplyHorizontalConvolutionRowAction : IRowAction + private readonly struct ApplyHorizontalConvolutionRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Buffer2D targetValues; @@ -382,7 +385,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyHorizontalConvolutionRowAction( + public ApplyHorizontalConvolutionRowIntervalAction( ref Rectangle bounds, Buffer2D targetValues, Buffer2D sourceValues, @@ -402,13 +405,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X); - - for (int x = 0; x < this.bounds.Width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w); + 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); + } } } } @@ -416,7 +422,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the gamma exposure logic for . /// - private readonly struct ApplyGammaExposureRowAction : IRowAction + private readonly struct ApplyGammaExposureRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -424,7 +430,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly float gamma; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyGammaExposureRowAction( + public ApplyGammaExposureRowIntervalAction( Rectangle bounds, Buffer2D targetPixels, Configuration configuration, @@ -438,30 +444,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - int length = span.Length; + Span vectorSpan = memory.Span; + int length = vectorSpan.Length; - 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); - - for (int x = 0; x < this.bounds.Width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - 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), vectorSpan, PixelConversionModifiers.Premultiply); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span.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, vectorSpan.Slice(0, length), targetRowSpan); + } } } /// /// A implementing the inverse gamma exposure logic for . /// - private readonly struct ApplyInverseGammaExposureRowAction : IRowAction + private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -470,7 +480,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly float inverseGamma; [MethodImpl(InliningOptions.ShortMethod)] - public ApplyInverseGammaExposureRowAction( + public ApplyInverseGammaExposureRowIntervalAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourceValues, @@ -486,25 +496,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { Vector4 low = Vector4.Zero; var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); - 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++) + for (int y = rows.Min; y < rows.Max; y++) { - 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/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index a6d47fa587..cd550a3355 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -66,10 +66,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, this.Configuration, - new RowAction(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha)); + new RowIntervalAction(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha)); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly int maxY; @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -112,50 +112,54 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - int length = span.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(span); + Span vectorSpan = memory.Span; + int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span); - - if (this.preserveAlpha) + for (int y = rows.Min; y < rows.Max; y++) { - 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, length), vectorSpan); + + if (this.preserveAlpha) { - DenseMatrixUtils.Convolve2D3( - in this.kernelY, - in this.kernelX, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); + 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); + } } - } - else - { - for (int x = 0; x < this.bounds.Width; x++) + else { - DenseMatrixUtils.Convolve2D4( - in this.kernelY, - in this.kernelX, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); + 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); + } } - } - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index 1c6ca8b921..1be97a4f83 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -64,22 +64,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); // Horizontal convolution - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, this.Configuration, - new RowAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha)); + new RowIntervalAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha)); // Vertical convolution - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, this.Configuration, - new RowAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha)); + new RowIntervalAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha)); } /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly bool preserveAlpha; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -107,51 +107,55 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - int length = span.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(span); + Span vectorSpan = memory.Span; + int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); int maxY = this.bounds.Bottom - 1; int maxX = this.bounds.Right - 1; - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span); - - if (this.preserveAlpha) + for (int y = rows.Min; y < rows.Max; y++) { - 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, length), vectorSpan); + + if (this.preserveAlpha) { - DenseMatrixUtils.Convolve3( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - maxY, - this.bounds.X, - maxX); + 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); + } } - } - else - { - for (int x = 0; x < this.bounds.Width; x++) + else { - DenseMatrixUtils.Convolve4( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - maxY, - this.bounds.X, - maxX); + 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); + } } - } - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 6a2acf7707..b68dc56e05 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -57,10 +57,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, this.Configuration, - new RowAction(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha)); + new RowIntervalAction(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha)); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { 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 RowAction( + public RowIntervalAction( Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, @@ -100,48 +100,52 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - int length = span.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(span); + Span vectorSpan = memory.Span; + int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); - Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X); - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), span); - - if (this.preserveAlpha) + for (int y = rows.Min; y < rows.Max; y++) { - 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, length), vectorSpan); + + if (this.preserveAlpha) { - DenseMatrixUtils.Convolve3( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); + 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); + } } - } - else - { - for (int x = 0; x < this.bounds.Width; x++) + else { - DenseMatrixUtils.Convolve4( - in this.kernel, - this.sourcePixels, - ref vectorSpanRef, - y, - x, - this.bounds.Y, - this.maxY, - this.bounds.X, - this.maxX); + 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); + } } - } - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 85736cbd9a..e2480957ea 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -105,14 +105,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ParallelRowIterator.IterateRows( Rectangle.FromLTRB(minX, minY, maxX, maxY), this.Configuration, - new RowAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX)); + new RowIntervalAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX)); } } /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Buffer2D targetPixels; private readonly Buffer2D passPixels; @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int shiftX; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Buffer2D targetPixels, Buffer2D passPixels, int minX, @@ -140,26 +140,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - int offsetY = y - this.shiftY; + for (int y = rows.Min; y < rows.Max; y++) + { + 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 a12bcb1fdc..2a181174c7 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Drawing @@ -101,13 +102,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing ParallelRowIterator.IterateRows( workingRect, configuration, - new RowAction(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity)); + new RowIntervalAction(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity)); } /// /// A implementing the draw logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly ImageFrame sourceFrame; private readonly Image targetImage; @@ -120,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing private readonly float opacity; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( ImageFrame sourceFrame, Image targetImage, PixelBlender blender, @@ -144,11 +145,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - 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); + 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); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 45f221c937..4abaf7ac42 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects ParallelRowIterator.IterateRows( this.SourceRectangle, this.Configuration, - new RowAction(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels)); + new RowIntervalAction(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels)); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects private readonly int levels; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Rectangle bounds, Buffer2D targetPixels, ImageFrame source, @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { int maxY = this.bounds.Bottom - 1; int maxX = this.bounds.Right - 1; @@ -117,66 +117,69 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects ref float blueBinRef = ref Unsafe.Add(ref redBinRef, this.levels); ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, this.levels); - Span sourceRowPixelSpan = this.source.GetPixelRowSpan(y); - Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width); - - PixelOperations.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); - - for (int x = this.bounds.X; x < this.bounds.Right; x++) + for (int y = rows.Min; y < rows.Max; y++) { - int maxIntensity = 0; - int maxIndex = 0; + Span sourceRowPixelSpan = this.source.GetPixelRowSpan(y); + Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width); - // Clear the current shared buffer before processing each target pixel - bins.Memory.Span.Clear(); + PixelOperations.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); - for (int fy = 0; fy <= this.radius; fy++) + for (int x = this.bounds.X; x < this.bounds.Right; x++) { - int fyr = fy - this.radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); + int maxIntensity = 0; + int maxIndex = 0; - Span sourceOffsetRow = this.source.GetPixelRowSpan(offsetY); + // Clear the current shared buffer before processing each target pixel + bins.Memory.Span.Clear(); - for (int fx = 0; fx <= this.radius; fx++) + for (int fy = 0; fy <= this.radius; fy++) { - int fxr = fx - this.radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + int fyr = fy - this.radius; + int offsetY = y + fyr; - var vector = sourceOffsetRow[offsetX].ToVector4(); + offsetY = offsetY.Clamp(0, maxY); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + Span sourceOffsetRow = this.source.GetPixelRowSpan(offsetY); - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (this.levels - 1)); + for (int fx = 0; fx <= this.radius; fx++) + { + int fxr = fx - this.radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - Unsafe.Add(ref intensityBinRef, currentIntensity)++; - Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; - Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; - Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + var vector = sourceOffsetRow[offsetX].ToVector4(); - if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) - { - maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); - maxIndex = currentIntensity; + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; + + 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; + + 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/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index c0f4797560..fd725d3ba0 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -5,6 +5,7 @@ using System; 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 @@ -50,16 +51,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, this.Configuration, - new RowAction(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate)); + new RowIntervalAction(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate)); } /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly int startX; private readonly ImageFrame source; @@ -68,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects private readonly TDelegate rowProcessor; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( int startX, ImageFrame source, Configuration configuration, @@ -84,16 +85,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - int length = span.Length; - Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers); + 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); - // 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(vectorSpan, new Point(this.startX, y)); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers); + PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan, this.modifiers); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index c797c13587..cdb67e48b2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -5,6 +5,7 @@ using System; 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 @@ -36,16 +37,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, this.Configuration, - new RowAction(interest.X, source, this.definition.Matrix, this.Configuration)); + new RowIntervalAction(interest.X, source, this.definition.Matrix, this.Configuration)); } /// /// A implementing the convolution logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly int startX; private readonly ImageFrame source; @@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters private readonly Configuration configuration; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( int startX, ImageFrame source, ColorMatrix matrix, @@ -67,15 +68,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - int length = span.Length; - Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, 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); - Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix)); + Vector4Utils.Transform(vectorSpan, ref Unsafe.AsRef(this.matrix)); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan); + PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index a4a643425f..5d25bae821 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization ParallelRowIterator.IterateRows( workingRect, this.Configuration, - new GrayscaleLevelsRowAction(workingRect, histogramBuffer, source, this.LuminanceLevels)); + new GrayscaleLevelsRowIntervalAction(workingRect, histogramBuffer, source, this.LuminanceLevels)); Span histogram = histogramBuffer.GetSpan(); if (this.ClipHistogramEnabled) @@ -77,13 +77,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization ParallelRowIterator.IterateRows( workingRect, this.Configuration, - new CdfApplicationRowAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin)); + new CdfApplicationRowIntervalAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin)); } /// /// A implementing the grayscale levels logic for . /// - private readonly struct GrayscaleLevelsRowAction : IRowAction + private readonly struct GrayscaleLevelsRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly IMemoryOwner histogramBuffer; @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private readonly int luminanceLevels; [MethodImpl(InliningOptions.ShortMethod)] - public GrayscaleLevelsRowAction( + public GrayscaleLevelsRowIntervalAction( in Rectangle bounds, IMemoryOwner histogramBuffer, ImageFrame source, @@ -105,15 +105,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); - ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); - - for (int x = 0; x < this.bounds.Width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; + 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)++; + } } } } @@ -121,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// A implementing the cdf application levels logic for . /// - private readonly struct CdfApplicationRowAction : IRowAction + private readonly struct CdfApplicationRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; @@ -130,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private readonly float numberOfPixelsMinusCdfMin; [MethodImpl(InliningOptions.ShortMethod)] - public CdfApplicationRowAction( + public CdfApplicationRowIntervalAction( in Rectangle bounds, IMemoryOwner cdfBuffer, ImageFrame source, @@ -146,17 +149,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); - ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); - - for (int x = 0; x < this.bounds.Width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - 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 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)); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index 2c17d71c6c..a9b91e837f 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -52,10 +52,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays ParallelRowIterator.IterateRows( interest, configuration, - new RowAction(configuration, interest, blender, amount, colors, source)); + new RowIntervalAction(configuration, interest, blender, amount, colors, source)); } - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Configuration configuration, Rectangle bounds, PixelBlender blender, @@ -82,18 +82,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + for (int y = rows.Min; y < rows.Max; y++) + { + 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 6777e32345..65a87fbf01 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -55,13 +55,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(glowColor); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, configuration, - new RowAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); + new RowIntervalAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); } - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Configuration configuration, Rectangle bounds, IMemoryOwner colors, @@ -94,24 +94,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { + Span amountsSpan = memory.Span; Span colorSpan = this.colors.GetSpan(); - for (int i = 0; i < this.bounds.Width; i++) + for (int y = rows.Min; y < rows.Max; y++) { - 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); - } + 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); + } - 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, + amountsSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 6e2c3c4427..11887433c0 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -63,13 +63,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(vignetteColor); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( interest, configuration, - new RowAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); + new RowIntervalAction(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source)); } - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Configuration configuration; private readonly Rectangle bounds; @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Configuration configuration, Rectangle bounds, IMemoryOwner colors, @@ -102,24 +102,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { + Span amountsSpan = memory.Span; Span colorSpan = this.colors.GetSpan(); - for (int i = 0; i < this.bounds.Width; i++) + for (int y = rows.Min; y < rows.Max; y++) { - float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y)); - span[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1); + 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); } - - 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 447a99eeca..402a052496 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -5,6 +5,7 @@ using System; 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 @@ -60,23 +61,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( targetBounds, configuration, - new NearestNeighborRowAction(this.SourceRectangle, ref matrix, width, source, destination)); + new NearestNeighborRowIntervalAction(this.SourceRectangle, ref matrix, width, source, destination)); return; } using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( targetBounds, configuration, - new RowAction(configuration, kernelMap, ref matrix, width, source, destination)); + new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination)); } /// /// A implementing the nearest neighbor resampler logic for . /// - private readonly struct NearestNeighborRowAction : IRowAction + private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Matrix3x2 matrix; @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public NearestNeighborRowAction( + public NearestNeighborRowIntervalAction( Rectangle bounds, ref Matrix3x2 matrix, int maxX, @@ -100,17 +101,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// + /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span destRow = this.destination.GetPixelRowSpan(y); - - for (int x = 0; x < this.maxX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - var point = Point.Transform(new Point(x, y), this.matrix); - if (this.bounds.Contains(point.X, point.Y)) + Span destRow = this.destination.GetPixelRowSpan(y); + + for (int x = 0; x < this.maxX; x++) { - destRow[x] = this.source[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]; + } } } } @@ -119,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// A implementing the transformation logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Configuration configuration; private readonly TransformKernelMap kernelMap; @@ -129,7 +134,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Configuration configuration, TransformKernelMap kernelMap, ref Matrix3x2 matrix, @@ -147,31 +152,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - 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); - - for (int x = 0; x < this.maxX; x++) + Span vectorSpan = memory.Span; + for (int y = rows.Min; y < rows.Max; y++) { - // 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); - } + 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); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - span, - 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, + vectorSpan); + } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + vectorSpan, + targetRowSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index b294ef465b..103c5d3ffc 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -47,25 +48,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Rectangle bounds = this.cropRectangle; // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = + ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4); + + var rowAction = new RowIntervalAction(ref bounds, source, destination); ParallelRowIterator.IterateRows( bounds, in parallelSettings, - new RowAction(ref bounds, source, destination)); + in rowAction); } /// /// A implementing the processor logic for . /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { 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 RowAction(ref Rectangle bounds, ImageFrame source, ImageFrame destination) + public RowIntervalAction(ref Rectangle bounds, ImageFrame source, ImageFrame destination) { this.bounds = bounds; this.source = source; @@ -74,11 +84,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - 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); + 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); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs index 91cb478917..041f602a55 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -5,6 +5,7 @@ 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 @@ -78,23 +79,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( source.Bounds(), configuration, - new RowAction(source)); + new RowIntervalAction(source)); } - /// - /// A implementing the reverse logic for . - /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly ImageFrame source; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction(ImageFrame source) => this.source = source; + public RowIntervalAction(ImageFrame source) => this.source = source; [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - this.source.GetPixelRowSpan(y).Reverse(); + for (int y = rows.Min; y < rows.Max; y++) + { + 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 5989f23893..5034b072f5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -5,6 +5,7 @@ using System; 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 @@ -16,9 +17,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ProjectiveTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly Size targetSize; + private Size targetSize; private readonly IResampler resampler; - private readonly Matrix4x4 transformMatrix; + private Matrix4x4 transformMatrix; /// /// Initializes a new instance of the class. @@ -62,23 +63,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( targetBounds, configuration, - new NearestNeighborRowAction(ref sourceBounds, ref matrix, width, source, destination)); + new NearestNeighborRowIntervalAction(ref sourceBounds, ref matrix, width, source, destination)); return; } using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( targetBounds, configuration, - new RowAction(configuration, kernelMap, ref matrix, width, source, destination)); + new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination)); } - /// - /// A implementing the nearest neighbor interpolation logic for . - /// - private readonly struct NearestNeighborRowAction : IRowAction + private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly Matrix4x4 matrix; @@ -87,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public NearestNeighborRowAction( + public NearestNeighborRowIntervalAction( ref Rectangle bounds, ref Matrix4x4 matrix, int maxX, @@ -101,30 +99,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.destination = destination; } - /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span destRow = this.destination.GetPixelRowSpan(y); - - for (int x = 0; x < this.maxX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); - int px = (int)MathF.Round(point.X); - int py = (int)MathF.Round(point.Y); + Span destRow = this.destination.GetPixelRowSpan(y); - if (this.bounds.Contains(px, py)) + for (int x = 0; x < this.maxX; x++) { - destRow[x] = this.source[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]; + } } } } } - /// - /// A implementing the convolution logic for . - /// - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Configuration configuration; private readonly TransformKernelMap kernelMap; @@ -134,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Configuration configuration, TransformKernelMap kernelMap, ref Matrix4x4 matrix, @@ -150,33 +147,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.destination = destination; } - /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Memory memory) { - 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); - - for (int x = 0; x < this.maxX; x++) + Span vectorSpan = memory.Span; + for (int y = rows.Min; y < rows.Max; y++) { - // 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); - } + 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); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - span, - 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, + vectorSpan); + } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + vectorSpan, + targetRowSpan); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 3eeda1781f..02622622d7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( interest, configuration, - new RowAction(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination)); + new RowIntervalAction(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination)); return; } @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms base.Dispose(disposing); } - private readonly struct RowAction : IRowAction + private readonly struct RowIntervalAction : IRowIntervalAction { private readonly Rectangle sourceBounds; private readonly Rectangle destinationBounds; @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public RowAction( + public RowIntervalAction( Rectangle sourceBounds, Rectangle destinationBounds, float widthFactor, @@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { int sourceX = this.sourceBounds.X; int sourceY = this.sourceBounds.Y; @@ -183,14 +183,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int destLeft = this.destinationBounds.Left; int destRight = this.destinationBounds.Right; - // 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++) + for (int y = rows.Min; y < rows.Max; y++) { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - destX) * this.widthFactor) + sourceX)]; + // 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)]; + } } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index d20954d0f8..bf03ce319b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( source.Bounds(), configuration, - new Rotate180RowAction(source.Width, source.Height, source, destination)); + new Rotate180RowIntervalAction(source.Width, source.Height, source, destination)); } /// @@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( source.Bounds(), configuration, - new Rotate270RowAction(destination.Bounds(), source.Width, source.Height, source, destination)); + new Rotate270RowIntervalAction(destination.Bounds(), source.Width, source.Height, source, destination)); } /// @@ -162,10 +162,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ParallelRowIterator.IterateRows( source.Bounds(), configuration, - new Rotate90RowAction(destination.Bounds(), source.Width, source.Height, source, destination)); + new Rotate90RowIntervalAction(destination.Bounds(), source.Width, source.Height, source, destination)); } - private readonly struct Rotate180RowAction : IRowAction + private readonly struct Rotate180RowIntervalAction : IRowIntervalAction { private readonly int width; private readonly int height; @@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate180RowAction( + public Rotate180RowIntervalAction( int width, int height, ImageFrame source, @@ -186,19 +186,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span sourceRow = this.source.GetPixelRowSpan(y); - Span targetRow = this.destination.GetPixelRowSpan(this.height - y - 1); - - for (int x = 0; x < this.width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - targetRow[this.width - x - 1] = sourceRow[x]; + 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]; + } } } } - private readonly struct Rotate270RowAction : IRowAction + private readonly struct Rotate270RowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly int width; @@ -207,7 +210,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate270RowAction( + public Rotate270RowIntervalAction( Rectangle bounds, int width, int height, @@ -222,24 +225,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span sourceRow = this.source.GetPixelRowSpan(y); - for (int x = 0; x < this.width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - int newX = this.height - y - 1; - newX = this.height - newX - 1; - int newY = this.width - x - 1; - - if (this.bounds.Contains(newX, newY)) + Span sourceRow = this.source.GetPixelRowSpan(y); + for (int x = 0; x < this.width; x++) { - this.destination[newX, newY] = sourceRow[x]; + 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]; + } } } } } - private readonly struct Rotate90RowAction : IRowAction + private readonly struct Rotate90RowIntervalAction : IRowIntervalAction { private readonly Rectangle bounds; private readonly int width; @@ -248,7 +254,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; [MethodImpl(InliningOptions.ShortMethod)] - public Rotate90RowAction( + public Rotate90RowIntervalAction( Rectangle bounds, int width, int height, @@ -263,15 +269,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(in RowInterval rows) { - Span sourceRow = this.source.GetPixelRowSpan(y); - int newX = this.height - y - 1; - for (int x = 0; x < this.width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - if (this.bounds.Contains(newX, x)) + Span sourceRow = this.source.GetPixelRowSpan(y); + int newX = this.height - y - 1; + for (int x = 0; x < this.width; x++) { - this.destination[newX, x] = sourceRow[x]; + if (this.bounds.Contains(newX, x)) + { + this.destination[newX, x] = sourceRow[x]; + } } } }