From 756f5b6a93df3374d2c4717d887c06d096fe9d11 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 7 Jan 2016 15:36:51 +1100 Subject: [PATCH] Clean up primitives Former-commit-id: 5cf1ec2ba4cf646e94524fb425951760724b3eae Former-commit-id: cc0f2177a28de32d6df34b0d23bfcba3ec898c0c Former-commit-id: 8f2227320a0891bc824fb7256b97d536f381a76a --- src/ImageProcessor/Colors/Color.cs | 8 +- .../Colors/Colorspaces/Bgra32.cs | 2 +- .../Colors/Colorspaces/CieLab.cs | 2 +- .../Colors/Colorspaces/CieXyz.cs | 2 +- src/ImageProcessor/Colors/Colorspaces/Cmyk.cs | 2 +- src/ImageProcessor/Colors/Colorspaces/Hsl.cs | 2 +- src/ImageProcessor/Colors/Colorspaces/Hsv.cs | 2 +- .../Colors/Colorspaces/YCbCr.cs | 2 +- .../Common/Helpers/ImageMaths.cs | 27 --- src/ImageProcessor/Filters/Glow.cs | 2 +- src/ImageProcessor/Filters/Vignette.cs | 2 +- src/ImageProcessor/Numerics/Ellipse.cs | 60 ++--- src/ImageProcessor/Numerics/Point.cs | 155 ++++++++----- src/ImageProcessor/Numerics/Rectangle.cs | 188 +++++++++------ .../Numerics/RotatedRectangle.cs | 219 ------------------ src/ImageProcessor/Numerics/Size.cs | 150 +++++++----- src/ImageProcessor/Samplers/Resampler.cs | 25 +- .../Processors/Samplers/SamplerTests.cs | 22 +- 18 files changed, 367 insertions(+), 505 deletions(-) delete mode 100644 src/ImageProcessor/Numerics/RotatedRectangle.cs diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs index e140d91221..f7c60860c2 100644 --- a/src/ImageProcessor/Colors/Color.cs +++ b/src/ImageProcessor/Colors/Color.cs @@ -109,9 +109,7 @@ namespace ImageProcessor /// /// Initializes a new instance of the struct. /// - /// - /// The vector. - /// + /// The vector. public Color(Vector4 vector) { this.backingVector = vector; @@ -208,7 +206,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector4)); + public bool IsEmpty => this.Equals(Empty); /// /// Gets this color with the component values clamped from 0 to 1. @@ -308,7 +306,7 @@ namespace ImageProcessor } /// - /// Compares two objects for inequality. + /// Compares two objects for inequality. /// /// /// The on the left side of the operand. diff --git a/src/ImageProcessor/Colors/Colorspaces/Bgra32.cs b/src/ImageProcessor/Colors/Colorspaces/Bgra32.cs index f76753a791..3fff003ecc 100644 --- a/src/ImageProcessor/Colors/Colorspaces/Bgra32.cs +++ b/src/ImageProcessor/Colors/Colorspaces/Bgra32.cs @@ -80,7 +80,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector4)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Colors/Colorspaces/CieLab.cs b/src/ImageProcessor/Colors/Colorspaces/CieLab.cs index f278783332..596609e09c 100644 --- a/src/ImageProcessor/Colors/Colorspaces/CieLab.cs +++ b/src/ImageProcessor/Colors/Colorspaces/CieLab.cs @@ -66,7 +66,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector3)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs b/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs index 9a32723676..87969402f9 100644 --- a/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs +++ b/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs @@ -67,7 +67,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector3)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Colors/Colorspaces/Cmyk.cs b/src/ImageProcessor/Colors/Colorspaces/Cmyk.cs index ffe1936716..cdaab89896 100644 --- a/src/ImageProcessor/Colors/Colorspaces/Cmyk.cs +++ b/src/ImageProcessor/Colors/Colorspaces/Cmyk.cs @@ -73,7 +73,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector4)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Colors/Colorspaces/Hsl.cs b/src/ImageProcessor/Colors/Colorspaces/Hsl.cs index b10e915258..68990141e9 100644 --- a/src/ImageProcessor/Colors/Colorspaces/Hsl.cs +++ b/src/ImageProcessor/Colors/Colorspaces/Hsl.cs @@ -64,7 +64,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector3)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Colors/Colorspaces/Hsv.cs b/src/ImageProcessor/Colors/Colorspaces/Hsv.cs index a6736509a3..8db4c82c31 100644 --- a/src/ImageProcessor/Colors/Colorspaces/Hsv.cs +++ b/src/ImageProcessor/Colors/Colorspaces/Hsv.cs @@ -64,7 +64,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector3)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Colors/Colorspaces/YCbCr.cs b/src/ImageProcessor/Colors/Colorspaces/YCbCr.cs index 9aeccc79ff..120f805c75 100644 --- a/src/ImageProcessor/Colors/Colorspaces/YCbCr.cs +++ b/src/ImageProcessor/Colors/Colorspaces/YCbCr.cs @@ -67,7 +67,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.backingVector.Equals(default(Vector3)); + public bool IsEmpty => this.Equals(Empty); /// /// Allows the implicit conversion of an instance of to a diff --git a/src/ImageProcessor/Common/Helpers/ImageMaths.cs b/src/ImageProcessor/Common/Helpers/ImageMaths.cs index d3e85a7031..d78f6723e6 100644 --- a/src/ImageProcessor/Common/Helpers/ImageMaths.cs +++ b/src/ImageProcessor/Common/Helpers/ImageMaths.cs @@ -110,33 +110,6 @@ namespace ImageProcessor return angleInDegrees * (PI / 180); } - /// - /// Rotates one point around another - /// - /// - /// The point to rotate. - /// The origin point of rotation. - /// The rotation angle in degrees. - /// - public static Vector2 RotatePoint(Vector2 point, Vector2 origin, float degrees) - { - double radians = DegreesToRadians(degrees); - double cosTheta = Math.Cos(radians); - double sinTheta = Math.Sin(radians); - - Vector2 translatedPoint = new Vector2 - { - X = (float)(origin.X - + (point.X - origin.X) * cosTheta - - (point.Y - origin.Y) * sinTheta), - Y = (float)(origin.Y - + (point.Y - origin.Y) * cosTheta - + (point.X - origin.X) * sinTheta) - }; - - return translatedPoint; - } - /// /// Gets the bounding from the given points. /// diff --git a/src/ImageProcessor/Filters/Glow.cs b/src/ImageProcessor/Filters/Glow.cs index 29bba7cdbe..afc8b392ac 100644 --- a/src/ImageProcessor/Filters/Glow.cs +++ b/src/ImageProcessor/Filters/Glow.cs @@ -35,7 +35,7 @@ namespace ImageProcessor.Filters int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Color color = this.Color; - Vector2 centre = Rectangle.Center(targetRectangle); + Vector2 centre = Rectangle.Center(targetRectangle).ToVector2(); float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f; float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f; float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY); diff --git a/src/ImageProcessor/Filters/Vignette.cs b/src/ImageProcessor/Filters/Vignette.cs index a309c4648a..c4cf026859 100644 --- a/src/ImageProcessor/Filters/Vignette.cs +++ b/src/ImageProcessor/Filters/Vignette.cs @@ -35,7 +35,7 @@ namespace ImageProcessor.Filters int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Color color = this.Color; - Vector2 centre = Rectangle.Center(targetRectangle); + Vector2 centre = Rectangle.Center(targetRectangle).ToVector2(); float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f; float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f; float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY); diff --git a/src/ImageProcessor/Numerics/Ellipse.cs b/src/ImageProcessor/Numerics/Ellipse.cs index 1f581e8d1f..c4f9d130de 100644 --- a/src/ImageProcessor/Numerics/Ellipse.cs +++ b/src/ImageProcessor/Numerics/Ellipse.cs @@ -16,11 +16,6 @@ namespace ImageProcessor /// private Point center; - /// - /// The epsilon for comparing floating point numbers. - /// - private const float Epsilon = 0.0001f; - /// /// Represents a that has X and Y values set to zero. /// @@ -47,9 +42,7 @@ namespace ImageProcessor /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.center.IsEmpty - && Math.Abs(this.RadiusX) < Epsilon - && Math.Abs(this.RadiusY) < Epsilon; + public bool IsEmpty => this.Equals(Empty); /// /// Compares two objects for equality. @@ -109,34 +102,16 @@ namespace ImageProcessor return false; } - //This is a more general form of the circle equation + // TODO: SIMD? + // This is a more general form of the circle equation // X^2/a^2 + Y^2/b^2 <= 1 Point normalized = new Point(x - this.center.X, y - this.center.Y); int nX = normalized.X; int nY = normalized.Y; - //return (double)(nX * nX) / (this.RadiusX * this.RadiusX) - // + (double)(nY * nY) / (this.RadiusY * this.RadiusY) - // <= 1.0; - - return ((double)(nX * nX) / (this.RadiusX * this.RadiusX)) - + ((double)(nY * nY) / (this.RadiusY * this.RadiusY)) - <= 1.0; - } - - /// - public override bool Equals(object obj) - { - if (!(obj is Ellipse)) - { - return false; - } - - Ellipse other = (Ellipse)obj; - - return other.center == this.center - && Math.Abs(other.RadiusX - this.RadiusX) < Epsilon - && Math.Abs(other.RadiusY - this.RadiusY) < Epsilon; + return (double)(nX * nX) / (this.RadiusX * this.RadiusX) + + (double)(nY * nY) / (this.RadiusY * this.RadiusY) + <= 1.0; } /// @@ -157,30 +132,41 @@ namespace ImageProcessor $"Ellipse [ RadiusX={this.RadiusX}, RadiusY={this.RadiusX}, Centre={this.center.X},{this.center.Y} ]"; } + /// + public override bool Equals(object obj) + { + if (obj is Ellipse) + { + return this.Equals((Ellipse)obj); + } + + return false; + } + /// public bool Equals(Ellipse other) { return this.center.Equals(other.center) && this.RadiusX.Equals(other.RadiusX) - && this.RadiusX.Equals(other.RadiusY); + && this.RadiusY.Equals(other.RadiusY); } /// /// Returns the hash code for this instance. /// - /// + /// /// The instance of to return the hash code for. /// /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(Ellipse point) + private int GetHashCode(Ellipse ellipse) { unchecked { - int hashCode = point.center.GetHashCode(); - hashCode = (hashCode * 397) ^ point.RadiusX.GetHashCode(); - hashCode = (hashCode * 397) ^ point.RadiusY.GetHashCode(); + int hashCode = ellipse.center.GetHashCode(); + hashCode = (hashCode * 397) ^ ellipse.RadiusX.GetHashCode(); + hashCode = (hashCode * 397) ^ ellipse.RadiusY.GetHashCode(); return hashCode; } } diff --git a/src/ImageProcessor/Numerics/Point.cs b/src/ImageProcessor/Numerics/Point.cs index 9ada9530b5..f0b030364f 100644 --- a/src/ImageProcessor/Numerics/Point.cs +++ b/src/ImageProcessor/Numerics/Point.cs @@ -7,6 +7,7 @@ namespace ImageProcessor { using System; using System.ComponentModel; + using System.Numerics; /// /// Represents an ordered pair of integer x- and y-coordinates that defines a point in @@ -23,37 +24,99 @@ namespace ImageProcessor /// public static readonly Point Empty = default(Point); + /// + /// The backing vector for SIMD support. + /// + private Vector2 backingVector; + /// /// Initializes a new instance of the struct. /// /// The horizontal position of the point. /// The vertical position of the point. public Point(int x, int y) + : this() + { + this.backingVector = new Vector2(x, y); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector representing the width and height. + /// + public Point(Vector2 vector) { - this.X = x; - this.Y = y; + this.backingVector = new Vector2(vector.X, vector.Y); } /// /// The x-coordinate of this . /// - public int X { get; set; } + public int X + { + get + { + return (int)this.backingVector.X; + } + + set + { + this.backingVector.X = value; + } + } /// /// The y-coordinate of this . /// - public int Y { get; set; } + public int Y + { + get + { + return (int)this.backingVector.Y; + } + + set + { + this.backingVector.Y = value; + } + } /// /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.X == 0 && this.Y == 0; + public bool IsEmpty => this.Equals(Empty); + + /// + /// Computes the sum of adding two points. + /// + /// The point on the left hand of the operand. + /// The point on the right hand of the operand. + /// + /// The + /// + public static Point operator +(Point left, Point right) + { + return new Point(left.backingVector + right.backingVector); + } + + /// + /// Computes the difference left by subtracting one point from another. + /// + /// The point on the left hand of the operand. + /// The point on the right hand of the operand. + /// + /// The + /// + public static Point operator -(Point left, Point right) + { + return new Point(left.backingVector - right.backingVector); + } /// - /// Compares two objects. The result specifies whether the values - /// of the or properties of the two - /// objects are equal. + /// Compares two objects for equality. /// /// /// The on the left side of the operand. @@ -70,9 +133,7 @@ namespace ImageProcessor } /// - /// Compares two objects. The result specifies whether the values - /// of the or properties of the two - /// objects are unequal. + /// Compares two objects for inequality. /// /// /// The on the left side of the operand. @@ -89,44 +150,34 @@ namespace ImageProcessor } /// - /// Indicates whether this instance and a specified object are equal. + /// Gets a representation for this . /// - /// - /// The object to compare with the current instance. - /// - /// - /// true if and this instance are the same type and represent the - /// same value; otherwise, false. - /// - public override bool Equals(object obj) + /// A representation for this object. + public Vector2 ToVector2() { - if (!(obj is Point)) - { - return false; - } - - Point other = (Point)obj; - - return other.X == this.X && other.Y == this.Y; + return new Vector2(this.X, this.Y); } /// - /// Returns the hash code for this instance. + /// Rotates a point around a given origin by the specified angle in degrees. /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// + /// The point to rotate + /// The center point to rotate around. + /// The angle in degrees. + /// + public static Point Rotate(Point point, Point origin, float degrees) + { + float radians = (float)ImageMaths.DegreesToRadians(degrees); + return new Point(Vector2.Transform(point.backingVector, Matrix3x2.CreateRotation(radians, origin.backingVector))); + } + + /// public override int GetHashCode() { return this.GetHashCode(this); } - /// - /// Returns the fully qualified type name of this instance. - /// - /// - /// A containing a fully qualified type name. - /// + /// public override string ToString() { if (this.IsEmpty) @@ -138,16 +189,21 @@ namespace ImageProcessor $"Point [ X={this.X}, Y={this.Y} ]"; } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// True if the current object is equal to the parameter; otherwise, false. - /// - /// An object to compare with this object. + /// + public override bool Equals(object obj) + { + if (obj is Point) + { + return this.Equals((Point)obj); + } + + return false; + } + + /// public bool Equals(Point other) { - return this.X.Equals(other.X) && this.Y.Equals(other.Y); + return this.backingVector.Equals(other.backingVector); } /// @@ -161,12 +217,7 @@ namespace ImageProcessor /// private int GetHashCode(Point point) { - unchecked - { - int hashCode = point.X.GetHashCode(); - hashCode = (hashCode * 397) ^ point.Y.GetHashCode(); - return hashCode; - } + return point.backingVector.GetHashCode(); } } } diff --git a/src/ImageProcessor/Numerics/Rectangle.cs b/src/ImageProcessor/Numerics/Rectangle.cs index 3cd603d965..7d90af30b6 100644 --- a/src/ImageProcessor/Numerics/Rectangle.cs +++ b/src/ImageProcessor/Numerics/Rectangle.cs @@ -23,6 +23,11 @@ namespace ImageProcessor /// public static readonly Rectangle Empty = default(Rectangle); + /// + /// The backing vector for SIMD support. + /// + private Vector4 backingVector; + /// /// Initializes a new instance of the struct. /// @@ -32,10 +37,7 @@ namespace ImageProcessor /// The height of the rectangle. public Rectangle(int x, int y, int width, int height) { - this.X = x; - this.Y = y; - this.Width = width; - this.Height = height; + this.backingVector = new Vector4(x, y, width, height); } /// @@ -49,37 +51,87 @@ namespace ImageProcessor /// public Rectangle(Point point, Size size) { - this.X = point.X; - this.Y = point.Y; - this.Width = size.Width; - this.Height = size.Height; + this.backingVector = new Vector4(point.X, point.Y, size.Width, size.Height); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector. + public Rectangle(Vector4 vector) + { + this.backingVector = vector; } /// /// The x-coordinate of this . /// - public int X { get; set; } + public int X + { + get + { + return (int)this.backingVector.X; + } + + set + { + this.backingVector.X = value; + } + } /// /// The y-coordinate of this . /// - public int Y { get; set; } + public int Y + { + get + { + return (int)this.backingVector.Y; + } + + set + { + this.backingVector.Y = value; + } + } /// /// The width of this . /// - public int Width { get; set; } + public int Width + { + get + { + return (int)this.backingVector.W; + } + + set + { + this.backingVector.W = value; + } + } /// /// The height of this . /// - public int Height { get; set; } + public int Height + { + get + { + return (int)this.backingVector.Z; + } + + set + { + this.backingVector.Z = value; + } + } /// /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.X == 0 && this.Y == 0 && this.Width == 0 && this.Height == 0; + public bool IsEmpty => this.Equals(Empty); /// /// Gets the y-coordinate of the top edge of this . @@ -102,10 +154,33 @@ namespace ImageProcessor public int Left => this.X; /// - /// Compares two objects. The result specifies whether the values - /// of the , , , - /// and the properties of the two - /// objects are equal. + /// Computes the sum of adding two rectangles. + /// + /// The rectangle on the left hand of the operand. + /// The rectangle on the right hand of the operand. + /// + /// The + /// + public static Rectangle operator +(Rectangle left, Rectangle right) + { + return new Rectangle(left.backingVector + right.backingVector); + } + + /// + /// Computes the difference left by subtracting one rectangle from another. + /// + /// The rectangle on the left hand of the operand. + /// The rectangle on the right hand of the operand. + /// + /// The + /// + public static Rectangle operator -(Rectangle left, Rectangle right) + { + return new Rectangle(left.backingVector - right.backingVector); + } + + /// + /// Compares two objects for equality. /// /// /// The on the left side of the operand. @@ -122,10 +197,7 @@ namespace ImageProcessor } /// - /// Compares two objects. The result specifies whether the values - /// of the , , , - /// and the properties of the two - /// objects are unequal. + /// Compares two objects for inequality. /// /// /// The on the left side of the operand. @@ -150,6 +222,7 @@ namespace ImageProcessor /// The public bool Contains(int x, int y) { + // TODO: SIMD? return this.X <= x && x < this.Right && this.Y <= y @@ -160,49 +233,19 @@ namespace ImageProcessor /// Returns the center point of the given /// /// The rectangle - /// - public static Vector2 Center(Rectangle rectangle) - { - return new Vector2(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2); - } - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// - /// True if and this instance are the same type and represent the same value; otherwise, false. - /// - /// The object to compare with the current instance. - public override bool Equals(object obj) + /// + public static Point Center(Rectangle rectangle) { - if (!(obj is Rectangle)) - { - return false; - } - - Rectangle other = (Rectangle)obj; - - return other.X == this.X && other.Y == this.Y - && other.Width == this.Width && other.Height == this.Height; + return new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2); } - /// - /// Returns the hash code for this instance. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// + /// public override int GetHashCode() { return this.GetHashCode(this); } - /// - /// Returns the fully qualified type name of this instance. - /// - /// - /// A containing a fully qualified type name. - /// + /// public override string ToString() { if (this.IsEmpty) @@ -214,19 +257,21 @@ namespace ImageProcessor $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// True if the current object is equal to the parameter; otherwise, false. - /// - /// An object to compare with this object. + /// + public override bool Equals(object obj) + { + if (obj is Rectangle) + { + return this.Equals((Rectangle)obj); + } + + return false; + } + + /// public bool Equals(Rectangle other) { - return this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Width.Equals(other.Width) - && this.Height.Equals(other.Height); + return this.backingVector.Equals(other.backingVector); } /// @@ -240,14 +285,7 @@ namespace ImageProcessor /// private int GetHashCode(Rectangle rectangle) { - unchecked - { - int hashCode = rectangle.X.GetHashCode(); - hashCode = (hashCode * 397) ^ rectangle.Y.GetHashCode(); - hashCode = (hashCode * 397) ^ rectangle.Width.GetHashCode(); - hashCode = (hashCode * 397) ^ rectangle.Height.GetHashCode(); - return hashCode; - } + return rectangle.backingVector.GetHashCode(); } } } diff --git a/src/ImageProcessor/Numerics/RotatedRectangle.cs b/src/ImageProcessor/Numerics/RotatedRectangle.cs deleted file mode 100644 index b75cf56a94..0000000000 --- a/src/ImageProcessor/Numerics/RotatedRectangle.cs +++ /dev/null @@ -1,219 +0,0 @@ -namespace ImageProcessor -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - - /// - /// A rotated rectangle. Adapted from the excellent sample. TODO: Refactor into a struct. - /// - /// - public class RotatedRectangle - { - public Rectangle CollisionRectangle; - public float Rotation; - public Vector2 Origin; - - public RotatedRectangle(Rectangle theRectangle, float theInitialRotation) - { - CollisionRectangle = theRectangle; - Rotation = theInitialRotation; - - //Calculate the Rectangles origin. We assume the center of the Rectangle will - //be the point that we will be rotating around and we use that for the origin - Origin = new Vector2((int)theRectangle.Width / 2f, (int)theRectangle.Height / 2f); - } - - /// - /// Used for changing the X and Y position of the RotatedRectangle - /// - /// - /// - public void ChangePosition(int theXPositionAdjustment, int theYPositionAdjustment) - { - CollisionRectangle.X += theXPositionAdjustment; - CollisionRectangle.Y += theYPositionAdjustment; - } - - /// - /// Determines if the specfied point is contained within the rectangular region defined by - /// this . - /// - /// The x-coordinate of the given point. - /// The y-coordinate of the given point. - /// The - public bool Contains(int x, int y) - { - Rectangle rectangle = new Rectangle(x, y, 1, 1); - return this.Intersects(new RotatedRectangle(rectangle, 0.0f)); - } - - /// - /// This intersects method can be used to check a standard XNA framework Rectangle - /// object and see if it collides with a Rotated Rectangle object - /// - /// - /// - public bool Intersects(Rectangle rectangle) - { - return this.Intersects(new RotatedRectangle(rectangle, 0.0f)); - } - - /// - /// Check to see if two Rotated Rectangls have collided - /// - /// - /// - public bool Intersects(RotatedRectangle theRectangle) - { - //Calculate the Axis we will use to determine if a collision has occurred - //Since the objects are rectangles, we only have to generate 4 Axis (2 for - //each rectangle) since we know the other 2 on a rectangle are parallel. - List aRectangleAxis = new List - { - this.UpperRightCorner() - this.UpperLeftCorner(), - this.UpperRightCorner() - this.LowerRightCorner(), - theRectangle.UpperLeftCorner() - theRectangle.LowerLeftCorner(), - theRectangle.UpperLeftCorner() - theRectangle.UpperRightCorner() - }; - - //Cycle through all of the Axis we need to check. If a collision does not occur - //on ALL of the Axis, then a collision is NOT occurring. We can then exit out - //immediately and notify the calling function that no collision was detected. If - //a collision DOES occur on ALL of the Axis, then there is a collision occurring - //between the rotated rectangles. We know this to be true by the Seperating Axis Theorem - return aRectangleAxis.All(aAxis => this.IsAxisCollision(theRectangle, aAxis)); - } - - /// - /// Determines if a collision has occurred on an Axis of one of the - /// planes parallel to the Rectangle - /// - /// - /// - /// - private bool IsAxisCollision(RotatedRectangle theRectangle, Vector2 aAxis) - { - //Project the corners of the Rectangle we are checking on to the Axis and - //get a scalar value of that project we can then use for comparison - List aRectangleAScalars = new List - { - this.GenerateScalar(theRectangle.UpperLeftCorner(), aAxis), - this.GenerateScalar(theRectangle.UpperRightCorner(), aAxis), - this.GenerateScalar(theRectangle.LowerLeftCorner(), aAxis), - this.GenerateScalar(theRectangle.LowerRightCorner(), aAxis) - }; - - //Project the corners of the current Rectangle on to the Axis and - //get a scalar value of that project we can then use for comparison - List aRectangleBScalars = new List - { - this.GenerateScalar(this.UpperLeftCorner(), aAxis), - this.GenerateScalar(this.UpperRightCorner(), aAxis), - this.GenerateScalar(this.LowerLeftCorner(), aAxis), - this.GenerateScalar(this.LowerRightCorner(), aAxis) - }; - - //Get the Maximum and Minium Scalar values for each of the Rectangles - int aRectangleAMinimum = aRectangleAScalars.Min(); - int aRectangleAMaximum = aRectangleAScalars.Max(); - int aRectangleBMinimum = aRectangleBScalars.Min(); - int aRectangleBMaximum = aRectangleBScalars.Max(); - - //If we have overlaps between the Rectangles (i.e. Min of B is less than Max of A) - //then we are detecting a collision between the rectangles on this Axis - if (aRectangleBMinimum <= aRectangleAMaximum && aRectangleBMaximum >= aRectangleAMaximum) - { - return true; - } - else if (aRectangleAMinimum <= aRectangleBMaximum && aRectangleAMaximum >= aRectangleBMaximum) - { - return true; - } - - return false; - } - - /// - /// Generates a scalar value that can be used to compare where corners of - /// a rectangle have been projected onto a particular axis. - /// - /// - /// - /// - private int GenerateScalar(Vector2 corner, Vector2 axis) - { - // Using the formula for Vector projection. Take the corner being passed in - // and project it onto the given Axis - float numerator = Vector2.Dot(corner, axis); //(theRectangleCorner.X * theAxis.X) + (theRectangleCorner.Y * theAxis.Y); - float denominator = Vector2.Dot(axis, axis); //(theAxis.X * theAxis.X) + (theAxis.Y * theAxis.Y); - float aDivisionResult = numerator / denominator; - - Vector2 projected = new Vector2(aDivisionResult * axis.X, aDivisionResult * axis.Y); - - // Now that we have our projected Vector, calculate a scalar of that projection - // that can be used to more easily do comparisons - float scalar = Vector2.Dot(axis, projected); - return (int)scalar; - } - - /// - /// Rotate a point from a given location and adjust using the Origin we - /// are rotating around - /// - /// - /// - /// - /// - private Vector2 RotatePoint(Vector2 thePoint, Vector2 theOrigin, float theRotation) - { - Vector2 aTranslatedPoint = new Vector2 - { - X = (float)(theOrigin.X - + (thePoint.X - theOrigin.X) * Math.Cos(theRotation) - - (thePoint.Y - theOrigin.Y) * Math.Sin(theRotation)), - Y = (float)(theOrigin.Y - + (thePoint.Y - theOrigin.Y) * Math.Cos(theRotation) - + (thePoint.X - theOrigin.X) * Math.Sin(theRotation)) - }; - return aTranslatedPoint; - } - - public Vector2 UpperLeftCorner() - { - Vector2 aUpperLeft = new Vector2(this.CollisionRectangle.Left, this.CollisionRectangle.Top); - aUpperLeft = this.RotatePoint(aUpperLeft, aUpperLeft + this.Origin, this.Rotation); - return aUpperLeft; - } - - public Vector2 UpperRightCorner() - { - Vector2 aUpperRight = new Vector2(this.CollisionRectangle.Right, this.CollisionRectangle.Top); - aUpperRight = this.RotatePoint(aUpperRight, aUpperRight + new Vector2(-this.Origin.X, this.Origin.Y), this.Rotation); - return aUpperRight; - } - - public Vector2 LowerLeftCorner() - { - Vector2 aLowerLeft = new Vector2(this.CollisionRectangle.Left, this.CollisionRectangle.Bottom); - aLowerLeft = this.RotatePoint(aLowerLeft, aLowerLeft + new Vector2(this.Origin.X, -this.Origin.Y), this.Rotation); - return aLowerLeft; - } - - public Vector2 LowerRightCorner() - { - Vector2 aLowerRight = new Vector2(this.CollisionRectangle.Right, this.CollisionRectangle.Bottom); - aLowerRight = this.RotatePoint(aLowerRight, aLowerRight + new Vector2(-this.Origin.X, -this.Origin.Y), this.Rotation); - return aLowerRight; - } - - public int X => this.CollisionRectangle.X; - - public int Y => this.CollisionRectangle.Y; - - public int Width => this.CollisionRectangle.Width; - - public int Height => this.CollisionRectangle.Height; - } -} diff --git a/src/ImageProcessor/Numerics/Size.cs b/src/ImageProcessor/Numerics/Size.cs index 92064f0656..ba3a16e22c 100644 --- a/src/ImageProcessor/Numerics/Size.cs +++ b/src/ImageProcessor/Numerics/Size.cs @@ -7,6 +7,7 @@ namespace ImageProcessor { using System; using System.ComponentModel; + using System.Numerics; /// /// Stores an ordered pair of integers, which specify a height and width. @@ -22,41 +23,98 @@ namespace ImageProcessor /// public static readonly Size Empty = default(Size); + /// + /// The backing vector for SIMD support. + /// + private Vector2 backingVector; + /// /// Initializes a new instance of the struct. /// - /// - /// The width of the size. - /// - /// - /// The height of the size. - /// + /// The width of the size. + /// The height of the size. public Size(int width, int height) { - this.Width = width; - this.Height = height; + this.backingVector = new Vector2(width, height); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector representing the width and height. + /// + public Size(Vector2 vector) + { + this.backingVector = new Vector2(vector.X, vector.Y); } /// /// The width of this . /// - public int Width { get; set; } + public int Width + { + get + { + return (int)this.backingVector.X; + } + + set + { + this.backingVector.X = value; + } + } /// /// The height of this . /// - public int Height { get; set; } + public int Height + { + get + { + return (int)this.backingVector.Y; + } + + set + { + this.backingVector.Y = value; + } + } /// /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Width == 0 && this.Height == 0; + public bool IsEmpty => this.Equals(Empty); + + /// + /// Computes the sum of adding two sizes. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The + /// + public static Size operator +(Size left, Size right) + { + return new Size(left.backingVector + right.backingVector); + } + + /// + /// Computes the difference left by subtracting one size from another. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The + /// + public static Size operator -(Size left, Size right) + { + return new Size(left.backingVector - right.backingVector); + } /// - /// Compares two objects. The result specifies whether the values - /// and the properties of the two - /// objects are equal. + /// Compares two objects for equality. /// /// /// The on the left side of the operand. @@ -73,9 +131,7 @@ namespace ImageProcessor } /// - /// Compares two objects. The result specifies whether the values - /// and the properties of the two - /// objects are unequal. + /// Compares two objects for inequality. /// /// /// The on the left side of the operand. @@ -92,41 +148,21 @@ namespace ImageProcessor } /// - /// Indicates whether this instance and a specified object are equal. + /// Gets a representation for this . /// - /// - /// True if and this instance are the same type and represent the same value; otherwise, false. - /// - /// The object to compare with the current instance. - public override bool Equals(object obj) + /// A representation for this object. + public Vector2 ToVector2() { - if (!(obj is Size)) - { - return false; - } - - Size other = (Size)obj; - - return other.Width == this.Width && other.Height == this.Height; + return new Vector2(this.Width, this.Height); } - /// - /// Returns the hash code for this instance. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// + /// public override int GetHashCode() { return this.GetHashCode(this); } - /// - /// Returns the fully qualified type name of this instance. - /// - /// - /// A containing a fully qualified type name. - /// + /// public override string ToString() { if (this.IsEmpty) @@ -138,16 +174,21 @@ namespace ImageProcessor $"Size [ Width={this.Width}, Height={this.Height} ]"; } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// True if the current object is equal to the parameter; otherwise, false. - /// - /// An object to compare with this object. + /// + public override bool Equals(object obj) + { + if (obj is Size) + { + return this.Equals((Size)obj); + } + + return false; + } + + /// public bool Equals(Size other) { - return this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + return this.backingVector.Equals(other.backingVector); } /// @@ -161,12 +202,7 @@ namespace ImageProcessor /// private int GetHashCode(Size size) { - unchecked - { - int hashCode = size.Width.GetHashCode(); - hashCode = (hashCode * 397) ^ size.Height.GetHashCode(); - return hashCode; - } + return size.backingVector.GetHashCode(); } } } diff --git a/src/ImageProcessor/Samplers/Resampler.cs b/src/ImageProcessor/Samplers/Resampler.cs index 690c4cbd61..e357b685b3 100644 --- a/src/ImageProcessor/Samplers/Resampler.cs +++ b/src/ImageProcessor/Samplers/Resampler.cs @@ -230,7 +230,7 @@ namespace ImageProcessor.Samplers int startX = targetRectangle.X; int endX = targetRectangle.Right; float negativeAngle = -this.angle; - Vector2 centre = Rectangle.Center(sourceRectangle); + Point centre = Rectangle.Center(sourceRectangle); if (this.Sampler is NearestNeighborResampler) { @@ -254,13 +254,10 @@ namespace ImageProcessor.Samplers int originX = (int)((x - startX) * widthFactor); // Rotate at the centre point - Vector2 rotated = ImageMaths.RotatePoint(new Vector2(originX, originY), centre, negativeAngle); - int rotatedX = (int)rotated.X; - int rotatedY = (int)rotated.Y; - - if (sourceRectangle.Contains(rotatedX, rotatedY)) + Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle); + if (sourceRectangle.Contains(rotated.X, rotated.Y)) { - target[x, y] = source[rotatedX, rotatedY]; + target[x, y] = source[rotated.X, rotated.Y]; } } } @@ -296,13 +293,15 @@ namespace ImageProcessor.Samplers int originX = xw.Index; // Rotate at the centre point - Vector2 rotated = ImageMaths.RotatePoint(new Vector2(originX, originY), centre, negativeAngle); - int rotatedX = (int)rotated.X; - int rotatedY = (int)rotated.Y; + Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle); + if (sourceRectangle.Contains(rotated.X, rotated.Y)) + { + target[x, y] = source[rotated.X, rotated.Y]; + } - if (sourceRectangle.Contains(rotatedX, rotatedY)) + if (sourceRectangle.Contains(rotated.X, rotated.Y)) { - Color sourceColor = Color.Expand(source[rotatedX, rotatedY]); + Color sourceColor = Color.Expand(source[rotated.X, rotated.Y]); float weight = yw.Value * xw.Value; destination.R += sourceColor.R * weight; destination.G += sourceColor.G * weight; @@ -435,4 +434,4 @@ namespace ImageProcessor.Samplers public float Sum { get; set; } } } -} +} \ No newline at end of file diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs index 2c13e749db..8b045629d2 100644 --- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs @@ -13,20 +13,20 @@ public static readonly TheoryData Samplers = new TheoryData { - { "Bicubic", new BicubicResampler() }, - { "Triangle", new TriangleResampler() }, - { "Box", new BoxResampler() }, - { "Lanczos3", new Lanczos3Resampler() }, + //{ "Bicubic", new BicubicResampler() }, + //{ "Triangle", new TriangleResampler() }, + //{ "Box", new BoxResampler() }, + //{ "Lanczos3", new Lanczos3Resampler() }, //{ "Lanczos5", new Lanczos5Resampler() }, //{ "Lanczos8", new Lanczos8Resampler() }, - { "MitchellNetravali", new MitchellNetravaliResampler() }, + //{ "MitchellNetravali", new MitchellNetravaliResampler() }, { "NearestNeighbor", new NearestNeighborResampler() }, - { "Hermite", new HermiteResampler() }, - { "Spline", new SplineResampler() }, - { "Robidoux", new RobidouxResampler() }, - { "RobidouxSharp", new RobidouxSharpResampler() }, - { "RobidouxSoft", new RobidouxSoftResampler() }, - { "Welch", new WelchResampler() } + //{ "Hermite", new HermiteResampler() }, + //{ "Spline", new SplineResampler() }, + //{ "Robidoux", new RobidouxResampler() }, + //{ "RobidouxSharp", new RobidouxSharpResampler() }, + //{ "RobidouxSoft", new RobidouxSoftResampler() }, + //{ "Welch", new WelchResampler() } }; public static readonly TheoryData RotateFlips = new TheoryData