Browse Source

Match APIs

af/merge-core
James Jackson-South 8 years ago
parent
commit
d992d85ef0
  1. 114
      src/ImageSharp/Processing/AffineTransformBuilder.cs
  2. 6
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
  3. 210
      src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
  4. 96
      src/ImageSharp/Processing/TransformExtensions.cs
  5. 15
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
  6. 40
      tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
  7. 6
      tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
  8. 12
      tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

114
src/ImageSharp/Processing/AffineTransformBuilder.cs

@ -17,22 +17,13 @@ namespace SixLabors.ImageSharp.Processing
private readonly List<Func<Size, Matrix3x2>> matrixFactories = new List<Func<Size, Matrix3x2>>(); private readonly List<Func<Size, Matrix3x2>> matrixFactories = new List<Func<Size, Matrix3x2>>();
/// <summary> /// <summary>
/// Prepends a centered rotation matrix using the given rotation in radians. /// Prepends a rotation matrix using the given rotation angle in degrees
/// </summary> /// and the image center point as rotation center.
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center point</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
=> this.PrependMatrix(Matrix3x2.CreateRotation(radians, centerPoint));
/// <summary>
/// Appends a centered rotation matrix using the given rotation in radians.
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="degrees">The amount of rotation, in degrees.</param>
/// <param name="centerPoint">The rotation center point</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint) public AffineTransformBuilder PrependRotationDegrees(float degrees)
=> this.AppendMatrix(Matrix3x2.CreateRotation(radians, centerPoint)); => this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees));
/// <summary> /// <summary>
/// Prepends a rotation matrix using the given rotation angle in radians /// Prepends a rotation matrix using the given rotation angle in radians
@ -44,31 +35,40 @@ namespace SixLabors.ImageSharp.Processing
=> this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
/// <summary> /// <summary>
/// Appends a rotation matrix using the given rotation angle in radians /// Prepends a centered rotation matrix using the given rotation in radians.
/// and the image center point as rotation center.
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="origin">The rotation origin point.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendRotationRadians(float radians) public AffineTransformBuilder PrependRotationRadians(float radians, Vector2 origin)
=> this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); => this.PrependMatrix(Matrix3x2.CreateRotation(radians, origin));
/// <summary> /// <summary>
/// Prepends a rotation matrix using the given rotation angle in degrees /// Appends a rotation matrix using the given rotation angle in degrees
/// and the image center point as rotation center. /// and the image center point as rotation center.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependRotationDegrees(float degrees) public AffineTransformBuilder AppendRotationDegrees(float degrees)
=> this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); => this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees));
/// <summary> /// <summary>
/// Appends a rotation matrix using the given rotation angle in degrees /// Appends a rotation matrix using the given rotation angle in radians
/// and the image center point as rotation center. /// and the image center point as rotation center.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendRotationDegrees(float degrees) public AffineTransformBuilder AppendRotationRadians(float radians)
=> this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); => this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
/// <summary>
/// Appends a centered rotation matrix using the given rotation in radians.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="origin">The rotation origin point.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendRotationRadians(float radians, Vector2 origin)
=> this.AppendMatrix(Matrix3x2.CreateRotation(radians, origin));
/// <summary> /// <summary>
/// Prepends a scale matrix from the given uniform scale. /// Prepends a scale matrix from the given uniform scale.
@ -79,12 +79,12 @@ namespace SixLabors.ImageSharp.Processing
=> this.PrependMatrix(Matrix3x2.CreateScale(scale)); => this.PrependMatrix(Matrix3x2.CreateScale(scale));
/// <summary> /// <summary>
/// Appends a scale matrix from the given uniform scale. /// Prepends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="scale">The uniform scale.</param> /// <param name="scale">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendScale(float scale) public AffineTransformBuilder PrependScale(SizeF scale)
=> this.AppendMatrix(Matrix3x2.CreateScale(scale)); => this.PrependScale((Vector2)scale);
/// <summary> /// <summary>
/// Prepends a scale matrix from the given vector scale. /// Prepends a scale matrix from the given vector scale.
@ -95,28 +95,28 @@ namespace SixLabors.ImageSharp.Processing
=> this.PrependMatrix(Matrix3x2.CreateScale(scales)); => this.PrependMatrix(Matrix3x2.CreateScale(scales));
/// <summary> /// <summary>
/// Appends a scale matrix from the given vector scale. /// Appends a scale matrix from the given uniform scale.
/// </summary> /// </summary>
/// <param name="scales">The horizontal and vertical scale.</param> /// <param name="scale">The uniform scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendScale(Vector2 scales) public AffineTransformBuilder AppendScale(float scale)
=> this.AppendMatrix(Matrix3x2.CreateScale(scales)); => this.AppendMatrix(Matrix3x2.CreateScale(scale));
/// <summary> /// <summary>
/// Prepends a scale matrix from the given vector scale. /// Appends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="scale">The horizontal and vertical scale.</param> /// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependScale(SizeF scale) public AffineTransformBuilder AppendScale(SizeF scales)
=> this.PrependScale((Vector2)scale); => this.AppendScale((Vector2)scales);
/// <summary> /// <summary>
/// Appends a scale matrix from the given vector scale. /// Appends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="scales">The horizontal and vertical scale.</param> /// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendScale(SizeF scales) public AffineTransformBuilder AppendScale(Vector2 scales)
=> this.AppendScale((Vector2)scales); => this.AppendMatrix(Matrix3x2.CreateScale(scales));
/// <summary> /// <summary>
/// Prepends a centered skew matrix from the give angles in degrees. /// Prepends a centered skew matrix from the give angles in degrees.
@ -141,66 +141,58 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependTranslation(Vector2 position) public AffineTransformBuilder PrependTranslation(PointF position)
=> this.PrependMatrix(Matrix3x2.CreateTranslation(position)); => this.PrependTranslation((Vector2)position);
/// <summary> /// <summary>
/// Appends a translation matrix from the given vector. /// Prepends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendTranslation(Vector2 position) public AffineTransformBuilder PrependTranslation(Vector2 position)
=> this.AppendMatrix(Matrix3x2.CreateTranslation(position)); => this.PrependMatrix(Matrix3x2.CreateTranslation(position));
/// <summary> /// <summary>
/// Prepends a translation matrix from the given vector. /// Appends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependTranslation(PointF position) public AffineTransformBuilder AppendTranslation(PointF position)
=> this.PrependTranslation((Vector2)position); => this.AppendTranslation((Vector2)position);
/// <summary> /// <summary>
/// Appends a translation matrix from the given vector. /// Appends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendTranslation(PointF position) public AffineTransformBuilder AppendTranslation(Vector2 position)
=> this.AppendTranslation((Vector2)position); => this.AppendMatrix(Matrix3x2.CreateTranslation(position));
/// <summary> /// <summary>
/// Prepends a raw matrix. /// Prepends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to prepend.</param> /// <param name="matrix">The matrix to prepend.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) => this.Prepend(_ => matrix);
{
this.matrixFactories.Insert(0, _ => matrix);
return this;
}
/// <summary> /// <summary>
/// Appends a raw matrix. /// Appends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to append.</param> /// <param name="matrix">The matrix to append.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) => this.Append(_ => matrix);
{
this.matrixFactories.Add(_ => matrix);
return this;
}
/// <summary> /// <summary>
/// Returns the combined matrix for a given source size. /// Returns the combined matrix for a given source size.
/// </summary> /// </summary>
/// <param name="sourceSize">The source image size</param> /// <param name="sourceSize">The source image size.</param>
/// <returns>The <see cref="Matrix3x2"/>.</returns> /// <returns>The <see cref="Matrix3x2"/>.</returns>
public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
/// <summary> /// <summary>
/// Returns the combined matrix for a given source rectangle. /// Returns the combined matrix for a given source rectangle.
/// </summary> /// </summary>
/// <param name="sourceRectangle">The rectangle in the source image</param> /// <param name="sourceRectangle">The rectangle in the source image.</param>
/// <returns>The <see cref="Matrix3x2"/>.</returns> /// <returns>The <see cref="Matrix3x2"/>.</returns>
public Matrix3x2 BuildMatrix(Rectangle sourceRectangle) public Matrix3x2 BuildMatrix(Rectangle sourceRectangle)
{ {

6
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs

@ -24,13 +24,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
/// <param name="matrix">The transform matrix.</param> /// <param name="matrix">The transform matrix.</param>
/// <param name="sampler">The sampler to perform the transform operation.</param> /// <param name="sampler">The sampler to perform the transform operation.</param>
/// <param name="sourceSize">The source image size.</param> /// <param name="targetDimensions">The target dimensions.</param>
public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size sourceSize) public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions)
{ {
Guard.NotNull(sampler, nameof(sampler)); Guard.NotNull(sampler, nameof(sampler));
this.Sampler = sampler; this.Sampler = sampler;
this.TransformMatrix = matrix; this.TransformMatrix = matrix;
this.TargetDimensions = TransformUtils.GetTransformedSize(sourceSize, matrix); this.TargetDimensions = targetDimensions;
} }
/// <summary> /// <summary>

210
src/ImageSharp/Processing/ProjectiveTransformBuilder.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms;
@ -13,34 +14,7 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
public class ProjectiveTransformBuilder public class ProjectiveTransformBuilder
{ {
private readonly List<Matrix4x4> matrices = new List<Matrix4x4>(); private readonly List<Func<Size, Matrix4x4>> matrixFactories = new List<Func<Size, Matrix4x4>>();
private Rectangle sourceRectangle;
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformBuilder"/> class.
/// </summary>
/// <param name="sourceSize">The source image size.</param>
public ProjectiveTransformBuilder(Size sourceSize)
: this(new Rectangle(Point.Empty, sourceSize))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformBuilder"/> class.
/// </summary>
/// <param name="sourceRectangle">The source rectangle.</param>
public ProjectiveTransformBuilder(Rectangle sourceRectangle)
{
Guard.MustBeGreaterThan(sourceRectangle.Width, 0, nameof(sourceRectangle));
Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle));
this.sourceRectangle = sourceRectangle;
}
/// <summary>
/// Gets the source image size.
/// </summary>
internal Size Size => this.sourceRectangle.Size;
/// <summary> /// <summary>
/// Prepends a matrix that performs a tapering projective transform. /// Prepends a matrix that performs a tapering projective transform.
@ -50,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="fraction">The amount to taper.</param> /// <param name="fraction">The amount to taper.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction) public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction)
=> this.PrependMatrix(TransformUtils.CreateTaperMatrix(this.Size, side, corner, fraction)); => this.Prepend(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction));
/// <summary> /// <summary>
/// Appends a matrix that performs a tapering projective transform. /// Appends a matrix that performs a tapering projective transform.
@ -60,7 +34,15 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="fraction">The amount to taper.</param> /// <param name="fraction">The amount to taper.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction) public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction)
=> this.AppendMatrix(TransformUtils.CreateTaperMatrix(this.Size, side, corner, fraction)); => this.Append(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction));
/// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees.
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependRotationDegrees(float degrees)
=> this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees));
/// <summary> /// <summary>
/// Prepends a centered rotation matrix using the given rotation in radians. /// Prepends a centered rotation matrix using the given rotation in radians.
@ -68,33 +50,32 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependRotationRadians(float radians) public ProjectiveTransformBuilder PrependRotationRadians(float radians)
{ => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size)));
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.PrependMatrix(m);
}
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in radians. /// Appends a centered rotation matrix using the given rotation in radians.
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendRotationRadians(float radians) internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
{ => this.PrependMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0)));
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.AppendMatrix(m); /// <summary>
} /// Appends a centered rotation matrix using the given rotation in degrees.
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendRotationDegrees(float degrees)
=> this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees));
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in radians. /// Appends a centered rotation matrix using the given rotation in radians.
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint) public ProjectiveTransformBuilder AppendRotationRadians(float radians)
{ => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size)));
var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
return this.PrependMatrix(m);
}
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in radians. /// Appends a centered rotation matrix using the given rotation in radians.
@ -103,80 +84,69 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="centerPoint">The rotation center.</param> /// <param name="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint) internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint)
{ => this.AppendMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0)));
var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
return this.AppendMatrix(m);
}
/// <summary> /// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees. /// Prepends a scale matrix from the given uniform scale.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="scale">The uniform scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependCenteredRotationDegrees(float degrees) public ProjectiveTransformBuilder PrependScale(float scale)
=> this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); => this.PrependMatrix(Matrix4x4.CreateScale(scale));
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in degrees. /// Prepends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="scale">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendRotationDegrees(float degrees) public ProjectiveTransformBuilder PrependScale(SizeF scale)
=> this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); => this.PrependScale((Vector2)scale);
/// <summary> /// <summary>
/// Prepends a scale matrix from the given uniform scale. /// Prepends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="scale">The uniform scale.</param> /// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(float scale) public ProjectiveTransformBuilder PrependScale(Vector2 scales)
=> this.PrependMatrix(Matrix4x4.CreateScale(scale)); => this.PrependMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1F)));
/// <summary> /// <summary>
/// Appends a scale matrix from the given uniform scale. /// Appends a scale matrix from the given uniform scale.
/// </summary> /// </summary>
/// <param name="scale">The uniform scale.</param> /// <param name="scale">The uniform scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendScale(float scale) public ProjectiveTransformBuilder AppendScale(float scale)
=> this.AppendMatrix(Matrix4x4.CreateScale(scale)); => this.AppendMatrix(Matrix4x4.CreateScale(scale));
/// <summary> /// <summary>
/// Prepends a scale matrix from the given vector scale. /// Appends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="scales">The horizontal and vertical scale.</param> /// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(Vector2 scales) public ProjectiveTransformBuilder AppendScale(SizeF scales)
=> this.PrependMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1f))); => this.AppendScale((Vector2)scales);
/// <summary> /// <summary>
/// Appends a scale matrix from the given vector scale. /// Appends a scale matrix from the given vector scale.
/// </summary> /// </summary>
/// <param name="scales">The horizontal and vertical scale.</param> /// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendScale(Vector2 scales) public ProjectiveTransformBuilder AppendScale(Vector2 scales)
=> this.AppendMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1f))); => this.AppendMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1F)));
/// <summary>
/// Prepends a scale matrix from the given vector scale.
/// </summary>
/// <param name="scale">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(SizeF scale)
=> this.PrependScale((Vector2)scale);
/// <summary> /// <summary>
/// Appends a scale matrix from the given vector scale. /// Prepends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="scales">The horizontal and vertical scale.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendScale(SizeF scales) public ProjectiveTransformBuilder PrependTranslation(PointF position)
=> this.AppendScale((Vector2)scales); => this.PrependTranslation((Vector2)position);
/// <summary> /// <summary>
/// Prepends a translation matrix from the given vector. /// Prepends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependTranslation(Vector2 position) public ProjectiveTransformBuilder PrependTranslation(Vector2 position)
=> this.PrependMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0))); => this.PrependMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0)));
@ -184,68 +154,72 @@ namespace SixLabors.ImageSharp.Processing
/// Appends a translation matrix from the given vector. /// Appends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTranslation(Vector2 position) public ProjectiveTransformBuilder AppendTranslation(PointF position)
=> this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0))); => this.AppendTranslation((Vector2)position);
/// <summary>
/// Prepends a translation matrix from the given vector.
/// </summary>
/// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependTranslation(PointF position)
=> this.PrependTranslation((Vector2)position);
/// <summary> /// <summary>
/// Appends a translation matrix from the given vector. /// Appends a translation matrix from the given vector.
/// </summary> /// </summary>
/// <param name="position">The translation position.</param> /// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTranslation(PointF position) public ProjectiveTransformBuilder AppendTranslation(Vector2 position)
=> this.AppendTranslation((Vector2)position); => this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0)));
/// <summary> /// <summary>
/// Prepends a raw matrix. /// Prepends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to prepend.</param> /// <param name="matrix">The matrix to prepend.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) => this.Prepend(_ => matrix);
{
this.matrices.Insert(0, matrix);
return this;
}
/// <summary> /// <summary>
/// Appends a raw matrix. /// Appends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to append.</param> /// <param name="matrix">The matrix to append.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) => this.Append(_ => matrix);
{
this.matrices.Add(matrix); /// <summary>
return this; /// Returns the combined matrix for a given source size.
} /// </summary>
/// <param name="sourceSize">The source image size.</param>
/// <returns>The <see cref="Matrix4x4"/>.</returns>
public Matrix4x4 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
/// <summary> /// <summary>
/// Returns the combined matrix. /// Returns the combined matrix for a given source rectangle.
/// </summary> /// </summary>
/// <param name="sourceRectangle">The rectangle in the source image.</param>
/// <returns>The <see cref="Matrix4x4"/>.</returns> /// <returns>The <see cref="Matrix4x4"/>.</returns>
public Matrix4x4 BuildMatrix() public Matrix4x4 BuildMatrix(Rectangle sourceRectangle)
{ {
Matrix4x4 matrix = Matrix4x4.Identity; Guard.MustBeGreaterThan(sourceRectangle.Width, 0, nameof(sourceRectangle));
Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle));
// Translate the origin matrix to cater for source rectangle offsets. // Translate the origin matrix to cater for source rectangle offsets.
if (!this.sourceRectangle.Equals(default)) var matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0));
{
matrix *= Matrix4x4.CreateTranslation(new Vector3(-this.sourceRectangle.Location, 0)); Size size = sourceRectangle.Size;
}
foreach (Matrix4x4 m in this.matrices) foreach (Func<Size, Matrix4x4> factory in this.matrixFactories)
{ {
matrix *= m; matrix *= factory(size);
} }
return matrix; return matrix;
} }
private ProjectiveTransformBuilder Prepend(Func<Size, Matrix4x4> factory)
{
this.matrixFactories.Insert(0, factory);
return this;
}
private ProjectiveTransformBuilder Append(Func<Size, Matrix4x4> factory)
{
this.matrixFactories.Add(factory);
return this;
}
} }
} }

96
src/ImageSharp/Processing/TransformExtensions.cs

@ -15,40 +15,32 @@ namespace SixLabors.ImageSharp.Processing
public static class TransformExtensions public static class TransformExtensions
{ {
/// <summary> /// <summary>
/// Performs an affine transform of an image using the specified sampling algorithm. /// Performs an affine transform of an image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param> /// <param name="source">The image to transform.</param>
/// <param name="sourceRectangle">The source rectangle</param> /// <param name="builder">The affine transform builder.</param>
/// <param name="transform">The transformation matrix.</param>
/// <param name="targetDimensions">The size of the result image.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>( public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx, this IImageProcessingContext<TPixel> source,
Rectangle sourceRectangle, AffineTransformBuilder builder)
Matrix3x2 transform,
Size targetDimensions,
IResampler sampler)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ => Transform(source, builder, KnownResamplers.Bicubic);
return ctx.ApplyProcessor(
new AffineTransformProcessor<TPixel>(transform, sampler, targetDimensions),
sourceRectangle);
}
/// <summary> /// <summary>
/// Performs an affine transform of an image. /// Performs an affine transform of an image using the specified sampling algorithm.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to transform.</param> /// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param>
/// <param name="builder">The affine transform builder.</param> /// <param name="builder">The affine transform builder.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>( public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> source, this IImageProcessingContext<TPixel> ctx,
AffineTransformBuilder builder) AffineTransformBuilder builder,
IResampler sampler)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> Transform(source, builder, KnownResamplers.Bicubic); => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler);
/// <summary> /// <summary>
/// Performs an affine transform of an image using the specified sampling algorithm. /// Performs an affine transform of an image using the specified sampling algorithm.
@ -76,16 +68,22 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param> /// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param>
/// <param name="builder">The affine transform builder.</param> /// <param name="sourceRectangle">The source rectangle</param>
/// <param name="transform">The transformation matrix.</param>
/// <param name="targetDimensions">The size of the result image.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param> /// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>( public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx, this IImageProcessingContext<TPixel> ctx,
AffineTransformBuilder builder, Rectangle sourceRectangle,
Matrix3x2 transform,
Size targetDimensions,
IResampler sampler) IResampler sampler)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); return ctx.ApplyProcessor(
new AffineTransformProcessor<TPixel>(transform, sampler, targetDimensions),
sourceRectangle);
} }
/// <summary> /// <summary>
@ -105,15 +103,59 @@ namespace SixLabors.ImageSharp.Processing
/// Performs a projective transform of an image using the specified sampling algorithm. /// Performs a projective transform of an image using the specified sampling algorithm.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to transform.</param> /// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param>
/// <param name="builder">The projective transform builder.</param> /// <param name="builder">The projective transform builder.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param> /// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>( public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> source, this IImageProcessingContext<TPixel> ctx,
ProjectiveTransformBuilder builder,
IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler);
/// <summary>
/// Performs a projective transform of an image using the specified sampling algorithm.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param>
/// <param name="sourceRectangle">The source rectangle</param>
/// <param name="builder">The projective transform builder.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx,
Rectangle sourceRectangle,
ProjectiveTransformBuilder builder, ProjectiveTransformBuilder builder,
IResampler sampler) IResampler sampler)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new ProjectiveTransformProcessor<TPixel>(builder.BuildMatrix(), sampler, builder.Size)); {
Matrix4x4 transform = builder.BuildMatrix(sourceRectangle);
Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform);
return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler);
}
/// <summary>
/// Performs a projective transform of an image using the specified sampling algorithm.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="ctx">The <see cref="IImageProcessingContext{TPixel}"/>.</param>
/// <param name="sourceRectangle">The source rectangle</param>
/// <param name="transform">The transformation matrix.</param>
/// <param name="targetDimensions">The size of the result image.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx,
Rectangle sourceRectangle,
Matrix4x4 transform,
Size targetDimensions,
IResampler sampler)
where TPixel : struct, IPixel<TPixel>
{
return ctx.ApplyProcessor(
new ProjectiveTransformProcessor<TPixel>(transform, sampler, targetDimensions),
sourceRectangle);
}
} }
} }

15
tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs

@ -1,28 +1,31 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder> public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder>
{ {
protected override void AppendTranslation(AffineTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate); protected override void AppendTranslation(AffineTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate);
protected override void AppendScale(AffineTransformBuilder builder, SizeF scale) => builder.AppendScale(scale); protected override void AppendScale(AffineTransformBuilder builder, SizeF scale) => builder.AppendScale(scale);
protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians); protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians, Vector2 center) => protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians, Vector2 center) =>
builder.AppendRotationRadians(radians, center); builder.AppendRotationRadians(radians, center);
protected override void PrependTranslation(AffineTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); protected override void PrependTranslation(AffineTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate);
protected override void PrependScale(AffineTransformBuilder builder, SizeF scale) => builder.PrependScale(scale); protected override void PrependScale(AffineTransformBuilder builder, SizeF scale) => builder.PrependScale(scale);
protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians); protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians);
protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians, Vector2 center) =>
builder.PrependRotationRadians(radians, center); protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians, Vector2 origin) =>
builder.PrependRotationRadians(radians, origin);
protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder(); protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder();
@ -35,4 +38,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
return Vector2.Transform(sourcePoint, matrix); return Vector2.Transform(sourcePoint, matrix);
} }
} }
} }

40
tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs

@ -1,60 +1,40 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
public class ProjectiveTransformBuilderTests : TransformBuilderTestBase<ProjectiveTransformBuilder> public class ProjectiveTransformBuilderTests : TransformBuilderTestBase<ProjectiveTransformBuilder>
{ {
[Fact] protected override ProjectiveTransformBuilder CreateBuilder(Rectangle rectangle) => new ProjectiveTransformBuilder();
public void ConstructorAssignsProperties()
{
var s = new Size(1, 1);
var builder = new ProjectiveTransformBuilder(new Rectangle(Point.Empty, s));
Assert.Equal(s, builder.Size);
}
[Fact]
public void ConstructorThrowsInvalid()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
var s = new Size(0, 1);
var builder = new ProjectiveTransformBuilder(new Rectangle(Point.Empty, s));
});
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
var s = new Size(1, 0);
var builder = new ProjectiveTransformBuilder(new Rectangle(Point.Empty, s));
});
}
protected override ProjectiveTransformBuilder CreateBuilder(Rectangle rectangle) => new ProjectiveTransformBuilder(rectangle);
protected override void AppendTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate); protected override void AppendTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate);
protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale); protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians); protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 center) => protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 center) =>
builder.AppendRotationRadians(radians, center); builder.AppendRotationRadians(radians, center);
protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate);
protected override void PrependScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.PrependScale(scale); protected override void PrependScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.PrependScale(scale);
protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians); protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians);
protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 center) =>
builder.PrependRotationRadians(radians, center); protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) =>
builder.PrependRotationRadians(radians, origin);
protected override Vector2 Execute( protected override Vector2 Execute(
ProjectiveTransformBuilder builder, ProjectiveTransformBuilder builder,
Rectangle rectangle, Rectangle rectangle,
Vector2 sourcePoint) Vector2 sourcePoint)
{ {
Matrix4x4 matrix = builder.BuildMatrix(); Matrix4x4 matrix = builder.BuildMatrix(rectangle);
return Vector2.Transform(sourcePoint, matrix); return Vector2.Transform(sourcePoint, matrix);
} }
} }

6
tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs

@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
IResampler sampler = GetResampler(resamplerName); IResampler sampler = GetResampler(resamplerName);
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder(image.Size()) ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendTaper(TaperSide.Right, TaperCorner.Both, .5F); .AppendTaper(TaperSide.Right, TaperCorner.Both, .5F);
image.Mutate(i => i.Transform(builder, sampler)); image.Mutate(i => i.Transform(builder, sampler));
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder(image.Size()) ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendTaper(taperSide, taperCorner, .5F); .AppendTaper(taperSide, taperCorner, .5F);
image.Mutate(i => i.Transform(builder)); image.Mutate(i => i.Transform(builder));
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Matrix4x4 matrix = Matrix4x4.Identity; Matrix4x4 matrix = Matrix4x4.Identity;
matrix.M13 = 0.01F; matrix.M13 = 0.01F;
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder(image.Size()) ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendMatrix(matrix); .AppendMatrix(matrix);
image.Mutate(i => i.Transform(builder)); image.Mutate(i => i.Transform(builder));

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

@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
new TheoryData<Vector2, Vector2, Vector2, Vector2> new TheoryData<Vector2, Vector2, Vector2, Vector2>
{ {
// scale, translate, source, expectedDest // scale, translate, source, expectedDest
{ Vector2.One, Vector2.Zero, Vector2.Zero, Vector2.Zero }, { Vector2.One, Vector2.Zero, Vector2.Zero, Vector2.Zero },
{ Vector2.One, Vector2.Zero, new Vector2(10, 20), new Vector2(10, 20) }, { Vector2.One, Vector2.Zero, new Vector2(10, 20), new Vector2(10, 20) },
{ Vector2.One, new Vector2(3, 1), new Vector2(10, 20), new Vector2(13, 21) }, { Vector2.One, new Vector2(3, 1), new Vector2(10, 20), new Vector2(13, 21) },
@ -44,7 +43,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
new TheoryData<Vector2, Vector2, Vector2, Vector2> new TheoryData<Vector2, Vector2, Vector2, Vector2>
{ {
// translate, scale, source, expectedDest // translate, scale, source, expectedDest
{ Vector2.Zero, Vector2.One, Vector2.Zero, Vector2.Zero }, { Vector2.Zero, Vector2.One, Vector2.Zero, Vector2.Zero },
{ Vector2.Zero, Vector2.One, new Vector2(10, 20), new Vector2(10, 20) }, { Vector2.Zero, Vector2.One, new Vector2(10, 20), new Vector2(10, 20) },
{ new Vector2(3, 1), new Vector2(2, 0.5f), new Vector2(10, 20), new Vector2(26, 10.5f) }, { new Vector2(3, 1), new Vector2(2, 0.5f), new Vector2(10, 20), new Vector2(26, 10.5f) },
@ -183,14 +181,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
protected abstract TBuilder CreateBuilder(Rectangle rectangle); protected abstract TBuilder CreateBuilder(Rectangle rectangle);
protected abstract void AppendTranslation(TBuilder builder, PointF translate); protected abstract void AppendTranslation(TBuilder builder, PointF translate);
protected abstract void AppendScale(TBuilder builder, SizeF scale); protected abstract void AppendScale(TBuilder builder, SizeF scale);
protected abstract void AppendRotationRadians(TBuilder builder, float radians); protected abstract void AppendRotationRadians(TBuilder builder, float radians);
protected abstract void AppendRotationRadians(TBuilder builder, float radians, Vector2 center); protected abstract void AppendRotationRadians(TBuilder builder, float radians, Vector2 center);
protected abstract void PrependTranslation(TBuilder builder, PointF translate); protected abstract void PrependTranslation(TBuilder builder, PointF translate);
protected abstract void PrependScale(TBuilder builder, SizeF scale); protected abstract void PrependScale(TBuilder builder, SizeF scale);
protected abstract void PrependRotationRadians(TBuilder builder, float radians); protected abstract void PrependRotationRadians(TBuilder builder, float radians);
protected abstract void PrependRotationRadians(TBuilder b1, float v, Vector2 vector2);
protected abstract void PrependRotationRadians(TBuilder builder, float radians, Vector2 origin);
protected virtual void AppendRotationDegrees(TBuilder builder, float degrees) => protected virtual void AppendRotationDegrees(TBuilder builder, float degrees) =>
this.AppendRotationRadians(builder, GeometryUtilities.DegreeToRadian(degrees)); this.AppendRotationRadians(builder, GeometryUtilities.DegreeToRadian(degrees));
@ -199,7 +203,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
this.AppendRotationRadians(builder, GeometryUtilities.DegreeToRadian(degrees), center); this.AppendRotationRadians(builder, GeometryUtilities.DegreeToRadian(degrees), center);
protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint); protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint);
private static float Sqrt(float a) => (float)Math.Sqrt(a);
} }
} }
Loading…
Cancel
Save