|
|
|
@ -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<TPixel> cleanCopy = source.Clone()) |
|
|
|
// We need a clean copy for each pass to start from
|
|
|
|
using ImageFrame<TPixel> cleanCopy = source.Clone(); |
|
|
|
|
|
|
|
using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, kernels[0], true, this.Source, this.SourceRectangle)) |
|
|
|
{ |
|
|
|
using (var processor = new ConvolutionProcessor<TPixel>(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<TPixel> pass = cleanCopy.Clone(); |
|
|
|
|
|
|
|
using (var processor = new ConvolutionProcessor<TPixel>(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++) |
|
|
|
/// <summary>
|
|
|
|
/// A <see langword="struct"/> implementing the convolution logic for <see cref="EdgeDetectorCompassProcessor{T}"/>.
|
|
|
|
/// </summary>
|
|
|
|
private readonly struct RowIntervalAction : IRowIntervalAction |
|
|
|
{ |
|
|
|
private readonly Buffer2D<TPixel> targetPixels; |
|
|
|
private readonly Buffer2D<TPixel> passPixels; |
|
|
|
private readonly int minX; |
|
|
|
private readonly int maxX; |
|
|
|
private readonly int shiftY; |
|
|
|
private readonly int shiftX; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="RowIntervalAction"/> struct.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="targetPixels">The target pixel buffer to adjust.</param>
|
|
|
|
/// <param name="passPixels">The processed pixels for the current iteration. Cannot be null.</param>
|
|
|
|
/// <param name="minX">The minimum horizontal offset.</param>
|
|
|
|
/// <param name="maxX">The maximum horizontal offset.</param>
|
|
|
|
/// <param name="shiftY">The vertical offset shift.</param>
|
|
|
|
/// <param name="shiftX">The horizontal offset shift.</param>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public RowIntervalAction( |
|
|
|
Buffer2D<TPixel> targetPixels, |
|
|
|
Buffer2D<TPixel> 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; |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public void Invoke(in RowInterval rows) |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
using (ImageFrame<TPixel> 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<TPixel>(this.Configuration, kernels[i], true, this.Source, this.SourceRectangle)) |
|
|
|
{ |
|
|
|
processor.Apply(pass); |
|
|
|
} |
|
|
|
|
|
|
|
Buffer2D<TPixel> passPixels = pass.PixelBuffer; |
|
|
|
Buffer2D<TPixel> 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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|