diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs
index 90e00924ad..dde7beb3e9 100644
--- a/src/ImageSharp/Processing/AffineTransformBuilder.cs
+++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs
@@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing
/// The amount of rotation, in radians.
/// The .
public AffineTransformBuilder PrependRotationRadians(float radians)
- => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
+ => this.Prepend(size => TransformUtilities.CreateRotationMatrixRadians(radians, size));
///
/// Prepends a rotation matrix using the given rotation in degrees at the given origin.
@@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing
/// The amount of rotation, in radians.
/// The .
public AffineTransformBuilder AppendRotationRadians(float radians)
- => this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
+ => this.Append(size => TransformUtilities.CreateRotationMatrixRadians(radians, size));
///
/// Appends a rotation matrix using the given rotation in degrees at the given origin.
@@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing
/// The Y angle, in degrees.
/// The .
public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY)
- => this.Prepend(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size));
+ => this.Prepend(size => TransformUtilities.CreateSkewMatrixDegrees(degreesX, degreesY, size));
///
/// Prepends a centered skew matrix from the give angles in radians.
@@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Processing
/// The Y angle, in radians.
/// The .
public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY)
- => this.Prepend(size => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size));
+ => this.Prepend(size => TransformUtilities.CreateSkewMatrixRadians(radiansX, radiansY, size));
///
/// Prepends a skew matrix using the given angles in degrees at the given origin.
@@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Processing
/// The Y angle, in degrees.
/// The .
public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY)
- => this.Append(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size));
+ => this.Append(size => TransformUtilities.CreateSkewMatrixDegrees(degreesX, degreesY, size));
///
/// Appends a centered skew matrix from the give angles in radians.
@@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.Processing
/// The Y angle, in radians.
/// The .
public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY)
- => this.Append(size => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size));
+ => this.Append(size => TransformUtilities.CreateSkewMatrixRadians(radiansX, radiansY, size));
///
/// Appends a skew matrix using the given angles in degrees at the given origin.
diff --git a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs
index 6305649557..ee8f3854e8 100644
--- a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs
@@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
IResampler sampler)
{
Matrix3x2 transform = builder.BuildMatrix(sourceRectangle);
- Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform);
+ Size targetDimensions = TransformUtilities.GetTransformedSize(sourceRectangle.Size, transform);
return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler);
}
@@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing
IResampler sampler)
{
Matrix4x4 transform = builder.BuildMatrix(sourceRectangle);
- Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform);
+ Size targetDimensions = TransformUtilities.GetTransformedSize(sourceRectangle.Size, transform);
return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler);
}
diff --git a/src/ImageSharp/Processing/KnownResamplers.cs b/src/ImageSharp/Processing/KnownResamplers.cs
index 621215b280..6c73513c87 100644
--- a/src/ImageSharp/Processing/KnownResamplers.cs
+++ b/src/ImageSharp/Processing/KnownResamplers.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Transforms;
@@ -13,86 +13,86 @@ namespace SixLabors.ImageSharp.Processing
///
/// Gets the Bicubic sampler that implements the bicubic kernel algorithm W(x)
///
- public static IResampler Bicubic { get; } = new BicubicResampler();
+ public static IResampler Bicubic { get; } = default(BicubicResampler);
///
/// Gets the Box sampler that implements the box algorithm. Similar to nearest neighbor when upscaling.
/// When downscaling the pixels will average, merging pixels together.
///
- public static IResampler Box { get; } = new BoxResampler();
+ public static IResampler Box { get; } = default(BoxResampler);
///
/// Gets the Catmull-Rom sampler, a well known standard Cubic Filter often used as a interpolation function
///
- public static IResampler CatmullRom { get; } = new CatmullRomResampler();
+ public static IResampler CatmullRom { get; } = default(CatmullRomResampler);
///
/// Gets the Hermite sampler. A type of smoothed triangular interpolation filter that rounds off strong edges while
/// preserving flat 'color levels' in the original image.
///
- public static IResampler Hermite { get; } = new HermiteResampler();
+ public static IResampler Hermite { get; } = default(HermiteResampler);
///
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 2 pixels.
/// This algorithm provides sharpened results when compared to others when downsampling.
///
- public static IResampler Lanczos2 { get; } = new Lanczos2Resampler();
+ public static IResampler Lanczos2 { get; } = default(Lanczos2Resampler);
///
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 3 pixels
/// This algorithm provides sharpened results when compared to others when downsampling.
///
- public static IResampler Lanczos3 { get; } = new Lanczos3Resampler();
+ public static IResampler Lanczos3 { get; } = default(Lanczos3Resampler);
///
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 5 pixels
/// This algorithm provides sharpened results when compared to others when downsampling.
///
- public static IResampler Lanczos5 { get; } = new Lanczos5Resampler();
+ public static IResampler Lanczos5 { get; } = default(Lanczos5Resampler);
///
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 8 pixels
/// This algorithm provides sharpened results when compared to others when downsampling.
///
- public static IResampler Lanczos8 { get; } = new Lanczos8Resampler();
+ public static IResampler Lanczos8 { get; } = default(Lanczos8Resampler);
///
/// Gets the Mitchell-Netravali sampler. This seperable cubic algorithm yields a very good equilibrium between
/// detail preservation (sharpness) and smoothness.
///
- public static IResampler MitchellNetravali { get; } = new MitchellNetravaliResampler();
+ public static IResampler MitchellNetravali { get; } = default(MitchellNetravaliResampler);
///
/// Gets the Nearest-Neighbour sampler that implements the nearest neighbor algorithm. This uses a very fast, unscaled filter
/// which will select the closest pixel to the new pixels position.
///
- public static IResampler NearestNeighbor { get; } = new NearestNeighborResampler();
+ public static IResampler NearestNeighbor { get; } = default(NearestNeighborResampler);
///
/// Gets the Robidoux sampler. This algorithm developed by Nicolas Robidoux providing a very good equilibrium between
/// detail preservation (sharpness) and smoothness comparable to .
///
- public static IResampler Robidoux { get; } = new RobidouxResampler();
+ public static IResampler Robidoux { get; } = default(RobidouxResampler);
///
/// Gets the Robidoux Sharp sampler. A sharpened form of the sampler
///
- public static IResampler RobidouxSharp { get; } = new RobidouxSharpResampler();
+ public static IResampler RobidouxSharp { get; } = default(RobidouxSharpResampler);
///
/// Gets the Spline sampler. A seperable cubic algorithm similar to but yielding smoother results.
///
- public static IResampler Spline { get; } = new SplineResampler();
+ public static IResampler Spline { get; } = default(SplineResampler);
///
/// Gets the Triangle sampler, otherwise known as Bilinear. This interpolation algorithm can be used where perfect image transformation
/// with pixel matching is impossible, so that one can calculate and assign appropriate intensity values to pixels
///
- public static IResampler Triangle { get; } = new TriangleResampler();
+ public static IResampler Triangle { get; } = default(TriangleResampler);
///
/// Gets the Welch sampler. A high speed algorithm that delivers very sharpened results.
///
- public static IResampler Welch { get; } = new WelchResampler();
+ public static IResampler Welch { get; } = default(WelchResampler);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
index 0d9055f340..130cc1bf05 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
@@ -1,11 +1,7 @@
// 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
@@ -40,149 +36,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
protected override void OnFrameApply(ImageFrame source, ImageFrame destination)
- {
- // Handle transforms that result in output identical to the original.
- if (this.transformMatrix.Equals(default) || this.transformMatrix.Equals(Matrix3x2.Identity))
- {
- // The clone will be blank here copy all the pixel data over
- source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
- return;
- }
-
- int width = this.targetSize.Width;
- var targetBounds = new Rectangle(Point.Empty, this.targetSize);
- Configuration configuration = this.Configuration;
-
- // Convert from screen to world space.
- Matrix3x2.Invert(this.transformMatrix, out Matrix3x2 matrix);
-
- if (this.resampler is NearestNeighborResampler)
- {
- var nnOperation = new NearestNeighborRowIntervalOperation(this.SourceRectangle, ref matrix, width, source, destination);
- ParallelRowIterator.IterateRows(
- configuration,
- targetBounds,
- in nnOperation);
-
- return;
- }
-
- using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler);
-
- var operation = new RowIntervalOperation(configuration, kernelMap, ref matrix, width, source, destination);
- ParallelRowIterator.IterateRows(
- configuration,
- targetBounds,
- in operation);
- }
-
- ///
- /// A implementing the nearest neighbor resampler logic for .
- ///
- private readonly struct NearestNeighborRowIntervalOperation : IRowIntervalOperation
- {
- private readonly Rectangle bounds;
- private readonly Matrix3x2 matrix;
- private readonly int maxX;
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public NearestNeighborRowIntervalOperation(
- Rectangle bounds,
- ref Matrix3x2 matrix,
- int maxX,
- ImageFrame source,
- ImageFrame destination)
- {
- 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++)
- {
- var point = Point.Transform(new Point(x, y), this.matrix);
- if (this.bounds.Contains(point.X, point.Y))
- {
- destRow[x] = this.source[point.X, point.Y];
- }
- }
- }
- }
- }
-
- ///
- /// A implementing the transformation logic for .
- ///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
- {
- private readonly Configuration configuration;
- private readonly TransformKernelMap kernelMap;
- private readonly Matrix3x2 matrix;
- private readonly int maxX;
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
- Configuration configuration,
- TransformKernelMap kernelMap,
- ref Matrix3x2 matrix,
- int maxX,
- ImageFrame source,
- ImageFrame destination)
- {
- 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, Span span)
- {
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = this.destination.GetPixelRowSpan(y);
- PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan, span);
- 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.
- var point = Vector2.Transform(new Vector2(x, y), this.matrix);
- this.kernelMap.Convolve(
- point,
- x,
- ref ySpanRef,
- ref xSpanRef,
- this.source.PixelBuffer,
- span);
- }
-
- PixelOperations.Instance.FromVector4Destructive(
- this.configuration,
- span,
- targetRowSpan);
- }
- }
- }
+ => this.resampler.ApplyAffineTransform(this.Configuration, source, destination, this.transformMatrix);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
index 6db03d5b41..fb095b70ab 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
@@ -1,6 +1,9 @@
-// Copyright (c) Six Labors and contributors.
+// 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
{
///
@@ -21,5 +24,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The
///
float GetValue(float x);
+
+ ///
+ /// 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)
+ where TPixel : struct, IPixel;
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
index da071e3f22..50315ac95c 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
@@ -1,11 +1,7 @@
// 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
@@ -40,145 +36,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
protected override void OnFrameApply(ImageFrame source, ImageFrame destination)
- {
- // Handle transforms that result in output identical to the original.
- if (this.transformMatrix.Equals(default) || this.transformMatrix.Equals(Matrix4x4.Identity))
- {
- // The clone will be blank here copy all the pixel data over
- source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
- return;
- }
-
- int width = this.targetSize.Width;
- var targetBounds = new Rectangle(Point.Empty, this.targetSize);
- Configuration configuration = this.Configuration;
-
- // Convert from screen to world space.
- Matrix4x4.Invert(this.transformMatrix, out Matrix4x4 matrix);
-
- if (this.resampler is NearestNeighborResampler)
- {
- Rectangle sourceBounds = this.SourceRectangle;
-
- var nnOperation = new NearestNeighborRowIntervalOperation(sourceBounds, ref matrix, width, source, destination);
- ParallelRowIterator.IterateRows(
- configuration,
- targetBounds,
- in nnOperation);
-
- return;
- }
-
- using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler);
-
- var operation = new RowIntervalOperation(configuration, kernelMap, ref matrix, width, source, destination);
- ParallelRowIterator.IterateRows(
- configuration,
- targetBounds,
- in operation);
- }
-
- private readonly struct NearestNeighborRowIntervalOperation : IRowIntervalOperation
- {
- private readonly Rectangle bounds;
- private readonly Matrix4x4 matrix;
- private readonly int maxX;
- private readonly ImageFrame source;
- private readonly ImageFrame destination;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public NearestNeighborRowIntervalOperation(
- Rectangle bounds,
- ref Matrix4x4 matrix,
- int maxX,
- ImageFrame source,
- ImageFrame destination)
- {
- 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))
- {
- destRow[x] = this.source[px, py];
- }
- }
- }
- }
- }
-
- private readonly struct RowIntervalOperation : IRowIntervalOperation
- {
- 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 RowIntervalOperation(
- Configuration configuration,
- TransformKernelMap kernelMap,
- ref Matrix4x4 matrix,
- int maxX,
- ImageFrame source,
- ImageFrame destination)
- {
- 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, Span span)
- {
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = this.destination.GetPixelRowSpan(y);
- PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan, span);
- 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,
- span);
- }
-
- PixelOperations.Instance.FromVector4Destructive(
- this.configuration,
- span,
- targetRowSpan);
- }
- }
- }
+ => this.resampler.ApplyProjectiveTransform(this.Configuration, source, destination, this.transformMatrix);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs
new file mode 100644
index 0000000000..96fcc49f7b
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs
@@ -0,0 +1,263 @@
+// 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 = destination.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 = destination.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
new file mode 100644
index 0000000000..674b7f4231
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.cs
@@ -0,0 +1,249 @@
+// 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.GetPixelSpan().CopyTo(destination.GetPixelSpan());
+ 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.GetPixelSpan().CopyTo(destination.GetPixelSpan());
+ 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 199563bc7e..ea68715753 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Wikipedia
/// A commonly used algorithm within image processing that preserves sharpness better than triangle interpolation.
///
- public class BicubicResampler : IResampler
+ public readonly struct BicubicResampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -21,21 +26,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
x = -x;
}
- float result = 0;
-
// Given the coefficient "a" as -0.5F.
if (x <= 1F)
{
// Below simplified result = ((a + 2F) * (x * x * x)) - ((a + 3F) * (x * x)) + 1;
- result = (((1.5F * x) - 2.5F) * x * x) + 1;
+ return (((1.5F * x) - 2.5F) * x * x) + 1;
}
else if (x < 2F)
{
// Below simplified result = (a * (x * x * x)) - ((5F * a) * (x * x)) + ((8F * a) * x) - (4F * a);
- result = (((((-0.5F * x) + 2.5F) * x) - 4) * x) + 2;
+ return (((((-0.5F * x) + 2.5F) * x) - 4) * x) + 2;
}
- return result;
+ return 0;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
index 0667226d9c..49b53378f9 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
@@ -1,18 +1,23 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the box algorithm. Similar to nearest neighbor when upscaling.
/// When downscaling the pixels will average, merging together.
///
- public class BoxResampler : IResampler
+ public readonly struct BoxResampler : IResampler
{
///
public float Radius => 0.5F;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x > -0.5F && x <= 0.5F)
@@ -22,5 +27,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs
index 8995d2d8a8..5a2992595d 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -9,12 +13,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// scale image enlargements that a 'Lagrange' filter can produce.
///
///
- public class CatmullRomResampler : IResampler
+ public readonly struct CatmullRomResampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
const float B = 0;
@@ -22,5 +27,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ImageMaths.GetBcValue(x, B, C);
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs
index 18c3fda7c0..80aa69acd6 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// This filter rounds off strong edges while preserving flat 'color levels' in the original image.
///
///
- public class HermiteResampler : IResampler
+ public readonly struct HermiteResampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
const float B = 0F;
@@ -21,5 +26,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ImageMaths.GetBcValue(x, B, C);
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs
index 2294696de4..3228a709d2 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Wikipedia
/// with a radius of 2 pixels.
///
- public class Lanczos2Resampler : IResampler
+ public readonly struct Lanczos2Resampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -28,5 +33,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0F;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs
index 95fb206a96..a9388575be 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Wikipedia
/// with a radius of 3 pixels.
///
- public class Lanczos3Resampler : IResampler
+ public readonly struct Lanczos3Resampler : IResampler
{
///
public float Radius => 3;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -28,5 +33,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0F;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs
index c99ed1e855..7662f26160 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Wikipedia
/// with a radius of 5 pixels.
///
- public class Lanczos5Resampler : IResampler
+ public readonly struct Lanczos5Resampler : IResampler
{
///
public float Radius => 5;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -28,5 +33,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0F;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs
index 4efdb882b0..e886f41db9 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Wikipedia
/// with a radius of 8 pixels.
///
- public class Lanczos8Resampler : IResampler
+ public readonly struct Lanczos8Resampler : IResampler
{
///
public float Radius => 8;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -28,5 +33,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0F;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs
index d4ba954f20..ef97be92b5 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs
@@ -1,13 +1,17 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the mitchell algorithm as described on
/// Wikipedia
///
- public class MitchellNetravaliResampler : IResampler
+ public readonly struct MitchellNetravaliResampler : IResampler
{
///
public float Radius => 2;
@@ -20,5 +24,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ImageMaths.GetBcValue(x, B, C);
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
index 1f12334f4f..e4cec5f4d5 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
@@ -1,21 +1,51 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the nearest neighbor algorithm. This uses an unscaled filter
/// which will select the closest pixel to the new pixels position.
///
- public class NearestNeighborResampler : IResampler
+ public readonly struct NearestNeighborResampler : IResampler
{
///
public float Radius => 1;
///
- public float GetValue(float x)
- {
- return x;
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public float GetValue(float x) => x;
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs
index 51938566c8..6d9e7641e0 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs
@@ -1,18 +1,23 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the Robidoux algorithm.
///
///
- public class RobidouxResampler : IResampler
+ public readonly struct RobidouxResampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
const float B = 0.37821575509399867F;
@@ -20,5 +25,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ImageMaths.GetBcValue(x, B, C);
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs
index 015b7f0af3..eaf5fa6e80 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs
@@ -1,18 +1,23 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the Robidoux Sharp algorithm.
///
///
- public class RobidouxSharpResampler : IResampler
+ public readonly struct RobidouxSharpResampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
const float B = 0.2620145123990142F;
@@ -20,5 +25,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ImageMaths.GetBcValue(x, B, C);
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs
index df6c2a338f..d2608b2faa 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs
@@ -1,18 +1,23 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the spline algorithm.
///
///
- public class SplineResampler : IResampler
+ public readonly struct SplineResampler : IResampler
{
///
public float Radius => 2;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
const float B = 1F;
@@ -20,5 +25,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ImageMaths.GetBcValue(x, B, C);
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
index 57d1fa11dc..b403621241 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
@@ -1,6 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
@@ -8,12 +12,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Bilinear interpolation can be used where perfect image transformation with pixel matching is impossible,
/// so that one can calculate and assign appropriate intensity values to pixels.
///
- public class TriangleResampler : IResampler
+ public readonly struct TriangleResampler : IResampler
{
///
public float Radius => 1;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -28,5 +33,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0F;
}
+
+ ///
+ [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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
index edce5fcf9e..8a92ea7c00 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
@@ -1,18 +1,23 @@
-// Copyright (c) Six Labors and contributors.
+// 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;
+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// The function implements the welch algorithm.
///
///
- public class WelchResampler : IResampler
+ public readonly struct WelchResampler : IResampler
{
///
public float Radius => 3;
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x)
{
if (x < 0F)
@@ -27,5 +32,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return 0F;
}
+
+ ///
+ [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);
}
-}
\ 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 aae66e9eac..b53e7b5c05 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
@@ -28,14 +28,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The source image size
public RotateProcessor(float degrees, IResampler sampler, Size sourceSize)
: this(
- TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize),
+ TransformUtilities.CreateRotationMatrixDegrees(degrees, sourceSize),
sampler,
sourceSize)
=> this.Degrees = degrees;
// Helper constructor
private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize)
- : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, rotationMatrix))
+ : base(rotationMatrix, sampler, TransformUtilities.GetTransformedSize(sourceSize, rotationMatrix))
{
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
index 4d07333345..1bcfa5fd28 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
@@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The source image size
public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize)
: this(
- TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, sourceSize),
+ TransformUtilities.CreateSkewMatrixDegrees(degreesX, degreesY, sourceSize),
sampler,
sourceSize)
{
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Helper constructor:
private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize)
- : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, skewMatrix))
+ : base(skewMatrix, sampler, TransformUtilities.GetTransformedSize(sourceSize, skewMatrix))
{
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs
deleted file mode 100644
index a0d44cb7a1..0000000000
--- a/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs
+++ /dev/null
@@ -1,160 +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.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp.Processing.Processors.Transforms
-{
- ///
- /// Contains the methods required to calculate transform kernel convolution.
- ///
- internal class TransformKernelMap : IDisposable
- {
- private readonly Buffer2D yBuffer;
- private readonly Buffer2D xBuffer;
- private readonly Vector2 extents;
- private Vector4 maxSourceExtents;
- private readonly IResampler sampler;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The configuration.
- /// The source size.
- /// The destination size.
- /// The sampler.
- public TransformKernelMap(Configuration configuration, Size source, Size destination, IResampler sampler)
- {
- this.sampler = sampler;
- float yRadius = this.GetSamplingRadius(source.Height, destination.Height);
- float xRadius = this.GetSamplingRadius(source.Width, destination.Width);
-
- this.extents = new Vector2(xRadius, yRadius);
- int xLength = (int)MathF.Ceiling((this.extents.X * 2) + 2);
- int yLength = (int)MathF.Ceiling((this.extents.Y * 2) + 2);
-
- // We use 2D buffers so that we can access the weight spans per row in parallel.
- this.yBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height);
- this.xBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height);
-
- int maxX = source.Width - 1;
- int maxY = source.Height - 1;
- this.maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
- }
-
- ///
- /// Gets a reference to the first item of the y window.
- ///
- /// The reference to the first item of the window.
- [MethodImpl(InliningOptions.ShortMethod)]
- public ref float GetYStartReference(int y)
- => ref MemoryMarshal.GetReference(this.yBuffer.GetRowSpan(y));
-
- ///
- /// Gets a reference to the first item of the x window.
- ///
- /// The reference to the first item of the window.
- [MethodImpl(InliningOptions.ShortMethod)]
- public ref float GetXStartReference(int y)
- => ref MemoryMarshal.GetReference(this.xBuffer.GetRowSpan(y));
-
- public void Convolve(
- Vector2 transformedPoint,
- int column,
- ref float ySpanRef,
- ref float xSpanRef,
- Buffer2D sourcePixels,
- Span targetRow)
- where TPixel : struct, IPixel
- {
- // Clamp sampling pixel radial extents to the source image edges
- Vector2 minXY = transformedPoint - this.extents;
- Vector2 maxXY = transformedPoint + this.extents;
-
- // left, top, right, bottom
- var extents = new Vector4(
- MathF.Ceiling(minXY.X - .5F),
- MathF.Ceiling(minXY.Y - .5F),
- MathF.Floor(maxXY.X + .5F),
- MathF.Floor(maxXY.Y + .5F));
-
- extents = Vector4.Clamp(extents, Vector4.Zero, this.maxSourceExtents);
-
- int left = (int)extents.X;
- int top = (int)extents.Y;
- int right = (int)extents.Z;
- int bottom = (int)extents.W;
-
- if (left == right || top == bottom)
- {
- return;
- }
-
- this.CalculateWeights(top, bottom, transformedPoint.Y, ref ySpanRef);
- this.CalculateWeights(left, right, transformedPoint.X, ref xSpanRef);
-
- Vector4 sum = Vector4.Zero;
- for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++)
- {
- float yWeight = Unsafe.Add(ref ySpanRef, kernelY);
-
- for (int kernelX = 0, x = left; x <= right; x++, kernelX++)
- {
- float xWeight = Unsafe.Add(ref xSpanRef, 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;
- }
-
- ///
- /// Calculated the normalized weights for the given point.
- ///
- /// The minimum sampling offset
- /// The maximum sampling offset
- /// The transformed point dimension
- /// The reference to the collection of weights
- [MethodImpl(InliningOptions.ShortMethod)]
- private void CalculateWeights(int min, int max, float point, ref float weightsRef)
- {
- float sum = 0;
- for (int x = 0, i = min; i <= max; i++, x++)
- {
- float weight = this.sampler.GetValue(i - point);
- sum += weight;
- Unsafe.Add(ref weightsRef, x) = weight;
- }
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private float GetSamplingRadius(int sourceSize, int destinationSize)
- {
- float scale = (float)sourceSize / destinationSize;
-
- if (scale < 1F)
- {
- scale = 1F;
- }
-
- return MathF.Ceiling(scale * this.sampler.Radius);
- }
-
- public void Dispose()
- {
- this.yBuffer?.Dispose();
- this.xBuffer?.Dispose();
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
similarity index 96%
rename from src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
rename to src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
index e0fb554385..0760d2e3e7 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
/// Contains utility methods for working with transforms.
///
- internal static class TransformUtils
+ internal static class TransformUtilities
{
///
/// Applies the projective transform against the given coordinates flattened into the 2D space.
@@ -33,6 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The amount of rotation, in degrees.
/// The source image size.
/// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Matrix3x2 CreateRotationMatrixDegrees(float degrees, Size size)
=> CreateCenteredTransformMatrix(
new Rectangle(Point.Empty, size),
@@ -44,6 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The amount of rotation, in radians.
/// The source image size.
/// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Matrix3x2 CreateRotationMatrixRadians(float radians, Size size)
=> CreateCenteredTransformMatrix(
new Rectangle(Point.Empty, size),
@@ -56,6 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The Y angle, in degrees.
/// The source image size.
/// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Matrix3x2 CreateSkewMatrixDegrees(float degreesX, float degreesY, Size size)
=> CreateCenteredTransformMatrix(
new Rectangle(Point.Empty, size),
@@ -68,6 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The Y angle, in radians.
/// The source image size.
/// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Matrix3x2 CreateSkewMatrixRadians(float radiansX, float radiansY, Size size)
=> CreateCenteredTransformMatrix(
new Rectangle(Point.Empty, size),
@@ -79,6 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The source image bounds.
/// The transformation matrix.
/// The
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Matrix3x2 CreateCenteredTransformMatrix(Rectangle sourceRectangle, Matrix3x2 matrix)
{
Rectangle destinationRectangle = GetTransformedBoundingRectangle(sourceRectangle, matrix);
@@ -105,6 +110,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// An enumeration that indicates on which corners to taper the rectangle.
/// The amount to taper.
/// The
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide side, TaperCorner corner, float fraction)
{
Matrix4x4 matrix = Matrix4x4.Identity;
@@ -225,6 +231,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
/// The .
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix)
{
Rectangle transformed = GetTransformedRectangle(rectangle, matrix);
@@ -284,6 +291,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
/// The .
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Rectangle GetTransformedRectangle(Rectangle rectangle, Matrix4x4 matrix)
{
if (rectangle.Equals(default) || Matrix4x4.Identity.Equals(matrix))
@@ -307,6 +315,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
/// The .
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public static Size GetTransformedSize(Size size, Matrix4x4 matrix)
{
Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!");
@@ -321,6 +330,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return ConstrainSize(rectangle);
}
+ [MethodImpl(InliningOptions.ShortMethod)]
private static Size ConstrainSize(Rectangle rectangle)
{
// We want to resize the canvas here taking into account any translations.
@@ -342,6 +352,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return new Size(width, height);
}
+ [MethodImpl(InliningOptions.ShortMethod)]
private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br)
{
// Find the minimum and maximum "corners" based on the given vectors
diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
index 0ff693d81c..ef44dc16d0 100644
--- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
+++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
@@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing
/// The amount to taper.
/// The .
public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction)
- => this.Prepend(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction));
+ => this.Prepend(size => TransformUtilities.CreateTaperMatrix(size, side, corner, fraction));
///
/// Appends a matrix that performs a tapering projective transform.
@@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing
/// The amount to taper.
/// The .
public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction)
- => this.Append(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction));
+ => this.Append(size => TransformUtilities.CreateTaperMatrix(size, side, corner, fraction));
///
/// Prepends a centered rotation matrix using the given rotation in degrees.
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing
/// The amount of rotation, in radians.
/// The .
public ProjectiveTransformBuilder PrependRotationRadians(float radians)
- => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size)));
+ => this.Prepend(size => new Matrix4x4(TransformUtilities.CreateRotationMatrixRadians(radians, size)));
///
/// Prepends a centered rotation matrix using the given rotation in degrees at the given origin.
@@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing
/// The amount of rotation, in radians.
/// The