|
|
|
@ -5,7 +5,6 @@ using System; |
|
|
|
using System.Numerics; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using SixLabors.ImageSharp.Advanced; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
@ -17,9 +16,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
internal class ProjectiveTransformProcessor<TPixel> : TransformProcessor<TPixel> |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
private Size targetSize; |
|
|
|
private readonly Size targetSize; |
|
|
|
private readonly IResampler resampler; |
|
|
|
private Matrix4x4 transformMatrix; |
|
|
|
private readonly Matrix4x4 transformMatrix; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class.
|
|
|
|
@ -70,12 +69,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
|
|
|
|
using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); |
|
|
|
|
|
|
|
ParallelRowIterator.IterateRows<RowIntervalAction, Vector4>( |
|
|
|
ParallelRowIterator.IterateRows2<RowAction, Vector4>( |
|
|
|
targetBounds, |
|
|
|
configuration, |
|
|
|
new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination)); |
|
|
|
new RowAction(configuration, kernelMap, ref matrix, width, source, destination)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A <see langword="struct"/> implementing the nearest neighbor interpolation logic for <see cref="ProjectiveTransformProcessor{T}"/>.
|
|
|
|
/// </summary>
|
|
|
|
private readonly struct NearestNeighborRowAction : IRowAction |
|
|
|
{ |
|
|
|
private readonly Rectangle bounds; |
|
|
|
@ -99,6 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
this.destination = destination; |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public void Invoke(int y) |
|
|
|
{ |
|
|
|
@ -118,7 +121,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private readonly struct RowIntervalAction : IRowIntervalAction<Vector4> |
|
|
|
/// <summary>
|
|
|
|
/// A <see langword="struct"/> implementing the convolution logic for <see cref="ProjectiveTransformProcessor{T}"/>.
|
|
|
|
/// </summary>
|
|
|
|
private readonly struct RowAction : IRowAction<Vector4> |
|
|
|
{ |
|
|
|
private readonly Configuration configuration; |
|
|
|
private readonly TransformKernelMap kernelMap; |
|
|
|
@ -128,7 +134,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
private readonly ImageFrame<TPixel> destination; |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public RowIntervalAction( |
|
|
|
public RowAction( |
|
|
|
Configuration configuration, |
|
|
|
TransformKernelMap kernelMap, |
|
|
|
ref Matrix4x4 matrix, |
|
|
|
@ -144,36 +150,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
this.destination = destination; |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public void Invoke(in RowInterval rows, Memory<Vector4> memory) |
|
|
|
public void Invoke(int y, 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); |
|
|
|
ref float ySpanRef = ref this.kernelMap.GetYStartReference(y); |
|
|
|
ref float xSpanRef = ref this.kernelMap.GetXStartReference(y); |
|
|
|
|
|
|
|
for (int x = 0; x < this.maxX; x++) |
|
|
|
{ |
|
|
|
// Use the single precision position to calculate correct bounding pixels
|
|
|
|
// otherwise we get rogue pixels outside of the bounds.
|
|
|
|
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); |
|
|
|
this.kernelMap.Convolve( |
|
|
|
point, |
|
|
|
x, |
|
|
|
ref ySpanRef, |
|
|
|
ref xSpanRef, |
|
|
|
this.source.PixelBuffer, |
|
|
|
vectorSpan); |
|
|
|
} |
|
|
|
Span<TPixel> targetRowSpan = this.destination.GetPixelRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan, span); |
|
|
|
ref float ySpanRef = ref this.kernelMap.GetYStartReference(y); |
|
|
|
ref float xSpanRef = ref this.kernelMap.GetXStartReference(y); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive( |
|
|
|
this.configuration, |
|
|
|
vectorSpan, |
|
|
|
targetRowSpan); |
|
|
|
for (int x = 0; x < this.maxX; x++) |
|
|
|
{ |
|
|
|
// Use the single precision position to calculate correct bounding pixels
|
|
|
|
// otherwise we get rogue pixels outside of the bounds.
|
|
|
|
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); |
|
|
|
this.kernelMap.Convolve( |
|
|
|
point, |
|
|
|
x, |
|
|
|
ref ySpanRef, |
|
|
|
ref xSpanRef, |
|
|
|
this.source.PixelBuffer, |
|
|
|
span); |
|
|
|
} |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive( |
|
|
|
this.configuration, |
|
|
|
span, |
|
|
|
targetRowSpan); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|