diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs
index 42d555f20..eab75cfdb 100644
--- a/src/ImageSharp/Processing/AffineTransformBuilder.cs
+++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs
@@ -20,33 +20,55 @@ namespace SixLabors.ImageSharp.Processing
/// Prepends a centered rotation matrix using the given rotation in radians.
///
/// The amount of rotation, in radians.
+ /// The rotation center point
/// The .
- public AffineTransformBuilder PrependCenteredRotationRadians(float radians)
- => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
+ public AffineTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
+ => this.PrependMatrix(Matrix3x2.CreateRotation(radians, centerPoint));
///
/// Appends a centered rotation matrix using the given rotation in radians.
///
/// The amount of rotation, in radians.
+ /// The rotation center point
+ /// The .
+ public AffineTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint)
+ => this.AppendMatrix(Matrix3x2.CreateRotation(radians, centerPoint));
+
+ ///
+ /// Prepends a rotation matrix using the given rotation angle in radians
+ /// and the image center point as rotation center.
+ ///
+ /// The amount of rotation, in radians.
+ /// The .
+ public AffineTransformBuilder PrependRotationRadians(float radians)
+ => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
+
+ ///
+ /// Appends a rotation matrix using the given rotation angle in radians
+ /// and the image center point as rotation center.
+ ///
+ /// The amount of rotation, in radians.
/// The .
- public AffineTransformBuilder AppendCenteredRotationRadians(float radians)
+ public AffineTransformBuilder AppendRotationRadians(float radians)
=> this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
///
- /// Prepends a centered rotation matrix using the given rotation in degrees.
+ /// Prepends a rotation matrix using the given rotation angle in degrees
+ /// and the image center point as rotation center.
///
/// The amount of rotation, in degrees.
/// The .
- public AffineTransformBuilder PrependCenteredRotationDegrees(float degrees)
- => this.PrependCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees));
+ public AffineTransformBuilder PrependRotationDegrees(float degrees)
+ => this.PrependRotationRadians(ImageMaths.DegreesToRadians(degrees));
///
- /// Appends a centered rotation matrix using the given rotation in degrees.
+ /// Appends a rotation matrix using the given rotation angle in degrees
+ /// and the image center point as rotation center.
///
/// The amount of rotation, in degrees.
/// The .
- public AffineTransformBuilder AppendCenteredRotationDegrees(float degrees)
- => this.AppendCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees));
+ public AffineTransformBuilder AppendRotationDegrees(float degrees)
+ => this.AppendRotationRadians(ImageMaths.DegreesToRadians(degrees));
///
/// Prepends a scale matrix from the given uniform scale.
diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
index 9bcbaf834..8bde90d51 100644
--- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
+++ b/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;
@@ -67,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing
///
/// The amount of rotation, in radians.
/// The .
- public ProjectiveTransformBuilder PrependCenteredRotationRadians(float radians)
+ public ProjectiveTransformBuilder PrependRotationRadians(float radians)
{
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.PrependMatrix(m);
@@ -78,27 +79,51 @@ namespace SixLabors.ImageSharp.Processing
///
/// The amount of rotation, in radians.
/// The .
- public ProjectiveTransformBuilder AppendCenteredRotationRadians(float radians)
+ public ProjectiveTransformBuilder AppendRotationRadians(float radians)
{
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.AppendMatrix(m);
}
+ ///
+ /// Appends a centered rotation matrix using the given rotation in radians.
+ ///
+ /// The amount of rotation, in radians.
+ /// The rotation center.
+ /// The .
+ internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
+ {
+ var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
+ return this.PrependMatrix(m);
+ }
+
+ ///
+ /// Appends a centered rotation matrix using the given rotation in radians.
+ ///
+ /// The amount of rotation, in radians.
+ /// The rotation center.
+ /// The .
+ internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint)
+ {
+ var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
+ return this.AppendMatrix(m);
+ }
+
///
/// Prepends a centered rotation matrix using the given rotation in degrees.
///
/// The amount of rotation, in degrees.
/// The .
public ProjectiveTransformBuilder PrependCenteredRotationDegrees(float degrees)
- => this.PrependCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees));
+ => this.PrependRotationRadians(ImageMaths.DegreesToRadians(degrees));
///
/// Appends a centered rotation matrix using the given rotation in degrees.
///
/// The amount of rotation, in degrees.
/// The .
- public ProjectiveTransformBuilder AppendCenteredRotationDegrees(float degrees)
- => this.AppendCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees));
+ public ProjectiveTransformBuilder AppendRotationDegrees(float degrees)
+ => this.AppendRotationRadians(ImageMaths.DegreesToRadians(degrees));
///
/// Prepends a scale matrix from the given uniform scale.
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
index f3772e3aa..374454afb 100644
--- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
@@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests
using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes))
{
AffineTransformBuilder builder = new AffineTransformBuilder()
- .AppendCenteredRotationDegrees(45F)
+ .AppendRotationDegrees(45F)
.AppendScale(new SizeF(.25F, .25F))
.AppendTranslation(new PointF(10, 10));
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
index cbbf59caf..e02585168 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
@@ -14,11 +14,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
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.AppendCenteredRotationRadians(radians);
+ 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.PrependCenteredRotationRadians(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 AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder();
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs
index 0c167d7f9..ed6d3ef2b 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs
@@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image image = provider.GetImage())
{
AffineTransformBuilder builder = new AffineTransformBuilder()
- .AppendCenteredRotationDegrees(30);
+ .AppendRotationDegrees(30);
image.Mutate(c => c.Transform(builder, resampler));
image.DebugSave(provider, resamplerName);
@@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
image.DebugSave(provider, $"_original");
AffineTransformBuilder builder = new AffineTransformBuilder()
- .AppendCenteredRotationDegrees(angleDeg)
+ .AppendRotationDegrees(angleDeg)
.AppendScale(new SizeF(sx, sy))
.AppendTranslation(new PointF(tx, ty));
@@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image image = provider.GetImage())
{
AffineTransformBuilder builder = new AffineTransformBuilder()
- .AppendCenteredRotationDegrees(angleDeg)
+ .AppendRotationDegrees(angleDeg)
.AppendScale(new SizeF(s, s));
image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic));
@@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image image = provider.GetImage())
{
AffineTransformBuilder builder = new AffineTransformBuilder()
- .AppendCenteredRotationDegrees(50)
+ .AppendRotationDegrees(50)
.AppendScale(new SizeF(.6F, .6F));
image.Mutate(i => i.Transform(builder, sampler));
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
index 14ed57033..01448ac59 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
@@ -39,11 +39,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
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.AppendCenteredRotationRadians(radians);
+ 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.PrependCenteredRotationRadians(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 Vector2 Execute(
ProjectiveTransformBuilder builder,
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs
index 648ed5861..badf43013 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs
@@ -87,7 +87,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
[InlineData(200, 100, 10, 42, 84)]
[InlineData(200, 100, 100, 42, 84)]
[InlineData(100, 200, -10, 42, 84)]
- public void RotateDegrees_ShouldCreateCenteredMatrix(int width, int height, float deg, float x, float y)
+ public void AppendRotationDegrees_WithoutSpecificRotationCenter_RotationIsCenteredAroundImageCenter(
+ int width,
+ int height,
+ float deg,
+ float x,
+ float y)
{
var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size);
@@ -97,13 +102,41 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
// TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness
Matrix3x2 matrix = TransformUtils.CreateRotationMatrixDegrees(deg, size);
- var position = new Vector2(x, y);
+ var position = new Vector2(x, y);
var expected = Vector2.Transform(position, matrix);
Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position);
Assert.Equal(actual, expected, Comparer);
}
-
+
+ [Theory]
+ [InlineData(200, 100, 10, 30, 61, 42, 84)]
+ [InlineData(200, 100, 100, 30, 10, 20, 84)]
+ [InlineData(100, 200, -10, 30, 20, 11, 84)]
+ public void AppendRotationDegrees_WithRotationCenter(
+ int width,
+ int height,
+ float deg,
+ float cx,
+ float cy,
+ float x,
+ float y)
+ {
+ var size = new Size(width, height);
+ TBuilder builder = this.CreateBuilder(size);
+
+ var centerPoint = new Vector2(cx, cy);
+ this.AppendRotationDegrees(builder, deg, centerPoint);
+
+ var matrix = Matrix3x2.CreateRotation(ImageMaths.DegreesToRadians(deg), centerPoint);
+
+ var position = new Vector2(x, y);
+ var expected = Vector2.Transform(position, matrix);
+ Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position);
+
+ Assert.Equal(actual, expected, Comparer);
+ }
+
[Fact]
public void AppendPrependOpposite()
{
@@ -116,10 +149,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
// Forwards
this.AppendRotationRadians(b1, pi);
this.AppendScale(b1, new SizeF(2, 0.5f));
+ this.AppendRotationRadians(b1, pi / 2, new Vector2(-0.5f, -0.1f));
this.AppendTranslation(b1, new PointF(123, 321));
// Backwards
this.PrependTranslation(b2, new PointF(123, 321));
+ this.PrependRotationRadians(b2, pi / 2, new Vector2(-0.5f, -0.1f));
this.PrependScale(b2, new SizeF(2, 0.5f));
this.PrependRotationRadians(b2, pi);
@@ -152,14 +187,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
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 virtual void AppendRotationDegrees(TBuilder builder, float degrees) =>
this.AppendRotationRadians(builder, ImageMaths.DegreesToRadians(degrees));
+ protected virtual void AppendRotationDegrees(TBuilder builder, float degrees, Vector2 center) =>
+ this.AppendRotationRadians(builder, ImageMaths.DegreesToRadians(degrees), center);
+
protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint);
private static float Sqrt(float a) => (float)Math.Sqrt(a);