From 02f97c93b3443d3964ca505aff68d1cbab80fae0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 21 Dec 2017 12:21:30 +1100 Subject: [PATCH] Update default behaviours --- src/ImageSharp/Common/Helpers/ImageMaths.cs | 29 ------------- .../Processors/Transforms/AffineProcessor.cs | 16 +++++-- .../Transforms/CenteredAffineProcessor.cs | 8 ++++ .../Processors/Transforms/RotateProcessor.cs | 4 +- .../Processors/Transforms/SkewProcessor.cs | 2 +- .../Processing/Transforms/Rotate.cs | 2 +- src/ImageSharp/Processing/Transforms/Skew.cs | 2 +- .../Processing/Transforms/TransformHelpers.cs | 43 +++++++++++++++++++ .../Processing/Transforms/TransformTests.cs | 9 +++- 9 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 src/ImageSharp/Processing/Transforms/TransformHelpers.cs diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index a1c83415b..75c9190d2 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -139,35 +139,6 @@ namespace SixLabors.ImageSharp return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); } - /// - /// Gets the bounding from the given matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// - /// The . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) - { - // Calculate the position of the four corners in world space by applying - // The world matrix to the four corners in object space (0, 0, width, height) - var tl = Vector2.Transform(Vector2.Zero, matrix); - var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix); - var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix); - var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix); - - // Find the minimum and maximum "corners" based on the ones above - float minX = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); - float maxX = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); - float minY = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); - float maxY = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); - float sizeX = maxX - minX + .5F; - float sizeY = maxY - minY + .5F; - - return new Rectangle((int)(MathF.Ceiling(minX) - .5F), (int)(MathF.Ceiling(minY) - .5F), (int)MathF.Floor(sizeX), (int)MathF.Floor(sizeY)); - } - /// /// Finds the bounding rectangle based on the first instance of any color component other /// than the given one. diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs index ad8221b88..59b844263 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs @@ -67,9 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { if (this.targetRectangle == Rectangle.Empty) { - this.targetRectangle = Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix) - ? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix) - : sourceRectangle; + this.targetRectangle = this.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix); } // We will always be creating the clone even for mutate because we may need to resize the canvas @@ -148,6 +146,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Vector2 maxXY = point + radius; Vector2 minXY = point - radius; + // max, maxY, minX, minY var extents = new Vector4( MathF.Floor(maxXY.X + .5F), MathF.Floor(maxXY.Y + .5F), @@ -245,6 +244,17 @@ namespace SixLabors.ImageSharp.Processing.Processors return this.TransformMatrix; } + /// + /// Gets the bounding relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// The + protected virtual Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix3x2 matrix) + { + return sourceRectangle; + } + /// /// Calculated the weights for the given point. /// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered. diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineProcessor.cs index 5f03f94e2..5631af3aa 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineProcessor.cs @@ -27,5 +27,13 @@ namespace SixLabors.ImageSharp.Processing.Processors var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F); return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter; } + + /// + protected override Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix3x2 matrix) + { + return Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix) + ? TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix) + : sourceRectangle; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 7af1c68f1..fa47dadaa 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The angle of rotation in degrees. public RotateProcessor(float degrees) - : this(degrees, KnownResamplers.NearestNeighbor) + : this(degrees, KnownResamplers.Bicubic) { } @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { degrees = degrees % 360; - if (degrees < 0) + while (degrees < 0) { degrees += 360; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 07f082838..b123a309b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. public SkewProcessor(float degreesX, float degreesY) - : this(degreesX, degreesY, KnownResamplers.NearestNeighbor) + : this(degreesX, degreesY, KnownResamplers.Bicubic) { } diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index e9ae4fcf3..69fb7ebf0 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) where TPixel : struct, IPixel - => Rotate(source, degrees, KnownResamplers.NearestNeighbor); + => Rotate(source, degrees, KnownResamplers.Bicubic); /// /// Rotates an image by the given angle in degrees using the specified sampling algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index b7a431cce..0613a690b 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) where TPixel : struct, IPixel - => Skew(source, degreesX, degreesY, KnownResamplers.NearestNeighbor); + => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); /// /// Skews an image by the given angles in degrees using the specified sampling algorithm. diff --git a/src/ImageSharp/Processing/Transforms/TransformHelpers.cs b/src/ImageSharp/Processing/Transforms/TransformHelpers.cs new file mode 100644 index 000000000..6f9560a9f --- /dev/null +++ b/src/ImageSharp/Processing/Transforms/TransformHelpers.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Contains helper methods for working with affine transforms + /// + public class TransformHelpers + { + /// + /// Returns the bounding relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// + /// The . + /// + public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) + { + // Calculate the position of the four corners in world space by applying + // The world matrix to the four corners in object space (0, 0, width, height) + var tl = Vector2.Transform(Vector2.Zero, matrix); + var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix); + var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix); + var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix); + + // Find the minimum and maximum "corners" based on the ones above + float minX = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); + float maxX = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); + float minY = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); + float maxY = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); + float sizeX = maxX - minX + .5F; + float sizeY = maxY - minY + .5F; + + return new Rectangle((int)(MathF.Ceiling(minX) - .5F), (int)(MathF.Ceiling(minY) - .5F), (int)MathF.Floor(sizeX), (int)MathF.Floor(sizeY)); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformTests.cs index 59a16eb78..79a1fd445 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformTests.cs @@ -72,7 +72,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms var rotate = Matrix3x2.CreateRotation((float)Math.PI / 4F, new Vector2(5 / 2F, 5 / 2F)); var translate = Matrix3x2.CreateTranslation((7 - 5) / 2F, (7 - 5) / 2F); - image.Mutate(c => c.Transform(rotate * translate, resampler)); + Rectangle sourceRectangle = image.Bounds(); + Matrix3x2 matrix = rotate * translate; + + Rectangle destRectangle = TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix); + + image.Mutate(c => c.Transform(matrix, resampler, destRectangle)); image.DebugSave(provider, resamplerName); VerifyAllPixelsAreWhiteOrTransparent(image); @@ -96,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Matrix3x2 m = rotate * scale * translate; this.PrintMatrix(m); - + image.Mutate(i => i.Transform(m, KnownResamplers.Bicubic)); image.DebugSave(provider, $"R({angleDeg})_S({sx},{sy})_T({tx},{ty})"); }