diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs index 4d70fba84..87f28045c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -91,5 +92,48 @@ namespace SixLabors.ImageSharp.Processing.Processors var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); return (translationToTargetCenter * matrix) * translateToSourceCenter; } + + /// + /// Computes the weighted sum at the given XY position + /// + /// The source image + /// The maximum x value + /// The maximum y value + /// The horizontal weights + /// The vertical weights + /// The transformed position + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected Vector4 ComputeWeightedSumAtPosition(ImageFrame source, int maxX, int maxY, ref WeightsWindow windowX, ref WeightsWindow windowY, ref Point point) + { + ref float horizontalValues = ref windowX.GetStartReference(); + ref float verticalValues = ref windowY.GetStartReference(); + int xLeft = windowX.Left; + int yLeft = windowY.Left; + int xLength = windowX.Length; + int yLength = windowY.Length; + Vector4 result = Vector4.Zero; + + // TODO: Fix this. + // Currently the output image is the separable values duplicated with half the transform applied + // and not the combined values as it should be. I must be sampling the wrong values. + for (int i = 0; i < xLength; i++) + { + int offsetX = xLeft + i + point.X; + offsetX = offsetX.Clamp(0, maxX); + float weight = Unsafe.Add(ref horizontalValues, i); + result += source[offsetX, point.Y].ToVector4() * weight; + } + + for (int i = 0; i < yLength; i++) + { + int offsetY = yLeft + i + point.Y; + offsetY = offsetY.Clamp(0, maxY); + float weight = Unsafe.Add(ref verticalValues, i); + result += source[point.X, offsetY].ToVector4() * weight; + } + + return result; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index f47d48359..50b28a831 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -86,7 +86,33 @@ namespace SixLabors.ImageSharp.Processing.Processors Matrix3x2 matrix = this.GetCenteredMatrix(source, this.CreateProcessingMatrix()); Rectangle sourceBounds = source.Bounds(); - // TODO: Use our new weights functionality to resample on transform + if (this.Sampler is NearestNeighborResampler) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + var transformedPoint = Point.Rotate(new Point(x, y), matrix); + + if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) + { + destRow[x] = source[transformedPoint.X, transformedPoint.Y]; + } + } + }); + + return; + } + + int maxX = source.Height - 1; + int maxY = source.Width - 1; + Parallel.For( 0, height, @@ -94,14 +120,16 @@ namespace SixLabors.ImageSharp.Processing.Processors y => { Span destRow = destination.GetPixelRowSpan(y); - for (int x = 0; x < width; x++) { var transformedPoint = Point.Rotate(new Point(x, y), matrix); - if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) { - destRow[x] = source[transformedPoint.X, transformedPoint.Y]; + WeightsWindow windowX = this.HorizontalWeights.Weights[transformedPoint.X]; + WeightsWindow windowY = this.VerticalWeights.Weights[transformedPoint.Y]; + Vector4 dXY = this.ComputeWeightedSumAtPosition(source, maxX, maxY, ref windowX, ref windowY, ref transformedPoint); + ref TPixel dest = ref destRow[x]; + dest.PackFromVector4(dXY); } } }); diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index ba84eab9e..233055995 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -58,7 +58,33 @@ namespace SixLabors.ImageSharp.Processing.Processors Matrix3x2 matrix = this.GetCenteredMatrix(source, this.CreateProcessingMatrix()); Rectangle sourceBounds = source.Bounds(); - // TODO: Use our new weights functionality to resample on transform + if (this.Sampler is NearestNeighborResampler) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + var transformedPoint = Point.Skew(new Point(x, y), matrix); + + if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) + { + destRow[x] = source[transformedPoint.X, transformedPoint.Y]; + } + } + }); + + return; + } + + int maxX = source.Height - 1; + int maxY = source.Width - 1; + Parallel.For( 0, height, @@ -66,14 +92,17 @@ namespace SixLabors.ImageSharp.Processing.Processors y => { Span destRow = destination.GetPixelRowSpan(y); - for (int x = 0; x < width; x++) { var transformedPoint = Point.Skew(new Point(x, y), matrix); - if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) { - destRow[x] = source[transformedPoint.X, transformedPoint.Y]; + WeightsWindow windowX = this.HorizontalWeights.Weights[transformedPoint.X]; + WeightsWindow windowY = this.VerticalWeights.Weights[transformedPoint.Y]; + + Vector4 dXY = this.ComputeWeightedSumAtPosition(source, maxX, maxY, ref windowX, ref windowY, ref transformedPoint); + ref TPixel dest = ref destRow[x]; + dest.PackFromVector4(dXY); } } });