diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs
new file mode 100644
index 0000000000..1d215d2860
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Guard.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp;
+
+namespace SixLabors
+{
+ internal static partial class Guard
+ {
+ ///
+ /// Ensures that the value is a value type.
+ ///
+ /// The target object, which cannot be null.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ /// is not a value type.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void MustBeValueType(TValue value, string parameterName)
+ {
+ if (!value.GetType().GetTypeInfo().IsValueType)
+ {
+ ThrowArgumentException("Type must be a struct.", parameterName);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor.cs
similarity index 96%
rename from src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor.cs
index d0000edcf9..fec41dbffe 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor.cs
@@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions)
{
Guard.NotNull(sampler, nameof(sampler));
+ Guard.MustBeValueType(sampler, nameof(sampler));
+
this.Sampler = sampler;
this.TransformMatrix = matrix;
this.DestinationSize = targetDimensions;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.Transforms.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.Transforms.cs
new file mode 100644
index 0000000000..3190857dd0
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.Transforms.cs
@@ -0,0 +1,209 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms
+{
+ ///
+ /// Contains the application code for performing an affine transform.
+ ///
+ internal partial class AffineTransformProcessor
+ {
+ ///
+ /// Applies an affine transformation upon an image.
+ ///
+ /// The type of sampler.
+ /// The configuration.
+ /// The pixel sampler.
+ /// The source image frame.
+ /// The destination image frame.
+ /// The transform matrix.
+ public static void ApplyAffineTransform(
+ Configuration configuration,
+ in TResampler sampler,
+ ImageFrame source,
+ ImageFrame destination,
+ Matrix3x2 matrix)
+ where TResampler : struct, IResampler
+ {
+ // Handle transforms that result in output identical to the original.
+ if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity))
+ {
+ // The clone will be blank here copy all the pixel data over
+ source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
+ return;
+ }
+
+ // Convert from screen to world space.
+ Matrix3x2.Invert(matrix, out matrix);
+
+ if (sampler is NearestNeighborResampler)
+ {
+ var nnOperation = new NNAffineOperation(source, destination, matrix);
+ ParallelRowIterator.IterateRows(
+ configuration,
+ destination.Bounds(),
+ in nnOperation);
+
+ return;
+ }
+
+ int yRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
+ int xRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
+ var radialExtents = new Vector2(xRadius, yRadius);
+ int yLength = (yRadius * 2) + 1;
+ int xLength = (xRadius * 2) + 1;
+
+ // We use 2D buffers so that we can access the weight spans per row in parallel.
+ using Buffer2D yKernelBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height);
+ using Buffer2D xKernelBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height);
+
+ int maxX = source.Width - 1;
+ int maxY = source.Height - 1;
+ var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
+
+ var operation = new AffineOperation(
+ configuration,
+ source,
+ destination,
+ yKernelBuffer,
+ xKernelBuffer,
+ in sampler,
+ matrix,
+ radialExtents,
+ maxSourceExtents);
+
+ ParallelRowIterator.IterateRows, Vector4>(
+ configuration,
+ destination.Bounds(),
+ in operation);
+ }
+
+ private readonly struct NNAffineOperation : IRowIntervalOperation
+ {
+ private readonly ImageFrame source;
+ private readonly ImageFrame destination;
+ private readonly Rectangle bounds;
+ private readonly Matrix3x2 matrix;
+ private readonly int maxX;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public NNAffineOperation(
+ ImageFrame source,
+ ImageFrame destination,
+ Matrix3x2 matrix)
+ {
+ this.source = source;
+ this.destination = destination;
+ this.bounds = source.Bounds();
+ this.matrix = matrix;
+ this.maxX = destination.Width;
+ }
+
+ [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++)
+ {
+ var point = Vector2.Transform(new Vector2(x, y), this.matrix);
+ int px = (int)MathF.Round(point.X);
+ int py = (int)MathF.Round(point.Y);
+
+ if (this.bounds.Contains(px, py))
+ {
+ destRow[x] = this.source[px, py];
+ }
+ }
+ }
+ }
+ }
+
+ private readonly struct AffineOperation : IRowIntervalOperation
+ where TResampler : struct, IResampler
+ {
+ private readonly Configuration configuration;
+ private readonly ImageFrame source;
+ private readonly ImageFrame destination;
+ private readonly Buffer2D yKernelBuffer;
+ private readonly Buffer2D xKernelBuffer;
+ private readonly TResampler sampler;
+ private readonly Matrix3x2 matrix;
+ private readonly Vector2 radialExtents;
+ private readonly Vector4 maxSourceExtents;
+ private readonly int maxX;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public AffineOperation(
+ Configuration configuration,
+ ImageFrame source,
+ ImageFrame destination,
+ Buffer2D yKernelBuffer,
+ Buffer2D xKernelBuffer,
+ in TResampler sampler,
+ Matrix3x2 matrix,
+ Vector2 radialExtents,
+ Vector4 maxSourceExtents)
+ {
+ this.configuration = configuration;
+ this.source = source;
+ this.destination = destination;
+ this.yKernelBuffer = yKernelBuffer;
+ this.xKernelBuffer = xKernelBuffer;
+ this.sampler = sampler;
+ this.matrix = matrix;
+ this.radialExtents = radialExtents;
+ this.maxSourceExtents = maxSourceExtents;
+ this.maxX = destination.Width;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void Invoke(in RowInterval rows, Span span)
+ {
+ Buffer2D sourceBuffer = this.source.PixelBuffer;
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ PixelOperations.Instance.ToVector4(
+ this.configuration,
+ this.destination.GetPixelRowSpan(y),
+ span);
+
+ ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
+ ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(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.
+ var point = Vector2.Transform(new Vector2(x, y), this.matrix);
+ AutomorphicTransformUtilities.Convolve(
+ in this.sampler,
+ point,
+ sourceBuffer,
+ span,
+ x,
+ ref yKernelSpanRef,
+ ref xKernelSpanRef,
+ this.radialExtents,
+ this.maxSourceExtents);
+ }
+
+ PixelOperations.Instance.FromVector4Destructive(
+ this.configuration,
+ span,
+ this.destination.GetPixelRowSpan(y));
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs
similarity index 72%
rename from src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs
index dddeba33fa..78310707c8 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs
@@ -10,12 +10,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Provides the base methods to perform affine transforms on an image.
///
/// The pixel format.
- internal class AffineTransformProcessor : TransformProcessor
+ internal partial class AffineTransformProcessor : TransformProcessor, IResamplingImageProcessor
where TPixel : struct, IPixel
{
private readonly Size destinationSize;
private readonly Matrix3x2 transformMatrix;
private readonly IResampler resampler;
+ private ImageFrame source;
+ private ImageFrame destination;
///
/// Initializes a new instance of the class.
@@ -36,6 +38,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
protected override void OnFrameApply(ImageFrame source, ImageFrame destination)
- => this.resampler.ApplyAffineTransform(this.Configuration, source, destination, this.transformMatrix);
+ {
+ this.source = source;
+ this.destination = destination;
+ this.resampler.ApplyTransform(this);
+ }
+
+ ///
+ public void ApplyTransform(in TResampler sampler)
+ where TResampler : struct, IResampler
+ => ApplyAffineTransform(
+ this.Configuration,
+ in sampler,
+ this.source,
+ this.destination,
+ this.transformMatrix);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor{TPixel}.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor{TPixel}.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs
new file mode 100644
index 0000000000..b2283af010
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs
@@ -0,0 +1,104 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms
+{
+ ///
+ /// Utility methods for affine and projective transforms.
+ ///
+ internal static class AutomorphicTransformUtilities
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static int GetSamplingRadius(in TResampler sampler, int sourceSize, int destinationSize)
+ where TResampler : struct, IResampler
+ {
+ double scale = sourceSize / destinationSize;
+ if (scale < 1)
+ {
+ scale = 1;
+ }
+
+ return (int)Math.Ceiling(scale * sampler.Radius);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void Convolve(
+ in TResampler sampler,
+ Vector2 transformedPoint,
+ Buffer2D sourcePixels,
+ Span targetRow,
+ int column,
+ ref float yKernelSpanRef,
+ ref float xKernelSpanRef,
+ Vector2 radialExtents,
+ Vector4 maxSourceExtents)
+ where TResampler : struct, IResampler
+ where TPixel : struct, IPixel
+ {
+ // Clamp sampling pixel radial extents to the source image edges
+ Vector2 minXY = transformedPoint - radialExtents;
+ Vector2 maxXY = transformedPoint + radialExtents;
+
+ // left, top, right, bottom
+ var sourceExtents = new Vector4(
+ MathF.Ceiling(minXY.X),
+ MathF.Ceiling(minXY.Y),
+ MathF.Floor(maxXY.X),
+ MathF.Floor(maxXY.Y));
+
+ sourceExtents = Vector4.Clamp(sourceExtents, Vector4.Zero, maxSourceExtents);
+
+ int left = (int)sourceExtents.X;
+ int top = (int)sourceExtents.Y;
+ int right = (int)sourceExtents.Z;
+ int bottom = (int)sourceExtents.W;
+
+ if (left == right || top == bottom)
+ {
+ return;
+ }
+
+ CalculateWeights(in sampler, top, bottom, transformedPoint.Y, ref yKernelSpanRef);
+ CalculateWeights(in sampler, left, right, transformedPoint.X, ref xKernelSpanRef);
+
+ Vector4 sum = Vector4.Zero;
+ for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++)
+ {
+ float yWeight = Unsafe.Add(ref yKernelSpanRef, kernelY);
+
+ for (int kernelX = 0, x = left; x <= right; x++, kernelX++)
+ {
+ float xWeight = Unsafe.Add(ref xKernelSpanRef, kernelX);
+
+ // Values are first premultiplied to prevent darkening of edge pixels.
+ var current = sourcePixels[x, y].ToVector4();
+ Vector4Utils.Premultiply(ref current);
+ sum += current * xWeight * yWeight;
+ }
+ }
+
+ // Reverse the premultiplication
+ Vector4Utils.UnPremultiply(ref sum);
+ targetRow[column] = sum;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CalculateWeights(in TResampler sampler, int min, int max, float point, ref float weightsRef)
+ where TResampler : struct, IResampler
+ {
+ float sum = 0;
+ for (int x = 0, i = min; i <= max; i++, x++)
+ {
+ float weight = sampler.GetValue(i - point);
+ sum += weight;
+ Unsafe.Add(ref weightsRef, x) = weight;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor{TPixel}.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor{TPixel}.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor.cs
similarity index 96%
rename from src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor.cs
index 6f17c11450..f716ba701e 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor.cs
@@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions)
{
Guard.NotNull(sampler, nameof(sampler));
+ Guard.MustBeValueType(sampler, nameof(sampler));
+
this.Sampler = sampler;
this.TransformMatrix = matrix;
this.DestinationSize = targetDimensions;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.Transforms.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.Transforms.cs
new file mode 100644
index 0000000000..c824bebaec
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.Transforms.cs
@@ -0,0 +1,209 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms
+{
+ ///
+ /// Contains the application code for performing a projective transform.
+ ///
+ internal partial class ProjectiveTransformProcessor
+ {
+ ///
+ /// Applies a projective transformation upon an image.
+ ///
+ /// The type of sampler.
+ /// The configuration.
+ /// The pixel sampler.
+ /// The source image frame.
+ /// The destination image frame.
+ /// The transform matrix.
+ public static void ApplyProjectiveTransform(
+ Configuration configuration,
+ in TResampler sampler,
+ ImageFrame source,
+ ImageFrame destination,
+ Matrix4x4 matrix)
+ where TResampler : struct, IResampler
+ {
+ // Handle transforms that result in output identical to the original.
+ if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity))
+ {
+ // The clone will be blank here copy all the pixel data over
+ source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
+ return;
+ }
+
+ // Convert from screen to world space.
+ Matrix4x4.Invert(matrix, out matrix);
+
+ if (sampler is NearestNeighborResampler)
+ {
+ var nnOperation = new NNProjectiveOperation(source, destination, matrix);
+ ParallelRowIterator.IterateRows(
+ configuration,
+ destination.Bounds(),
+ in nnOperation);
+
+ return;
+ }
+
+ int yRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
+ int xRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
+ var radialExtents = new Vector2(xRadius, yRadius);
+ int yLength = (yRadius * 2) + 1;
+ int xLength = (xRadius * 2) + 1;
+
+ // We use 2D buffers so that we can access the weight spans per row in parallel.
+ using Buffer2D yKernelBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height);
+ using Buffer2D xKernelBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height);
+
+ int maxX = source.Width - 1;
+ int maxY = source.Height - 1;
+ var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
+
+ var operation = new ProjectiveOperation(
+ configuration,
+ source,
+ destination,
+ yKernelBuffer,
+ xKernelBuffer,
+ in sampler,
+ matrix,
+ radialExtents,
+ maxSourceExtents);
+
+ ParallelRowIterator.IterateRows, Vector4>(
+ configuration,
+ destination.Bounds(),
+ in operation);
+ }
+
+ private readonly struct NNProjectiveOperation : IRowIntervalOperation
+ {
+ private readonly ImageFrame source;
+ private readonly ImageFrame destination;
+ private readonly Rectangle bounds;
+ private readonly Matrix4x4 matrix;
+ private readonly int maxX;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public NNProjectiveOperation(
+ ImageFrame source,
+ ImageFrame destination,
+ Matrix4x4 matrix)
+ {
+ this.source = source;
+ this.destination = destination;
+ this.bounds = source.Bounds();
+ this.matrix = matrix;
+ this.maxX = destination.Width;
+ }
+
+ [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 = TransformUtilities.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))
+ {
+ destRow[x] = this.source[px, py];
+ }
+ }
+ }
+ }
+ }
+
+ private readonly struct ProjectiveOperation : IRowIntervalOperation
+ where TResampler : struct, IResampler
+ {
+ private readonly Configuration configuration;
+ private readonly ImageFrame source;
+ private readonly ImageFrame destination;
+ private readonly Buffer2D yKernelBuffer;
+ private readonly Buffer2D xKernelBuffer;
+ private readonly TResampler sampler;
+ private readonly Matrix4x4 matrix;
+ private readonly Vector2 radialExtents;
+ private readonly Vector4 maxSourceExtents;
+ private readonly int maxX;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public ProjectiveOperation(
+ Configuration configuration,
+ ImageFrame source,
+ ImageFrame destination,
+ Buffer2D yKernelBuffer,
+ Buffer2D xKernelBuffer,
+ in TResampler sampler,
+ Matrix4x4 matrix,
+ Vector2 radialExtents,
+ Vector4 maxSourceExtents)
+ {
+ this.configuration = configuration;
+ this.source = source;
+ this.destination = destination;
+ this.yKernelBuffer = yKernelBuffer;
+ this.xKernelBuffer = xKernelBuffer;
+ this.sampler = sampler;
+ this.matrix = matrix;
+ this.radialExtents = radialExtents;
+ this.maxSourceExtents = maxSourceExtents;
+ this.maxX = destination.Width;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void Invoke(in RowInterval rows, Span span)
+ {
+ Buffer2D sourceBuffer = this.source.PixelBuffer;
+ for (int y = rows.Min; y < rows.Max; y++)
+ {
+ PixelOperations.Instance.ToVector4(
+ this.configuration,
+ this.destination.GetPixelRowSpan(y),
+ span);
+
+ ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
+ ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(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 = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
+ AutomorphicTransformUtilities.Convolve(
+ in this.sampler,
+ point,
+ sourceBuffer,
+ span,
+ x,
+ ref yKernelSpanRef,
+ ref xKernelSpanRef,
+ this.radialExtents,
+ this.maxSourceExtents);
+ }
+
+ PixelOperations.Instance.FromVector4Destructive(
+ this.configuration,
+ span,
+ this.destination.GetPixelRowSpan(y));
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs
similarity index 72%
rename from src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs
index 6ab1e1358d..8954d826f5 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs
@@ -10,12 +10,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Provides the base methods to perform non-affine transforms on an image.
///
/// The pixel format.
- internal class ProjectiveTransformProcessor : TransformProcessor
+ internal partial class ProjectiveTransformProcessor : TransformProcessor, IResamplingImageProcessor
where TPixel : struct, IPixel
{
private readonly Size destinationSize;
private readonly IResampler resampler;
private readonly Matrix4x4 transformMatrix;
+ private ImageFrame source;
+ private ImageFrame destination;
///
/// Initializes a new instance of the class.
@@ -36,6 +38,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
protected override void OnFrameApply(ImageFrame source, ImageFrame destination)
- => this.resampler.ApplyProjectiveTransform(this.Configuration, source, destination, this.transformMatrix);
+ {
+ this.source = source;
+ this.destination = destination;
+ this.resampler.ApplyTransform(this);
+ }
+
+ ///
+ public void ApplyTransform(in TResampler sampler)
+ where TResampler : struct, IResampler
+ => ApplyProjectiveTransform(
+ this.Configuration,
+ in sampler,
+ this.source,
+ this.destination,
+ this.transformMatrix);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor{TPixel}.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor{TPixel}.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Automorphic/SkewProcessor.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Automorphic/SkewProcessor.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
index c7557461a8..616872f2ab 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@@ -26,52 +25,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float GetValue(float x);
///
- /// Applies an resizing transformation upon an image.
+ /// Applies a transformation upon an image.
///
/// The pixel format.
- /// The configuration.
- /// The source image.
- /// The destination image.
- /// The source bounds.
- /// The target location.
- /// Whether to compress or expand individual pixel color values on processing.
- void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle targetRectangle,
- bool compand)
- where TPixel : struct, IPixel;
-
- ///
- /// Applies an affine transformation upon an image.
- ///
- /// The pixel format.
- /// The configuration.
- /// The source image frame.
- /// The destination image frame.
- /// The transform matrix.
- void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel;
-
- ///
- /// Applies a projective transformation upon an image.
- ///
- /// The pixel format.
- /// The configuration.
- /// The source image frame.
- /// The destination image frame.
- /// The transform matrix.
- void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
+ /// The transforming image processor.
+ void ApplyTransform(IResamplingImageProcessor processor)
where TPixel : struct, IPixel;
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs
new file mode 100644
index 0000000000..cfa2df641d
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms
+{
+ ///
+ /// Implements an algorithm to alter the pixels of an image via a resampling transforms.
+ ///
+ /// The pixel format.
+ public interface IResamplingImageProcessor : IImageProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Applies a resampling transform with the given sampler.
+ ///
+ /// The type of sampler.
+ /// The sampler to use.
+ void ApplyTransform(in TResampler sampler)
+ where TResampler : struct, IResampler;
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs
deleted file mode 100644
index ec2aef9c50..0000000000
--- a/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp.Processing.Processors.Transforms
-{
- ///
- /// Extensions for .
- ///
- public static partial class ResamplerExtensions
- {
- private readonly struct NNAffineOperation : IRowIntervalOperation
- where TPixel : struct, IPixel
- {
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
- private readonly Rectangle bounds;
- private readonly Matrix3x2 matrix;
- private readonly int maxX;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public NNAffineOperation(
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- {
- this.source = source;
- this.destination = destination;
- this.bounds = source.Bounds();
- this.matrix = matrix;
- this.maxX = destination.Width;
- }
-
- [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++)
- {
- var point = Vector2.Transform(new Vector2(x, y), this.matrix);
- int px = (int)MathF.Round(point.X);
- int py = (int)MathF.Round(point.Y);
-
- if (this.bounds.Contains(px, py))
- {
- destRow[x] = this.source[px, py];
- }
- }
- }
- }
- }
-
- private readonly struct NNProjectiveOperation : IRowIntervalOperation
- where TPixel : struct, IPixel
- {
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
- private readonly Rectangle bounds;
- private readonly Matrix4x4 matrix;
- private readonly int maxX;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public NNProjectiveOperation(
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- {
- this.source = source;
- this.destination = destination;
- this.bounds = source.Bounds();
- this.matrix = matrix;
- this.maxX = destination.Width;
- }
-
- [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 = TransformUtilities.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))
- {
- destRow[x] = this.source[px, py];
- }
- }
- }
- }
- }
-
- private readonly struct AffineOperation : IRowIntervalOperation
- where TResampler : unmanaged, IResampler
- where TPixel : struct, IPixel
- {
- private readonly Configuration configuration;
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
- private readonly Buffer2D yKernelBuffer;
- private readonly Buffer2D xKernelBuffer;
- private readonly TResampler sampler;
- private readonly Matrix3x2 matrix;
- private readonly Vector2 radialExtents;
- private readonly Vector4 maxSourceExtents;
- private readonly int maxX;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public AffineOperation(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Buffer2D yKernelBuffer,
- Buffer2D xKernelBuffer,
- in TResampler sampler,
- Matrix3x2 matrix,
- Vector2 radialExtents,
- Vector4 maxSourceExtents)
- {
- this.configuration = configuration;
- this.source = source;
- this.destination = destination;
- this.yKernelBuffer = yKernelBuffer;
- this.xKernelBuffer = xKernelBuffer;
- this.sampler = sampler;
- this.matrix = matrix;
- this.radialExtents = radialExtents;
- this.maxSourceExtents = maxSourceExtents;
- this.maxX = destination.Width;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
- {
- Buffer2D sourceBuffer = this.source.PixelBuffer;
- for (int y = rows.Min; y < rows.Max; y++)
- {
- PixelOperations.Instance.ToVector4(
- this.configuration,
- this.destination.GetPixelRowSpan(y),
- span);
-
- ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
- ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(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.
- var point = Vector2.Transform(new Vector2(x, y), this.matrix);
- Convolve(
- in this.sampler,
- point,
- sourceBuffer,
- span,
- x,
- ref yKernelSpanRef,
- ref xKernelSpanRef,
- this.radialExtents,
- this.maxSourceExtents);
- }
-
- PixelOperations.Instance.FromVector4Destructive(
- this.configuration,
- span,
- this.destination.GetPixelRowSpan(y));
- }
- }
- }
-
- private readonly struct ProjectiveOperation : IRowIntervalOperation
- where TResampler : unmanaged, IResampler
- where TPixel : struct, IPixel
- {
- private readonly Configuration configuration;
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
- private readonly Buffer2D yKernelBuffer;
- private readonly Buffer2D xKernelBuffer;
- private readonly TResampler sampler;
- private readonly Matrix4x4 matrix;
- private readonly Vector2 radialExtents;
- private readonly Vector4 maxSourceExtents;
- private readonly int maxX;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public ProjectiveOperation(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Buffer2D yKernelBuffer,
- Buffer2D xKernelBuffer,
- in TResampler sampler,
- Matrix4x4 matrix,
- Vector2 radialExtents,
- Vector4 maxSourceExtents)
- {
- this.configuration = configuration;
- this.source = source;
- this.destination = destination;
- this.yKernelBuffer = yKernelBuffer;
- this.xKernelBuffer = xKernelBuffer;
- this.sampler = sampler;
- this.matrix = matrix;
- this.radialExtents = radialExtents;
- this.maxSourceExtents = maxSourceExtents;
- this.maxX = destination.Width;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
- {
- Buffer2D sourceBuffer = this.source.PixelBuffer;
- for (int y = rows.Min; y < rows.Max; y++)
- {
- PixelOperations.Instance.ToVector4(
- this.configuration,
- this.destination.GetPixelRowSpan(y),
- span);
-
- ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
- ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(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 = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
- Convolve(
- in this.sampler,
- point,
- sourceBuffer,
- span,
- x,
- ref yKernelSpanRef,
- ref xKernelSpanRef,
- this.radialExtents,
- this.maxSourceExtents);
- }
-
- PixelOperations.Instance.FromVector4Destructive(
- this.configuration,
- span,
- this.destination.GetPixelRowSpan(y));
- }
- }
- }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.cs
deleted file mode 100644
index 245adb2383..0000000000
--- a/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-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
-{
- ///
- /// Extensions for .
- ///
- public static partial class ResamplerExtensions
- {
- ///
- /// Applies an affine transformation upon an image.
- ///
- /// The type of sampler.
- /// The pixel format.
- /// The configuration.
- /// The pixel sampler.
- /// The source image frame.
- /// The destination image frame.
- /// The transform matrix.
- public static void ApplyAffineTransform(
- Configuration configuration,
- in TResampler sampler,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TResampler : unmanaged, IResampler
- where TPixel : struct, IPixel
- {
- // Handle transforms that result in output identical to the original.
- if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity))
- {
- // The clone will be blank here copy all the pixel data over
- source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
- return;
- }
-
- // Convert from screen to world space.
- Matrix3x2.Invert(matrix, out matrix);
-
- if (sampler is NearestNeighborResampler)
- {
- var nnOperation = new NNAffineOperation(source, destination, matrix);
- ParallelRowIterator.IterateRows(
- configuration,
- destination.Bounds(),
- in nnOperation);
-
- return;
- }
-
- int yRadius = GetSamplingRadius(in sampler, source.Height, destination.Height);
- int xRadius = GetSamplingRadius(in sampler, source.Width, destination.Width);
- var radialExtents = new Vector2(xRadius, yRadius);
- int yLength = (yRadius * 2) + 1;
- int xLength = (xRadius * 2) + 1;
-
- // We use 2D buffers so that we can access the weight spans per row in parallel.
- using Buffer2D yKernelBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height);
- using Buffer2D xKernelBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height);
-
- int maxX = source.Width - 1;
- int maxY = source.Height - 1;
- var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
-
- var operation = new AffineOperation(
- configuration,
- source,
- destination,
- yKernelBuffer,
- xKernelBuffer,
- in sampler,
- matrix,
- radialExtents,
- maxSourceExtents);
-
- ParallelRowIterator.IterateRows, Vector4>(
- configuration,
- destination.Bounds(),
- in operation);
- }
-
- ///
- /// Applies a projective transformation upon an image.
- ///
- /// The type of sampler.
- /// The pixel format.
- /// The configuration.
- /// The pixel sampler.
- /// The source image frame.
- /// The destination image frame.
- /// The transform matrix.
- public static void ApplyProjectiveTransform(
- Configuration configuration,
- in TResampler sampler,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TResampler : unmanaged, IResampler
- where TPixel : struct, IPixel
- {
- // Handle transforms that result in output identical to the original.
- if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity))
- {
- // The clone will be blank here copy all the pixel data over
- source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
- return;
- }
-
- // Convert from screen to world space.
- Matrix4x4.Invert(matrix, out matrix);
-
- if (sampler is NearestNeighborResampler)
- {
- var nnOperation = new NNProjectiveOperation(source, destination, matrix);
- ParallelRowIterator.IterateRows(
- configuration,
- destination.Bounds(),
- in nnOperation);
-
- return;
- }
-
- int yRadius = GetSamplingRadius(in sampler, source.Height, destination.Height);
- int xRadius = GetSamplingRadius(in sampler, source.Width, destination.Width);
- var radialExtents = new Vector2(xRadius, yRadius);
- int yLength = (yRadius * 2) + 1;
- int xLength = (xRadius * 2) + 1;
-
- // We use 2D buffers so that we can access the weight spans per row in parallel.
- using Buffer2D yKernelBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height);
- using Buffer2D xKernelBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height);
-
- int maxX = source.Width - 1;
- int maxY = source.Height - 1;
- var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
-
- var operation = new ProjectiveOperation(
- configuration,
- source,
- destination,
- yKernelBuffer,
- xKernelBuffer,
- in sampler,
- matrix,
- radialExtents,
- maxSourceExtents);
-
- ParallelRowIterator.IterateRows, Vector4>(
- configuration,
- destination.Bounds(),
- in operation);
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- internal static void Convolve(
- in TResampler sampler,
- Vector2 transformedPoint,
- Buffer2D sourcePixels,
- Span targetRow,
- int column,
- ref float yKernelSpanRef,
- ref float xKernelSpanRef,
- Vector2 radialExtents,
- Vector4 maxSourceExtents)
- where TResampler : unmanaged, IResampler
- where TPixel : struct, IPixel
- {
- // Clamp sampling pixel radial extents to the source image edges
- Vector2 minXY = transformedPoint - radialExtents;
- Vector2 maxXY = transformedPoint + radialExtents;
-
- // left, top, right, bottom
- var sourceExtents = new Vector4(
- MathF.Ceiling(minXY.X),
- MathF.Ceiling(minXY.Y),
- MathF.Floor(maxXY.X),
- MathF.Floor(maxXY.Y));
-
- sourceExtents = Vector4.Clamp(sourceExtents, Vector4.Zero, maxSourceExtents);
-
- int left = (int)sourceExtents.X;
- int top = (int)sourceExtents.Y;
- int right = (int)sourceExtents.Z;
- int bottom = (int)sourceExtents.W;
-
- if (left == right || top == bottom)
- {
- return;
- }
-
- CalculateWeights(in sampler, top, bottom, transformedPoint.Y, ref yKernelSpanRef);
- CalculateWeights(in sampler, left, right, transformedPoint.X, ref xKernelSpanRef);
-
- Vector4 sum = Vector4.Zero;
- for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++)
- {
- float yWeight = Unsafe.Add(ref yKernelSpanRef, kernelY);
-
- for (int kernelX = 0, x = left; x <= right; x++, kernelX++)
- {
- float xWeight = Unsafe.Add(ref xKernelSpanRef, kernelX);
-
- // Values are first premultiplied to prevent darkening of edge pixels.
- var current = sourcePixels[x, y].ToVector4();
- Vector4Utils.Premultiply(ref current);
- sum += current * xWeight * yWeight;
- }
- }
-
- // Reverse the premultiplication
- Vector4Utils.UnPremultiply(ref sum);
- targetRow[column] = sum;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private static void CalculateWeights(in TResampler sampler, int min, int max, float point, ref float weightsRef)
- where TResampler : unmanaged, IResampler
- {
- float sum = 0;
- for (int x = 0, i = min; i <= max; i++, x++)
- {
- float weight = sampler.GetValue(i - point);
- sum += weight;
- Unsafe.Add(ref weightsRef, x) = weight;
- }
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private static int GetSamplingRadius(in TResampler sampler, int sourceSize, int destinationSize)
- where TResampler : unmanaged, IResampler
- {
- double scale = sourceSize / destinationSize;
- if (scale < 1)
- {
- scale = 1;
- }
-
- return (int)Math.Ceiling(scale * sampler.Radius);
- }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
index 5f9669f6f8..2992bbf5ac 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -43,48 +42,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
index ecaa28c804..98a789e342 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -30,48 +29,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs
index a8f3f0b63a..a6fba1b33d 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -106,48 +105,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
index 4ed2d541c2..90d60e1b08 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -62,48 +61,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
index 94b0b0405f..20f0a9fb85 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -22,48 +21,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
index c8409e1859..9cf9ae66af 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -36,48 +35,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
index 673cbd5d74..a162c7411f 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
@@ -35,48 +34,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyResizeTransform(
- Configuration configuration,
- Image source,
- Image destination,
- Rectangle sourceRectangle,
- Rectangle destinationRectangle,
- bool compand)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform(
- configuration,
- in this,
- source,
- destination,
- sourceRectangle,
- destinationRectangle,
- compand);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyAffineTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix3x2 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyAffineTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
-
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyProjectiveTransform(
- Configuration configuration,
- ImageFrame source,
- ImageFrame destination,
- Matrix4x4 matrix)
- where TPixel : struct, IPixel => ResamplerExtensions.ApplyProjectiveTransform(
- configuration,
- in this,
- source,
- destination,
- matrix);
+ public void ApplyTransform(IResamplingImageProcessor processor)
+ where TPixel : struct, IPixel
+ => processor.ApplyTransform(in this);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
index a6e6bf6126..3e7ccbd0af 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
@@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int destinationSize,
int sourceSize,
MemoryAllocator memoryAllocator)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
double ratio = (double)sourceSize / destinationSize;
double scale = ratio;
@@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Initializes the kernel map.
///
protected internal virtual void Initialize(in TResampler sampler)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
for (int i = 0; i < this.DestinationLength; i++)
{
@@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// so the data reusable by other data rows.
///
private ResizeKernel BuildKernel(in TResampler sampler, int destRowIndex, int dataRowIndex)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
double center = ((destRowIndex + .5) * this.ratio) - .5;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
index 520370b6ef..4e6e7a48c1 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
@@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(options.Sampler, nameof(options.Sampler));
+ Guard.MustBeValueType(options.Sampler, nameof(options.Sampler));
(Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options);
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs
similarity index 91%
rename from src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs
index 2cd903924a..78f63ee0d3 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs
@@ -10,15 +10,15 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
- /// Extensions for .
+ /// Contains the application code for resizing.
///
- public static partial class ResamplerExtensions
+ internal partial class ResizeProcessor
+ where TPixel : struct, IPixel
{
///
/// Applies an resizing transformation upon an image.
///
/// The type of sampler.
- /// The pixel format.
/// The configuration.
/// The pixel sampler.
/// The source image.
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The source bounds.
/// The destination location.
/// Whether to compress or expand individual pixel color values on processing.
- public static void ApplyResizeTransform(
+ public static void ApplyResizeTransform(
Configuration configuration,
in TResampler sampler,
Image source,
@@ -34,8 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
- where TResampler : unmanaged, IResampler
- where TPixel : struct, IPixel
+ where TResampler : struct, IResampler
{
// Handle resize dimensions identical to the original
if (source.Width == destination.Width
@@ -108,20 +107,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
}
- private static void ApplyNNResizeFrameTransform(
+ private static void ApplyNNResizeFrameTransform(
Configuration configuration,
ImageFrame source,
ImageFrame destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
Rectangle interest)
- where TPixel : struct, IPixel
{
// Scaling factors
float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height;
- var operation = new NNRowIntervalOperation(
+ var operation = new NNRowIntervalOperation(
sourceRectangle,
destinationRectangle,
widthFactor,
@@ -135,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
in operation);
}
- private static void ApplyResizeFrameTransform(
+ private static void ApplyResizeFrameTransform(
Configuration configuration,
ImageFrame source,
ImageFrame destination,
@@ -145,7 +143,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle destinationRectangle,
Rectangle interest,
bool compand)
- where TPixel : struct, IPixel
{
PixelConversionModifiers conversionModifiers =
PixelConversionModifiers.Premultiply.ApplyCompanding(compand);
@@ -171,8 +168,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
}
- private readonly struct NNRowIntervalOperation : IRowIntervalOperation
- where TPixel : struct, IPixel
+ private readonly struct NNRowIntervalOperation : IRowIntervalOperation
{
private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
index 72064d0e36..5b69184d7c 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Implements resizing of images using various resamplers.
///
/// The pixel format.
- internal class ResizeProcessor : TransformProcessor
+ internal partial class ResizeProcessor : TransformProcessor, IResamplingImageProcessor
where TPixel : struct, IPixel
{
private readonly int destinationWidth;
@@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly IResampler resampler;
private readonly Rectangle destinationRectangle;
private readonly bool compand;
+ private Image destination;
public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
@@ -34,13 +35,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
protected override void BeforeImageApply(Image destination)
{
- this.resampler.ApplyResizeTransform(
- this.Configuration,
- this.Source,
- destination,
- this.SourceRectangle,
- this.destinationRectangle,
- this.compand);
+ this.destination = destination;
+ this.resampler.ApplyTransform(this);
base.BeforeImageApply(destination);
}
@@ -50,5 +46,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
// Everything happens in BeforeImageApply.
}
+
+ public void ApplyTransform(in TResampler sampler)
+ where TResampler : struct, IResampler =>
+ ApplyResizeTransform(
+ this.Configuration,
+ in sampler,
+ this.Source,
+ this.destination,
+ this.SourceRectangle,
+ this.destinationRectangle,
+ this.compand);
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs
index 17477c83bd..3d08cf1a4c 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs
@@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex];
public static ReferenceKernelMap Calculate(in TResampler sampler, int destinationSize, int sourceSize, bool normalize = true)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
double ratio = (double)sourceSize / destinationSize;
double scale = ratio;
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
index e404c6460a..8dbc056550 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[Theory(Skip = "Only for debugging and development")]
[MemberData(nameof(KernelMapData))]
public void PrintNonNormalizedKernelMap(TResampler resampler, int srcSize, int destSize)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
var kernelMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize, false);
@@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[Theory]
[MemberData(nameof(KernelMapData))]
public void KernelMapContentIsCorrect(TResampler resampler, int srcSize, int destSize)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
this.VerifyKernelMapContentIsCorrect(resampler, srcSize, destSize);
}
@@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
#endif
private void VerifyKernelMapContentIsCorrect(TResampler resampler, int srcSize, int destSize)
- where TResampler : unmanaged, IResampler
+ where TResampler : struct, IResampler
{
var referenceMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize);
var kernelMap = ResizeKernelMap.Calculate(in resampler, destSize, srcSize, Configuration.Default.MemoryAllocator);