diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs index 802d1809f..dd3e98609 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs @@ -80,8 +80,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ref Vector4 targetBaseY = ref MemoryMarshal.GetReference(targetYBuffer); ref Vector4 targetBaseX = ref MemoryMarshal.GetReference(targetXBuffer); - ReadOnlyKernel kernelY = state.KernelY; - ReadOnlyKernel kernelX = state.KernelX; + ReadOnlyKernel kernelY = state.KernelY; + ReadOnlyKernel kernelX = state.KernelX; Span sourceRow; for (int kY = 0; kY < kernelY.Rows; kY++) { @@ -146,8 +146,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ref Vector4 targetBaseY = ref MemoryMarshal.GetReference(targetYBuffer); ref Vector4 targetBaseX = ref MemoryMarshal.GetReference(targetXBuffer); - ReadOnlyKernel kernelY = state.KernelY; - ReadOnlyKernel kernelX = state.KernelX; + ReadOnlyKernel kernelY = state.KernelY; + ReadOnlyKernel kernelX = state.KernelX; for (int kY = 0; kY < kernelY.Rows; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DState.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DState.cs index 218093ac4..6f9b11857 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DState.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DState.cs @@ -23,21 +23,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution KernelSamplingMap map) { // We check the kernels are the same size upstream. - this.KernelY = new ReadOnlyKernel(kernelY); - this.KernelX = new ReadOnlyKernel(kernelX); + this.KernelY = new ReadOnlyKernel(kernelY); + this.KernelX = new ReadOnlyKernel(kernelX); this.kernelHeight = kernelY.Rows; this.kernelWidth = kernelY.Columns; this.rowOffsetMap = map.GetRowOffsetSpan(); this.columnOffsetMap = map.GetColumnOffsetSpan(); } - public readonly ReadOnlyKernel KernelY + public readonly ReadOnlyKernel KernelY { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } - public readonly ReadOnlyKernel KernelX + public readonly ReadOnlyKernel KernelX { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 924a1125b..b0254bc91 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span); Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(boundsX, boundsWidth); - var state = new ConvolutionState(in this.kernel, this.map); + var state = new ConvolutionState(in this.kernel, this.map); int row = y - this.bounds.Y; ref int sampleRowBase = ref state.GetSampleRow(row); diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionRowOperation{TPixel}.cs index 9876b2885..beccfff01 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionRowOperation{TPixel}.cs @@ -67,14 +67,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution Span sourceBuffer = span.Slice(0, this.bounds.Width); Span targetBuffer = span.Slice(this.bounds.Width); - var state = new ConvolutionState(in this.kernelMatrix, this.map); + var state = new ConvolutionState(in this.kernelMatrix, this.map); ref int sampleRowBase = ref state.GetSampleRow(y - this.bounds.Y); // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); - ReadOnlyKernel kernel = state.Kernel; + ReadOnlyKernel kernel = state.Kernel; Span sourceRow; for (int kY = 0; kY < kernel.Rows; kY++) { @@ -119,17 +119,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // Span is 2x bounds. int boundsX = this.bounds.X; int boundsWidth = this.bounds.Width; - Span sourceBuffer = span.Slice(0, this.bounds.Width); - Span targetBuffer = span.Slice(this.bounds.Width); + Span sourceBuffer = span.Slice(0, boundsWidth); + Span targetBuffer = span.Slice(boundsWidth); - var state = new ConvolutionState(in this.kernelMatrix, this.map); + var state = new ConvolutionState(in this.kernelMatrix, this.map); ref int sampleRowBase = ref state.GetSampleRow(y - this.bounds.Y); // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); - ReadOnlyKernel kernel = state.Kernel; + ReadOnlyKernel kernel = state.Kernel; for (int kY = 0; kY < kernel.Rows; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs index 3f296c67d..0b3dbc2d1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs @@ -10,7 +10,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// A stack only struct used for reducing reference indirection during convolution operations. /// - internal readonly ref struct ConvolutionState + /// The type of values for the kernel in use. + internal readonly ref struct ConvolutionState + where T : unmanaged, IEquatable { private readonly Span rowOffsetMap; private readonly Span columnOffsetMap; @@ -18,17 +20,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private readonly int kernelWidth; public ConvolutionState( - in DenseMatrix kernel, + in DenseMatrix kernel, KernelSamplingMap map) { - this.Kernel = new ReadOnlyKernel(kernel); + this.Kernel = new ReadOnlyKernel(kernel); this.kernelHeight = kernel.Rows; this.kernelWidth = kernel.Columns; this.rowOffsetMap = map.GetRowOffsetSpan(); this.columnOffsetMap = map.GetColumnOffsetSpan(); } - public readonly ReadOnlyKernel Kernel + public ConvolutionState( + T[] kernel, + int height, + int width, + KernelSamplingMap map) + { + this.Kernel = new ReadOnlyKernel(kernel, height, width); + this.kernelHeight = height; + this.kernelWidth = width; + this.rowOffsetMap = map.GetRowOffsetSpan(); + this.columnOffsetMap = map.GetColumnOffsetSpan(); + } + + public readonly ReadOnlyKernel Kernel { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; diff --git a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs index e4b7dbea0..f912b9562 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs @@ -31,9 +31,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The convolution kernel. /// The source bounds. public void BuildSamplingOffsetMap(DenseMatrix kernel, Rectangle bounds) + => this.BuildSamplingOffsetMap(kernel.Rows, kernel.Columns, bounds); + + /// + /// Builds a map of the sampling offsets for the kernel clamped by the given bounds. + /// + /// The height (number of rows) of the convolution kernel to use. + /// The width (number of columns) of the convolution kernel to use. + /// The source bounds. + public void BuildSamplingOffsetMap(int kernelHeight, int kernelWidth, Rectangle bounds) { - int kernelHeight = kernel.Rows; - int kernelWidth = kernel.Columns; this.yOffsets = this.allocator.Allocate(bounds.Height * kernelHeight); this.xOffsets = this.allocator.Allocate(bounds.Width * kernelWidth); diff --git a/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel{T}.cs similarity index 73% rename from src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs rename to src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel{T}.cs index 37e006005..f95c3dc0a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel{T}.cs @@ -12,17 +12,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// A stack only, readonly, kernel matrix that can be indexed without /// bounds checks when compiled in release mode. /// - internal readonly ref struct ReadOnlyKernel + /// The type of items in the kernel. + internal readonly ref struct ReadOnlyKernel + where T : unmanaged, IEquatable { - private readonly ReadOnlySpan values; + private readonly ReadOnlySpan values; - public ReadOnlyKernel(DenseMatrix matrix) + public ReadOnlyKernel(DenseMatrix matrix) { this.Columns = matrix.Columns; this.Rows = matrix.Rows; this.values = matrix.Span; } + public ReadOnlyKernel(T[] kernel, int height, int width) + { + this.Columns = width; + this.Rows = height; + this.values = kernel; + } + public int Columns { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -35,13 +44,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution get; } - public float this[int row, int column] + public T this[int row, int column] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { this.CheckCoordinates(row, column); - ref float vBase = ref MemoryMarshal.GetReference(this.values); + ref T vBase = ref MemoryMarshal.GetReference(this.values); return Unsafe.Add(ref vBase, (row * this.Columns) + column); } }