diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index d4fb27a57f..55cef5df54 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -129,8 +129,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int boundsWidth = this.bounds.Width; int kernelSize = this.kernel.Length; - Span rowOffsets = this.map.GetRowOffsetSpan(); - ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowOffsets), (y - this.bounds.Y) * kernelSize); + ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (y - this.bounds.Y) * kernelSize); // The target buffer is zeroed initially and then it accumulates the results // of each partial convolution, so we don't have to clear it here as well diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index c2f3ec59a1..c7f5c94dd2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -156,33 +156,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Span is 2x bounds. int boundsX = this.bounds.X; int boundsWidth = this.bounds.Width; + int kernelSize = this.kernelMatrix.Columns; + Span sourceBuffer = span.Slice(0, this.bounds.Width); Span targetBuffer = span.Slice(this.bounds.Width); - var state = new ConvolutionState(in this.kernelMatrix, this.map); - // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); // Get the precalculated source sample row for this kernel row and copy to our buffer. - ReadOnlyKernel kernel = state.Kernel; Span sourceRow = this.sourcePixels.GetRowSpan(y).Slice(boundsX, boundsWidth); PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); + ref float kernelBase = ref this.kernelMatrix[0, 0]; + ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan()); for (int x = 0; x < sourceBuffer.Length; x++) { - ref int sampleColumnBase = ref state.GetSampleColumn(x); ref Vector4 target = ref Unsafe.Add(ref targetBase, x); - for (int kX = 0; kX < kernel.Columns; kX++) + for (int kX = 0; kX < kernelSize; kX++) { int sampleX = Unsafe.Add(ref sampleColumnBase, kX) - boundsX; Vector4 sample = Unsafe.Add(ref sourceBase, sampleX); - target += kernel[0, kX] * sample; + float factor = Unsafe.Add(ref kernelBase, kX); + + target += factor * sample; } + + // Shift the base column sampling reference by one row + sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, kernelSize); } // Now we need to copy the original alpha values from the source row. @@ -205,34 +210,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Span is 2x bounds. int boundsX = this.bounds.X; int boundsWidth = this.bounds.Width; + int kernelSize = this.kernelMatrix.Columns; + Span sourceBuffer = span.Slice(0, this.bounds.Width); Span targetBuffer = span.Slice(this.bounds.Width); - var state = new ConvolutionState(in this.kernelMatrix, this.map); - // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); // Get the precalculated source sample row for this kernel row and copy to our buffer. - ReadOnlyKernel kernel = state.Kernel; Span sourceRow = this.sourcePixels.GetRowSpan(y).Slice(boundsX, boundsWidth); PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); Numerics.Premultiply(sourceBuffer); + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); + ref float kernelBase = ref this.kernelMatrix[0, 0]; + ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan()); for (int x = 0; x < sourceBuffer.Length; x++) { - ref int sampleColumnBase = ref state.GetSampleColumn(x); ref Vector4 target = ref Unsafe.Add(ref targetBase, x); - for (int kX = 0; kX < kernel.Columns; kX++) + for (int kX = 0; kX < kernelSize; kX++) { int sampleX = Unsafe.Add(ref sampleColumnBase, kX) - boundsX; Vector4 sample = Unsafe.Add(ref sourceBase, sampleX); - target += kernel[0, kX] * sample; + float factor = Unsafe.Add(ref kernelBase, kX); + + target += factor * sample; } + + sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, kernelSize); } Numerics.UnPremultiply(targetBuffer); @@ -294,33 +304,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Span is 2x bounds. int boundsX = this.bounds.X; int boundsWidth = this.bounds.Width; + int kernelSize = this.kernelMatrix.Rows; + Span sourceBuffer = span.Slice(0, this.bounds.Width); Span targetBuffer = span.Slice(this.bounds.Width); - var state = new ConvolutionState(in this.kernelMatrix, this.map); - ref int sampleRowBase = ref state.GetSampleRow(y - this.bounds.Y); + ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (y - this.bounds.Y) * kernelSize); // Clear the target buffer for each row run. targetBuffer.Clear(); + ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); + ref float kernelBase = ref this.kernelMatrix[0, 0]; - ReadOnlyKernel kernel = state.Kernel; Span sourceRow; - for (int kY = 0; kY < kernel.Rows; kY++) + for (int kY = 0; kY < kernelSize; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. int sampleY = Unsafe.Add(ref sampleRowBase, kY); sourceRow = this.sourcePixels.GetRowSpan(sampleY).Slice(boundsX, boundsWidth); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); + float factor = Unsafe.Add(ref kernelBase, kY); for (int x = 0; x < sourceBuffer.Length; x++) { ref Vector4 target = ref Unsafe.Add(ref targetBase, x); Vector4 sample = Unsafe.Add(ref sourceBase, x); - target += kernel[kY, 0] * sample; + target += factor * sample; } } @@ -344,6 +358,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Span is 2x bounds. int boundsX = this.bounds.X; int boundsWidth = this.bounds.Width; + int kernelSize = this.kernelMatrix.Rows; + Span sourceBuffer = span.Slice(0, this.bounds.Width); Span targetBuffer = span.Slice(this.bounds.Width); @@ -352,25 +368,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Clear the target buffer for each row run. targetBuffer.Clear(); + ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); + ref float kernelBase = ref this.kernelMatrix[0, 0]; - ReadOnlyKernel kernel = state.Kernel; - for (int kY = 0; kY < kernel.Rows; kY++) + for (int kY = 0; kY < kernelSize; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. int sampleY = Unsafe.Add(ref sampleRowBase, kY); Span sourceRow = this.sourcePixels.GetRowSpan(sampleY).Slice(boundsX, boundsWidth); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); Numerics.Premultiply(sourceBuffer); + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); + float factor = Unsafe.Add(ref kernelBase, kY); for (int x = 0; x < sourceBuffer.Length; x++) { ref Vector4 target = ref Unsafe.Add(ref targetBase, x); Vector4 sample = Unsafe.Add(ref sourceBase, x); - target += kernel[kY, 0] * sample; + target += factor * sample; } }