mirror of https://github.com/SixLabors/ImageSharp
6 changed files with 234 additions and 58 deletions
@ -0,0 +1,151 @@ |
|||
// 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 abstract class TransformBuilderTestBase<TBuilder> |
|||
{ |
|||
private static readonly ApproximateFloatComparer Comparer = new ApproximateFloatComparer(1e-6f); |
|||
|
|||
public static readonly TheoryData<Vector2, Vector2, Vector2, Vector2> ScaleTranslate_Data = |
|||
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) }, |
|||
{ new Vector2(2, 0.5f), new Vector2(3, 1), new Vector2(10, 20), new Vector2(23, 11) }, |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(ScaleTranslate_Data))] |
|||
public void _1Scale_2Translate(Vector2 scale, Vector2 translate, Vector2 source, Vector2 expectedDest) |
|||
{ |
|||
// These operations should be size-agnostic:
|
|||
var size = new Size(123, 321); |
|||
TBuilder builder = this.CreateBuilder(size); |
|||
|
|||
this.AppendScale(builder, new SizeF(scale)); |
|||
this.AppendTranslation(builder, translate); |
|||
|
|||
Vector2 actualDest = this.Execute(builder, new Rectangle(Point.Empty, size), source); |
|||
Assert.True(Comparer.Equals(expectedDest, actualDest)); |
|||
} |
|||
|
|||
public static readonly TheoryData<Vector2, Vector2, Vector2, Vector2> TranslateScale_Data = |
|||
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) }, |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(TranslateScale_Data))] |
|||
public void _1Translate_2Scale(Vector2 translate, Vector2 scale, Vector2 source, Vector2 expectedDest) |
|||
{ |
|||
// Translate ans scale are size-agnostic:
|
|||
var size = new Size(456, 432); |
|||
TBuilder builder = this.CreateBuilder(size); |
|||
|
|||
this.AppendTranslation(builder, translate); |
|||
this.AppendScale(builder, new SizeF(scale)); |
|||
|
|||
Vector2 actualDest = this.Execute(builder, new Rectangle(Point.Empty, size), source); |
|||
Assert.Equal(expectedDest, actualDest, Comparer); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(10, 20)] |
|||
[InlineData(-20, 10)] |
|||
public void LocationOffsetIsPrepended(int locationX, int locationY) |
|||
{ |
|||
var rectangle = new Rectangle(locationX, locationY, 10, 10); |
|||
TBuilder builder = this.CreateBuilder(rectangle); |
|||
|
|||
this.AppendScale(builder, new SizeF(2, 2)); |
|||
|
|||
Vector2 actual = this.Execute(builder, rectangle, Vector2.One); |
|||
Vector2 expected = new Vector2(-locationX + 1, -locationY + 1) * 2; |
|||
|
|||
Assert.Equal(actual, expected, Comparer); |
|||
} |
|||
|
|||
[Theory] |
|||
[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) |
|||
{ |
|||
var size = new Size(width, height); |
|||
TBuilder builder = this.CreateBuilder(size); |
|||
|
|||
this.AppendRotationDegrees(builder, deg); |
|||
|
|||
// 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 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() |
|||
{ |
|||
var rectangle = new Rectangle(-1, -1, 3, 3); |
|||
TBuilder b1 = this.CreateBuilder(rectangle); |
|||
TBuilder b2 = this.CreateBuilder(rectangle); |
|||
|
|||
const float pi = (float)Math.PI; |
|||
|
|||
// Forwards
|
|||
this.AppendRotationRadians(b1, pi); |
|||
this.AppendScale(b1, new SizeF(2, 0.5f)); |
|||
this.AppendTranslation(b1, new PointF(123, 321)); |
|||
|
|||
// Backwards
|
|||
this.PrependTranslation(b2, new PointF(123, 321)); |
|||
this.PrependScale(b2, new SizeF(2, 0.5f)); |
|||
this.PrependRotationRadians(b2, pi); |
|||
|
|||
Vector2 p1 = this.Execute(b1, rectangle, new Vector2(32, 65)); |
|||
Vector2 p2 = this.Execute(b2, rectangle, new Vector2(32, 65)); |
|||
|
|||
Assert.Equal(p1, p2, Comparer); |
|||
} |
|||
|
|||
protected TBuilder CreateBuilder(Size size) => this.CreateBuilder(new Rectangle(Point.Empty, size)); |
|||
|
|||
protected virtual TBuilder CreateBuilder(Rectangle rectangle) => (TBuilder)Activator.CreateInstance(typeof(TBuilder), 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 PrependTranslation(TBuilder builder, PointF translate); |
|||
protected abstract void PrependScale(TBuilder builder, SizeF scale); |
|||
protected abstract void PrependRotationRadians(TBuilder builder, float radians); |
|||
|
|||
protected virtual void AppendRotationDegrees(TBuilder builder, float degrees) => |
|||
this.AppendRotationRadians(builder, ImageMaths.ToRadian(degrees)); |
|||
|
|||
protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint); |
|||
|
|||
private static float Sqrt(float a) => (float)Math.Sqrt(a); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue