diff --git a/src/ImageSharp/Common/Helpers/QuadDistortionHelper.cs b/src/ImageSharp/Common/Helpers/QuadDistortionHelper.cs
deleted file mode 100644
index c1eee3daff..0000000000
--- a/src/ImageSharp/Common/Helpers/QuadDistortionHelper.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using System.Numerics;
-
-namespace SixLabors.ImageSharp.Common.Helpers;
-
-///
-/// Provides helper methods for performing quad distortion transformations.
-///
-internal static class QuadDistortionHelper
-{
- ///
- /// Computes the projection matrix for a quad distortion transformation.
- ///
- /// The source rectangle.
- /// The top-left point of the distorted quad.
- /// The top-right point of the distorted quad.
- /// The bottom-right point of the distorted quad.
- /// The bottom-left point of the distorted quad.
- /// The computed projection matrix for the quad distortion.
- ///
- /// This method is based on the algorithm described in the following article:
- ///
- ///
- public static Matrix4x4 ComputeQuadDistortMatrix(
- Rectangle rectangle,
- PointF topLeft,
- PointF topRight,
- PointF bottomRight,
- PointF bottomLeft)
- {
- PointF p1 = new(rectangle.X, rectangle.Y);
- PointF p2 = new(rectangle.X + rectangle.Width, rectangle.Y);
- PointF p3 = new(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height);
- PointF p4 = new(rectangle.X, rectangle.Y + rectangle.Height);
-
- PointF q1 = topLeft;
- PointF q2 = topRight;
- PointF q3 = bottomRight;
- PointF q4 = bottomLeft;
-
- double[][] matrixData =
- [
- [p1.X, p1.Y, 1, 0, 0, 0, -p1.X * q1.X, -p1.Y * q1.X],
- [0, 0, 0, p1.X, p1.Y, 1, -p1.X * q1.Y, -p1.Y * q1.Y],
- [p2.X, p2.Y, 1, 0, 0, 0, -p2.X * q2.X, -p2.Y * q2.X],
- [0, 0, 0, p2.X, p2.Y, 1, -p2.X * q2.Y, -p2.Y * q2.Y],
- [p3.X, p3.Y, 1, 0, 0, 0, -p3.X * q3.X, -p3.Y * q3.X],
- [0, 0, 0, p3.X, p3.Y, 1, -p3.X * q3.Y, -p3.Y * q3.Y],
- [p4.X, p4.Y, 1, 0, 0, 0, -p4.X * q4.X, -p4.Y * q4.X],
- [0, 0, 0, p4.X, p4.Y, 1, -p4.X * q4.Y, -p4.Y * q4.Y],
- ];
-
- double[] b =
- [
- q1.X,
- q1.Y,
- q2.X,
- q2.Y,
- q3.X,
- q3.Y,
- q4.X,
- q4.Y,
- ];
-
- GaussianEliminationSolver.Solve(matrixData, b);
-
-#pragma warning disable SA1117
- Matrix4x4 projectionMatrix = new(
- (float)b[0], (float)b[3], 0, (float)b[6],
- (float)b[1], (float)b[4], 0, (float)b[7],
- 0, 0, 1, 0,
- (float)b[2], (float)b[5], 0, 1);
-#pragma warning restore SA1117
-
- return projectionMatrix;
- }
-}
diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs
index 4ac9546f39..6d1e8aaa55 100644
--- a/src/ImageSharp/Processing/AffineTransformBuilder.cs
+++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing;
///
public class AffineTransformBuilder
{
- private readonly List> transformMatrixFactories = new();
+ private readonly List> transformMatrixFactories = [];
///
/// Initializes a new instance of the class.
@@ -301,7 +301,8 @@ public class AffineTransformBuilder
///
/// The source image size.
/// The .
- public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
+ public Matrix3x2 BuildMatrix(Size sourceSize)
+ => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
///
/// Returns the combined transform matrix for a given source rectangle.
@@ -345,18 +346,8 @@ public class AffineTransformBuilder
/// The .
public Size GetTransformedSize(Rectangle sourceRectangle)
{
- Size size = sourceRectangle.Size;
-
- // Translate the origin matrix to cater for source rectangle offsets.
- Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location);
-
- foreach (Func factory in this.transformMatrixFactories)
- {
- matrix *= factory(size);
- CheckDegenerate(matrix);
- }
-
- return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace);
+ Matrix3x2 matrix = this.BuildMatrix(sourceRectangle);
+ return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace);
}
private static void CheckDegenerate(Matrix3x2 matrix)
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
index c5c2a778eb..888d513206 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
@@ -61,12 +61,12 @@ internal class AffineTransformProcessor : TransformProcessor, IR
if (matrix.Equals(Matrix3x2.Identity))
{
// The clone will be blank here copy all the pixel data over
- var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest);
- Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest);
+ Buffer2DRegion destinationBuffer = destination.PixelBuffer.GetRegion(interest);
for (int y = 0; y < sourceBuffer.Height; y++)
{
- sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y));
+ sourceBuffer.DangerousGetRowSpan(y).CopyTo(destinationBuffer.DangerousGetRowSpan(y));
}
return;
@@ -77,7 +77,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR
if (sampler is NearestNeighborResampler)
{
- var nnOperation = new NNAffineOperation(
+ NNAffineOperation nnOperation = new(
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
destination.PixelBuffer,
@@ -91,7 +91,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR
return;
}
- var operation = new AffineOperation(
+ AffineOperation operation = new(
configuration,
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
@@ -128,17 +128,17 @@ internal class AffineTransformProcessor : TransformProcessor, IR
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
- Span destRow = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
- for (int x = 0; x < destRow.Length; x++)
+ for (int x = 0; x < destinationRowSpan.Length; x++)
{
- var point = Vector2.Transform(new Vector2(x, y), this.matrix);
+ Vector2 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.GetElementUnsafe(px, py);
+ destinationRowSpan[x] = this.source.GetElementUnsafe(px, py);
}
}
}
@@ -195,16 +195,16 @@ internal class AffineTransformProcessor : TransformProcessor, IR
for (int y = rows.Min; y < rows.Max; y++)
{
- Span rowSpan = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations.Instance.ToVector4(
this.configuration,
- rowSpan,
+ destinationRowSpan,
span,
PixelConversionModifiers.Scale);
for (int x = 0; x < span.Length; x++)
{
- var point = Vector2.Transform(new Vector2(x, y), matrix);
+ Vector2 point = Vector2.Transform(new Vector2(x, y), matrix);
float pY = point.Y;
float pX = point.X;
@@ -221,13 +221,14 @@ internal class AffineTransformProcessor : TransformProcessor, IR
Vector4 sum = Vector4.Zero;
for (int yK = top; yK <= bottom; yK++)
{
+ Span sourceRowSpan = this.source.DangerousGetRowSpan(yK);
float yWeight = sampler.GetValue(yK - pY);
for (int xK = left; xK <= right; xK++)
{
float xWeight = sampler.GetValue(xK - pX);
- Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
+ Vector4 current = sourceRowSpan[xK].ToScaledVector4();
Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight;
}
@@ -240,7 +241,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR
PixelOperations.Instance.FromVector4Destructive(
this.configuration,
span,
- rowSpan,
+ destinationRowSpan,
PixelConversionModifiers.Scale);
}
}
diff --git a/src/ImageSharp/Common/Helpers/GaussianEliminationSolver.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs
similarity index 97%
rename from src/ImageSharp/Common/Helpers/GaussianEliminationSolver.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs
index ee87cdc864..1190de4352 100644
--- a/src/ImageSharp/Common/Helpers/GaussianEliminationSolver.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-namespace SixLabors.ImageSharp.Common.Helpers;
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms.Linear;
///
/// Represents a solver for systems of linear equations using the Gaussian Elimination method.
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
index b741dc4ee6..068f69cebc 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
@@ -61,12 +61,12 @@ internal class ProjectiveTransformProcessor : TransformProcessor
if (matrix.Equals(Matrix4x4.Identity))
{
// The clone will be blank here copy all the pixel data over
- var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest);
- Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest);
+ Buffer2DRegion destinationBuffer = destination.PixelBuffer.GetRegion(interest);
for (int y = 0; y < sourceBuffer.Height; y++)
{
- sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y));
+ sourceBuffer.DangerousGetRowSpan(y).CopyTo(destinationBuffer.DangerousGetRowSpan(y));
}
return;
@@ -77,7 +77,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
if (sampler is NearestNeighborResampler)
{
- var nnOperation = new NNProjectiveOperation(
+ NNProjectiveOperation nnOperation = new(
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
destination.PixelBuffer,
@@ -91,7 +91,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
return;
}
- var operation = new ProjectiveOperation(
+ ProjectiveOperation operation = new(
configuration,
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
@@ -128,9 +128,9 @@ internal class ProjectiveTransformProcessor : TransformProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
- Span destRow = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
- for (int x = 0; x < destRow.Length; x++)
+ for (int x = 0; x < destinationRowSpan.Length; x++)
{
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix);
int px = (int)MathF.Round(point.X);
@@ -138,7 +138,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
if (this.bounds.Contains(px, py))
{
- destRow[x] = this.source.GetElementUnsafe(px, py);
+ destinationRowSpan[x] = this.source.GetElementUnsafe(px, py);
}
}
}
@@ -195,10 +195,10 @@ internal class ProjectiveTransformProcessor : TransformProcessor
for (int y = rows.Min; y < rows.Max; y++)
{
- Span rowSpan = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations.Instance.ToVector4(
this.configuration,
- rowSpan,
+ destinationRowSpan,
span,
PixelConversionModifiers.Scale);
@@ -221,13 +221,14 @@ internal class ProjectiveTransformProcessor : TransformProcessor
Vector4 sum = Vector4.Zero;
for (int yK = top; yK <= bottom; yK++)
{
+ Span sourceRowSpan = this.source.DangerousGetRowSpan(yK);
float yWeight = sampler.GetValue(yK - pY);
for (int xK = left; xK <= right; xK++)
{
float xWeight = sampler.GetValue(xK - pX);
- Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
+ Vector4 current = sourceRowSpan[xK].ToScaledVector4();
Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight;
}
@@ -240,7 +241,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
PixelOperations.Instance.FromVector4Destructive(
this.configuration,
span,
- rowSpan,
+ destinationRowSpan,
PixelConversionModifiers.Scale);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
index fa6ab137b9..47b3250b87 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
@@ -3,6 +3,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Processing.Processors.Transforms.Linear;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms;
@@ -278,6 +279,91 @@ internal static class TransformUtils
return matrix;
}
+ ///
+ /// Computes the projection matrix for a quad distortion transformation.
+ ///
+ /// The source rectangle.
+ /// The top-left point of the distorted quad.
+ /// The top-right point of the distorted quad.
+ /// The bottom-right point of the distorted quad.
+ /// The bottom-left point of the distorted quad.
+ /// The to use when creating the matrix.
+ /// The computed projection matrix for the quad distortion.
+ ///
+ /// This method is based on the algorithm described in the following article:
+ ///
+ ///
+ public static Matrix4x4 CreateQuadDistortionMatrix(
+ Rectangle rectangle,
+ PointF topLeft,
+ PointF topRight,
+ PointF bottomRight,
+ PointF bottomLeft,
+ TransformSpace transformSpace)
+ {
+ PointF p1 = new(rectangle.X, rectangle.Y);
+ PointF p2 = new(rectangle.X + rectangle.Width, rectangle.Y);
+ PointF p3 = new(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height);
+ PointF p4 = new(rectangle.X, rectangle.Y + rectangle.Height);
+
+ PointF q1 = topLeft;
+ PointF q2 = topRight;
+ PointF q3 = bottomRight;
+ PointF q4 = bottomLeft;
+
+ double[][] matrixData =
+ [
+ [p1.X, p1.Y, 1, 0, 0, 0, -p1.X * q1.X, -p1.Y * q1.X],
+ [0, 0, 0, p1.X, p1.Y, 1, -p1.X * q1.Y, -p1.Y * q1.Y],
+ [p2.X, p2.Y, 1, 0, 0, 0, -p2.X * q2.X, -p2.Y * q2.X],
+ [0, 0, 0, p2.X, p2.Y, 1, -p2.X * q2.Y, -p2.Y * q2.Y],
+ [p3.X, p3.Y, 1, 0, 0, 0, -p3.X * q3.X, -p3.Y * q3.X],
+ [0, 0, 0, p3.X, p3.Y, 1, -p3.X * q3.Y, -p3.Y * q3.Y],
+ [p4.X, p4.Y, 1, 0, 0, 0, -p4.X * q4.X, -p4.Y * q4.X],
+ [0, 0, 0, p4.X, p4.Y, 1, -p4.X * q4.Y, -p4.Y * q4.Y],
+ ];
+
+ double[] b =
+ [
+ q1.X,
+ q1.Y,
+ q2.X,
+ q2.Y,
+ q3.X,
+ q3.Y,
+ q4.X,
+ q4.Y,
+ ];
+
+ GaussianEliminationSolver.Solve(matrixData, b);
+
+#pragma warning disable SA1117
+ Matrix4x4 projectionMatrix = new(
+ (float)b[0], (float)b[3], 0, (float)b[6],
+ (float)b[1], (float)b[4], 0, (float)b[7],
+ 0, 0, 1, 0,
+ (float)b[2], (float)b[5], 0, 1);
+#pragma warning restore SA1117
+
+ // Check if the matrix involves only affine transformations by inspecting the relevant components.
+ // We want to use pixel space for calculations only if the transformation is purely 2D and does not include
+ // any perspective effects, non-standard scaling, or unusual translations that could distort the image.
+ if (transformSpace == TransformSpace.Pixel && IsAffineRotationOrSkew(projectionMatrix))
+ {
+ if (projectionMatrix.M41 != 0)
+ {
+ projectionMatrix.M41--;
+ }
+
+ if (projectionMatrix.M42 != 0)
+ {
+ projectionMatrix.M42--;
+ }
+ }
+
+ return projectionMatrix;
+ }
+
///
/// Returns the size relative to the source for the given transformation matrix.
///
@@ -293,11 +379,12 @@ internal static class TransformUtils
///
/// The transformation matrix.
/// The source size.
+ /// The used when generating the matrix.
///
/// The .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Size GetTransformedSize(Matrix4x4 matrix, Size size)
+ public static Size GetTransformedSize(Matrix4x4 matrix, Size size, TransformSpace transformSpace)
{
Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!");
@@ -309,27 +396,7 @@ internal static class TransformUtils
// Check if the matrix involves only affine transformations by inspecting the relevant components.
// We want to use pixel space for calculations only if the transformation is purely 2D and does not include
// any perspective effects, non-standard scaling, or unusual translations that could distort the image.
- // The conditions are as follows:
- bool usePixelSpace =
-
- // 1. Ensure there's no perspective distortion:
- // M34 corresponds to the perspective component. For a purely 2D affine transformation, this should be 0.
- (matrix.M34 == 0) &&
-
- // 2. Ensure standard affine transformation without any unusual depth or perspective scaling:
- // M44 should be 1 for a standard affine transformation. If M44 is not 1, it indicates non-standard depth
- // scaling or perspective, which suggests a more complex transformation.
- (matrix.M44 == 1) &&
-
- // 3. Ensure no unusual translation in the x-direction:
- // M14 represents translation in the x-direction that might be part of a more complex transformation.
- // For standard affine transformations, M14 should be 0.
- (matrix.M14 == 0) &&
-
- // 4. Ensure no unusual translation in the y-direction:
- // M24 represents translation in the y-direction that might be part of a more complex transformation.
- // For standard affine transformations, M24 should be 0.
- (matrix.M24 == 0);
+ bool usePixelSpace = transformSpace == TransformSpace.Pixel && IsAffineRotationOrSkew(matrix);
// Define an offset size to translate between pixel space and coordinate space.
// When using pixel space, apply a scaling sensitive offset to translate to discrete pixel coordinates.
@@ -492,4 +559,44 @@ internal static class TransformUtils
(int)Math.Ceiling(right),
(int)Math.Ceiling(bottom));
}
+
+ private static bool IsAffineRotationOrSkew(Matrix4x4 matrix)
+ {
+ const float epsilon = 1e-6f;
+
+ // Check if the matrix is affine (last column should be [0, 0, 0, 1])
+ if (Math.Abs(matrix.M14) > epsilon ||
+ Math.Abs(matrix.M24) > epsilon ||
+ Math.Abs(matrix.M34) > epsilon ||
+ Math.Abs(matrix.M44 - 1f) > epsilon)
+ {
+ return false;
+ }
+
+ // Translation component (M41, m42) are allowed, others are not.
+ if (Math.Abs(matrix.M43) > epsilon)
+ {
+ return false;
+ }
+
+ // Extract the linear (rotation and skew) part of the matrix
+ // Upper-left 3x3 matrix
+ float m11 = matrix.M11, m12 = matrix.M12, m13 = matrix.M13;
+ float m21 = matrix.M21, m22 = matrix.M22, m23 = matrix.M23;
+ float m31 = matrix.M31, m32 = matrix.M32, m33 = matrix.M33;
+
+ // Compute the determinant of the linear part
+ float determinant = (m11 * ((m22 * m33) - (m23 * m32))) -
+ (m12 * ((m21 * m33) - (m23 * m31))) +
+ (m13 * ((m21 * m32) - (m22 * m31)));
+
+ // Check if the determinant is approximately ±1 (no scaling)
+ if (Math.Abs(Math.Abs(determinant) - 1f) > epsilon)
+ {
+ return false;
+ }
+
+ // All checks passed; the matrix represents rotation and/or skew (with possible translation)
+ return true;
+ }
}
diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
index d5dedb1a7c..82b897ea5d 100644
--- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
+++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Numerics;
-using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
namespace SixLabors.ImageSharp.Processing;
@@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing;
///
public class ProjectiveTransformBuilder
{
- private readonly List> transformMatrixFactories = new();
+ private readonly List> transformMatrixFactories = [];
///
/// Initializes a new instance of the class.
@@ -289,7 +288,8 @@ public class ProjectiveTransformBuilder
/// The bottom-left corner point of the distorted quad.
/// The .
public ProjectiveTransformBuilder PrependQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft)
- => this.Prepend(size => QuadDistortionHelper.ComputeQuadDistortMatrix(new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft));
+ => this.Prepend(size => TransformUtils.CreateQuadDistortionMatrix(
+ new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft, this.TransformSpace));
///
/// Appends a quad distortion matrix using the specified corner points.
@@ -300,7 +300,8 @@ public class ProjectiveTransformBuilder
/// The bottom-left corner point of the distorted quad.
/// The .
public ProjectiveTransformBuilder AppendQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft)
- => this.Append(size => QuadDistortionHelper.ComputeQuadDistortMatrix(new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft));
+ => this.Append(size => TransformUtils.CreateQuadDistortionMatrix(
+ new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft, this.TransformSpace));
///
/// Prepends a raw matrix.
@@ -384,18 +385,8 @@ public class ProjectiveTransformBuilder
/// The .
public Size GetTransformedSize(Rectangle sourceRectangle)
{
- Size size = sourceRectangle.Size;
-
- // Translate the origin matrix to cater for source rectangle offsets.
- Matrix4x4 matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0));
-
- foreach (Func factory in this.transformMatrixFactories)
- {
- matrix *= factory(size);
- CheckDegenerate(matrix);
- }
-
- return TransformUtils.GetTransformedSize(matrix, size);
+ Matrix4x4 matrix = this.BuildMatrix(sourceRectangle);
+ return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace);
}
private static void CheckDegenerate(Matrix4x4 matrix)
diff --git a/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs b/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs
index 03bac732b0..95b8d2013f 100644
--- a/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs
+++ b/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using SixLabors.ImageSharp.Common.Helpers;
+using SixLabors.ImageSharp.Processing.Processors.Transforms.Linear;
namespace SixLabors.ImageSharp.Tests.Common;
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
index a8e05a3327..6b6db69c11 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
@@ -154,11 +154,11 @@ public class ProjectiveTransformTests
using (Image image = provider.GetImage())
{
#pragma warning disable SA1117 // Parameters should be on same line or separate lines
- Matrix4x4 matrix = new(
- 0.260987f, -0.434909f, 0, -0.0022184f,
- 0.373196f, 0.949882f, 0, -0.000312129f,
- 0, 0, 1, 0,
- 52, 165, 0, 1);
+ Matrix4x4 matrix = new(
+ 0.260987f, -0.434909f, 0, -0.0022184f,
+ 0.373196f, 0.949882f, 0, -0.000312129f,
+ 0, 0, 1, 0,
+ 52, 165, 0, 1);
#pragma warning restore SA1117 // Parameters should be on same line or separate lines
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png
index cac4e4ab24..38c603855c 100644
--- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png
+++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fb2804f09a350d9889d57bec60972b6a60ac341f179926cbb8361e9809e47883
-size 33139
+oid sha256:abce6af307a81a8ebac8e502142b00b2615403b5570c8dbe7b6895cfdd1a6d60
+size 66879
diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png
index 2c4cb321b4..f7ea0d0060 100644
--- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png
+++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0f468d2def55cda480d1adb077d8d087b4b15229d25bd39cd9e4fe7a203d6747
-size 1982
+oid sha256:d4cda265a50aa26711efafdbcd947c9a01eff872611df5298920583f9a3d4224
+size 26458
diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png
index cf48a0dcf8..78c37cc448 100644
--- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png
+++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e5db33fb6cd26e7641370ece52710e3ec3ab2e23da1b4586616f2fa6ce0c4d46
-size 3030
+oid sha256:278a488a858b8eda141493fe00c617eb1f664196853da8341d7e5b7f231ddce4
+size 24645
diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png
index e0be7d88d5..b4740828d4 100644
--- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png
+++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9a1ff90af73904b689c8ef1a63f0194c86ec46596a51c9c6cff7483d326b9968
-size 36739
+oid sha256:e03e79e6fab3a9e43041e54640a04c7cc3677709e7d879f9f410cf8afc7547a7
+size 42691