Browse Source

Capture and throw degenerate matrices.

pull/1181/head
James Jackson-South 6 years ago
parent
commit
cbab62dac3
  1. 13
      src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
  2. 9
      src/ImageSharp/Processing/AffineTransformBuilder.cs
  3. 42
      src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs
  4. 29
      src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
  5. 9
      src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
  6. 7
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
  7. 23
      tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
  8. 34
      tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

13
src/ImageSharp/Common/Exceptions/ImageProcessingException.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -8,8 +8,15 @@ namespace SixLabors.ImageSharp
/// <summary>
/// The exception that is thrown when an error occurs when applying a process to an image.
/// </summary>
public sealed class ImageProcessingException : Exception
public class ImageProcessingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.
/// </summary>
public ImageProcessingException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class with the name of the
/// parameter that causes this exception.
@ -32,4 +39,4 @@ namespace SixLabors.ImageSharp
{
}
}
}
}

9
src/ImageSharp/Processing/AffineTransformBuilder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -284,6 +284,11 @@ namespace SixLabors.ImageSharp.Processing
matrix *= factory(size);
}
if (TransformUtilities.IsNaN(matrix))
{
throw new DegenerateTransformException("Matrix is NaN. Check input values.");
}
return matrix;
}
@ -299,4 +304,4 @@ namespace SixLabors.ImageSharp.Processing
return this;
}
}
}
}

42
src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs

@ -0,0 +1,42 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Represents an error that occurs during a transform operation.
/// </summary>
public sealed class DegenerateTransformException : ImageProcessingException
{
/// <summary>
/// Initializes a new instance of the <see cref="DegenerateTransformException"/> class.
/// </summary>
public DegenerateTransformException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DegenerateTransformException" /> class
/// with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public DegenerateTransformException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DegenerateTransformException" /> class
/// with a specified error message and a reference to the inner exception that is
/// the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public DegenerateTransformException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

29
src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs

@ -12,6 +12,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary>
internal static class TransformUtilities
{
/// <summary>
/// Returns a value that indicates whether the specified matrix contains any values
/// that are not a number <see cref="float.NaN"/>.
/// </summary>
/// <param name="matrix">The transform matrix.</param>
/// <returns>The <see cref="bool"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsNaN(Matrix3x2 matrix)
{
return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12)
|| float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22)
|| float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32);
}
/// <summary>
/// Returns a value that indicates whether the specified matrix contains any values
/// that are not a number <see cref="float.NaN"/>.
/// </summary>
/// <param name="matrix">The transform matrix.</param>
/// <returns>The <see cref="bool"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsNaN(Matrix4x4 matrix)
{
return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14)
|| float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24)
|| float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34)
|| float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44);
}
/// <summary>
/// Applies the projective transform against the given coordinates flattened into the 2D space.
/// </summary>

9
src/ImageSharp/Processing/ProjectiveTransformBuilder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -300,6 +300,11 @@ namespace SixLabors.ImageSharp.Processing
matrix *= factory(size);
}
if (TransformUtilities.IsNaN(matrix))
{
throw new DegenerateTransformException("Matrix is NaN. Check input values.");
}
return matrix;
}
@ -315,4 +320,4 @@ namespace SixLabors.ImageSharp.Processing
return this;
}
}
}
}

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
@ -8,7 +8,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder>
{
protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder();
protected override AffineTransformBuilder CreateBuilder()
=> new AffineTransformBuilder();
protected override void AppendRotationDegrees(AffineTransformBuilder builder, float degrees)
=> builder.AppendRotationDegrees(degrees);
@ -67,4 +68,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
return Vector2.Transform(sourcePoint, matrix);
}
}
}
}

23
tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.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.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Processing;
@ -8,20 +9,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
public class ProjectiveTransformBuilderTests : TransformBuilderTestBase<ProjectiveTransformBuilder>
{
protected override ProjectiveTransformBuilder CreateBuilder(Rectangle rectangle) => new ProjectiveTransformBuilder();
protected override ProjectiveTransformBuilder CreateBuilder()
=> new ProjectiveTransformBuilder();
protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees) => builder.AppendRotationDegrees(degrees);
protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees)
=> builder.AppendRotationDegrees(degrees);
protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees, Vector2 origin) => builder.AppendRotationDegrees(degrees, origin);
protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees, Vector2 origin)
=> builder.AppendRotationDegrees(degrees, origin);
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 origin) => builder.AppendRotationRadians(radians, origin);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin)
=> builder.AppendRotationRadians(radians, origin);
protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale);
protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY)
=> builder.AppendSkewDegrees(degreesX, degreesY);
=> builder.AppendSkewDegrees(degreesX, degreesY);
protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY, Vector2 origin)
=> builder.AppendSkewDegrees(degreesX, degreesY, origin);
@ -44,7 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
protected override void PrependSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY, Vector2 origin)
=> builder.PrependSkewRadians(radiansX, radiansY, origin);
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 PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) =>
builder.PrependRotationRadians(radians, origin);

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

@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
// These operations should be size-agnostic:
var size = new Size(123, 321);
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
this.AppendScale(builder, new SizeF(scale));
this.AppendTranslation(builder, translate);
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
// Translate ans scale are size-agnostic:
var size = new Size(456, 432);
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
this.AppendTranslation(builder, translate);
this.AppendScale(builder, new SizeF(scale));
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
public void LocationOffsetIsPrepended(int locationX, int locationY)
{
var rectangle = new Rectangle(locationX, locationY, 10, 10);
TBuilder builder = this.CreateBuilder(rectangle);
TBuilder builder = this.CreateBuilder();
this.AppendScale(builder, new SizeF(2, 2));
@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y)
{
var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
this.AppendRotationDegrees(builder, degrees);
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y)
{
var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
var centerPoint = new Vector2(cx, cy);
this.AppendRotationDegrees(builder, degrees, centerPoint);
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y)
{
var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
this.AppendSkewDegrees(builder, degreesX, degreesY);
@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y)
{
var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
var centerPoint = new Vector2(cx, cy);
this.AppendSkewDegrees(builder, degreesX, degreesY, centerPoint);
@ -194,8 +194,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
public void AppendPrependOpposite()
{
var rectangle = new Rectangle(-1, -1, 3, 3);
TBuilder b1 = this.CreateBuilder(rectangle);
TBuilder b2 = this.CreateBuilder(rectangle);
TBuilder b1 = this.CreateBuilder();
TBuilder b2 = this.CreateBuilder();
const float pi = (float)Math.PI;
@ -232,14 +232,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.ThrowsAny<ArgumentOutOfRangeException>(
() =>
{
TBuilder builder = this.CreateBuilder(size);
TBuilder builder = this.CreateBuilder();
this.Execute(builder, new Rectangle(Point.Empty, size), Vector2.Zero);
});
}
protected TBuilder CreateBuilder(Size size) => this.CreateBuilder(new Rectangle(Point.Empty, size));
[Fact]
public void ThrowsForInvalidMatrix()
{
Assert.ThrowsAny<DegenerateTransformException>(
() =>
{
TBuilder builder = this.CreateBuilder();
this.AppendSkewDegrees(builder, 45, 45);
this.Execute(builder, new Rectangle(0, 0, 150, 150), Vector2.Zero);
});
}
protected abstract TBuilder CreateBuilder(Rectangle rectangle);
protected abstract TBuilder CreateBuilder();
protected abstract void AppendRotationDegrees(TBuilder builder, float degrees);

Loading…
Cancel
Save