Browse Source

Moves the new methods to Imaging.Helpers.ImageMaths and auto-cleanup (reordering of methods) of the ImageMaths class

Former-commit-id: 95c1fd0f4d0cb88994167ca2679aa4e3e07961e3
Former-commit-id: c0e799a4576f17abc343e2bcc4a7465fe0cc3cdb
pull/17/head
Thomas Broust 11 years ago
parent
commit
013638c554
  1. 10
      src/ImageProcessor.UnitTests/Imaging/Helpers/ImageMathsUnitTests.cs
  2. 168
      src/ImageProcessor/Imaging/Helpers/ImageMaths.cs
  3. 65
      src/ImageProcessor/Imaging/Rotation.cs
  4. 2
      src/ImageProcessor/Processors/Rotate.cs
  5. 2
      src/ImageProcessor/Processors/RotateInside.cs

10
src/ImageProcessor.UnitTests/Imaging/RotationUnitTests.cs → 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");
}

168
src/ImageProcessor/Imaging/Helpers/ImageMaths.cs

@ -12,7 +12,6 @@ namespace ImageProcessor.Imaging.Helpers
{
using System;
using System.Drawing;
using ImageProcessor.Imaging.Colors;
/// <summary>
@ -20,6 +19,27 @@ namespace ImageProcessor.Imaging.Helpers
/// </summary>
public static class ImageMaths
{
/// <summary>
/// Gets a <see cref="Rectangle"/> representing the child centered relative to the parent.
/// </summary>
/// <param name="parent">
/// The parent <see cref="Rectangle"/>.
/// </param>
/// <param name="child">
/// The child <see cref="Rectangle"/>.
/// </param>
/// <returns>
/// The centered <see cref="Rectangle"/>.
/// </returns>
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);
}
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
@ -53,6 +73,20 @@ namespace ImageProcessor.Imaging.Helpers
return value;
}
/// <summary>
/// Returns the given degrees converted to radians.
/// </summary>
/// <param name="angleInDegrees">
/// The angle in degrees.
/// </param>
/// <returns>
/// The <see cref="double"/> representing the degree as radians.
/// </returns>
public static double DegreesToRadians(double angleInDegrees)
{
return angleInDegrees * (Math.PI / 180);
}
/// <summary>
/// Gets the bounding <see cref="Rectangle"/> from the given points.
/// </summary>
@ -70,6 +104,38 @@ namespace ImageProcessor.Imaging.Helpers
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
/// <summary>
/// Calculates the new size after rotation.
/// </summary>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="angle">The angle of rotation.</param>
/// <returns>The new size of the image</returns>
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;
}
/// <summary>
/// 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
}
/// <summary>
/// Gets a <see cref="Rectangle"/> representing the child centered relative to the parent.
/// Rotates one point around another
/// <see href="http://stackoverflow.com/questions/13695317/rotate-a-point-around-another-point"/>
/// </summary>
/// <param name="parent">
/// The parent <see cref="Rectangle"/>.
/// </param>
/// <param name="child">
/// The child <see cref="Rectangle"/>.
/// <param name="pointToRotate">The point to rotate.</param>
/// <param name="angleInDegrees">The rotation angle in degrees.</param>
/// <param name="centerPoint">The centre point of rotation. If not set the point will equal
/// <see cref="Point.Empty"/>
/// </param>
/// <returns>
/// The centered <see cref="Rectangle"/>.
/// </returns>
public static RectangleF CenteredRectangle(Rectangle parent, Rectangle child)
/// <returns>Rotated point</returns>
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))
};
}
/// <summary>
@ -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)
};
}
/// <summary>
/// Returns the given degrees converted to radians.
/// Calculates the zoom needed after the rotation.
/// </summary>
/// <param name="angleInDegrees">
/// The angle in degrees.
/// </param>
/// <returns>
/// The <see cref="double"/> representing the degree as radians.
/// </returns>
public static double DegreesToRadians(double angleInDegrees)
/// <param name="imageWidth">Width of the image.</param>
/// <param name="imageHeight">Height of the image.</param>
/// <param name="angle">The angle.</param>
/// <remarks>
/// Based on <see href="http://math.stackexchange.com/questions/1070853/"/>
/// </remarks>
/// <returns>The zoom needed</returns>
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);
/// <summary>
/// Rotates one point around another
/// <see href="http://stackoverflow.com/questions/13695317/rotate-a-point-around-another-point"/>
/// </summary>
/// <param name="pointToRotate">The point to rotate.</param>
/// <param name="angleInDegrees">The rotation angle in degrees.</param>
/// <param name="centerPoint">The centre point of rotation. If not set the point will equal
/// <see cref="Point.Empty"/>
/// </param>
/// <returns>Rotated point</returns>
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);
}
}
}
}

65
src/ImageProcessor/Imaging/Rotation.cs

@ -1,65 +0,0 @@
namespace ImageProcessor.Imaging
{
using System;
using System.Drawing;
/// <summary>
/// Provides rotation calculation methods
/// </summary>
internal class Rotation
{
/// <summary>
/// Calculates the new size after rotation.
/// </summary>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="angle">The angle of rotation.</param>
/// <returns>The new size of the image</returns>
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;
}
/// <summary>
/// Calculates the zoom needed after the rotation.
/// </summary>
/// <param name="imageWidth">Width of the image.</param>
/// <param name="imageHeight">Height of the image.</param>
/// <param name="angle">The angle.</param>
/// <remarks>
/// Based on <see href="http://math.stackexchange.com/questions/1070853/"/>
/// </remarks>
/// <returns>The zoom needed</returns>
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);
}
}
}

2
src/ImageProcessor/Processors/Rotate.cs

@ -104,7 +104,7 @@ namespace ImageProcessor.Processors
/// </remarks>
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;

2
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)

Loading…
Cancel
Save