Browse Source

Refactor Convolution2PassProcessor<TPixel>

af/octree-no-pixelmap
Sergio Pedri 6 years ago
parent
commit
d0b4dcb3d4
  1. 4
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  2. 181
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs

4
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -434,7 +434,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="BokehBlurProcessor{T}"/>. /// A <see langword="struct"/> implementing the gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct ApplyGammaExposureRowIntervalAction : IRowIntervalAction<Vector4> private readonly struct ApplyGammaExposureRowIntervalAction : IRowIntervalAction<Vector4>
{ {
@ -490,7 +490,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
} }
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="BokehBlurProcessor{T}"/>. /// A <see langword="struct"/> implementing the inverse gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary> /// </summary>
private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction private readonly struct ApplyInverseGammaExposureRowIntervalAction : IRowIntervalAction
{ {

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

@ -3,6 +3,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -58,95 +59,113 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
using (Buffer2D<TPixel> firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size())) using Buffer2D<TPixel> firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size());
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, this.Configuration);
this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, this.Configuration); // Horizontal convolution
} ParallelRowIterator.IterateRows<RowIntervalAction, Vector4>(
interest,
this.Configuration,
new RowIntervalAction(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha));
// Vertical convolution
ParallelRowIterator.IterateRows<RowIntervalAction, Vector4>(
interest,
this.Configuration,
new RowIntervalAction(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha));
} }
/// <summary> /// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location /// A <see langword="struct"/> implementing the convolution logic for <see cref="Convolution2PassProcessor{T}"/>.
/// and with the specified size.
/// </summary> /// </summary>
/// <param name="targetPixels">The target pixels to apply the process to.</param> private readonly struct RowIntervalAction : IRowIntervalAction<Vector4>
/// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="kernel">The kernel operator.</param>
/// <param name="configuration">The <see cref="Configuration"/></param>
private void ApplyConvolution(
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
Rectangle sourceRectangle,
in DenseMatrix<float> kernel,
Configuration configuration)
{ {
DenseMatrix<float> matrix = kernel; private readonly Rectangle bounds;
bool preserveAlpha = this.PreserveAlpha; private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
int startY = sourceRectangle.Y; private readonly DenseMatrix<float> kernel;
int endY = sourceRectangle.Bottom; private readonly Configuration configuration;
int startX = sourceRectangle.X; private readonly bool preserveAlpha;
int endX = sourceRectangle.Right;
int maxY = endY - 1;
int maxX = endX - 1;
var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
int width = workingRectangle.Width;
ParallelRowIterator.IterateRows<Vector4>(
workingRectangle,
configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
int length = vectorSpan.Length;
ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
for (int y = rows.Min; y < rows.Max; y++) /// <summary>
/// Initializes a new instance of the <see cref="RowIntervalAction"/> struct.
/// </summary>
/// <param name="bounds">The target processing bounds for the current instance.</param>
/// <param name="targetPixels">The target pixel buffer to adjust.</param>
/// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="kernel">The kernel operator.</param>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
DenseMatrix<float> kernel,
Configuration configuration,
bool preserveAlpha)
{
this.bounds = bounds;
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.kernel = kernel;
this.configuration = configuration;
this.preserveAlpha = preserveAlpha;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
int maxY = this.bounds.Bottom - 1;
int maxX = this.bounds.Right - 1;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan);
if (this.preserveAlpha)
{
for (int x = 0; x < this.bounds.Width; x++)
{ {
Span<TPixel> targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); DenseMatrixUtils.Convolve3(
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); in this.kernel,
this.sourcePixels,
if (preserveAlpha) ref vectorSpanRef,
{ y,
for (int x = 0; x < width; x++) x,
{ this.bounds.Y,
DenseMatrixUtils.Convolve3( maxY,
in matrix, this.bounds.X,
sourcePixels, maxX);
ref vectorSpanRef,
y,
x,
startY,
maxY,
startX,
maxX);
}
}
else
{
for (int x = 0; x < width; x++)
{
DenseMatrixUtils.Convolve4(
in matrix,
sourcePixels,
ref vectorSpanRef,
y,
x,
startY,
maxY,
startX,
maxX);
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
} }
}); }
else
{
for (int x = 0; x < this.bounds.Width; x++)
{
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref vectorSpanRef,
y,
x,
this.bounds.Y,
maxY,
this.bounds.X,
maxX);
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan);
}
}
} }
} }
} }

Loading…
Cancel
Save