Browse Source

Use Span<T> as buffer param

af/octree-no-pixelmap
James Jackson-South 6 years ago
parent
commit
f11a3a019b
  1. 81
      src/ImageSharp/Advanced/IRowIntervalOperation.cs
  2. 78
      src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
  3. 110
      src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
  4. 4
      src/ImageSharp/Advanced/ParallelRowIterator.cs
  5. 11
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  6. 14
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
  7. 14
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  8. 14
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  9. 12
      src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
  10. 12
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
  11. 7
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
  12. 7
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
  13. 9
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  14. 9
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
  15. 26
      tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs
  16. 11
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

81
src/ImageSharp/Advanced/IRowIntervalOperation.cs

@ -18,85 +18,4 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="rows">The row interval.</param>
void Invoke(in RowInterval rows);
}
internal readonly struct WrappingRowIntervalInfo
{
public readonly int MinY;
public readonly int MaxY;
public readonly int StepY;
public readonly int MaxX;
public WrappingRowIntervalInfo(int minY, int maxY, int stepY)
: this(minY, maxY, stepY, 0)
{
}
public WrappingRowIntervalInfo(int minY, int maxY, int stepY, int maxX)
{
this.MinY = minY;
this.MaxY = maxY;
this.StepY = stepY;
this.MaxX = maxX;
}
}
internal readonly struct WrappingRowIntervalOperation
{
private readonly WrappingRowIntervalInfo info;
private readonly Action<RowInterval> action;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowIntervalOperation(in WrappingRowIntervalInfo info, Action<RowInterval> action)
{
this.info = info;
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
this.action(rows);
}
}
internal readonly struct WrappingRowIntervalOperation<T>
where T : struct, IRowIntervalOperation
{
private readonly WrappingRowIntervalInfo info;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowIntervalOperation(in WrappingRowIntervalInfo info, in T operation)
{
this.info = info;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
// Skip the safety copy when invoking a potentially impure method on a readonly field
Unsafe.AsRef(this.operation).Invoke(in rows);
}
}
}

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

@ -19,81 +19,7 @@ namespace SixLabors.ImageSharp.Advanced
/// Invokes the method passing the row interval and a buffer.
/// </summary>
/// <param name="rows">The row interval.</param>
/// <param name="memory">The contiguous region of memory.</param>
void Invoke(in RowInterval rows, Memory<TBuffer> memory);
}
internal readonly struct WrappingRowIntervalBufferOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly WrappingRowIntervalInfo info;
private readonly MemoryAllocator allocator;
private readonly Action<RowInterval, Memory<TBuffer>> action;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowIntervalBufferOperation(
in WrappingRowIntervalInfo info,
MemoryAllocator allocator,
Action<RowInterval, Memory<TBuffer>> action)
{
this.info = info;
this.allocator = allocator;
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.MaxX);
this.action(rows, buffer.Memory);
}
}
internal readonly struct WrappingRowIntervalBufferOperation<T, TBuffer>
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly WrappingRowIntervalInfo info;
private readonly MemoryAllocator allocator;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowIntervalBufferOperation(
in WrappingRowIntervalInfo info,
MemoryAllocator allocator,
in T operation)
{
this.info = info;
this.allocator = allocator;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.MaxX);
Unsafe.AsRef(this.operation).Invoke(in rows, buffer.Memory);
}
/// <param name="span">The contiguous region of memory.</param>
void Invoke(in RowInterval rows, Span<TBuffer> span);
}
}

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

@ -0,0 +1,110 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <content>
/// Utility methods for batched processing of pixel row intervals.
/// Parallel execution is optimized for image processing based on values defined
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </content>
public static partial class ParallelRowIterator
{
private readonly struct WrappingRowIntervalInfo
{
public readonly int MinY;
public readonly int MaxY;
public readonly int StepY;
public readonly int MaxX;
public WrappingRowIntervalInfo(int minY, int maxY, int stepY)
: this(minY, maxY, stepY, 0)
{
}
public WrappingRowIntervalInfo(int minY, int maxY, int stepY, int maxX)
{
this.MinY = minY;
this.MaxY = maxY;
this.StepY = stepY;
this.MaxX = maxX;
}
}
private readonly struct WrappingRowIntervalOperation<T>
where T : struct, IRowIntervalOperation
{
private readonly WrappingRowIntervalInfo info;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowIntervalOperation(in WrappingRowIntervalInfo info, in T operation)
{
this.info = info;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
// Skip the safety copy when invoking a potentially impure method on a readonly field
Unsafe.AsRef(this.operation).Invoke(in rows);
}
}
private readonly struct WrappingRowIntervalBufferOperation<T, TBuffer>
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly WrappingRowIntervalInfo info;
private readonly MemoryAllocator allocator;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowIntervalBufferOperation(
in WrappingRowIntervalInfo info,
MemoryAllocator allocator,
in T operation)
{
this.info = info;
this.allocator = allocator;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.MaxX);
Unsafe.AsRef(this.operation).Invoke(in rows, buffer.Memory.Span);
}
}
}
}

4
src/ImageSharp/Advanced/ParallelRowIterator.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </summary>
public static class ParallelRowIterator
public static partial class ParallelRowIterator
{
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Advanced
var rows = new RowInterval(top, bottom);
using (IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width))
{
Unsafe.AsRef(operation).Invoke(rows, buffer.Memory);
Unsafe.AsRef(operation).Invoke(rows, buffer.Memory.Span);
}
return;

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

@ -448,16 +448,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
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, PixelConversionModifiers.Premultiply);
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply);
ref Vector4 baseRef = ref MemoryMarshal.GetReference(span);
for (int x = 0; x < this.bounds.Width; x++)
{
@ -467,7 +464,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
v.Z = MathF.Pow(v.Z, this.gamma);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan.Slice(0, length), targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

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

@ -113,16 +113,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
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);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
if (this.preserveAlpha)
{
@ -132,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
in this.kernelY,
in this.kernelX,
this.sourcePixels,
ref vectorSpanRef,
ref spanRef,
y,
x,
this.bounds.Y,
@ -149,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
in this.kernelY,
in this.kernelX,
this.sourcePixels,
ref vectorSpanRef,
ref spanRef,
y,
x,
this.bounds.Y,
@ -159,7 +157,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

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

@ -109,11 +109,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
int maxY = this.bounds.Bottom - 1;
int maxX = this.bounds.Right - 1;
@ -121,7 +119,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
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);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
if (this.preserveAlpha)
{
@ -130,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve3(
in this.kernel,
this.sourcePixels,
ref vectorSpanRef,
ref spanRef,
y,
x,
this.bounds.Y,
@ -146,7 +144,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref vectorSpanRef,
ref spanRef,
y,
x,
this.bounds.Y,
@ -156,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

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

@ -100,16 +100,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
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);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
if (this.preserveAlpha)
{
@ -118,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve3(
in this.kernel,
this.sourcePixels,
ref vectorSpanRef,
ref spanRef,
y,
x,
this.bounds.Y,
@ -134,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve4(
in this.kernel,
this.sourcePixels,
ref vectorSpanRef,
ref spanRef,
y,
x,
this.bounds.Y,
@ -144,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}

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

@ -86,19 +86,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, vectorSpan, this.modifiers);
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers);
// Run the user defined pixel shader to the current row of pixels
Unsafe.AsRef(this.rowProcessor).Invoke(vectorSpan, new Point(this.startX, y));
Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y));
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan, this.modifiers);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers);
}
}
}

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

@ -69,18 +69,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length;
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, vectorSpan);
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span);
Vector4Utils.Transform(vectorSpan, ref Unsafe.AsRef(this.matrix));
Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix));
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan);
}
}
}

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

@ -95,9 +95,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<float> memory)
public void Invoke(in RowInterval rows, Span<float> span)
{
Span<float> amountsSpan = memory.Span;
Span<TPixel> colorSpan = this.colors.GetSpan();
for (int y = rows.Min; y < rows.Max; y++)
@ -105,7 +104,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
for (int i = 0; i < this.bounds.Width; i++)
{
float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y));
amountsSpan[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1);
span[i] = (this.blendPercent * (1 - (.95F * (distance / this.maxDistance)))).Clamp(0, 1);
}
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
@ -115,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
destination,
destination,
colorSpan,
amountsSpan);
span);
}
}
}

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

@ -103,9 +103,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<float> memory)
public void Invoke(in RowInterval rows, Span<float> span)
{
Span<float> amountsSpan = memory.Span;
Span<TPixel> colorSpan = this.colors.GetSpan();
for (int y = rows.Min; y < rows.Max; y++)
@ -113,7 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
for (int i = 0; i < this.bounds.Width; i++)
{
float distance = Vector2.Distance(this.center, new Vector2(i + this.bounds.X, y));
amountsSpan[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1);
span[i] = (this.blendPercent * (.9F * (distance / this.maxDistance))).Clamp(0, 1);
}
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
@ -123,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
destination,
destination,
colorSpan,
amountsSpan);
span);
}
}
}

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

@ -154,13 +154,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> vectorSpan = memory.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = this.destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan, vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan, span);
ref float ySpanRef = ref this.kernelMap.GetYStartReference(y);
ref float xSpanRef = ref this.kernelMap.GetXStartReference(y);
@ -175,12 +174,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ref ySpanRef,
ref xSpanRef,
this.source.PixelBuffer,
vectorSpan);
span);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
vectorSpan,
span,
targetRowSpan);
}
}

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

@ -150,13 +150,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> vectorSpan = memory.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = this.destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan, vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan, span);
ref float ySpanRef = ref this.kernelMap.GetYStartReference(y);
ref float xSpanRef = ref this.kernelMap.GetXStartReference(y);
@ -171,12 +170,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ref ySpanRef,
ref xSpanRef,
this.source.PixelBuffer,
vectorSpan);
span);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
vectorSpan,
span,
targetRowSpan);
}
}

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

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Numerics;
using System.Threading;
@ -17,6 +16,8 @@ namespace SixLabors.ImageSharp.Tests.Helpers
{
public class ParallelRowIteratorTests
{
public delegate void RowIntervalAction<T>(RowInterval rows, Span<T> span);
private readonly ITestOutputHelper output;
public ParallelRowIteratorTests(ITestOutputHelper output)
@ -140,17 +141,13 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var rectangle = new Rectangle(0, minY, 10, maxY - minY);
var bufferHashes = new ConcurrentBag<int>();
int actualNumberOfSteps = 0;
void RowAction(RowInterval rows, Memory<Vector4> buffer)
void RowAction(RowInterval rows, Span<Vector4> buffer)
{
Assert.True(rows.Min >= minY);
Assert.True(rows.Max <= maxY);
bufferHashes.Add(buffer.GetHashCode());
int step = rows.Max - rows.Min;
int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength;
@ -166,9 +163,6 @@ namespace SixLabors.ImageSharp.Tests.Helpers
in operation);
Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
int numberOfDifferentBuffers = bufferHashes.Distinct().Count();
Assert.Equal(actualNumberOfSteps, numberOfDifferentBuffers);
}
[Theory]
@ -191,7 +185,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray();
var actualData = new int[maxY];
void RowAction(RowInterval rows, Memory<Vector4> buffer)
void RowAction(RowInterval rows, Span<Vector4> buffer)
{
for (int y = rows.Min; y < rows.Max; y++)
{
@ -283,7 +277,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
int actualNumberOfSteps = 0;
void RowAction(RowInterval rows, Memory<Vector4> buffer)
void RowAction(RowInterval rows, Span<Vector4> buffer)
{
Assert.True(rows.Min >= 0);
Assert.True(rows.Max <= height);
@ -405,7 +399,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var rect = new Rectangle(0, 0, width, height);
void RowAction(RowInterval rows, Memory<Rgba32> memory)
void RowAction(RowInterval rows, Span<Rgba32> memory)
{
}
@ -430,13 +424,13 @@ namespace SixLabors.ImageSharp.Tests.Helpers
private readonly struct TestRowIntervalOperation<TBuffer> : IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly Action<RowInterval, Memory<TBuffer>> action;
private readonly RowIntervalAction<TBuffer> action;
public TestRowIntervalOperation(Action<RowInterval, Memory<TBuffer>> action)
public TestRowIntervalOperation(RowIntervalAction<TBuffer> action)
=> this.action = action;
public void Invoke(in RowInterval rows, Memory<TBuffer> memory)
=> this.action(rows, memory);
public void Invoke(in RowInterval rows, Span<TBuffer> span)
=> this.action(rows, span);
}
}
}

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

@ -724,20 +724,19 @@ namespace SixLabors.ImageSharp.Tests
this.source = source;
}
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Span<Vector4> tempSpan = memory.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left, this.bounds.Width);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, tempSpan, PixelConversionModifiers.Scale);
for (int i = 0; i < tempSpan.Length; i++)
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale);
for (int i = 0; i < span.Length; i++)
{
ref Vector4 v = ref tempSpan[i];
ref Vector4 v = ref span[i];
v.W = 1F;
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, tempSpan, rowSpan, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan, PixelConversionModifiers.Scale);
}
}
}

Loading…
Cancel
Save