diff --git a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs index 3c75a6418..c254eaeb3 100644 --- a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs +++ b/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 /// /// The exception that is thrown when an error occurs when applying a process to an image. /// - public sealed class ImageProcessingException : Exception + public class ImageProcessingException : Exception { + /// + /// Initializes a new instance of the class. + /// + public ImageProcessingException() + { + } + /// /// Initializes a new instance of the class with the name of the /// parameter that causes this exception. @@ -32,4 +39,4 @@ namespace SixLabors.ImageSharp { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index dde7beb3e..51a110dfc 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/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; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs b/src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs new file mode 100644 index 000000000..970a044dc --- /dev/null +++ b/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 +{ + /// + /// Represents an error that occurs during a transform operation. + /// + public sealed class DegenerateTransformException : ImageProcessingException + { + /// + /// Initializes a new instance of the class. + /// + public DegenerateTransformException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The message that describes the error. + public DegenerateTransformException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is + /// the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. + public DegenerateTransformException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs index 0760d2e3e..329d57f3d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs @@ -12,6 +12,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// internal static class TransformUtilities { + /// + /// Returns a value that indicates whether the specified matrix contains any values + /// that are not a number . + /// + /// The transform matrix. + /// The . + [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); + } + + /// + /// Returns a value that indicates whether the specified matrix contains any values + /// that are not a number . + /// + /// The transform matrix. + /// The . + [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); + } + /// /// Applies the projective transform against the given coordinates flattened into the 2D space. /// diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index ef44dc16d..caebc34a3 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/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; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs index 42017f3af..70b5be73e 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs +++ b/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 { - 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); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs index 309a73fb4..22388a0ac 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs +++ b/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 { - 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); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index 21359799e..8c75cea7f 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/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( () => { - 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( + () => + { + 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);