Browse Source

Match APIs

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

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

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

210
src/ImageSharp/Processing/ProjectiveTransformBuilder.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Numerics;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
@ -13,34 +14,7 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
public class ProjectiveTransformBuilder
{
private readonly List<Matrix4x4> matrices = new List<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;
private readonly List<Func<Size, Matrix4x4>> matrixFactories = new List<Func<Size, Matrix4x4>>();
/// <summary>
/// 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>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
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>
/// 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>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
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>
/// 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>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependRotationRadians(float radians)
{
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.PrependMatrix(m);
}
=> this.Prepend(size => new Matrix4x4(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="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendRotationRadians(float radians)
{
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.AppendMatrix(m);
}
internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
=> this.PrependMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0)));
/// <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>
/// Appends a centered rotation matrix using the given rotation in radians.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
{
var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
return this.PrependMatrix(m);
}
public ProjectiveTransformBuilder AppendRotationRadians(float radians)
=> this.Append(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size)));
/// <summary>
/// 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>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint)
{
var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
return this.AppendMatrix(m);
}
=> this.AppendMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0)));
/// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees.
/// Prepends a scale matrix from the given uniform scale.
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependCenteredRotationDegrees(float degrees)
=> this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees));
/// <param name="scale">The uniform scale.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(float scale)
=> this.PrependMatrix(Matrix4x4.CreateScale(scale));
/// <summary>
/// Appends a centered rotation matrix using the given rotation in degrees.
/// Prepends a scale matrix from the given vector scale.
/// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendRotationDegrees(float degrees)
=> this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees));
/// <param name="scale">The horizontal and vertical scale.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(SizeF scale)
=> this.PrependScale((Vector2)scale);
/// <summary>
/// Prepends a scale matrix from the given uniform scale.
/// Prepends a scale matrix from the given vector scale.
/// </summary>
/// <param name="scale">The uniform scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(float scale)
=> this.PrependMatrix(Matrix4x4.CreateScale(scale));
/// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(Vector2 scales)
=> this.PrependMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1F)));
/// <summary>
/// Appends a scale matrix from the given uniform scale.
/// </summary>
/// <param name="scale">The uniform scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendScale(float scale)
=> this.AppendMatrix(Matrix4x4.CreateScale(scale));
/// <summary>
/// Prepends a scale matrix from the given vector scale.
/// Appends a scale matrix from the given vector scale.
/// </summary>
/// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependScale(Vector2 scales)
=> this.PrependMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1f)));
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendScale(SizeF scales)
=> this.AppendScale((Vector2)scales);
/// <summary>
/// Appends a scale matrix from the given vector scale.
/// </summary>
/// <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)
=> 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);
=> this.AppendMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1F)));
/// <summary>
/// Appends a scale matrix from the given vector scale.
/// Prepends a translation matrix from the given vector.
/// </summary>
/// <param name="scales">The horizontal and vertical scale.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendScale(SizeF scales)
=> this.AppendScale((Vector2)scales);
/// <param name="position">The translation position.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependTranslation(PointF position)
=> this.PrependTranslation((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>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependTranslation(Vector2 position)
=> this.PrependMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0)));
@ -184,68 +154,72 @@ namespace SixLabors.ImageSharp.Processing
/// Appends a translation matrix from the given vector.
/// </summary>
/// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTranslation(Vector2 position)
=> this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0)));
/// <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);
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTranslation(PointF position)
=> this.AppendTranslation((Vector2)position);
/// <summary>
/// Appends a translation matrix from the given vector.
/// </summary>
/// <param name="position">The translation position.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTranslation(PointF position)
=> this.AppendTranslation((Vector2)position);
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendTranslation(Vector2 position)
=> this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0)));
/// <summary>
/// Prepends a raw matrix.
/// </summary>
/// <param name="matrix">The matrix to prepend.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix)
{
this.matrices.Insert(0, matrix);
return this;
}
public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) => this.Prepend(_ => matrix);
/// <summary>
/// Appends a raw matrix.
/// </summary>
/// <param name="matrix">The matrix to append.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix)
{
this.matrices.Add(matrix);
return this;
}
public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) => this.Append(_ => matrix);
/// <summary>
/// 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>
/// Returns the combined matrix.
/// Returns the combined matrix for a given source rectangle.
/// </summary>
/// <param name="sourceRectangle">The rectangle in the source image.</param>
/// <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.
if (!this.sourceRectangle.Equals(default))
{
matrix *= Matrix4x4.CreateTranslation(new Vector3(-this.sourceRectangle.Location, 0));
}
var matrix = Matrix4x4.CreateTranslation(new Vector3(-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;
}
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
{
/// <summary>
/// Performs an affine transform of an image using the specified sampling algorithm.
/// Performs an affine transform of an image.
/// </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>
/// <param name="source">The image to transform.</param>
/// <param name="builder">The affine transform builder.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx,
Rectangle sourceRectangle,
Matrix3x2 transform,
Size targetDimensions,
IResampler sampler)
this IImageProcessingContext<TPixel> source,
AffineTransformBuilder builder)
where TPixel : struct, IPixel<TPixel>
{
return ctx.ApplyProcessor(
new AffineTransformProcessor<TPixel>(transform, sampler, targetDimensions),
sourceRectangle);
}
=> Transform(source, builder, KnownResamplers.Bicubic);
/// <summary>
/// Performs an affine transform of an image.
/// Performs an affine transform of an image using the specified sampling algorithm.
/// </summary>
/// <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="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> source,
AffineTransformBuilder builder)
this IImageProcessingContext<TPixel> ctx,
AffineTransformBuilder builder,
IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> Transform(source, builder, KnownResamplers.Bicubic);
=> ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler);
/// <summary>
/// Performs an affine transform of an image using the specified sampling algorithm.
@ -76,16 +68,22 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx,
AffineTransformBuilder builder,
Rectangle sourceRectangle,
Matrix3x2 transform,
Size targetDimensions,
IResampler sampler)
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>
@ -105,15 +103,59 @@ namespace SixLabors.ImageSharp.Processing
/// Performs a projective transform of an image using the specified sampling algorithm.
/// </summary>
/// <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="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> 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,
IResampler sampler)
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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder>
{
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 AppendRotationRadians(AffineTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians, Vector2 center) =>
builder.AppendRotationRadians(radians, center);
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 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();
@ -35,4 +38,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
return Vector2.Transform(sourcePoint, matrix);
}
}
}
}

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

@ -1,60 +1,40 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
public class ProjectiveTransformBuilderTests : TransformBuilderTestBase<ProjectiveTransformBuilder>
{
[Fact]
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 ProjectiveTransformBuilder CreateBuilder(Rectangle rectangle) => new ProjectiveTransformBuilder();
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 AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 center) =>
builder.AppendRotationRadians(radians, center);
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 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(
ProjectiveTransformBuilder builder,
Rectangle rectangle,
Vector2 sourcePoint)
{
Matrix4x4 matrix = builder.BuildMatrix();
Matrix4x4 matrix = builder.BuildMatrix(rectangle);
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);
using (Image<TPixel> image = provider.GetImage())
{
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder(image.Size())
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendTaper(TaperSide.Right, TaperCorner.Both, .5F);
image.Mutate(i => i.Transform(builder, sampler));
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
using (Image<TPixel> image = provider.GetImage())
{
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder(image.Size())
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendTaper(taperSide, taperCorner, .5F);
image.Mutate(i => i.Transform(builder));
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Matrix4x4 matrix = Matrix4x4.Identity;
matrix.M13 = 0.01F;
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder(image.Size())
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendMatrix(matrix);
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>
{
// scale, translate, source, expectedDest
{ Vector2.One, Vector2.Zero, Vector2.Zero, Vector2.Zero },
{ 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) },
@ -44,7 +43,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
new TheoryData<Vector2, Vector2, Vector2, Vector2>
{
// translate, scale, source, expectedDest
{ Vector2.Zero, Vector2.One, Vector2.Zero, Vector2.Zero },
{ 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) },
@ -183,14 +181,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
protected abstract TBuilder CreateBuilder(Rectangle rectangle);
protected abstract void AppendTranslation(TBuilder builder, PointF translate);
protected abstract void AppendScale(TBuilder builder, SizeF scale);
protected abstract void AppendRotationRadians(TBuilder builder, float radians);
protected abstract void AppendRotationRadians(TBuilder builder, float radians, Vector2 center);
protected abstract void PrependTranslation(TBuilder builder, PointF translate);
protected abstract void PrependScale(TBuilder builder, SizeF scale);
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) =>
this.AppendRotationRadians(builder, GeometryUtilities.DegreeToRadian(degrees));
@ -199,7 +203,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
this.AppendRotationRadians(builder, GeometryUtilities.DegreeToRadian(degrees), center);
protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint);
private static float Sqrt(float a) => (float)Math.Sqrt(a);
}
}
Loading…
Cancel
Save