Browse Source

Make AffineTransformProcessor and AffineTransformBuilder API more flexible

pull/775/head
Anton Firszov 8 years ago
parent
commit
103d129012
  1. 83
      src/ImageSharp/Processing/AffineTransformBuilder.cs
  2. 6
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
  3. 71
      src/ImageSharp/Processing/TransformExtensions.cs
  4. 2
      tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
  5. 28
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
  6. 18
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs
  7. 2
      tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
  8. 18
      tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

83
src/ImageSharp/Processing/AffineTransformBuilder.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 AffineTransformBuilder public class AffineTransformBuilder
{ {
private readonly List<Matrix3x2> matrices = new List<Matrix3x2>(); private readonly List<Func<Size, Matrix3x2>> matrixFactories = new List<Func<Size, Matrix3x2>>();
private readonly Rectangle sourceRectangle;
/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformBuilder"/> class.
/// </summary>
/// <param name="sourceSize">The source image size.</param>
public AffineTransformBuilder(Size sourceSize)
: this(new Rectangle(Point.Empty, sourceSize))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformBuilder"/> class.
/// </summary>
/// <param name="sourceRectangle">The source rectangle.</param>
public AffineTransformBuilder(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 centered rotation matrix using the given rotation in radians. /// Prepends a centered rotation matrix using the given rotation in radians.
@ -48,7 +22,7 @@ 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="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependCenteredRotationRadians(float radians) public AffineTransformBuilder PrependCenteredRotationRadians(float radians)
=> this.PrependMatrix(TransformUtils.CreateRotationMatrixRadians(radians, this.Size)); => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in radians. /// Appends a centered rotation matrix using the given rotation in radians.
@ -56,7 +30,7 @@ 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="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendCenteredRotationRadians(float radians) public AffineTransformBuilder AppendCenteredRotationRadians(float radians)
=> this.AppendMatrix(TransformUtils.CreateRotationMatrixRadians(radians, this.Size)); => this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
/// <summary> /// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees. /// Prepends a centered rotation matrix using the given rotation in degrees.
@ -129,7 +103,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="degreesY">The Y angle, in degrees.</param> /// <param name="degreesY">The Y angle, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY)
=> this.PrependMatrix(TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, this.Size)); => this.Prepend(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size));
/// <summary> /// <summary>
/// Appends a centered skew matrix from the give angles in degrees. /// Appends a centered skew matrix from the give angles in degrees.
@ -138,7 +112,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="degreesY">The Y angle, in degrees.</param> /// <param name="degreesY">The Y angle, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY)
=> this.AppendMatrix(TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, this.Size)); => this.Append(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size));
/// <summary> /// <summary>
/// Prepends a translation matrix from the given vector. /// Prepends a translation matrix from the given vector.
@ -179,7 +153,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
{ {
this.matrices.Insert(0, matrix); this.matrixFactories.Insert(0, _ => matrix);
return this; return this;
} }
@ -190,35 +164,50 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix)
{ {
this.matrices.Add(matrix); this.matrixFactories.Add(_ => matrix);
return this; return this;
} }
/// <summary> /// <summary>
/// Returns the combined matrix. /// Returns the combined matrix for a given source size.
/// </summary>
/// <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> /// </summary>
/// <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() public Matrix3x2 BuildMatrix(Rectangle sourceRectangle)
{ {
Matrix3x2 matrix = Matrix3x2.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 = Matrix3x2.CreateTranslation(-sourceRectangle.Location);
{
matrix *= Matrix3x2.CreateTranslation(-this.sourceRectangle.Location);
}
foreach (Matrix3x2 m in this.matrices) Size size = sourceRectangle.Size;
foreach (Func<Size, Matrix3x2> factory in this.matrixFactories)
{ {
matrix *= m; matrix *= factory(size);
} }
return matrix; return matrix;
} }
/// <summary> private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> factory)
/// Removes all matrices from the builder. {
/// </summary> this.matrixFactories.Insert(0, factory);
public void Clear() => this.matrices.Clear(); return this;
}
private AffineTransformBuilder Append(Func<Size, Matrix3x2> factory)
{
this.matrixFactories.Add(factory);
return this;
}
} }
} }

6
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.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 AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size sourceSize) public AffineTransformProcessor(Matrix3x2 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>

71
src/ImageSharp/Processing/TransformExtensions.cs

@ -1,8 +1,11 @@
// 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.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
{ {
@ -11,6 +14,29 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
public static class TransformExtensions public static class TransformExtensions
{ {
/// <summary>
/// Performs an affine 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,
Matrix3x2 transform,
Size targetDimensions,
IResampler sampler)
where TPixel : struct, IPixel<TPixel>
{
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.
/// </summary> /// </summary>
@ -18,7 +44,9 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image to transform.</param> /// <param name="source">The image to transform.</param>
/// <param name="builder">The affine transform builder.</param> /// <param name="builder">The affine transform builder.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(this IImageProcessingContext<TPixel> source, AffineTransformBuilder builder) public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> source,
AffineTransformBuilder builder)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> Transform(source, builder, KnownResamplers.Bicubic); => Transform(source, builder, KnownResamplers.Bicubic);
@ -26,13 +54,39 @@ namespace SixLabors.ImageSharp.Processing
/// Performs an affine transform of an image using the specified sampling algorithm. /// 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="sourceRectangle">The source rectangle</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> ctx,
Rectangle sourceRectangle,
AffineTransformBuilder builder,
IResampler sampler)
where TPixel : struct, IPixel<TPixel>
{
Matrix3x2 transform = builder.BuildMatrix(sourceRectangle);
Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform);
return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler);
}
/// <summary>
/// Performs an affine 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="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> /// <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>(this IImageProcessingContext<TPixel> source, AffineTransformBuilder builder, IResampler sampler) public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> ctx,
AffineTransformBuilder builder,
IResampler sampler)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new AffineTransformProcessor<TPixel>(builder.BuildMatrix(), sampler, builder.Size)); {
return ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler);
}
/// <summary> /// <summary>
/// Performs a projective transform of an image. /// Performs a projective transform of an image.
@ -41,7 +95,9 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image to transform.</param> /// <param name="source">The image to transform.</param>
/// <param name="builder">The affine transform builder.</param> /// <param name="builder">The affine transform builder.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(this IImageProcessingContext<TPixel> source, ProjectiveTransformBuilder builder) public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> source,
ProjectiveTransformBuilder builder)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> Transform(source, builder, KnownResamplers.Bicubic); => Transform(source, builder, KnownResamplers.Bicubic);
@ -53,7 +109,10 @@ namespace SixLabors.ImageSharp.Processing
/// <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>(this IImageProcessingContext<TPixel> source, ProjectiveTransformBuilder builder, IResampler sampler) public static IImageProcessingContext<TPixel> Transform<TPixel>(
this IImageProcessingContext<TPixel> source,
ProjectiveTransformBuilder builder,
IResampler sampler)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new ProjectiveTransformProcessor<TPixel>(builder.BuildMatrix(), sampler, builder.Size)); => source.ApplyProcessor(new ProjectiveTransformProcessor<TPixel>(builder.BuildMatrix(), sampler, builder.Size));
} }

2
tests/ImageSharp.Tests/Drawing/DrawImageTest.cs

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes)) using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes))
{ {
AffineTransformBuilder builder = new AffineTransformBuilder(blend.Size()) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(45F) .AppendCenteredRotationDegrees(45F)
.AppendScale(new SizeF(.25F, .25F)) .AppendScale(new SizeF(.25F, .25F))
.AppendTranslation(new PointF(10, 10)); .AppendTranslation(new PointF(10, 10));

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

@ -12,30 +12,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder> public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder>
{ {
[Fact]
public void ConstructorAssignsProperties()
{
var s = new Size(1, 1);
var builder = new AffineTransformBuilder(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 AffineTransformBuilder(new Rectangle(Point.Empty, s));
});
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
var s = new Size(1, 0);
var builder = new AffineTransformBuilder(new Rectangle(Point.Empty, s));
});
}
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.AppendCenteredRotationRadians(radians); protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians) => builder.AppendCenteredRotationRadians(radians);
@ -44,12 +20,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
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.PrependCenteredRotationRadians(radians); protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians) => builder.PrependCenteredRotationRadians(radians);
protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder();
protected override Vector2 Execute( protected override Vector2 Execute(
AffineTransformBuilder builder, AffineTransformBuilder builder,
Rectangle rectangle, Rectangle rectangle,
Vector2 sourcePoint) Vector2 sourcePoint)
{ {
Matrix3x2 matrix = builder.BuildMatrix(); Matrix3x2 matrix = builder.BuildMatrix(rectangle);
return Vector2.Transform(sourcePoint, matrix); return Vector2.Transform(sourcePoint, matrix);
} }
} }

18
tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs

@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
IResampler resampler = GetResampler(resamplerName); IResampler resampler = GetResampler(resamplerName);
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder(image.Size()) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(30); .AppendCenteredRotationDegrees(30);
image.Mutate(c => c.Transform(builder, resampler)); image.Mutate(c => c.Transform(builder, resampler));
@ -101,12 +101,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
image.DebugSave(provider, $"_original"); image.DebugSave(provider, $"_original");
AffineTransformBuilder builder = new AffineTransformBuilder(image.Size()) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(angleDeg) .AppendCenteredRotationDegrees(angleDeg)
.AppendScale(new SizeF(sx, sy)) .AppendScale(new SizeF(sx, sy))
.AppendTranslation(new PointF(tx, ty)); .AppendTranslation(new PointF(tx, ty));
this.PrintMatrix(builder.BuildMatrix()); this.PrintMatrix(builder.BuildMatrix(image.Size()));
image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic));
@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder(image.Size()) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(angleDeg) .AppendCenteredRotationDegrees(angleDeg)
.AppendScale(new SizeF(s, s)); .AppendScale(new SizeF(s, s));
@ -159,10 +159,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
image.DebugSave(provider, $"_original"); image.DebugSave(provider, $"_original");
AffineTransformBuilder builder = new AffineTransformBuilder(rectangle) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendScale(new SizeF(2, 1.5F)); .AppendScale(new SizeF(2, 1.5F));
image.Mutate(i => i.Transform(builder, KnownResamplers.Spline)); image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider);
@ -178,10 +178,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder(rectangle) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendScale(new SizeF(1F, 2F)); .AppendScale(new SizeF(1F, 2F));
image.Mutate(i => i.Transform(builder, KnownResamplers.Spline)); image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider);
@ -196,7 +196,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())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder(image.Size()) AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(50) .AppendCenteredRotationDegrees(50)
.AppendScale(new SizeF(.6F, .6F)); .AppendScale(new SizeF(.6F, .6F));

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

@ -35,6 +35,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
}); });
} }
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.AppendCenteredRotationRadians(radians); protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendCenteredRotationRadians(radians);

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

@ -129,9 +129,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(p1, p2, Comparer); Assert.Equal(p1, p2, Comparer);
} }
[Theory]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(-1, 0)]
public void ThrowsForInvalidSizes(int width, int height)
{
var size = new Size(width, height);
Assert.ThrowsAny<ArgumentOutOfRangeException>(
() =>
{
TBuilder builder = this.CreateBuilder(size);
this.Execute(builder, new Rectangle(Point.Empty, size), Vector2.Zero);
});
}
protected TBuilder CreateBuilder(Size size) => this.CreateBuilder(new Rectangle(Point.Empty, size)); 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 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);

Loading…
Cancel
Save