@@ -11,7 +11,8 @@ namespace SixLabors.ImageSharp.Processing;
1111/// </summary>
1212public class AffineTransformBuilder
1313{
14- private readonly List < Func < Size , Matrix3x2 > > matrixFactories = new List < Func < Size , Matrix3x2 > > ( ) ;
14+ private readonly List < Func < Size , Matrix3x2 > > transformMatrixFactories = new ( ) ;
15+ private readonly List < Func < Size , Matrix3x2 > > boundsMatrixFactories = new ( ) ;
1516
1617 /// <summary>
1718 /// Prepends a rotation matrix using the given rotation angle in degrees
@@ -29,7 +30,9 @@ public AffineTransformBuilder PrependRotationDegrees(float degrees)
2930 /// <param name="radians">The amount of rotation, in radians.</param>
3031 /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
3132 public AffineTransformBuilder PrependRotationRadians ( float radians )
32- => this . Prepend ( size => TransformUtils . CreateRotationMatrixRadians ( radians , size ) ) ;
33+ => this . Prepend (
34+ size => TransformUtils . CreateRotationTransformMatrixRadians ( radians , size ) ,
35+ size => TransformUtils . CreateRotationBoundsMatrixRadians ( radians , size ) ) ;
3336
3437 /// <summary>
3538 /// Prepends a rotation matrix using the given rotation in degrees at the given origin.
@@ -65,7 +68,9 @@ public AffineTransformBuilder AppendRotationDegrees(float degrees)
6568 /// <param name="radians">The amount of rotation, in radians.</param>
6669 /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
6770 public AffineTransformBuilder AppendRotationRadians ( float radians )
68- => this . Append ( size => TransformUtils . CreateRotationMatrixRadians ( radians , size ) ) ;
71+ => this . Append (
72+ size => TransformUtils . CreateRotationTransformMatrixRadians ( radians , size ) ,
73+ size => TransformUtils . CreateRotationBoundsMatrixRadians ( radians , size ) ) ;
6974
7075 /// <summary>
7176 /// Appends a rotation matrix using the given rotation in degrees at the given origin.
@@ -140,7 +145,9 @@ public AffineTransformBuilder AppendScale(Vector2 scales)
140145 /// <param name="degreesY">The Y angle, in degrees.</param>
141146 /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
142147 public AffineTransformBuilder PrependSkewDegrees ( float degreesX , float degreesY )
143- => this . Prepend ( size => TransformUtils . CreateSkewMatrixDegrees ( degreesX , degreesY , size ) ) ;
148+ => this . Prepend (
149+ size => TransformUtils . CreateSkewTransformMatrixDegrees ( degreesX , degreesY , size ) ,
150+ size => TransformUtils . CreateSkewBoundsMatrixDegrees ( degreesX , degreesY , size ) ) ;
144151
145152 /// <summary>
146153 /// Prepends a centered skew matrix from the give angles in radians.
@@ -149,7 +156,9 @@ public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY)
149156 /// <param name="radiansY">The Y angle, in radians.</param>
150157 /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
151158 public AffineTransformBuilder PrependSkewRadians ( float radiansX , float radiansY )
152- => this . Prepend ( size => TransformUtils . CreateSkewMatrixRadians ( radiansX , radiansY , size ) ) ;
159+ => this . Prepend (
160+ size => TransformUtils . CreateSkewTransformMatrixRadians ( radiansX , radiansY , size ) ,
161+ size => TransformUtils . CreateSkewBoundsMatrixRadians ( radiansX , radiansY , size ) ) ;
153162
154163 /// <summary>
155164 /// Prepends a skew matrix using the given angles in degrees at the given origin.
@@ -178,7 +187,9 @@ public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY,
178187 /// <param name="degreesY">The Y angle, in degrees.</param>
179188 /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
180189 public AffineTransformBuilder AppendSkewDegrees ( float degreesX , float degreesY )
181- => this . Append ( size => TransformUtils . CreateSkewMatrixDegrees ( degreesX , degreesY , size ) ) ;
190+ => this . Append (
191+ size => TransformUtils . CreateSkewTransformMatrixDegrees ( degreesX , degreesY , size ) ,
192+ size => TransformUtils . CreateSkewBoundsMatrixDegrees ( degreesX , degreesY , size ) ) ;
182193
183194 /// <summary>
184195 /// Appends a centered skew matrix from the give angles in radians.
@@ -187,7 +198,9 @@ public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY)
187198 /// <param name="radiansY">The Y angle, in radians.</param>
188199 /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
189200 public AffineTransformBuilder AppendSkewRadians ( float radiansX , float radiansY )
190- => this . Append ( size => TransformUtils . CreateSkewMatrixRadians ( radiansX , radiansY , size ) ) ;
201+ => this . Append (
202+ size => TransformUtils . CreateSkewTransformMatrixRadians ( radiansX , radiansY , size ) ,
203+ size => TransformUtils . CreateSkewBoundsMatrixRadians ( radiansX , radiansY , size ) ) ;
191204
192205 /// <summary>
193206 /// Appends a skew matrix using the given angles in degrees at the given origin.
@@ -254,7 +267,7 @@ public AffineTransformBuilder AppendTranslation(Vector2 position)
254267 public AffineTransformBuilder PrependMatrix ( Matrix3x2 matrix )
255268 {
256269 CheckDegenerate ( matrix ) ;
257- return this . Prepend ( _ => matrix ) ;
270+ return this . Prepend ( _ => matrix , _ => matrix ) ;
258271 }
259272
260273 /// <summary>
@@ -270,7 +283,7 @@ public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
270283 public AffineTransformBuilder AppendMatrix ( Matrix3x2 matrix )
271284 {
272285 CheckDegenerate ( matrix ) ;
273- return this . Append ( _ => matrix ) ;
286+ return this . Append ( _ => matrix , _ => matrix ) ;
274287 }
275288
276289 /// <summary>
@@ -281,7 +294,7 @@ public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix)
281294 public Matrix3x2 BuildMatrix ( Size sourceSize ) => this . BuildMatrix ( new Rectangle ( Point . Empty , sourceSize ) ) ;
282295
283296 /// <summary>
284- /// Returns the combined matrix for a given source rectangle.
297+ /// Returns the combined transform matrix for a given source rectangle.
285298 /// </summary>
286299 /// <param name="sourceRectangle">The rectangle in the source image.</param>
287300 /// <exception cref="DegenerateTransformException">
@@ -296,11 +309,11 @@ public Matrix3x2 BuildMatrix(Rectangle sourceRectangle)
296309 Guard . MustBeGreaterThan ( sourceRectangle . Height , 0 , nameof ( sourceRectangle ) ) ;
297310
298311 // Translate the origin matrix to cater for source rectangle offsets.
299- var matrix = Matrix3x2 . CreateTranslation ( - sourceRectangle . Location ) ;
312+ Matrix3x2 matrix = Matrix3x2 . CreateTranslation ( - sourceRectangle . Location ) ;
300313
301314 Size size = sourceRectangle . Size ;
302315
303- foreach ( Func < Size , Matrix3x2 > factory in this . matrixFactories )
316+ foreach ( Func < Size , Matrix3x2 > factory in this . transformMatrixFactories )
304317 {
305318 matrix *= factory ( size ) ;
306319 }
@@ -310,6 +323,32 @@ public Matrix3x2 BuildMatrix(Rectangle sourceRectangle)
310323 return matrix ;
311324 }
312325
326+ /// <summary>
327+ /// Returns the size of a rectangle large enough to contain the transformed source rectangle.
328+ /// </summary>
329+ /// <param name="sourceRectangle">The rectangle in the source image.</param>
330+ /// <exception cref="DegenerateTransformException">
331+ /// The resultant matrix is degenerate containing one or more values equivalent
332+ /// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
333+ /// for linear transforms.
334+ /// </exception>
335+ /// <returns>The <see cref="Size"/>.</returns>
336+ public Size GetTransformedSize ( Rectangle sourceRectangle )
337+ {
338+ Size size = sourceRectangle . Size ;
339+
340+ // Translate the origin matrix to cater for source rectangle offsets.
341+ Matrix3x2 matrix = Matrix3x2 . CreateTranslation ( - sourceRectangle . Location ) ;
342+
343+ foreach ( Func < Size , Matrix3x2 > factory in this . boundsMatrixFactories )
344+ {
345+ matrix *= factory ( size ) ;
346+ CheckDegenerate ( matrix ) ;
347+ }
348+
349+ return TransformUtils . GetTransformedSize ( size , matrix ) ;
350+ }
351+
313352 private static void CheckDegenerate ( Matrix3x2 matrix )
314353 {
315354 if ( TransformUtils . IsDegenerate ( matrix ) )
@@ -318,15 +357,17 @@ private static void CheckDegenerate(Matrix3x2 matrix)
318357 }
319358 }
320359
321- private AffineTransformBuilder Prepend ( Func < Size , Matrix3x2 > factory )
360+ private AffineTransformBuilder Prepend ( Func < Size , Matrix3x2 > transformFactory , Func < Size , Matrix3x2 > boundsFactory )
322361 {
323- this . matrixFactories . Insert ( 0 , factory ) ;
362+ this . transformMatrixFactories . Insert ( 0 , transformFactory ) ;
363+ this . boundsMatrixFactories . Insert ( 0 , boundsFactory ) ;
324364 return this ;
325365 }
326366
327- private AffineTransformBuilder Append ( Func < Size , Matrix3x2 > factory )
367+ private AffineTransformBuilder Append ( Func < Size , Matrix3x2 > transformFactory , Func < Size , Matrix3x2 > boundsFactory )
328368 {
329- this . matrixFactories . Add ( factory ) ;
369+ this . transformMatrixFactories . Add ( transformFactory ) ;
370+ this . boundsMatrixFactories . Add ( boundsFactory ) ;
330371 return this ;
331372 }
332373}
0 commit comments