diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 1851427589..e85205c419 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -65,77 +65,112 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); - // we need a clean copy for each pass to start from - using (ImageFrame cleanCopy = source.Clone()) + // We need a clean copy for each pass to start from + using ImageFrame cleanCopy = source.Clone(); + + using (var processor = new ConvolutionProcessor(this.Configuration, kernels[0], true, this.Source, this.SourceRectangle)) { - using (var processor = new ConvolutionProcessor(this.Configuration, kernels[0], true, this.Source, this.SourceRectangle)) - { - processor.Apply(source); - } + processor.Apply(source); + } - if (kernels.Length == 1) - { - return; - } + if (kernels.Length == 1) + { + return; + } - int shiftY = startY; - int shiftX = startX; + int shiftY = startY; + int shiftX = startX; - // Reset offset if necessary. - if (minX > 0) - { - shiftX = 0; - } + // Reset offset if necessary + if (minX > 0) + { + shiftX = 0; + } - if (minY > 0) + if (minY > 0) + { + shiftY = 0; + } + + // Additional runs + for (int i = 1; i < kernels.Length; i++) + { + using ImageFrame pass = cleanCopy.Clone(); + + using (var processor = new ConvolutionProcessor(this.Configuration, kernels[i], true, this.Source, this.SourceRectangle)) { - shiftY = 0; + processor.Apply(pass); } - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + ParallelRowIterator.IterateRows( + Rectangle.FromLTRB(minX, minY, maxX, maxY), + this.Configuration, + new RowIntervalAction(source.PixelBuffer, pass.PixelBuffer, minX, maxX, shiftY, shiftX)); + } + } - // Additional runs. - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 1; i < kernels.Length; i++) + /// + /// A implementing the convolution logic for . + /// + private readonly struct RowIntervalAction : IRowIntervalAction + { + private readonly Buffer2D targetPixels; + private readonly Buffer2D passPixels; + private readonly int minX; + private readonly int maxX; + private readonly int shiftY; + private readonly int shiftX; + + /// + /// Initializes a new instance of the struct. + /// + /// The target pixel buffer to adjust. + /// The processed pixels for the current iteration. Cannot be null. + /// The minimum horizontal offset. + /// The maximum horizontal offset. + /// The vertical offset shift. + /// The horizontal offset shift. + [MethodImpl(InliningOptions.ShortMethod)] + public RowIntervalAction( + Buffer2D targetPixels, + Buffer2D passPixels, + int minX, + int maxX, + int shiftY, + int shiftX) + { + this.targetPixels = targetPixels; + this.passPixels = passPixels; + this.minX = minX; + this.maxX = maxX; + this.shiftY = shiftY; + this.shiftX = shiftX; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(in RowInterval rows) + { + for (int y = rows.Min; y < rows.Max; y++) { - using (ImageFrame pass = cleanCopy.Clone()) + 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)); + + for (int x = this.minX; x < this.maxX; x++) { - using (var processor = new ConvolutionProcessor(this.Configuration, kernels[i], true, this.Source, this.SourceRectangle)) - { - processor.Apply(pass); - } - - Buffer2D passPixels = pass.PixelBuffer; - Buffer2D targetPixels = source.PixelBuffer; - - ParallelRowIterator.IterateRows( - workingRect, - this.Configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - int offsetY = y - shiftY; - - ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY)); - ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY)); - - for (int x = minX; x < maxX; x++) - { - int offsetX = x - 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); - - var pixelValue = Vector4.Max( - currentPassPixel.ToVector4(), - currentTargetPixel.ToVector4()); - - currentTargetPixel.FromVector4(pixelValue); - } - } - }); + 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); + + var pixelValue = Vector4.Max( + currentPassPixel.ToVector4(), + currentTargetPixel.ToVector4()); + + currentTargetPixel.FromVector4(pixelValue); } } }