Browse Source

Separate transform spaces

pull/2791/head
James Jackson-South 2 years ago
parent
commit
c579547a4f
  1. 36
      src/ImageSharp/Processing/AffineTransformBuilder.cs
  2. 4
      src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs
  3. 4
      src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs
  4. 70
      src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
  5. 30
      src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
  6. 26
      src/ImageSharp/Processing/TransformSpace.cs
  7. 5
      tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs
  8. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png
  9. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png
  10. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png
  11. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png
  12. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png
  13. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png
  14. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png
  15. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png
  16. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png
  17. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png
  18. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png
  19. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png
  20. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png
  21. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png
  22. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png
  23. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png
  24. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png
  25. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png
  26. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png
  27. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png
  28. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png
  29. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png
  30. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png
  31. 4
      tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png
  32. 4
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png
  33. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/PerspectiveTransformMatchesCSS_Rgba32_Solid290x154_(0,0,255,255).png
  34. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png
  35. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png
  36. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png
  37. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png
  38. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png
  39. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png
  40. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png
  41. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png
  42. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png
  43. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png
  44. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png
  45. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png
  46. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png
  47. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png
  48. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png
  49. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png
  50. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png
  51. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png
  52. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png
  53. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png
  54. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png
  55. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png
  56. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png
  57. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png
  58. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png
  59. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png
  60. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png
  61. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png
  62. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png
  63. 4
      tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png
  64. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png
  65. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png
  66. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png
  67. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png
  68. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png
  69. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png
  70. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png
  71. 4
      tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png
  72. 4
      tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png
  73. 4
      tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png
  74. 4
      tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png
  75. 4
      tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png

36
src/ImageSharp/Processing/AffineTransformBuilder.cs

@ -13,6 +13,28 @@ public class AffineTransformBuilder
{
private readonly List<Func<Size, Matrix3x2>> transformMatrixFactories = 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
/// and the image center point as rotation center.
@ -30,7 +52,7 @@ public class AffineTransformBuilder
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependRotationRadians(float radians)
=> this.Prepend(
size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size));
size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace));
/// <summary>
/// Prepends a rotation matrix using the given rotation in degrees at the given origin.
@ -66,7 +88,7 @@ public class AffineTransformBuilder
/// <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));
=> this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace));
/// <summary>
/// Appends a rotation matrix using the given rotation in degrees at the given origin.
@ -141,7 +163,7 @@ public class AffineTransformBuilder
/// <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));
=> this.PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY));
/// <summary>
/// Prepends a centered skew matrix from the give angles in radians.
@ -150,7 +172,7 @@ public class AffineTransformBuilder
/// <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));
=> 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.
@ -179,7 +201,7 @@ public class AffineTransformBuilder
/// <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));
=> this.AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY));
/// <summary>
/// Appends a centered skew matrix from the give angles in radians.
@ -188,7 +210,7 @@ public class AffineTransformBuilder
/// <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));
=> 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.
@ -334,7 +356,7 @@ public class AffineTransformBuilder
CheckDegenerate(matrix);
}
return TransformUtils.GetTransformedSize(matrix, size);
return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace);
}
private static void CheckDegenerate(Matrix3x2 matrix)

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

@ -28,14 +28,14 @@ public sealed class RotateProcessor : AffineTransformProcessor
/// <param name="sourceSize">The source image size</param>
public RotateProcessor(float degrees, IResampler sampler, Size sourceSize)
: this(
TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize),
TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize, TransformSpace.Pixel),
sampler,
sourceSize)
=> this.Degrees = degrees;
// Helper constructor
private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize)
: base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(rotationMatrix, sourceSize))
: base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(rotationMatrix, sourceSize, TransformSpace.Pixel))
{
}

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

@ -30,7 +30,7 @@ public sealed class SkewProcessor : AffineTransformProcessor
/// <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.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize, TransformSpace.Pixel),
sampler,
sourceSize)
{
@ -40,7 +40,7 @@ public sealed class SkewProcessor : AffineTransformProcessor
// Helper constructor:
private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize)
: base(skewMatrix, sampler, TransformUtils.GetTransformedSize(skewMatrix, sourceSize))
: base(skewMatrix, sampler, TransformUtils.GetTransformedSize(skewMatrix, sourceSize, TransformSpace.Pixel))
{
}

70
src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs

@ -83,20 +83,22 @@ internal static class TransformUtils
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <param name="size">The source image size.</param>
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when creating the centered matrix.</param>
/// <returns>The <see cref="Matrix3x2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size)
=> CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty), size);
public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size, TransformSpace transformSpace)
=> CreateRotationTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degrees), size, transformSpace);
/// <summary>
/// Creates a centered rotation transform matrix using the given rotation in radians and the source size.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="size">The source image size.</param>
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when creating the centered matrix.</param>
/// <returns>The <see cref="Matrix3x2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size)
=> CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size);
public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size, TransformSpace transformSpace)
=> CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size, transformSpace);
/// <summary>
/// Creates a centered skew transform matrix from the give angles in degrees and the source size.
@ -104,10 +106,11 @@ internal static class TransformUtils
/// <param name="degreesX">The X angle, in degrees.</param>
/// <param name="degreesY">The Y angle, in degrees.</param>
/// <param name="size">The source image size.</param>
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when creating the centered matrix.</param>
/// <returns>The <see cref="Matrix3x2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size)
=> CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty), size);
public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size, TransformSpace transformSpace)
=> CreateSkewTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), size, transformSpace);
/// <summary>
/// Creates a centered skew transform matrix from the give angles in radians and the source size.
@ -115,21 +118,25 @@ internal static class TransformUtils
/// <param name="radiansX">The X angle, in radians.</param>
/// <param name="radiansY">The Y angle, in radians.</param>
/// <param name="size">The source image size.</param>
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when creating the centered matrix.</param>
/// <returns>The <see cref="Matrix3x2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size)
=> CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty), size);
public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size, TransformSpace transformSpace)
=> CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty), size, transformSpace);
/// <summary>
/// Gets the centered transform matrix based upon the source rectangle.
/// </summary>
/// <param name="matrix">The transformation matrix.</param>
/// <param name="size">The source image size.</param>
/// <param name="transformSpace">
/// The <see cref="TransformSpace"/> to use when creating the centered matrix.
/// </param>
/// <returns>The <see cref="Matrix3x2"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateCenteredTransformMatrix(Matrix3x2 matrix, Size size)
public static Matrix3x2 CreateCenteredTransformMatrix(Matrix3x2 matrix, Size size, TransformSpace transformSpace)
{
Size destinationSize = GetUnboundedTransformedSize(matrix, size);
Size transformSize = GetUnboundedTransformedSize(matrix, size, transformSpace);
// We invert the matrix to handle the transformation from screen to world space.
// This ensures scaling matrices are correct.
@ -138,8 +145,10 @@ internal static class TransformUtils
// The source size is provided using the coordinate space of the source image.
// however the transform should always be applied in the pixel space.
// To account for this we offset by the size - 1 to translate to the pixel space.
Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-(destinationSize.Width - 1), -(destinationSize.Height - 1)) * .5F);
Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(size.Width - 1, size.Height - 1) * .5F);
float offset = transformSpace == TransformSpace.Pixel ? 1F : 0F;
Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-(transformSize.Width - offset), -(transformSize.Height - offset)) * .5F);
Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(size.Width - offset, size.Height - offset) * .5F);
// Translate back to world space.
Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered);
@ -161,12 +170,6 @@ internal static class TransformUtils
{
Matrix4x4 matrix = Matrix4x4.Identity;
// The source size is provided using the Coordinate/Geometric space of the source image.
// However, the transform should always be applied in the Discrete/Pixel space to ensure
// that the transformation fully encompasses all pixels without clipping at the edges.
// To account for this, we subtract [1,1] from the size to translate to the Discrete/Pixel space.
// size -= new Size(1, 1);
/*
* SkMatrix is laid out in the following manner:
*
@ -280,11 +283,10 @@ internal static class TransformUtils
/// </summary>
/// <param name="matrix">The transformation matrix.</param>
/// <param name="size">The source size.</param>
/// <returns>
/// The <see cref="Size"/>.
/// </returns>
public static Size GetTransformedSize(Matrix3x2 matrix, Size size)
=> GetTransformedSize(matrix, size, true);
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when calculating the size.</param>
/// <returns>The <see cref="Size"/>.</returns>
public static Size GetTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace)
=> GetTransformedSize(matrix, size, transformSpace, true);
/// <summary>
/// Returns the size relative to the source for the given transformation matrix.
@ -355,22 +357,22 @@ internal static class TransformUtils
/// </summary>
/// <param name="matrix">The transformation matrix.</param>
/// <param name="size">The source size.</param>
/// <returns>
/// The <see cref="Size"/>.
/// </returns>
private static Size GetUnboundedTransformedSize(Matrix3x2 matrix, Size size)
=> GetTransformedSize(matrix, size, false);
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when calculating the size.</param>
/// <returns>The <see cref="Size"/>.</returns>
private static Size GetUnboundedTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace)
=> GetTransformedSize(matrix, size, transformSpace, false);
/// <summary>
/// Returns the size relative to the source for the given transformation matrix.
/// </summary>
/// <param name="matrix">The transformation matrix.</param>
/// <param name="size">The source size.</param>
/// <param name="transformSpace">The <see cref="TransformSpace"/> to use when calculating the size.</param>
/// <param name="constrain">Whether to constrain the size to ensure that the dimensions are positive.</param>
/// <returns>
/// The <see cref="Size"/>.
/// </returns>
private static Size GetTransformedSize(Matrix3x2 matrix, Size size, bool constrain)
private static Size GetTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace, bool constrain)
{
Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!");
@ -381,9 +383,13 @@ internal static class TransformUtils
// Define an offset size to translate between coordinate space and pixel space.
// Compute scaling factors from the matrix
float scaleX = 1F / new Vector2(matrix.M11, matrix.M21).Length(); // sqrt(M11^2 + M21^2)
float scaleY = 1F / new Vector2(matrix.M12, matrix.M22).Length(); // sqrt(M12^2 + M22^2)
SizeF offsetSize = new(scaleX, scaleY);
SizeF offsetSize = SizeF.Empty;
if (transformSpace == TransformSpace.Pixel)
{
float scaleX = 1F / new Vector2(matrix.M11, matrix.M21).Length(); // sqrt(M11^2 + M21^2)
float scaleY = 1F / new Vector2(matrix.M12, matrix.M22).Length(); // sqrt(M12^2 + M22^2)
offsetSize = new(scaleX, scaleY);
}
// Subtract the offset size to translate to the pixel space.
if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size - offsetSize), matrix, out Rectangle bounds))

30
src/ImageSharp/Processing/ProjectiveTransformBuilder.cs

@ -13,6 +13,28 @@ public class ProjectiveTransformBuilder
{
private readonly List<Func<Size, Matrix4x4>> transformMatrixFactories = new();
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformBuilder"/> class.
/// </summary>
public ProjectiveTransformBuilder()
: this(TransformSpace.Pixel)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformBuilder"/> class.
/// </summary>
/// <param name="transformSpace">
/// The <see cref="TransformSpace"/> to use when applying the projective transform.
/// </param>
public ProjectiveTransformBuilder(TransformSpace transformSpace)
=> this.TransformSpace = transformSpace;
/// <summary>
/// Gets the <see cref="TransformSpace"/> to use when applying the projective transform.
/// </summary>
public TransformSpace TransformSpace { get; }
/// <summary>
/// Prepends a matrix that performs a tapering projective transform.
/// </summary>
@ -47,7 +69,7 @@ public class ProjectiveTransformBuilder
/// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependRotationRadians(float radians)
=> this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size)));
=> this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace)));
/// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees at the given origin.
@ -81,7 +103,7 @@ public class ProjectiveTransformBuilder
/// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendRotationRadians(float radians)
=> this.Append(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size)));
=> this.Append(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace)));
/// <summary>
/// Appends a centered rotation matrix using the given rotation in degrees at the given origin.
@ -165,7 +187,7 @@ public class ProjectiveTransformBuilder
/// <param name="radiansY">The Y angle, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY)
=> this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)));
=> this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace)));
/// <summary>
/// Prepends a skew matrix using the given angles in degrees at the given origin.
@ -203,7 +225,7 @@ public class ProjectiveTransformBuilder
/// <param name="radiansY">The Y angle, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY)
=> this.Append(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)));
=> this.Append(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace)));
/// <summary>
/// Appends a skew matrix using the given angles in degrees at the given origin.

26
src/ImageSharp/Processing/TransformSpace.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Processing;
/// <summary>
/// Represents the different spaces used in transformation operations.
/// </summary>
public enum TransformSpace
{
/// <summary>
/// Coordinate space is a continuous, mathematical grid where objects and positions
/// are defined with precise, often fractional values. This space allows for fine-grained
/// transformations like scaling, rotation, and translation with high precision.
/// In coordinate space, an image can span from (0,0) to (4,4) for a 4x4 image, including the boundaries.
/// </summary>
Coordinate,
/// <summary>
/// Pixel space is a discrete grid where each position corresponds to a specific pixel on the screen.
/// In this space, positions are defined by whole numbers, with no fractional values.
/// A 4x4 image in pixel space covers exactly 4 pixels wide and 4 pixels tall, ranging from (0,0) to (3,3).
/// Pixel space is used when rendering images to ensure that everything aligns with the actual pixels on the screen.
/// </summary>
Pixel
}

5
tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms;
@ -97,7 +98,7 @@ public abstract class TransformBuilderTestBase<TBuilder>
this.AppendRotationDegrees(builder, degrees);
// TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness
Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size);
Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size, TransformSpace.Pixel);
var position = new Vector2(x, y);
var expected = Vector2.Transform(position, matrix);
@ -151,7 +152,7 @@ public abstract class TransformBuilderTestBase<TBuilder>
this.AppendSkewDegrees(builder, degreesX, degreesY);
Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size);
Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size, TransformSpace.Pixel);
var position = new Vector2(x, y);
var expected = Vector2.Transform(position, matrix);

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b926c8335eca5530d8704739cecae0799cc651139daedb1f88ac85b0ee1bd5d
size 9484
oid sha256:5ae57ca0658b1ffa7aca9031f4ec065ab5a9813fb8a9c5acd221526df6a4f729
size 9747

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f79389f79d91ac6749f21c27a592edfd2cff6efbb1d46296a26ae60d4e721f8
size 10103
oid sha256:0fced9def2b41cbbf215a49ea6ef6baf4c3c041fd180671eb209db5c6e7177e5
size 10470

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:322c7e061f8565efdc19642e27353ec3073ee43d8c17fbef8c13be3bb60d11dc
size 10190
oid sha256:1e4cc16c2f1b439f8780dead04db01fed95f8e20b68270ae8e7a988af999e3db
size 10561

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:544e7bac188d0869f98ec075fa0e73ab831e4dafe40c1520dce194df6a53c9b8
size 12737
oid sha256:06e3966550f1c3ae72796e5522f7829cf1f86daca469c479acf49e6fae72e3d0
size 13227

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2ccc08769974e4088702d2c95fd274af7e02095955953b424a6313d656a77735
size 19974
oid sha256:8ce5fefe04cc2a036fddcfcf038901a7a09b4ea5d0621a1e0d3abc8430953ae3
size 20778

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c1179b300b35d526bab148833ab6240f1207b8ade36674b1f47cc5a2d47a084c
size 10603
oid sha256:b653c0fe761d351cb15b09f35da578a954d103dea7507e2c1d7c4ebf3bdac49a
size 10943

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f666fe67ee4a1c7152fc6190affba95ea4cbd857d96bac0968e5f1fd89792d32
size 13486
oid sha256:3a17bb1653cc6d6ecc292ce0670c651bfea032f61c6a0e84636205bde53a86f8
size 13536

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5b05406a1d95f0709a7aaab7c1f57ba161b7907b76746f61788cfe527796a489
size 4131
oid sha256:b8970378312c0d479d618e4d5b8da54175c127db517fbe54f9057188d02cc735
size 4165

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be52d36cc8f616a781c8b1416ca0bf6207b9acd580e9c06e1ee5ad434d48ab38
size 13481
oid sha256:3a17bb1653cc6d6ecc292ce0670c651bfea032f61c6a0e84636205bde53a86f8
size 13536

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:33c99b8f0fb5d10a273a90946767f93ab6cd2dd1942f9829d695987db30dccfa
size 12488
oid sha256:9bbf7ef00f98b410f309b3bf70ce87d3c6455666a26e89cd004744145a10408a
size 12559

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:af2c0201c59065a500ae985e9b7ca164e5bcb4ce2d8d8305103398830472e07c
size 14206
oid sha256:7f9ab86abad276d58bb029bd8e2c2aaffac5618322788cb3619577c7643e10d2
size 14223

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4cef17988c4a3a667dede3dd86ed61d0507a84e5b846f52459683fd04e5a396a
size 17297
oid sha256:05c4dc9af1fef422fd5ada2fa1459c26253e0fb5e5a13226fa2e7445ece32272
size 17927

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9699a81572c03c2bc47d8bbdd1d64df26f87df3d4ad59fb6f164f6e82786d91d
size 18853
oid sha256:82b47e1cad2eea417b99a2e4b68a5ba1a6cd6703f360e8402f3dca8b92373ecc
size 18945

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4fb1f59c5393debdff9bd4b7a6c222b7a0686e6d5ef24363e3d5c94ba9b5bc27
size 20725
oid sha256:b15ce5a201ee6b946de485a58d3d8e779b6841457e096b2bd7a92968a122f9af
size 20844

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8ffa8ca6a60de9fe26a191edc2127886c61c072c1aa2b91fe3125512fe40e1b3
size 13848
oid sha256:a1622a48b3f4790d66b229ed29acd18504cedf68d0a548832665c28d47ea663b
size 13857

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:abf1d0f323795c0aaff0ff8b488d9866c5b2f7c64aad83701cb1f60e22668b0e
size 4161
oid sha256:74df7b82e2148cfc8dae7e05c96009c0d70c09bf39cdc5ef9d727063d2a8cb3f
size 4154

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b0f3c41248138bd501ae844e5b54fb9f49e5d22bab9b2ef0a0654034708b99f
size 14027
oid sha256:cc740ccd76910e384ad84a780591652ac7ee0ea30abf7fd7f5b146f8ff380f07
size 13991

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a3f839d64984b9fda4125c6643f4699add6f95373a2194c5726ed3740565a47
size 13725
oid sha256:ccdc54e814604d4d339f6083091abf852aae65052ceb731af998208faddb5b0b
size 13744

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2ac143bc73612cecfffbec049a7b68234e7bf7581e680c3f996a977c6d671cc1
size 14865
oid sha256:cd24e0a52c7743ab7d3ed255e3757c2d5495b3f56198556a157df589b1fb67ca
size 14889

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4eb9dab20d5a03c0adde05a9b741d9e1b0fb8c3d79054a8bc5788de496e5c7f8
size 12420
oid sha256:878f1aab39b0b2405498c24146b8f81248b37b974e5ea7882e96174a034b645f
size 12374

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0f56ee78cc2fd698ac8ea84912648f0b49d4b4d66b439f6976447c56a44c2998
size 16909
oid sha256:dcc2bf4f7e0ab3d56ee71ac1e1855dababeb2e4ec167fd5dc264efdc9e727328
size 17027

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dd3b29b530e221618f65cd5e493b21fe3c27804fde7664636b7bb002f72abbb2
size 3663
oid sha256:6c733878f4c0cc6075a01fbe7cb471f8b3e91c2c5eaf89309ea3c073d9cc4921
size 854

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:da8229605bda413676a42f587df250a743540e6e00c04eacb1e622f223e19595
size 3564
oid sha256:c86a0ceb875e02b58084fd95e5c439791af313e1fb273baf00b35187a2678d2f
size 657

4
tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a35757fef08a6fd9b37e719d5be7a82d5ff79f0395e082f697d9ebe9c7f03cc8
size 5748
oid sha256:af872886136893938aee82b1ac73e7a1820666a9a5f4bbf34159c09b3283169a
size 5520

4
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:00836a98742d177a2376af32d8d858fcf9f26a4da5a311dd5faf5cd80f233c0b
size 184397
oid sha256:0ba180567e820b145a13c9b26db9c777e95126adfe8e8cacec0ffe1060dcfe8d
size 184124

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/PerspectiveTransformMatchesCSS_Rgba32_Solid290x154_(0,0,255,255).png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d40d0715f695dc55dc2e435cab2c999b657b59ead2e0cc8a95edf7cea7782750
size 6842
oid sha256:455b17bc432490435c2453424d17b92b77d036dcbc2b2ab06b960a398bd3136b
size 11119

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ac88c089bb5106d9dc9ed8e764f6495b74e1cd98d1df61319934ed78a7578866
size 13233
oid sha256:543dbf5376386bf518830850645d69934e2ca17ab208ce3fd5274a6a172f5206
size 10951

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:23d502e2d6b0eb5f12ed3262eb4654927cc937574ae1de61a1d89f6672592017
size 11828
oid sha256:0d0cf291ebf5d8cebab1cd76e2830e5e2d2e0d9a050f7187da72680ead39110c
size 2757

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ac88c089bb5106d9dc9ed8e764f6495b74e1cd98d1df61319934ed78a7578866
size 13233
oid sha256:543dbf5376386bf518830850645d69934e2ca17ab208ce3fd5274a6a172f5206
size 10951

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5f1e69bfd1f4e9479839d4bddfb2ecc68ff8cda9b15055427406691df94a16db
size 9583
oid sha256:57698b6666029a55edf8bd35a7ba96f68d224988cf01308a3af1c6606ae9d0b1
size 10174

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cd1d0350ca49ff1726c2a20769693698168497944413c653da6edcb6bc9a39e5
size 17344
oid sha256:fc7c9da04142a679887c714c43f1838eba0092a869140d234fce3412673207c6
size 13575

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2dc8ed5f721d2d8ae81f1b70308511737b0e649a6b05a011342efce29fa5b1cb
size 23022
oid sha256:d8b973f41f8afa39b94c71b307b7eb393953e2d083d56e1f0e8f43d6ab1f342a
size 16821

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a9514b25bbc875eb00b5c0dc4241f973d466075914a9ec4c4b64ba251323eb5a
size 22321
oid sha256:122c1501e09516244f0db36e1cca373ff68514a18e84f57ed3072d52d6112e36
size 17022

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:09138a5ec45480a72370eba3015e7dee33360712745cb2f00e36a5994c1e48a7
size 24090
oid sha256:12181516bce69c9302f15bba928fd530796449599cb9744a2411cc796788ee3b
size 18066

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ed80155afab90710ebab4babbbb787402ceac4f9dee1ff270361e5e8e495c73a
size 13867
oid sha256:1eb5accc5ada5b963ecef6ac15bfb1845f481e51aef63e06a522ea73bbeab945
size 11194

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:96fa0ebee48290be808d5c7dec561bbcab49e5222667e175ffff01b3a175c586
size 2308
oid sha256:0418f0ea38ec19b407f2b5046d7ff0ed207189ad71db1e50e82c419d83620543
size 2759

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19c3ce34e6bbd8e35ec979acc28510342dadf0bae3d91a9e1b8291cdff638465
size 13873
oid sha256:1233a9ab2c4b0b17b0248c3d40050c466330c22095287dfbdb8bf7dfbda4ff1f
size 11212

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:87676e4bf55e71815acc46bdeb3384d659fe6cb3ecc7b730fd9ecc8be00d181f
size 13847
oid sha256:e2912d4e42c7b76d9ff48a49921d6472e351662597d69b88bc3708683c7933e3
size 11221

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34a62df6b4e9756a7dfef965b669b522e2338b0c2e267d35e4a37fcd9d8a55c3
size 14818
oid sha256:51b05c38647e0c1d88cc722e4109a882305073a065d2a27ccd3bee82f727127d
size 11775

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60cf2a3fae30bee16131092590e2a8fe139070b6cf98c3b98698e34ededb711f
size 11996
oid sha256:b260e816b23a43d7efb7507897ba2f5dbb6a596dd67a5abd4c9a0c005e926ee0
size 9748

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c2140365f533762cfc4411105be04a13f7d85739d955152c3e1951c2c69c7d67
size 19934
oid sha256:50b03d627bb53048f5e56380500f116da4c503f5bb6a1b1d3c0d67ee4256d8f6
size 15977

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b57bd912acfd243882163775196e0b02c7d0374e81319cb29902cd560b5c6053
size 394
oid sha256:96454548849147d7c7f0ca507c8521a7d5eaa7771f9f383cc836858870b52c57
size 280

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8827b81929abd9000fcfd3ce747b82fec4c778bcd29bb12abaaed5f8b0dfc945
size 228
oid sha256:e94d224fdb284b6f1ba21b8caa66174edd7e6a3027f9dd03f4757e08296e6508
size 212

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5819f0deb160a7c7bf090919a010a2f8a3c074b3b718f56fe56f1f6eae1fcdcd
size 227
oid sha256:d1162be9fa1f31bee8d3cba05c1422a1945621a412be11cce13d376efd5c679c
size 173

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1381429147add3a9d3342bb2d618a47a1a5db997a384582a288705f68f5f937a
size 343
oid sha256:0ed262e9b885af773a4a40a4506e678630670e208bf7f9ec10307e943b166bed
size 258

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e59892f86d307e28457ebd621fec84b8ab839cdc11afe38e978420c4173058e4
size 236
oid sha256:3a24f2cfc225d01294b8bbc5ca7d7f1738fb0b79217046eb9edf04e4c4c01851
size 201

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d25a9407bb5a3c8ba7126c9082dfbc5f29c5b52fc46e1a21e1c8968ac9f1c11
size 230
oid sha256:938186fb3d0f468176988a9530efd22e66241a1361fff027005ec8a8ae323ff3
size 197

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:398d1ca71075c541fdb88eb63f5889edea2259fdd0df643bed77e64baa246ceb
size 317
oid sha256:4bc4b8ea7e7f10676d8de612fe6bc5144e100b95ff3fe7a1e3d4066a7684ce4d
size 239

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:62546cd019fa11a43d254936c12b5adcbe8eebe6ae012bc1024949df6b28303f
size 220
oid sha256:345337f7dffa48d95251503ee2ae8e91db98b5cbe06b579d73c38a018c781544
size 182

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:27e444aff3db9c5a1149de73f3b0ed18e48c8dc372be13c3555dcda0ec4ad893
size 221
oid sha256:de4e2b71dade9dfb750a2c614a684963d6962958db79145c87fd23d9f0f8c005
size 180

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6bdfa8eb65b5e54b9ccffb11acb2a3b7a5a434f1f22a2cdf792d892fd411f711
size 399
oid sha256:8d8b651663366e7543211635f337c229e2f88f1142886ea3a9b69587daaada97
size 288

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e11a1af248350450454ba1cb415357e58f905de402a921b213a305990e8f57c3
size 245
oid sha256:8ab8df31f1716c05bb8687f79c7d1154f6cc6f65e3917abe60ecc42d0df173dc
size 215

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e9cffb16fbff28901a3daf391469d32bf36f3c293aa870d169967f17611dea92
size 241
oid sha256:1a1671da9ea7702a37a866fabfb3ca0d746233ee108594198f23cb563af43ae6
size 180

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:39c25539c3c9b8926bf65c041df693a60617bbe8653bb72357bde5ab6342c59c
size 3618
oid sha256:f5aef9cd3b8bfd9859e5d2401e82f89d89407ab2834b09c43f0a3229c735e92b
size 724

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:da8229605bda413676a42f587df250a743540e6e00c04eacb1e622f223e19595
size 3564
oid sha256:c86a0ceb875e02b58084fd95e5c439791af313e1fb273baf00b35187a2678d2f
size 657

4
tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b1fc95fdf07c7443147205afffb157aa82f94818cfbb833a615c42f584fbda0
size 5070
oid sha256:1e7dedec16ccd66543a1d44052a104957ba62099ba2f2ccc72285c233c2ae3fa
size 4411

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:801067dfb19b2a9a1fbd14b771e780b475c21f3ccdc1e709bbc20d62061ad1d1
size 8782
oid sha256:3594547265b23603b1a76ff6bc6f0eab4af55d6e0070e53356123dfc7ae256f8
size 9034

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ccbdd3dcdbc888923e85495fbd7837478cca813be6ecece63ee645bdf39d436f
size 10325
oid sha256:5ae9ef073f3338b71d2a40fcf2e89d9b6ab62204d6de9b6a1f75f4705ee197f0
size 10704

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60d1be2ffa6d50f97561b92efe8d0c0337f9c121582e38c9ab9af75be8eed32d
size 8539
oid sha256:994dda7da034595aa77d107652bea06c86077d24ef8a6883b18f1f509bb19928
size 8906

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f79389f79d91ac6749f21c27a592edfd2cff6efbb1d46296a26ae60d4e721f8
size 10103
oid sha256:0fced9def2b41cbbf215a49ea6ef6baf4c3c041fd180671eb209db5c6e7177e5
size 10470

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:75fb59ea2947efb1abf73cd824e54b6e8f6271343bd2fdadb005b29476988921
size 9423
oid sha256:29c5f48f1ece0b12854b4c44fba84fdfc9ac5751cdf564a32478dcdaed43b2a4
size 9798

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9c2d94791af40e001fc44b23eeecac7a606492c747b22ede0cdc7069ef121cb8
size 11193
oid sha256:c7de58474c3f386c4ec31a9088d561a513f82c08d1157132d735169b847b9680
size 11579

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:016de6e82b6fb03fd55168ea7fc12ab245d0e0387ca7c32d3ef1158a85a8facd
size 9330
oid sha256:3ef9b7051d7a5733dfe2534fddefdc28dfbc49d087355f46c4d945b04f0e3936
size 9672

4
tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9219cc118fe7195b730cbe2e6407cde54e6f4c7930a71b7418bc7c273aa4120c
size 11050
oid sha256:825770c9b2e9f265d834eab6b40604df5508bf9bc5b4f82f5d3effd6d5a26935
size 11434

4
tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b243340f372220033b349a96cbd7ecd732395fa15e4d1ed62048d2031c42794
size 8398
oid sha256:1e283463b0f450dd72cf303acccf3dd1ff7a31fe401ff0f288d67c4baefca240
size 8742

4
tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fdd2745aba2f09bb4a09e881339fe62774ce5658902aa9d83b3a1e0718260084
size 8694
oid sha256:485d9d9ef955a04af43d17e6bc3952e9bf65a9752b6cf8ba9cbbe8f772f05a18
size 8995

4
tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:281bd00550ab1cf9b05011750e6de330cdd42a8644ecf3b7c176bd5c6e94c59b
size 6098
oid sha256:d3d749ac365764051ea16bc39d1aff84c06faf282359805b58bb97c9eed7f0bb
size 6400

4
tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e755ed61d7eab2c297f4b6592366b54ac801cbdb3d920741dfdd04dfaf73f9b9
size 6086
oid sha256:8d82f2a15502b0a29aa4df1077ec90c88f9211f283fdc0edd7b059ed9b387441
size 6334

Loading…
Cancel
Save