|
|
|
@ -3,7 +3,9 @@ |
|
|
|
|
|
|
|
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 |
|
|
|
@ -48,7 +50,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
} |
|
|
|
|
|
|
|
int width = this.targetSize.Width; |
|
|
|
Rectangle sourceBounds = this.SourceRectangle; |
|
|
|
var targetBounds = new Rectangle(Point.Empty, this.targetSize); |
|
|
|
Configuration configuration = this.Configuration; |
|
|
|
|
|
|
|
@ -57,73 +58,127 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
|
|
|
|
if (this.resampler is NearestNeighborResampler) |
|
|
|
{ |
|
|
|
Rectangle sourceBounds = this.SourceRectangle; |
|
|
|
var nearestRowAction = new NearestNeighborRowIntervalAction(ref sourceBounds, ref matrix, width, source, destination); |
|
|
|
|
|
|
|
ParallelRowIterator.IterateRows( |
|
|
|
targetBounds, |
|
|
|
configuration, |
|
|
|
rows => |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
Span<TPixel> destRow = destination.GetPixelRowSpan(y); |
|
|
|
|
|
|
|
for (int x = 0; x < width; x++) |
|
|
|
{ |
|
|
|
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); |
|
|
|
int px = (int)MathF.Round(point.X); |
|
|
|
int py = (int)MathF.Round(point.Y); |
|
|
|
|
|
|
|
if (sourceBounds.Contains(px, py)) |
|
|
|
{ |
|
|
|
destRow[x] = source[px, py]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
in nearestRowAction); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); |
|
|
|
using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); |
|
|
|
var rowAction = new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination); |
|
|
|
|
|
|
|
ParallelRowIterator.IterateRows<RowIntervalAction, Vector4>( |
|
|
|
targetBounds, |
|
|
|
configuration, |
|
|
|
in rowAction); |
|
|
|
} |
|
|
|
|
|
|
|
private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction |
|
|
|
{ |
|
|
|
private readonly Rectangle bounds; |
|
|
|
private readonly Matrix4x4 matrix; |
|
|
|
private readonly int maxX; |
|
|
|
private readonly ImageFrame<TPixel> source; |
|
|
|
private readonly ImageFrame<TPixel> destination; |
|
|
|
|
|
|
|
try |
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public NearestNeighborRowIntervalAction( |
|
|
|
ref Rectangle bounds, |
|
|
|
ref Matrix4x4 matrix, |
|
|
|
int maxX, |
|
|
|
ImageFrame<TPixel> source, |
|
|
|
ImageFrame<TPixel> destination) |
|
|
|
{ |
|
|
|
ParallelRowIterator.IterateRowsWithTempBuffer<Vector4>( |
|
|
|
targetBounds, |
|
|
|
configuration, |
|
|
|
(rows, vectorBuffer) => |
|
|
|
this.bounds = bounds; |
|
|
|
this.matrix = matrix; |
|
|
|
this.maxX = maxX; |
|
|
|
this.source = source; |
|
|
|
this.destination = destination; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public void Invoke(in RowInterval rows) |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y); |
|
|
|
|
|
|
|
for (int x = 0; x < this.maxX; x++) |
|
|
|
{ |
|
|
|
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); |
|
|
|
int px = (int)MathF.Round(point.X); |
|
|
|
int py = (int)MathF.Round(point.Y); |
|
|
|
|
|
|
|
if (this.bounds.Contains(px, py)) |
|
|
|
{ |
|
|
|
Span<Vector4> vectorSpan = vectorBuffer.Span; |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); |
|
|
|
ref float ySpanRef = ref kernel.GetYStartReference(y); |
|
|
|
ref float xSpanRef = ref kernel.GetXStartReference(y); |
|
|
|
|
|
|
|
for (int x = 0; x < width; 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, matrix); |
|
|
|
kernel.Convolve( |
|
|
|
point, |
|
|
|
x, |
|
|
|
ref ySpanRef, |
|
|
|
ref xSpanRef, |
|
|
|
source.PixelBuffer, |
|
|
|
vectorSpan); |
|
|
|
} |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive( |
|
|
|
configuration, |
|
|
|
vectorSpan, |
|
|
|
targetRowSpan); |
|
|
|
} |
|
|
|
}); |
|
|
|
destRow[x] = this.source[px, py]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
finally |
|
|
|
} |
|
|
|
|
|
|
|
private readonly struct RowIntervalAction : IRowIntervalAction<Vector4> |
|
|
|
{ |
|
|
|
private readonly Configuration configuration; |
|
|
|
private readonly TransformKernelMap kernelMap; |
|
|
|
private readonly Matrix4x4 matrix; |
|
|
|
private readonly int maxX; |
|
|
|
private readonly ImageFrame<TPixel> source; |
|
|
|
private readonly ImageFrame<TPixel> destination; |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public RowIntervalAction( |
|
|
|
Configuration configuration, |
|
|
|
TransformKernelMap kernelMap, |
|
|
|
ref Matrix4x4 matrix, |
|
|
|
int maxX, |
|
|
|
ImageFrame<TPixel> source, |
|
|
|
ImageFrame<TPixel> destination) |
|
|
|
{ |
|
|
|
kernel.Dispose(); |
|
|
|
this.configuration = configuration; |
|
|
|
this.kernelMap = kernelMap; |
|
|
|
this.matrix = matrix; |
|
|
|
this.maxX = maxX; |
|
|
|
this.source = source; |
|
|
|
this.destination = destination; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public void Invoke(in RowInterval rows, Memory<Vector4> memory) |
|
|
|
{ |
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive( |
|
|
|
this.configuration, |
|
|
|
vectorSpan, |
|
|
|
targetRowSpan); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|