Skip to content

Commit

Permalink
Merge pull request #2795 from SixLabors/js/affine-bounds-fixes
Browse files Browse the repository at this point in the history
V4 - Correctly handle transform spaces when building transform matrices.
  • Loading branch information
JimBobSquarePants committed Aug 17, 2024
2 parents 63f5351 + fbce0fa commit 7f720db
Show file tree
Hide file tree
Showing 78 changed files with 413 additions and 347 deletions.
60 changes: 34 additions & 26 deletions src/ImageSharp/Processing/AffineTransformBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,28 @@ namespace SixLabors.ImageSharp.Processing;
public class AffineTransformBuilder
{
private readonly List<Func<Size, Matrix3x2>> transformMatrixFactories = new();
private readonly List<Func<Size, Matrix3x2>> boundsMatrixFactories = new();

/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformBuilder"/> class.
/// </summary>
public AffineTransformBuilder()
: this(TransformSpace.Pixel)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformBuilder"/> class.
/// </summary>
/// <param name="transformSpace">
/// The <see cref="TransformSpace"/> to use when applying the affine transform.
/// </param>
public AffineTransformBuilder(TransformSpace transformSpace)
=> this.TransformSpace = transformSpace;

/// <summary>
/// Gets the <see cref="TransformSpace"/> to use when applying the affine transform.
/// </summary>
public TransformSpace TransformSpace { get; }

/// <summary>
/// Prepends a rotation matrix using the given rotation angle in degrees
Expand All @@ -31,8 +52,7 @@ public AffineTransformBuilder PrependRotationDegrees(float degrees)
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependRotationRadians(float radians)
=> this.Prepend(
size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size),
size => TransformUtils.CreateRotationBoundsMatrixRadians(radians, size));
size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace));

/// <summary>
/// Prepends a rotation matrix using the given rotation in degrees at the given origin.
Expand Down Expand Up @@ -68,9 +88,7 @@ public AffineTransformBuilder AppendRotationDegrees(float degrees)
/// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendRotationRadians(float radians)
=> this.Append(
size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size),
size => TransformUtils.CreateRotationBoundsMatrixRadians(radians, size));
=> this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace));

/// <summary>
/// Appends a rotation matrix using the given rotation in degrees at the given origin.
Expand Down Expand Up @@ -145,9 +163,7 @@ public AffineTransformBuilder AppendScale(Vector2 scales)
/// <param name="degreesY">The Y angle, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY)
=> this.Prepend(
size => TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size),
size => TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, size));
=> this.PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY));

/// <summary>
/// Prepends a centered skew matrix from the give angles in radians.
Expand All @@ -156,9 +172,7 @@ public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY)
/// <param name="radiansY">The Y angle, in radians.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY)
=> this.Prepend(
size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size),
size => TransformUtils.CreateSkewBoundsMatrixRadians(radiansX, radiansY, size));
=> this.Prepend(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace));

/// <summary>
/// Prepends a skew matrix using the given angles in degrees at the given origin.
Expand Down Expand Up @@ -187,9 +201,7 @@ public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY,
/// <param name="degreesY">The Y angle, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY)
=> this.Append(
size => TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size),
size => TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, size));
=> this.AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY));

/// <summary>
/// Appends a centered skew matrix from the give angles in radians.
Expand All @@ -198,9 +210,7 @@ public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY)
/// <param name="radiansY">The Y angle, in radians.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY)
=> this.Append(
size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size),
size => TransformUtils.CreateSkewBoundsMatrixRadians(radiansX, radiansY, size));
=> this.Append(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace));

/// <summary>
/// Appends a skew matrix using the given angles in degrees at the given origin.
Expand Down Expand Up @@ -267,7 +277,7 @@ public AffineTransformBuilder AppendTranslation(Vector2 position)
public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
{
CheckDegenerate(matrix);
return this.Prepend(_ => matrix, _ => matrix);
return this.Prepend(_ => matrix);
}

/// <summary>
Expand All @@ -283,7 +293,7 @@ public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix)
{
CheckDegenerate(matrix);
return this.Append(_ => matrix, _ => matrix);
return this.Append(_ => matrix);
}

/// <summary>
Expand Down Expand Up @@ -340,13 +350,13 @@ public Size GetTransformedSize(Rectangle sourceRectangle)
// Translate the origin matrix to cater for source rectangle offsets.
Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location);

foreach (Func<Size, Matrix3x2> factory in this.boundsMatrixFactories)
foreach (Func<Size, Matrix3x2> factory in this.transformMatrixFactories)
{
matrix *= factory(size);
CheckDegenerate(matrix);
}

return TransformUtils.GetTransformedSize(size, matrix);
return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace);
}

private static void CheckDegenerate(Matrix3x2 matrix)
Expand All @@ -357,17 +367,15 @@ private static void CheckDegenerate(Matrix3x2 matrix)
}
}

private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> transformFactory, Func<Size, Matrix3x2> boundsFactory)
private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> transformFactory)
{
this.transformMatrixFactories.Insert(0, transformFactory);
this.boundsMatrixFactories.Insert(0, boundsFactory);
return this;
}

private AffineTransformBuilder Append(Func<Size, Matrix3x2> transformFactory, Func<Size, Matrix3x2> boundsFactory)
private AffineTransformBuilder Append(Func<Size, Matrix3x2> transformFactory)
{
this.transformMatrixFactories.Add(transformFactory);
this.boundsMatrixFactories.Add(boundsFactory);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static float GetSamplingRadius<TResampler>(in TResampler sampler, int sou
/// <returns>The <see cref="int"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetRangeStart(float radius, float center, int min, int max)
=> Numerics.Clamp((int)MathF.Ceiling(center - radius), min, max);
=> Numerics.Clamp((int)MathF.Floor(center - radius), min, max);

/// <summary>
/// Gets the end position (inclusive) for a sampling range given
Expand All @@ -56,5 +56,5 @@ public static int GetRangeStart(float radius, float center, int min, int max)
/// <returns>The <see cref="int"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetRangeEnd(float radius, float center, int min, int max)
=> Numerics.Clamp((int)MathF.Floor(center + radius), min, max);
=> Numerics.Clamp((int)MathF.Ceiling(center + radius), min, max);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ public RotateProcessor(float degrees, Size sourceSize)
/// <param name="sourceSize">The source image size</param>
public RotateProcessor(float degrees, IResampler sampler, Size sourceSize)
: this(
TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize),
TransformUtils.CreateRotationBoundsMatrixDegrees(degrees, sourceSize),
TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize, TransformSpace.Pixel),
sampler,
sourceSize)
=> this.Degrees = degrees;

// Helper constructor
private RotateProcessor(Matrix3x2 rotationMatrix, Matrix3x2 boundsMatrix, IResampler sampler, Size sourceSize)
: base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, boundsMatrix))
private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize)
: base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(rotationMatrix, sourceSize, TransformSpace.Pixel))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ public SkewProcessor(float degreesX, float degreesY, Size sourceSize)
/// <param name="sourceSize">The source image size</param>
public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize)
: this(
TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize),
TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, sourceSize),
TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize, TransformSpace.Pixel),
sampler,
sourceSize)
{
Expand All @@ -40,8 +39,8 @@ public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size so
}

// Helper constructor:
private SkewProcessor(Matrix3x2 skewMatrix, Matrix3x2 boundsMatrix, IResampler sampler, Size sourceSize)
: base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, boundsMatrix))
private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize)
: base(skewMatrix, sampler, TransformUtils.GetTransformedSize(skewMatrix, sourceSize, TransformSpace.Pixel))
{
}

Expand Down
Loading

0 comments on commit 7f720db

Please sign in to comment.