From 01f2c05f72503ade92e8eb79a992358b6b5f2dcf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 6 Feb 2020 21:53:52 +1100 Subject: [PATCH] Update ProjectiveTransformProcessor{TPixel}.cs --- .../ProjectiveTransformProcessor{TPixel}.cs | 167 ++++++++++++------ 1 file changed, 111 insertions(+), 56 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index b63e7eff3..76bbc3a90 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -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 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( + targetBounds, + configuration, + in rowAction); + } + + private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction + { + private readonly Rectangle bounds; + private readonly Matrix4x4 matrix; + private readonly int maxX; + private readonly ImageFrame source; + private readonly ImageFrame destination; - try + [MethodImpl(InliningOptions.ShortMethod)] + public NearestNeighborRowIntervalAction( + ref Rectangle bounds, + ref Matrix4x4 matrix, + int maxX, + ImageFrame source, + ImageFrame destination) { - ParallelRowIterator.IterateRowsWithTempBuffer( - 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 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 vectorSpan = vectorBuffer.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.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.Instance.FromVector4Destructive( - configuration, - vectorSpan, - targetRowSpan); - } - }); + destRow[x] = this.source[px, py]; + } + } + } } - finally + } + + private readonly struct RowIntervalAction : IRowIntervalAction + { + private readonly Configuration configuration; + private readonly TransformKernelMap kernelMap; + private readonly Matrix4x4 matrix; + private readonly int maxX; + private readonly ImageFrame source; + private readonly ImageFrame destination; + + [MethodImpl(InliningOptions.ShortMethod)] + public RowIntervalAction( + Configuration configuration, + TransformKernelMap kernelMap, + ref Matrix4x4 matrix, + int maxX, + ImageFrame source, + ImageFrame 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 memory) + { + Span vectorSpan = memory.Span; + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.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.Instance.FromVector4Destructive( + this.configuration, + vectorSpan, + targetRowSpan); + } } } }