Browse Source

Introduce GetRequiredBufferLength method

pull/2241/head
Ynse Hoornenborg 3 years ago
parent
commit
d42d8df88d
  1. 7
      src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
  2. 7
      src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
  3. 16
      src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
  4. 10
      src/ImageSharp/Advanced/ParallelRowIterator.cs
  5. 5
      src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
  6. 5
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  7. 17
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  8. 5
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs
  9. 10
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  10. 5
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  11. 5
      src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs
  12. 5
      src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
  13. 5
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
  14. 5
      src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs
  15. 5
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
  16. 5
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
  17. 5
      src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
  18. 5
      src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
  19. 6
      tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs
  20. 3
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

7
src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs

@ -12,6 +12,13 @@ namespace SixLabors.ImageSharp.Advanced;
public interface IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Return the minimal required number of items in the buffer passed on <see cref="Invoke" />.
/// </summary>
/// <param name="bounds">The bounds of the operation.</param>
/// <returns>The required buffer length.</returns>
int GetRequiredBufferLength(Rectangle bounds);
/// <summary>
/// Invokes the method passing the row interval and a buffer.
/// </summary>

7
src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs

@ -10,6 +10,13 @@ namespace SixLabors.ImageSharp.Advanced;
public interface IRowOperation<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Return the minimal required number of items in the buffer passed on <see cref="Invoke" />.
/// </summary>
/// <param name="bounds">The bounds of the operation.</param>
/// <returns>The required buffer length.</returns>
int GetRequiredBufferLength(Rectangle bounds);
/// <summary>
/// Invokes the method passing the row and a buffer.
/// </summary>

16
src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs

@ -63,7 +63,7 @@ public static partial class ParallelRowIterator
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly int width;
private readonly int bufferLength;
private readonly MemoryAllocator allocator;
private readonly T action;
@ -72,14 +72,14 @@ public static partial class ParallelRowIterator
int minY,
int maxY,
int stepY,
int width,
int bufferLength,
MemoryAllocator allocator,
in T action)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.width = width;
this.bufferLength = bufferLength;
this.allocator = allocator;
this.action = action;
}
@ -96,7 +96,7 @@ public static partial class ParallelRowIterator
int yMax = Math.Min(yMin + this.stepY, this.maxY);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.bufferLength);
Span<TBuffer> span = buffer.Memory.Span;
@ -153,7 +153,7 @@ public static partial class ParallelRowIterator
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly int width;
private readonly int bufferLength;
private readonly MemoryAllocator allocator;
private readonly T operation;
@ -162,14 +162,14 @@ public static partial class ParallelRowIterator
int minY,
int maxY,
int stepY,
int width,
int bufferLength,
MemoryAllocator allocator,
in T operation)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.width = width;
this.bufferLength = bufferLength;
this.allocator = allocator;
this.operation = operation;
}
@ -187,7 +187,7 @@ public static partial class ParallelRowIterator
int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.bufferLength);
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span);
}

10
src/ImageSharp/Advanced/ParallelRowIterator.cs

@ -118,11 +118,12 @@ public static partial class ParallelRowIterator
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
int bufferLength = Unsafe.AsRef(operation).GetRequiredBufferLength(rectangle);
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width);
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(bufferLength);
Span<TBuffer> span = buffer.Memory.Span;
for (int y = top; y < bottom; y++)
@ -135,7 +136,7 @@ public static partial class ParallelRowIterator
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, in operation);
var wrappingOperation = new RowOperationWrapper<T, TBuffer>(top, bottom, verticalStep, bufferLength, allocator, in operation);
Parallel.For(
0,
@ -244,12 +245,13 @@ public static partial class ParallelRowIterator
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
int bufferLength = Unsafe.AsRef(operation).GetRequiredBufferLength(rectangle);
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
var rows = new RowInterval(top, bottom);
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width);
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(bufferLength);
Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
@ -258,7 +260,7 @@ public static partial class ParallelRowIterator
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, in operation);
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(top, bottom, verticalStep, bufferLength, allocator, in operation);
Parallel.For(
0,

5
src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs

@ -85,6 +85,11 @@ internal class AdaptiveThresholdProcessor<TPixel> : ImageProcessor<TPixel>
this.clusterSize = clusterSize;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<L8> span)

5
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs

@ -86,6 +86,11 @@ internal class BinaryThresholdProcessor<TPixel> : ImageProcessor<TPixel>
this.configuration = configuration;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(int y, Span<Rgb24> span)

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

@ -220,6 +220,11 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
this.configuration = configuration;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
@ -289,6 +294,11 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
this.gamma = gamma;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
@ -329,6 +339,13 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
this.configuration = configuration;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
{
return bounds.Width;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)

5
src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs

@ -46,6 +46,11 @@ internal readonly struct Convolution2DRowOperation<TPixel> : IRowOperation<Vecto
this.preserveAlpha = preserveAlpha;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(int y, Span<Vector4> span)

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

@ -143,6 +143,11 @@ internal class Convolution2PassProcessor<TPixel> : ImageProcessor<TPixel>
this.preserveAlpha = preserveAlpha;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(int y, Span<Vector4> span)
@ -304,6 +309,11 @@ internal class Convolution2PassProcessor<TPixel> : ImageProcessor<TPixel>
this.preserveAlpha = preserveAlpha;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(int y, Span<Vector4> span)

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

@ -106,6 +106,11 @@ internal class ConvolutionProcessor<TPixel> : ImageProcessor<TPixel>
this.preserveAlpha = preserveAlpha;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)

5
src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs

@ -43,6 +43,11 @@ internal readonly struct MedianRowOperation<TPixel> : IRowOperation<Vector4>
this.wChannelStart = this.zChannelStart + kernelCount;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
public void Invoke(int y, Span<Vector4> span)
{
// Span has kernelSize^2 followed by bound width.

5
src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs

@ -83,6 +83,11 @@ internal sealed class PixelRowDelegateProcessor<TPixel, TDelegate> : ImageProces
this.rowProcessor = rowProcessor;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)

5
src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs

@ -66,6 +66,11 @@ internal class FilterProcessor<TPixel> : ImageProcessor<TPixel>
this.configuration = configuration;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)

5
src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs

@ -46,6 +46,11 @@ internal sealed class OpaqueProcessor<TPixel> : ImageProcessor<TPixel>
this.bounds = bounds;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)

5
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs

@ -93,6 +93,11 @@ internal class GlowProcessor<TPixel> : ImageProcessor<TPixel>
this.source = source;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<float> span)
{

5
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs

@ -101,6 +101,11 @@ internal class VignetteProcessor<TPixel> : ImageProcessor<TPixel>
this.source = source;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<float> span)
{

5
src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs

@ -176,6 +176,11 @@ internal class AffineTransformProcessor<TPixel> : TransformProcessor<TPixel>, IR
this.xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Width, destination.Width);
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
{

5
src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs

@ -176,6 +176,11 @@ internal class ProjectiveTransformProcessor<TPixel> : TransformProcessor<TPixel>
this.xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, bounds.Width, destination.Width);
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
{

6
tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs

@ -413,6 +413,9 @@ public class ParallelRowIteratorTests
public TestRowIntervalOperation(Action<RowInterval> action)
=> this.action = action;
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
public void Invoke(in RowInterval rows) => this.action(rows);
}
@ -424,6 +427,9 @@ public class ParallelRowIteratorTests
public TestRowIntervalOperation(RowIntervalAction<TBuffer> action)
=> this.action = action;
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
public void Invoke(in RowInterval rows, Span<TBuffer> span)
=> this.action(rows, span);
}

3
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -755,6 +755,9 @@ public static class TestImageExtensions
this.source = source;
}
public int GetRequiredBufferLength(Rectangle bounds)
=> bounds.Width;
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
for (int y = rows.Min; y < rows.Max; y++)

Loading…
Cancel
Save