Skip to content

Commit 4584377

Browse files
Merge pull request #2791 from SixLabors/js/affine-bounds-fixes
V3 - Correctly handle transform spaces when building transform matrices.
2 parents aad5cfa + c579547 commit 4584377

File tree

78 files changed

+413
-347
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+413
-347
lines changed

src/ImageSharp/Processing/AffineTransformBuilder.cs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,28 @@ namespace SixLabors.ImageSharp.Processing;
1212
public class AffineTransformBuilder
1313
{
1414
private readonly List<Func<Size, Matrix3x2>> transformMatrixFactories = new();
15-
private readonly List<Func<Size, Matrix3x2>> boundsMatrixFactories = new();
15+
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="AffineTransformBuilder"/> class.
18+
/// </summary>
19+
public AffineTransformBuilder()
20+
: this(TransformSpace.Pixel)
21+
{
22+
}
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="AffineTransformBuilder"/> class.
26+
/// </summary>
27+
/// <param name="transformSpace">
28+
/// The <see cref="TransformSpace"/> to use when applying the affine transform.
29+
/// </param>
30+
public AffineTransformBuilder(TransformSpace transformSpace)
31+
=> this.TransformSpace = transformSpace;
32+
33+
/// <summary>
34+
/// Gets the <see cref="TransformSpace"/> to use when applying the affine transform.
35+
/// </summary>
36+
public TransformSpace TransformSpace { get; }
1637

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

3757
/// <summary>
3858
/// Prepends a rotation matrix using the given rotation in degrees at the given origin.
@@ -68,9 +88,7 @@ public AffineTransformBuilder AppendRotationDegrees(float degrees)
6888
/// <param name="radians">The amount of rotation, in radians.</param>
6989
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
7090
public AffineTransformBuilder AppendRotationRadians(float radians)
71-
=> this.Append(
72-
size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size),
73-
size => TransformUtils.CreateRotationBoundsMatrixRadians(radians, size));
91+
=> this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace));
7492

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

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

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

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

205215
/// <summary>
206216
/// Appends a skew matrix using the given angles in degrees at the given origin.
@@ -267,7 +277,7 @@ public AffineTransformBuilder AppendTranslation(Vector2 position)
267277
public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
268278
{
269279
CheckDegenerate(matrix);
270-
return this.Prepend(_ => matrix, _ => matrix);
280+
return this.Prepend(_ => matrix);
271281
}
272282

273283
/// <summary>
@@ -283,7 +293,7 @@ public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
283293
public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix)
284294
{
285295
CheckDegenerate(matrix);
286-
return this.Append(_ => matrix, _ => matrix);
296+
return this.Append(_ => matrix);
287297
}
288298

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

343-
foreach (Func<Size, Matrix3x2> factory in this.boundsMatrixFactories)
353+
foreach (Func<Size, Matrix3x2> factory in this.transformMatrixFactories)
344354
{
345355
matrix *= factory(size);
346356
CheckDegenerate(matrix);
347357
}
348358

349-
return TransformUtils.GetTransformedSize(size, matrix);
359+
return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace);
350360
}
351361

352362
private static void CheckDegenerate(Matrix3x2 matrix)
@@ -357,17 +367,15 @@ private static void CheckDegenerate(Matrix3x2 matrix)
357367
}
358368
}
359369

360-
private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> transformFactory, Func<Size, Matrix3x2> boundsFactory)
370+
private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> transformFactory)
361371
{
362372
this.transformMatrixFactories.Insert(0, transformFactory);
363-
this.boundsMatrixFactories.Insert(0, boundsFactory);
364373
return this;
365374
}
366375

367-
private AffineTransformBuilder Append(Func<Size, Matrix3x2> transformFactory, Func<Size, Matrix3x2> boundsFactory)
376+
private AffineTransformBuilder Append(Func<Size, Matrix3x2> transformFactory)
368377
{
369378
this.transformMatrixFactories.Add(transformFactory);
370-
this.boundsMatrixFactories.Add(boundsFactory);
371379
return this;
372380
}
373381
}

src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static float GetSamplingRadius<TResampler>(in TResampler sampler, int sou
4343
/// <returns>The <see cref="int"/>.</returns>
4444
[MethodImpl(InliningOptions.ShortMethod)]
4545
public static int GetRangeStart(float radius, float center, int min, int max)
46-
=> Numerics.Clamp((int)MathF.Ceiling(center - radius), min, max);
46+
=> Numerics.Clamp((int)MathF.Floor(center - radius), min, max);
4747

4848
/// <summary>
4949
/// Gets the end position (inclusive) for a sampling range given
@@ -56,5 +56,5 @@ public static int GetRangeStart(float radius, float center, int min, int max)
5656
/// <returns>The <see cref="int"/>.</returns>
5757
[MethodImpl(InliningOptions.ShortMethod)]
5858
public static int GetRangeEnd(float radius, float center, int min, int max)
59-
=> Numerics.Clamp((int)MathF.Floor(center + radius), min, max);
59+
=> Numerics.Clamp((int)MathF.Ceiling(center + radius), min, max);
6060
}

src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ public RotateProcessor(float degrees, Size sourceSize)
2828
/// <param name="sourceSize">The source image size</param>
2929
public RotateProcessor(float degrees, IResampler sampler, Size sourceSize)
3030
: this(
31-
TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize),
32-
TransformUtils.CreateRotationBoundsMatrixDegrees(degrees, sourceSize),
31+
TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize, TransformSpace.Pixel),
3332
sampler,
3433
sourceSize)
3534
=> this.Degrees = degrees;
3635

3736
// Helper constructor
38-
private RotateProcessor(Matrix3x2 rotationMatrix, Matrix3x2 boundsMatrix, IResampler sampler, Size sourceSize)
39-
: base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, boundsMatrix))
37+
private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize)
38+
: base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(rotationMatrix, sourceSize, TransformSpace.Pixel))
4039
{
4140
}
4241

src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ public SkewProcessor(float degreesX, float degreesY, Size sourceSize)
3030
/// <param name="sourceSize">The source image size</param>
3131
public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize)
3232
: this(
33-
TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize),
34-
TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, sourceSize),
33+
TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize, TransformSpace.Pixel),
3534
sampler,
3635
sourceSize)
3736
{
@@ -40,8 +39,8 @@ public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size so
4039
}
4140

4241
// Helper constructor:
43-
private SkewProcessor(Matrix3x2 skewMatrix, Matrix3x2 boundsMatrix, IResampler sampler, Size sourceSize)
44-
: base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, boundsMatrix))
42+
private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize)
43+
: base(skewMatrix, sampler, TransformUtils.GetTransformedSize(skewMatrix, sourceSize, TransformSpace.Pixel))
4544
{
4645
}
4746

0 commit comments

Comments
 (0)