Browse Source

All tests pass

js/color-alpha-handling
James Jackson-South 6 years ago
parent
commit
6455df3c4d
  1. 71
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
  2. 22
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  3. 18
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  4. 175
      src/ImageSharp/Processing/Processors/Convolution/Convolver.cs
  5. 37
      src/ImageSharp/Processing/Processors/Convolution/Kernels/KernelSamplingMap.cs

71
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs

@ -60,17 +60,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
using Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height);
MemoryAllocator allocator = this.Configuration.MemoryAllocator;
using Buffer2D<TPixel> targetPixels = allocator.Allocate2D<TPixel>(source.Width, source.Height);
source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha);
using (var map = new KernelSamplingMap(allocator))
{
// Since the kernel sizes are identical we can use a single map.
map.BuildSamplingOffsetMap(this.KernelY, interest);
var operation = new RowOperation(
interest,
targetPixels,
source.PixelBuffer,
map,
this.KernelY,
this.KernelX,
this.Configuration,
this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in operation);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration,
interest,
in operation);
}
Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}
@ -81,10 +96,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly struct RowOperation : IRowOperation<Vector4>
{
private readonly Rectangle bounds;
private readonly int maxY;
private readonly int maxX;
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
private readonly KernelSamplingMap map;
private readonly DenseMatrix<float> kernelY;
private readonly DenseMatrix<float> kernelX;
private readonly Configuration configuration;
@ -95,16 +109,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
KernelSamplingMap map,
DenseMatrix<float> kernelY,
DenseMatrix<float> kernelX,
Configuration configuration,
bool preserveAlpha)
{
this.bounds = bounds;
this.maxY = this.bounds.Bottom - 1;
this.maxX = this.bounds.Right - 1;
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.map = map;
this.kernelY = kernelY;
this.kernelX = kernelX;
this.configuration = configuration;
@ -115,42 +129,41 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span);
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
Span<int> yOffsets = this.map.GetYOffsetSpan();
Span<int> xOffsets = this.map.GetXOffsetSpan();
int row = y - this.bounds.Y;
if (this.preserveAlpha)
{
for (int x = 0; x < this.bounds.Width; x++)
for (int column = 0; column < this.bounds.Width; column++)
{
DenseMatrixUtils.Convolve2D3(
Convolver.Convolve2D3(
in this.kernelY,
in this.kernelX,
yOffsets,
xOffsets,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
ref targetRowRef,
row,
column);
}
}
else
{
for (int x = 0; x < this.bounds.Width; x++)
for (int column = 0; column < this.bounds.Width; column++)
{
DenseMatrixUtils.Convolve2D4(
Convolver.Convolve2D4(
in this.kernelY,
in this.kernelX,
yOffsets,
xOffsets,
this.sourcePixels,
ref spanRef,
y,
x,
this.bounds.Y,
this.maxY,
this.bounds.X,
this.maxX);
ref targetRowRef,
row,
column);
}
}

22
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs

@ -63,9 +63,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
using (var mapX = new KernelOffsetMap(this.Configuration.MemoryAllocator))
using (var mapX = new KernelSamplingMap(this.Configuration.MemoryAllocator))
{
mapX.BuildOffsetMap(this.KernelX, interest);
mapX.BuildSamplingOffsetMap(this.KernelX, interest);
// Horizontal convolution
var horizontalOperation = new RowOperation(
@ -83,9 +83,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
in horizontalOperation);
}
using (var mapY = new KernelOffsetMap(this.Configuration.MemoryAllocator))
using (var mapY = new KernelSamplingMap(this.Configuration.MemoryAllocator))
{
mapY.BuildOffsetMap(this.KernelY, interest);
mapY.BuildSamplingOffsetMap(this.KernelY, interest);
// Vertical convolution
var verticalOperation = new RowOperation(
@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
private readonly KernelOffsetMap map;
private readonly KernelSamplingMap map;
private readonly DenseMatrix<float> kernel;
private readonly Configuration configuration;
private readonly bool preserveAlpha;
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
KernelOffsetMap map,
KernelSamplingMap map,
DenseMatrix<float> kernel,
Configuration configuration,
bool preserveAlpha)
@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
ref Vector4 targetRef = ref MemoryMarshal.GetReference(span);
ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span);
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
Span<int> yOffsets = this.map.GetYOffsetSpan();
@ -151,12 +151,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
for (int column = 0; column < this.bounds.Width; column++)
{
DenseMatrixUtils.Convolve3(
Convolver.Convolve3(
in this.kernel,
yOffsets,
xOffsets,
this.sourcePixels,
ref targetRef,
ref targetRowRef,
row,
column);
}
@ -165,12 +165,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
for (int column = 0; column < this.bounds.Width; column++)
{
DenseMatrixUtils.Convolve4(
Convolver.Convolve4(
in this.kernel,
yOffsets,
xOffsets,
this.sourcePixels,
ref targetRef,
ref targetRowRef,
row,
column);
}

18
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs

@ -57,9 +57,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
using (var map = new KernelOffsetMap(allocator))
using (var map = new KernelSamplingMap(allocator))
{
map.BuildOffsetMap(this.KernelXY, interest);
map.BuildSamplingOffsetMap(this.KernelXY, interest);
var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows<RowOperation, Vector4>(
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
private readonly KernelOffsetMap map;
private readonly KernelSamplingMap map;
private readonly DenseMatrix<float> kernel;
private readonly Configuration configuration;
private readonly bool preserveAlpha;
@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
KernelOffsetMap map,
KernelSamplingMap map,
DenseMatrix<float> kernel,
Configuration configuration,
bool preserveAlpha)
@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
ref Vector4 targetRef = ref MemoryMarshal.GetReference(span);
ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span);
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
Span<int> yOffsets = this.map.GetYOffsetSpan();
@ -118,12 +118,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
for (int column = 0; column < this.bounds.Width; column++)
{
DenseMatrixUtils.Convolve3(
Convolver.Convolve3(
in this.kernel,
yOffsets,
xOffsets,
this.sourcePixels,
ref targetRef,
ref targetRowRef,
row,
column);
}
@ -132,12 +132,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
for (int column = 0; column < this.bounds.Width; column++)
{
DenseMatrixUtils.Convolve4(
Convolver.Convolve4(
in this.kernel,
yOffsets,
xOffsets,
this.sourcePixels,
ref targetRef,
ref targetRowRef,
row,
column);
}

175
src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs → src/ImageSharp/Processing/Processors/Convolution/Convolver.cs

@ -6,56 +6,50 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Convolution;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for <see cref="DenseMatrix{T}"/>.
/// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
/// Provides methods to perform convolution operations.
/// </summary>
internal static class DenseMatrixUtils
internal static class Convolver
{
/// <summary>
/// Computes the sum of vectors in the span referenced by <paramref name="targetRowRef"/> weighted by the two kernel weight values.
/// Using this method the convolution filter is not applied to alpha in addition to the color channels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="matrixY">The vertical dense matrix.</param>
/// <param name="matrixX">The horizontal dense matrix.</param>
/// <param name="kernelY">The vertical convolution kernel.</param>
/// <param name="kernelX">The horizontal convolution kernel.</param>
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-sampling offsets.</param>
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-sampling offsets.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRowRef">The target row base reference.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
/// <param name="minRow">The minimum working area row.</param>
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="minColumn">The minimum working area column.</param>
/// <param name="maxColumn">The maximum working area column.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Convolve2D3<TPixel>(
in DenseMatrix<float> matrixY,
in DenseMatrix<float> matrixX,
in DenseMatrix<float> kernelY,
in DenseMatrix<float> kernelX,
Span<int> rowSampleOffsets,
Span<int> columnSampleOffsets,
Buffer2D<TPixel> sourcePixels,
ref Vector4 targetRowRef,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
int maxColumn)
int column)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 vector = default;
Convolve2DImpl(
in matrixY,
in matrixX,
in kernelY,
in kernelX,
rowSampleOffsets,
columnSampleOffsets,
sourcePixels,
row,
column,
minRow,
maxRow,
minColumn,
maxColumn,
out Vector4 vector);
ref vector);
ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
vector.W = target.W;
@ -69,41 +63,37 @@ namespace SixLabors.ImageSharp
/// Using this method the convolution filter is applied to alpha in addition to the color channels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="matrixY">The vertical dense matrix.</param>
/// <param name="matrixX">The horizontal dense matrix.</param>
/// <param name="kernelY">The vertical convolution kernel.</param>
/// <param name="kernelX">The horizontal convolution kernel.</param>
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-sampling offsets.</param>
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-sampling offsets.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRowRef">The target row base reference.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
/// <param name="minRow">The minimum working area row.</param>
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="minColumn">The minimum working area column.</param>
/// <param name="maxColumn">The maximum working area column.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Convolve2D4<TPixel>(
in DenseMatrix<float> matrixY,
in DenseMatrix<float> matrixX,
in DenseMatrix<float> kernelY,
in DenseMatrix<float> kernelX,
Span<int> rowSampleOffsets,
Span<int> columnSampleOffsets,
Buffer2D<TPixel> sourcePixels,
ref Vector4 targetRowRef,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
int maxColumn)
int column)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 vector = default;
Convolve2DImpl(
in matrixY,
in matrixX,
in kernelY,
in kernelX,
rowSampleOffsets,
columnSampleOffsets,
sourcePixels,
row,
column,
minRow,
maxRow,
minColumn,
maxColumn,
out Vector4 vector);
ref vector);
ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
Numerics.UnPremultiply(ref vector);
@ -112,43 +102,38 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
public static void Convolve2DImpl<TPixel>(
in DenseMatrix<float> matrixY,
in DenseMatrix<float> matrixX,
in DenseMatrix<float> kernelY,
in DenseMatrix<float> kernelX,
Span<int> rowSampleOffsets,
Span<int> columnSampleOffsets,
Buffer2D<TPixel> sourcePixels,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
int maxColumn,
out Vector4 vector)
ref Vector4 targetVector)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 vectorY = default;
Vector4 vectorX = default;
int matrixHeight = matrixY.Rows;
int matrixWidth = matrixY.Columns;
int radiusY = matrixHeight >> 1;
int radiusX = matrixWidth >> 1;
int sourceOffsetColumnBase = column + minColumn;
int kernelHeight = kernelY.Rows;
int kernelWidth = kernelY.Columns;
for (int y = 0; y < matrixHeight; y++)
for (int y = 0; y < kernelHeight; y++)
{
int offsetY = Numerics.Clamp(row + y - radiusY, minRow, maxRow);
int offsetY = rowSampleOffsets[(row * kernelHeight) + y];
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
for (int x = 0; x < matrixWidth; x++)
for (int x = 0; x < kernelWidth; x++)
{
int offsetX = Numerics.Clamp(sourceOffsetColumnBase + x - radiusX, minColumn, maxColumn);
var currentColor = sourceRowSpan[offsetX].ToVector4();
Numerics.Premultiply(ref currentColor);
int offsetX = columnSampleOffsets[(column * kernelWidth) + x];
var sample = sourceRowSpan[offsetX].ToVector4();
Numerics.Premultiply(ref sample);
vectorX += matrixX[y, x] * currentColor;
vectorY += matrixY[y, x] * currentColor;
vectorX += kernelX[y, x] * sample;
vectorY += kernelY[y, x] * sample;
}
}
vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY));
targetVector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY));
}
/// <summary>
@ -156,18 +141,18 @@ namespace SixLabors.ImageSharp
/// Using this method the convolution filter is not applied to alpha in addition to the color channels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="matrix">The dense matrix.</param>
/// <param name="yOffsetSpan">The span containing precalculated kernel y-offsets.</param>
/// <param name="xOffsetSpan">The span containing precalculated kernel x-offsets.</param>
/// <param name="kernel">The convolution kernel.</param>
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-sampling offsets.</param>
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-sampling offsets.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRowRef">The target row base reference.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Convolve3<TPixel>(
in DenseMatrix<float> matrix,
Span<int> yOffsetSpan,
Span<int> xOffsetSpan,
in DenseMatrix<float> kernel,
Span<int> rowSampleOffsets,
Span<int> columnSampleOffsets,
Buffer2D<TPixel> sourcePixels,
ref Vector4 targetRowRef,
int row,
@ -177,9 +162,9 @@ namespace SixLabors.ImageSharp
Vector4 vector = default;
ConvolveImpl(
in matrix,
yOffsetSpan,
xOffsetSpan,
in kernel,
rowSampleOffsets,
columnSampleOffsets,
sourcePixels,
row,
column,
@ -197,18 +182,18 @@ namespace SixLabors.ImageSharp
/// Using this method the convolution filter is applied to alpha in addition to the color channels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="matrix">The dense matrix.</param>
/// <param name="yOffsetSpan">The span containing precalculated kernel y-offsets.</param>
/// <param name="xOffsetSpan">The span containing precalculated kernel x-offsets.</param>
/// <param name="kernel">The convolution kernel.</param>
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-offsets.</param>
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-offsets.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRowRef">The target row base reference.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Convolve4<TPixel>(
in DenseMatrix<float> matrix,
Span<int> yOffsetSpan,
Span<int> xOffsetSpan,
in DenseMatrix<float> kernel,
Span<int> rowSampleOffsets,
Span<int> columnSampleOffsets,
Buffer2D<TPixel> sourcePixels,
ref Vector4 targetRowRef,
int row,
@ -218,9 +203,9 @@ namespace SixLabors.ImageSharp
Vector4 vector = default;
ConvolveImpl(
in matrix,
yOffsetSpan,
xOffsetSpan,
in kernel,
rowSampleOffsets,
columnSampleOffsets,
sourcePixels,
row,
column,
@ -233,29 +218,29 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
private static void ConvolveImpl<TPixel>(
in DenseMatrix<float> matrix,
Span<int> yOffsetSpan,
Span<int> xOffsetSpan,
in DenseMatrix<float> kernel,
Span<int> rowSampleOffsets,
Span<int> columnSampleOffsets,
Buffer2D<TPixel> sourcePixels,
int row,
int column,
ref Vector4 targetVector)
where TPixel : unmanaged, IPixel<TPixel>
{
int matrixHeight = matrix.Rows;
int matrixWidth = matrix.Columns;
int kernelHeight = kernel.Rows;
int kernelWidth = kernel.Columns;
for (int y = 0; y < matrixHeight; y++)
for (int y = 0; y < kernelHeight; y++)
{
int offsetY = yOffsetSpan[(row * matrixHeight) + y];
int offsetY = rowSampleOffsets[(row * kernelHeight) + y];
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
for (int x = 0; x < matrixWidth; x++)
for (int x = 0; x < kernelWidth; x++)
{
int offsetX = xOffsetSpan[(column * matrixWidth) + x];
var currentColor = sourceRowSpan[offsetX].ToVector4();
Numerics.Premultiply(ref currentColor);
targetVector += matrix[y, x] * currentColor;
int offsetX = columnSampleOffsets[(column * kernelWidth) + x];
var sample = sourceRowSpan[offsetX].ToVector4();
Numerics.Premultiply(ref sample);
targetVector += kernel[y, x] * sample;
}
}
}

37
src/ImageSharp/Processing/Processors/Convolution/Kernels/KernelOffsetMap.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/KernelSamplingMap.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary>
/// Provides a map of the convolution kernel sampling offsets.
/// </summary>
internal sealed class KernelOffsetMap : IDisposable
internal sealed class KernelSamplingMap : IDisposable
{
private readonly MemoryAllocator allocator;
private bool isDisposed;
@ -19,37 +19,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private IMemoryOwner<int> xOffsets;
/// <summary>
/// Initializes a new instance of the <see cref="KernelOffsetMap"/> class.
/// Initializes a new instance of the <see cref="KernelSamplingMap"/> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
public KernelOffsetMap(MemoryAllocator allocator) => this.allocator = allocator;
public KernelSamplingMap(MemoryAllocator allocator) => this.allocator = allocator;
public void BuildOffsetMap(in DenseMatrix<float> matrix, Rectangle bounds)
/// <summary>
/// Builds a map of the sampling offsets for the kernel clamped by the given bounds.
/// </summary>
/// <param name="kernel">The convolution kernel.</param>
/// <param name="bounds">The source bounds.</param>
public void BuildSamplingOffsetMap(DenseMatrix<float> kernel, Rectangle bounds)
{
int matrixHeight = matrix.Rows;
int matrixWidth = matrix.Columns;
this.yOffsets = this.allocator.Allocate<int>(bounds.Height * matrixHeight);
this.xOffsets = this.allocator.Allocate<int>(bounds.Width * matrixWidth);
int kernelHeight = kernel.Rows;
int kernelWidth = kernel.Columns;
this.yOffsets = this.allocator.Allocate<int>(bounds.Height * kernelHeight);
this.xOffsets = this.allocator.Allocate<int>(bounds.Width * kernelWidth);
int minY = bounds.Y;
int maxY = bounds.Bottom - 1;
int minX = bounds.X;
int maxX = bounds.Right - 1;
int radiusY = matrixHeight >> 1;
int radiusX = matrixWidth >> 1;
int radiusY = kernelHeight >> 1;
int radiusX = kernelWidth >> 1;
// Calculate the potential sampling y-offsets.
Span<int> ySpan = this.yOffsets.GetSpan();
for (int row = 0; row < bounds.Height; row++)
{
for (int y = 0; y < matrixHeight; y++)
for (int y = 0; y < kernelHeight; y++)
{
ySpan[(row * matrixHeight) + y] = row + y + minY - radiusY;
ySpan[(row * kernelHeight) + y] = row + y + minY - radiusY;
}
}
if (matrixHeight > 1)
if (kernelHeight > 1)
{
Numerics.Clamp(ySpan, minY, maxY);
}
@ -58,13 +63,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Span<int> xSpan = this.xOffsets.GetSpan();
for (int column = 0; column < bounds.Width; column++)
{
for (int x = 0; x < matrixWidth; x++)
for (int x = 0; x < kernelWidth; x++)
{
xSpan[(column * matrixWidth) + x] = column + x + minX - radiusX;
xSpan[(column * kernelWidth) + x] = column + x + minX - radiusX;
}
}
if (matrixWidth > 1)
if (kernelWidth > 1)
{
Numerics.Clamp(xSpan, minX, maxX);
}
Loading…
Cancel
Save