diff --git a/src/ImageProcessor.UnitTests/Imaging/RotationUnitTests.cs b/src/ImageProcessor.UnitTests/Imaging/Helpers/ImageMathsUnitTests.cs similarity index 78% rename from src/ImageProcessor.UnitTests/Imaging/RotationUnitTests.cs rename to src/ImageProcessor.UnitTests/Imaging/Helpers/ImageMathsUnitTests.cs index 0e33eb5dc..9b42e7f85 100644 --- a/src/ImageProcessor.UnitTests/Imaging/RotationUnitTests.cs +++ b/src/ImageProcessor.UnitTests/Imaging/Helpers/ImageMathsUnitTests.cs @@ -1,11 +1,11 @@ -namespace ImageProcessor.UnitTests.Imaging +namespace ImageProcessor.UnitTests.Imaging.Helpers { using System.Drawing; using FluentAssertions; - using ImageProcessor.Imaging; + using ImageProcessor.Imaging.Helpers; using NUnit.Framework; - public class RotationUnitTests + public class ImageMathsUnitTests { [Test] [TestCase(100, 100, 45, 141, 141)] @@ -13,7 +13,7 @@ [TestCase(100, 200, 50, 217, 205)] public void NewSizeAfterRotationIsCalculated(int width, int height, float angle, int expectedWidth, int expectedHeight) { - Size result = Rotation.NewSizeAfterRotation(width, height, angle); + Size result = ImageMaths.GetBoundingRotatedRectangle(width, height, angle); result.Width.Should().Be(expectedWidth, "because the rotated width should have been calculated"); result.Height.Should().Be(expectedHeight, "because the rotated height should have been calculated"); @@ -28,7 +28,7 @@ [TestCase(600, 450, 45, 1.64f)] public void RotationZoomIsCalculated(int imageWidth, int imageHeight, float angle, float expected) { - float result = Rotation.ZoomAfterRotation(imageWidth, imageHeight, angle); + float result = ImageMaths.ZoomAfterRotation(imageWidth, imageHeight, angle); result.Should().BeApproximately(expected, 0.01f, "because the zoom level after rotation should have been calculated"); } diff --git a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs index af0593976..60b35989f 100644 --- a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs +++ b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs @@ -12,7 +12,6 @@ namespace ImageProcessor.Imaging.Helpers { using System; using System.Drawing; - using ImageProcessor.Imaging.Colors; /// @@ -20,6 +19,27 @@ namespace ImageProcessor.Imaging.Helpers /// public static class ImageMaths { + /// + /// Gets a representing the child centered relative to the parent. + /// + /// + /// The parent . + /// + /// + /// The child . + /// + /// + /// The centered . + /// + public static RectangleF CenteredRectangle(Rectangle parent, Rectangle child) + { + float x = (parent.Width - child.Width) / 2.0F; + float y = (parent.Height - child.Height) / 2.0F; + int width = child.Width; + int height = child.Height; + return new RectangleF(x, y, width, height); + } + /// /// Restricts a value to be within a specified range. /// @@ -53,6 +73,20 @@ namespace ImageProcessor.Imaging.Helpers return value; } + /// + /// Returns the given degrees converted to radians. + /// + /// + /// The angle in degrees. + /// + /// + /// The representing the degree as radians. + /// + public static double DegreesToRadians(double angleInDegrees) + { + return angleInDegrees * (Math.PI / 180); + } + /// /// Gets the bounding from the given points. /// @@ -70,6 +104,38 @@ namespace ImageProcessor.Imaging.Helpers return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); } + /// + /// Calculates the new size after rotation. + /// + /// The width of the image. + /// The height of the image. + /// The angle of rotation. + /// The new size of the image + public static Size GetBoundingRotatedRectangle(int width, int height, float angle) + { + double widthAsDouble = width; + double heightAsDouble = height; + + double radians = DegreesToRadians(angle); + double radiansSin = Math.Sin(radians); + double radiansCos = Math.Cos(radians); + double width1 = (heightAsDouble * radiansSin) + (widthAsDouble * radiansCos); + double height1 = (widthAsDouble * radiansSin) + (heightAsDouble * radiansCos); + + // Find dimensions in the other direction + radiansSin = Math.Sin(-radians); + radiansCos = Math.Cos(-radians); + double width2 = (heightAsDouble * radiansSin) + (widthAsDouble * radiansCos); + double height2 = (widthAsDouble * radiansSin) + (heightAsDouble * radiansCos); + + // Get the external vertex for the rotation + Size result = new Size(); + result.Width = Convert.ToInt32(Math.Max(Math.Abs(width1), Math.Abs(width2))); + result.Height = Convert.ToInt32(Math.Max(Math.Abs(height1), Math.Abs(height2))); + + return result; + } + /// /// Finds the bounding rectangle based on the first instance of any color component other /// than the given one. @@ -101,12 +167,15 @@ namespace ImageProcessor.Imaging.Helpers case RgbaComponent.R: delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).R != b; break; + case RgbaComponent.G: delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).G != b; break; + case RgbaComponent.A: delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).A != b; break; + default: delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).B != b; break; @@ -188,24 +257,31 @@ namespace ImageProcessor.Imaging.Helpers } /// - /// Gets a representing the child centered relative to the parent. + /// Rotates one point around another + /// /// - /// - /// The parent . - /// - /// - /// The child . + /// The point to rotate. + /// The rotation angle in degrees. + /// The centre point of rotation. If not set the point will equal + /// /// - /// - /// The centered . - /// - public static RectangleF CenteredRectangle(Rectangle parent, Rectangle child) + /// Rotated point + public static Point RotatePoint(Point pointToRotate, double angleInDegrees, Point? centerPoint = null) { - float x = (parent.Width - child.Width) / 2.0F; - float y = (parent.Height - child.Height) / 2.0F; - int width = child.Width; - int height = child.Height; - return new RectangleF(x, y, width, height); + Point center = centerPoint ?? Point.Empty; + + double angleInRadians = DegreesToRadians(angleInDegrees); + double cosTheta = Math.Cos(angleInRadians); + double sinTheta = Math.Sin(angleInRadians); + return new Point + { + X = + (int)((cosTheta * (pointToRotate.X - center.X)) - + ((sinTheta * (pointToRotate.Y - center.Y)) + center.X)), + Y = + (int)((sinTheta * (pointToRotate.X - center.X)) + + ((cosTheta * (pointToRotate.Y - center.Y)) + center.Y)) + }; } /// @@ -221,55 +297,33 @@ namespace ImageProcessor.Imaging.Helpers { return new[] { - new Point(rectangle.Left, rectangle.Top), - new Point(rectangle.Right, rectangle.Top), - new Point(rectangle.Right, rectangle.Bottom), + new Point(rectangle.Left, rectangle.Top), + new Point(rectangle.Right, rectangle.Top), + new Point(rectangle.Right, rectangle.Bottom), new Point(rectangle.Left, rectangle.Bottom) }; } /// - /// Returns the given degrees converted to radians. + /// Calculates the zoom needed after the rotation. /// - /// - /// The angle in degrees. - /// - /// - /// The representing the degree as radians. - /// - public static double DegreesToRadians(double angleInDegrees) + /// Width of the image. + /// Height of the image. + /// The angle. + /// + /// Based on + /// + /// The zoom needed + public static float ZoomAfterRotation(int imageWidth, int imageHeight, float angle) { - return angleInDegrees * (Math.PI / 180); - } + double radians = angle * Math.PI / 180d; + double radiansSin = Math.Sin(radians); + double radiansCos = Math.Cos(radians); - /// - /// Rotates one point around another - /// - /// - /// The point to rotate. - /// The rotation angle in degrees. - /// The centre point of rotation. If not set the point will equal - /// - /// - /// Rotated point - public static Point RotatePoint(Point pointToRotate, double angleInDegrees, Point? centerPoint = null) - { - Point center = centerPoint ?? Point.Empty; + double widthRotated = (imageWidth * radiansCos) + (imageHeight * radiansSin); + double heightRotated = (imageWidth * radiansSin) + (imageHeight * radiansCos); - double angleInRadians = DegreesToRadians(angleInDegrees); - double cosTheta = Math.Cos(angleInRadians); - double sinTheta = Math.Sin(angleInRadians); - return new Point - { - X = - (int) - ((cosTheta * (pointToRotate.X - center.X)) - - ((sinTheta * (pointToRotate.Y - center.Y)) + center.X)), - Y = - (int) - ((sinTheta * (pointToRotate.X - center.X)) + - ((cosTheta * (pointToRotate.Y - center.Y)) + center.Y)) - }; + return (float)Math.Max(widthRotated / imageWidth, heightRotated / imageHeight); } } -} +} \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/Rotation.cs b/src/ImageProcessor/Imaging/Rotation.cs deleted file mode 100644 index 0b841c297..000000000 --- a/src/ImageProcessor/Imaging/Rotation.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace ImageProcessor.Imaging -{ - using System; - using System.Drawing; - - /// - /// Provides rotation calculation methods - /// - internal class Rotation - { - /// - /// Calculates the new size after rotation. - /// - /// The width of the image. - /// The height of the image. - /// The angle of rotation. - /// The new size of the image - public static Size NewSizeAfterRotation(int width, int height, float angle) - { - double widthAsDouble = width; - double heightAsDouble = height; - - double radians = angle * Math.PI / 180d; - double radiansSin = Math.Sin(radians); - double radiansCos = Math.Cos(radians); - double width1 = (heightAsDouble * radiansSin) + (widthAsDouble * radiansCos); - double height1 = (widthAsDouble * radiansSin) + (heightAsDouble * radiansCos); - - // Find dimensions in the other direction - radiansSin = Math.Sin(-radians); - radiansCos = Math.Cos(-radians); - double width2 = (heightAsDouble * radiansSin) + (widthAsDouble * radiansCos); - double height2 = (widthAsDouble * radiansSin) + (heightAsDouble * radiansCos); - - // Get the external vertex for the rotation - Size result = new Size(); - result.Width = Convert.ToInt32(Math.Max(Math.Abs(width1), Math.Abs(width2))); - result.Height = Convert.ToInt32(Math.Max(Math.Abs(height1), Math.Abs(height2))); - - return result; - } - - /// - /// Calculates the zoom needed after the rotation. - /// - /// Width of the image. - /// Height of the image. - /// The angle. - /// - /// Based on - /// - /// The zoom needed - public static float ZoomAfterRotation(int imageWidth, int imageHeight, float angle) - { - double radians = angle * Math.PI / 180d; - double radiansSin = Math.Sin(radians); - double radiansCos = Math.Cos(radians); - - double widthRotated = (imageWidth * radiansCos) + (imageHeight * radiansSin); - double heightRotated = (imageWidth * radiansSin) + (imageHeight * radiansCos); - - return (float)Math.Max(widthRotated / imageWidth, heightRotated / imageHeight); - } - } -} \ No newline at end of file diff --git a/src/ImageProcessor/Processors/Rotate.cs b/src/ImageProcessor/Processors/Rotate.cs index d0715884f..cb8cc1733 100644 --- a/src/ImageProcessor/Processors/Rotate.cs +++ b/src/ImageProcessor/Processors/Rotate.cs @@ -104,7 +104,7 @@ namespace ImageProcessor.Processors /// private Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle) { - Size newSize = Imaging.Rotation.NewSizeAfterRotation(image.Width, image.Height, angle); + Size newSize = Imaging.Helpers.ImageMaths.GetBoundingRotatedRectangle(image.Width, image.Height, angle); int x = (newSize.Width - image.Width) / 2; int y = (newSize.Height - image.Height) / 2; diff --git a/src/ImageProcessor/Processors/RotateInside.cs b/src/ImageProcessor/Processors/RotateInside.cs index b715aa416..8d4436cda 100644 --- a/src/ImageProcessor/Processors/RotateInside.cs +++ b/src/ImageProcessor/Processors/RotateInside.cs @@ -84,7 +84,7 @@ namespace ImageProcessor.Processors { Size newSize = new Size(image.Width, image.Height); - float zoom = Imaging.Rotation.ZoomAfterRotation(image.Width, image.Height, rotateLayer.Angle); + float zoom = Imaging.Helpers.ImageMaths.ZoomAfterRotation(image.Width, image.Height, rotateLayer.Angle); // if we don't keep the image dimensions, calculate the new ones if (!rotateLayer.KeepImageDimensions)