From 01d6c0478f76be92900af5a9150c20274480f218 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 May 2017 01:23:01 +1000 Subject: [PATCH 1/6] Point, Size + F variants --- .../Numerics/Matrix3x2Extensions.cs | 72 +++++ src/ImageSharp/Numerics/Point.cs | 210 +++++++------ src/ImageSharp/Numerics/PointF.cs | 289 ++++++++++++++++++ src/ImageSharp/Numerics/Size.cs | 109 ++++++- src/ImageSharp/Numerics/SizeF.cs | 229 ++++++++++++++ .../Processors/Transforms/RotateProcessor.cs | 2 +- .../Processors/Transforms/SkewProcessor.cs | 2 +- .../ImageSharp.Tests/Numerics/PointFTests.cs | 170 +++++++++++ tests/ImageSharp.Tests/Numerics/PointTests.cs | 227 ++++++++++++-- tests/ImageSharp.Tests/Numerics/SizeFTests.cs | 163 ++++++++++ tests/ImageSharp.Tests/Numerics/SizeTests.cs | 187 ++++++++++-- 11 files changed, 1523 insertions(+), 137 deletions(-) create mode 100644 src/ImageSharp/Numerics/Matrix3x2Extensions.cs create mode 100644 src/ImageSharp/Numerics/PointF.cs create mode 100644 src/ImageSharp/Numerics/SizeF.cs create mode 100644 tests/ImageSharp.Tests/Numerics/PointFTests.cs create mode 100644 tests/ImageSharp.Tests/Numerics/SizeFTests.cs diff --git a/src/ImageSharp/Numerics/Matrix3x2Extensions.cs b/src/ImageSharp/Numerics/Matrix3x2Extensions.cs new file mode 100644 index 000000000..f42462402 --- /dev/null +++ b/src/ImageSharp/Numerics/Matrix3x2Extensions.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Extension methods for the struct + /// + public static class Matrix3x2Extensions + { + /// + /// Creates a rotation matrix for the given rotation in degrees and a center point. + /// + /// The angle in degrees + /// The center point + /// The rotation + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotation(float degree, Point centerPoint) + { + float radian = MathF.DegreeToRadian(degree); + return Matrix3x2.CreateRotation(radian, new Vector2(centerPoint.X, centerPoint.Y)); + } + + /// + /// Creates a rotation matrix for the given rotation in degrees and a center point. + /// + /// The angle in degrees + /// The center point + /// The rotation + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotation(float degree, PointF centerPoint) + { + float radian = MathF.DegreeToRadian(degree); + return Matrix3x2.CreateRotation(radian, centerPoint); + } + + /// + /// Creates a skew matrix for the given angle in degrees and a center point. + /// + /// The x-angle in degrees + /// The y-angle in degrees + /// The center point + /// The rotation + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkew(float degreesX, float degreesY, Point centerPoint) + { + float radiansX = MathF.DegreeToRadian(degreesX); + float radiansY = MathF.DegreeToRadian(degreesY); + return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(centerPoint.X, centerPoint.Y)); + } + + /// + /// Creates a skew matrix for the given angle in degrees and a center point. + /// + /// The x-angle in degrees + /// The y-angle in degrees + /// The rotation + /// The center point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkew(float degreesX, float degreesY, PointF centerPoint) + { + float radiansX = MathF.DegreeToRadian(degreesX); + float radiansY = MathF.DegreeToRadian(degreesY); + return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(centerPoint.X, centerPoint.Y)); + } + } +} diff --git a/src/ImageSharp/Numerics/Point.cs b/src/ImageSharp/Numerics/Point.cs index 8d523895f..7c607885f 100644 --- a/src/ImageSharp/Numerics/Point.cs +++ b/src/ImageSharp/Numerics/Point.cs @@ -25,6 +25,17 @@ namespace ImageSharp /// public static readonly Point Empty = default(Point); + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal and vertical position of the point. + public Point(int value) + : this() + { + this.X = LowInt16(value); + this.Y = HighInt16(value); + } + /// /// Initializes a new instance of the struct. /// @@ -38,15 +49,13 @@ namespace ImageSharp } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct from the given . /// - /// - /// The vector representing the width and height. - /// - public Point(Vector2 vector) + /// The size + public Point(Size size) { - this.X = (int)Math.Round(vector.X); - this.Y = (int)Math.Round(vector.Y); + this.X = size.Width; + this.Y = size.Height; } /// @@ -66,42 +75,66 @@ namespace ImageSharp public bool IsEmpty => this.Equals(Empty); /// - /// Computes the sum of adding two points. + /// Creates a with the coordinates of the specified . /// - /// The point on the left hand of the operand. - /// The point on the right hand of the operand. - /// - /// The - /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PointF(Point point) + { + return new PointF(point.X, point.Y); + } + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point operator +(Point left, Point right) + public static implicit operator Vector2(Point point) { - return new Point(left.X + right.X, left.Y + right.Y); + return new Vector2(point.X, point.Y); } /// - /// Computes the difference left by subtracting one point from another. + /// Creates a with the coordinates of the specified . /// - /// The point on the left hand of the operand. - /// The point on the right hand of the operand. + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Size(Point point) + { + return new Size(point.X, point.Y); + } + + /// + /// Translates a by a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. /// /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point operator -(Point left, Point right) + public static Point operator +(Point point, Size size) + { + return Add(point, size); + } + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point operator -(Point point, Size size) { - return new Point(left.X - right.X, left.Y - right.Y); + return Subtract(point, size); } /// /// Compares two objects for equality. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the current left is equal to the parameter; otherwise, false. /// @@ -114,12 +147,8 @@ namespace ImageSharp /// /// Compares two objects for inequality. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the current left is unequal to the parameter; otherwise, false. /// @@ -130,76 +159,84 @@ namespace ImageSharp } /// - /// Creates a rotation matrix for the given point and angle. + /// Translates a by the negative of a given . /// - /// The origin point to rotate around - /// Rotation in degrees - /// The rotation - public static Matrix3x2 CreateRotation(Point origin, float degrees) + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Add(Point point, Size size) { - float radians = MathF.DegreeToRadian(degrees); - return Matrix3x2.CreateRotation(radians, new Vector2(origin.X, origin.Y)); + return new Point(point.X + size.Width, point.Y + size.Height); } /// - /// Rotates a point around a given a rotation matrix. + /// Translates a by the negative of a given . /// - /// The point to rotate - /// Rotation matrix used - /// The rotated - public static Point Rotate(Point point, Matrix3x2 rotation) + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Subtract(Point point, Size size) { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); + return new Point(point.X - size.Width, point.Y - size.Height); } /// - /// Rotates a point around a given origin by the specified angle in degrees. + /// Converts a to a by performing a ceiling operation on all the coordinates. /// - /// The point to rotate - /// The center point to rotate around. - /// The angle in degrees. - /// The rotated - public static Point Rotate(Point point, Point origin, float degrees) + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Ceiling(PointF point) { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), CreateRotation(origin, degrees))); + return new Point((int)MathF.Ceiling(point.X), (int)MathF.Ceiling(point.Y)); } /// - /// Creates a skew matrix for the given point and angle. + /// Converts a to a by performing a round operation on all the coordinates. /// - /// The origin point to rotate around - /// The x-angle in degrees. - /// The y-angle in degrees. - /// The rotation - public static Matrix3x2 CreateSkew(Point origin, float degreesX, float degreesY) + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Round(PointF point) { - float radiansX = MathF.DegreeToRadian(degreesX); - float radiansY = MathF.DegreeToRadian(degreesY); - return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(origin.X, origin.Y)); + return new Point((int)MathF.Round(point.X), (int)MathF.Round(point.Y)); } /// - /// Skews a point using a given a skew matrix. + /// Converts a to a by performing a round operation on all the coordinates. + /// + /// The vector + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Round(Vector2 vector) + { + return new Point((int)MathF.Round(vector.X), (int)MathF.Round(vector.Y)); + } + + /// + /// Rotates a point around the given rotation matrix. /// /// The point to rotate - /// Rotation matrix used + /// Rotation matrix used /// The rotated - public static Point Skew(Point point, Matrix3x2 skew) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Rotate(Point point, Matrix3x2 rotation) { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), skew)); + return Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); } /// - /// Skews a point around a given origin by the specified angles in degrees. + /// Skews a point using the given skew matrix. /// - /// The point to skew. - /// The center point to rotate around. - /// The x-angle in degrees. - /// The y-angle in degrees. - /// The skewed - public static Point Skew(Point point, Point origin, float degreesX, float degreesY) + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Skew(Point point, Matrix3x2 skew) { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), CreateSkew(origin, degreesX, degreesY))); + return Round(Vector2.Transform(new Vector2(point.X, point.Y), skew)); } /// @@ -216,6 +253,7 @@ namespace ImageSharp /// /// The amount to offset the x-coordinate. /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(int dx, int dy) { this.X += dx; @@ -225,10 +263,11 @@ namespace ImageSharp /// /// Translates this by the specified amount. /// - /// The used offset this . - public void Offset(Point p) + /// The used offset this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(Point point) { - this.Offset(p.X, p.Y); + this.Offset(point.X, point.Y); } /// @@ -260,23 +299,22 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Point other) { return this.X == other.X && this.Y == other.Y; } - /// - /// 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 static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); + + private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); + private int GetHashCode(Point point) { - return point.X ^ point.Y; + unchecked + { + return point.X ^ point.Y; + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Numerics/PointF.cs b/src/ImageSharp/Numerics/PointF.cs new file mode 100644 index 000000000..1b76badb6 --- /dev/null +++ b/src/ImageSharp/Numerics/PointF.cs @@ -0,0 +1,289 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.ComponentModel; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Represents an ordered pair of single precision floating point x- and y-coordinates that defines a point in + /// a two-dimensional plane. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct PointF : IEquatable + { + /// + /// Represents a that has X and Y values set to zero. + /// + public static readonly PointF Empty = default(PointF); + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal position of the point. + /// The vertical position of the point. + public PointF(float x, float y) + : this() + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The size + public PointF(SizeF size) + { + this.X = size.Width; + this.Y = size.Height; + } + + /// + /// Gets or sets the x-coordinate of this . + /// + public float X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public float Y { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The vector. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PointF(Vector2 vector) + { + return new PointF(vector.X, vector.Y); + } + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector2(PointF point) + { + return new Vector2(point.X, point.Y); + } + + /// + /// Creates a with the coordinates of the specified by truncating each of the coordinates. + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Point(PointF point) + { + return new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); + } + + /// + /// Translates a by a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator +(PointF point, SizeF size) + { + return Add(point, size); + } + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator -(PointF point, SizeF size) + { + return Subtract(point, size); + } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(PointF left, PointF right) + { + return left.Equals(right); + } + + /// + /// Compares two objects for inequality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(PointF left, PointF right) + { + return !left.Equals(right); + } + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Add(PointF point, SizeF size) + { + return new PointF(point.X + size.Width, point.Y + size.Height); + } + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Subtract(PointF point, SizeF size) + { + return new PointF(point.X - size.Width, point.Y - size.Height); + } + + /// + /// Rotates a point around the given rotation matrix. + /// + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Rotate(PointF point, Matrix3x2 rotation) + { + return Vector2.Transform(new Vector2(point.X, point.Y), rotation); + } + + /// + /// Skews a point using the given skew matrix. + /// + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Skew(PointF point, Matrix3x2 skew) + { + return Vector2.Transform(new Vector2(point.X, point.Y), skew); + } + + /// + /// Translates this by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(float dx, float dy) + { + this.X += dx; + this.Y += dy; + } + + /// + /// Translates this by the specified amount. + /// + /// The used offset this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(PointF point) + { + this.Offset(point.X, point.Y); + } + + /// + public override int GetHashCode() + { + return this.GetHashCode(this); + } + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "PointF [ Empty ]"; + } + + return $"PointF [ X={this.X}, Y={this.Y} ]"; + } + + /// + public override bool Equals(object obj) + { + if (obj is PointF) + { + return this.Equals((PointF)obj); + } + + return false; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(PointF other) + { + return this.X.Equals(other.X) && this.Y.Equals(other.Y); + } + + /// + /// 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(PointF point) + { + unchecked + { + return point.X.GetHashCode() ^ point.Y.GetHashCode(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/Size.cs b/src/ImageSharp/Numerics/Size.cs index bae645ac8..c46247307 100644 --- a/src/ImageSharp/Numerics/Size.cs +++ b/src/ImageSharp/Numerics/Size.cs @@ -23,6 +23,17 @@ namespace ImageSharp /// public static readonly Size Empty = default(Size); + /// + /// Initializes a new instance of the struct. + /// + /// The width and height of the size + public Size(int value) + : this() + { + this.Width = value; + this.Height = value; + } + /// /// Initializes a new instance of the struct. /// @@ -34,6 +45,27 @@ namespace ImageSharp this.Height = height; } + /// + /// Initializes a new instance of the struct. + /// + /// The size + public Size(Size size) + : this() + { + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The point + public Size(Point point) + { + this.Width = point.X; + this.Height = point.Y; + } + /// /// Gets or sets the width of this . /// @@ -50,6 +82,26 @@ namespace ImageSharp [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => this.Equals(Empty); + /// + /// Creates a with the dimensions of the specified . + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SizeF(Size size) + { + return new SizeF(size.Width, size.Height); + } + + /// + /// Converts the given into a . + /// + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Point(Size size) + { + return new Point(size.Width, size.Height); + } + /// /// Computes the sum of adding two sizes. /// @@ -61,7 +113,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size operator +(Size left, Size right) { - return new Size(left.Width + right.Width, left.Height + right.Height); + return Add(left, right); } /// @@ -75,7 +127,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size operator -(Size left, Size right) { - return new Size(left.Width - right.Width, left.Height - right.Height); + return Subtract(left, right); } /// @@ -114,6 +166,52 @@ namespace ImageSharp return !left.Equals(right); } + /// + /// Performs vector addition of two objects. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Add(Size left, Size right) + { + return new Size(left.Width + right.Width, left.Height + right.Height); + } + + /// + /// Contracts a by another + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Subtract(Size left, Size right) + { + return new Size(left.Width - right.Width, left.Height - right.Height); + } + + /// + /// Converts a to a by performing a ceiling operation on all the dimensions. + /// + /// The size + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Ceiling(SizeF size) + { + return new Size((int)MathF.Ceiling(size.Width), (int)MathF.Ceiling(size.Height)); + } + + /// + /// Converts a to a by performing a round operation on all the dimensions. + /// + /// The size + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Round(SizeF size) + { + return new Size((int)MathF.Round(size.Width), (int)MathF.Round(size.Height)); + } + /// public override int GetHashCode() { @@ -132,7 +230,6 @@ namespace ImageSharp } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is Size) @@ -144,6 +241,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Size other) { return this.Width == other.Width && this.Height == other.Height; @@ -160,7 +258,10 @@ namespace ImageSharp /// private int GetHashCode(Size size) { - return size.Width ^ size.Height; + unchecked + { + return size.Width ^ size.Height; + } } } } diff --git a/src/ImageSharp/Numerics/SizeF.cs b/src/ImageSharp/Numerics/SizeF.cs new file mode 100644 index 000000000..f6b852cfa --- /dev/null +++ b/src/ImageSharp/Numerics/SizeF.cs @@ -0,0 +1,229 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.ComponentModel; + using System.Runtime.CompilerServices; + + /// + /// Stores an ordered pair of single precision floating points, which specify a height and width. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct SizeF : IEquatable + { + /// + /// Represents a that has Width and Height values set to zero. + /// + public static readonly SizeF Empty = default(SizeF); + + /// + /// Initializes a new instance of the struct. + /// + /// The width of the size. + /// The height of the size. + public SizeF(float width, float height) + { + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The size + public SizeF(SizeF size) + : this() + { + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The point + public SizeF(PointF point) + { + this.Width = point.X; + this.Height = point.Y; + } + + /// + /// Gets or sets the width of this . + /// + public float Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public float Height { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the dimensions of the specified by truncating each of the dimensions. + /// + /// The size. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Size(SizeF size) + { + return new Size(unchecked((int)size.Width), unchecked((int)size.Height)); + } + + /// + /// Converts the given into a . + /// + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator PointF(SizeF size) + { + return new PointF(size.Width, size.Height); + } + + /// + /// 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 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF operator +(SizeF left, SizeF right) + { + return Add(left, right); + } + + /// + /// 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 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF operator -(SizeF left, SizeF right) + { + return Subtract(left, right); + } + + /// + /// Compares two objects for equality. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SizeF left, SizeF right) + { + return left.Equals(right); + } + + /// + /// Compares two objects for inequality. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SizeF left, SizeF right) + { + return !left.Equals(right); + } + + /// + /// Performs vector addition of two objects. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF Add(SizeF left, SizeF right) + { + return new SizeF(left.Width + right.Width, left.Height + right.Height); + } + + /// + /// Contracts a by another + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF Subtract(SizeF left, SizeF right) + { + return new SizeF(left.Width - right.Width, left.Height - right.Height); + } + + /// + public override int GetHashCode() + { + return this.GetHashCode(this); + } + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "SizeF [ Empty ]"; + } + + return $"SizeF [ Width={this.Width}, Height={this.Height} ]"; + } + + /// + public override bool Equals(object obj) + { + if (obj is SizeF) + { + return this.Equals((SizeF)obj); + } + + return false; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(SizeF other) + { + return this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + } + + /// + /// 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(SizeF size) + { + unchecked + { + return size.Width.GetHashCode() ^ size.Height.GetHashCode(); + } + } + } +} \ 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 43dbb53ea..15d7ca641 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -78,7 +78,7 @@ namespace ImageSharp.Processing.Processors return; } - this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle); + this.processMatrix = Matrix3x2Extensions.CreateRotation(-this.Angle, new Point(0, 0)); if (this.Expand) { this.CreateNewCanvas(sourceRectangle, this.processMatrix); diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 7807d0dfc..264cf4a53 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -73,7 +73,7 @@ namespace ImageSharp.Processing.Processors /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { - this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY); + this.processMatrix = Matrix3x2Extensions.CreateSkew(-this.AngleX, -this.AngleY, new Point(0, 0)); if (this.Expand) { this.CreateNewCanvas(sourceRectangle, this.processMatrix); diff --git a/tests/ImageSharp.Tests/Numerics/PointFTests.cs b/tests/ImageSharp.Tests/Numerics/PointFTests.cs new file mode 100644 index 000000000..33de60ca7 --- /dev/null +++ b/tests/ImageSharp.Tests/Numerics/PointFTests.cs @@ -0,0 +1,170 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Numerics +{ + using System; + using System.Globalization; + using System.Reflection; + using Xunit; + + public class PointFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(PointF.Empty, new PointF()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(0.0, 0.0)] + public void NonDefaultConstructorTest(float x, float y) + { + var p1 = new PointF(x, y); + + Assert.Equal(x, p1.X); + Assert.Equal(y, p1.Y); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(PointF.Empty.IsEmpty); + Assert.True(new PointF().IsEmpty); + Assert.True(new PointF(0, 0).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + public void IsEmptyRandomTest(float x, float y) + { + Assert.False(new PointF(x, y).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void CoordinatesTest(float x, float y) + { + var p = new PointF(x, y); + Assert.Equal(x, p.X); + Assert.Equal(y, p.Y); + + p.X = 10; + Assert.Equal(10, p.X); + + p.Y = -10.123f; + Assert.Equal(-10.123, p.Y, 3); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, int.MaxValue, int.MinValue)] + [InlineData(float.MinValue, float.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(0, 0, 0, 0)] + public void ArithmeticTestWithSize(float x, float y, int x1, int y1) + { + var p = new PointF(x, y); + var s = new Size(x1, y1); + + var addExpected = new PointF(x + x1, y + y1); + var subExpected = new PointF(x - x1, y - y1); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, PointF.Add(p, s)); + Assert.Equal(subExpected, PointF.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTestWithSizeF(float x, float y) + { + var p = new PointF(x, y); + var s = new SizeF(y, x); + + var addExpected = new PointF(x + y, y + x); + var subExpected = new PointF(x - y, y - x); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, PointF.Add(p, s)); + Assert.Equal(subExpected, PointF.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(float x, float y) + { + var pLeft = new PointF(x, y); + var pRight = new PointF(y, x); + + if (x == y) + { + Assert.True(pLeft == pRight); + Assert.False(pLeft != pRight); + Assert.True(pLeft.Equals(pRight)); + Assert.True(pLeft.Equals((object)pRight)); + Assert.Equal(pLeft.GetHashCode(), pRight.GetHashCode()); + return; + } + + Assert.True(pLeft != pRight); + Assert.False(pLeft == pRight); + Assert.False(pLeft.Equals(pRight)); + Assert.False(pLeft.Equals((object)pRight)); + } + + [Fact] + public static void EqualityTest_NotPointF() + { + var point = new PointF(0, 0); + Assert.False(point.Equals(null)); + Assert.False(point.Equals(0)); + + // If PointF implements IEquatable (e.g. in .NET Core), then structs that are implicitly + // convertible to var can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToPointF = typeof(IEquatable).IsAssignableFrom(point.GetType()); + Assert.Equal(expectsImplicitCastToPointF, point.Equals(new Point(0, 0))); + + Assert.False(point.Equals((object)new Point(0, 0))); // No implicit cast + } + + [Fact] + public static void GetHashCodeTest() + { + var point = new PointF(10, 10); + Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode()); + } + + [Fact] + public void ToStringTest() + { + var p = new PointF(5.1F, -5.123F); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); + } + + [Fact] + public void ToStringEmptyTest() + { + var p = new PointF(0, 0); + Assert.Equal("PointF [ Empty ]", p.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/PointTests.cs b/tests/ImageSharp.Tests/Numerics/PointTests.cs index 82b26b946..8eb36b6e8 100644 --- a/tests/ImageSharp.Tests/Numerics/PointTests.cs +++ b/tests/ImageSharp.Tests/Numerics/PointTests.cs @@ -5,46 +5,225 @@ namespace ImageSharp.Tests { + using System.Globalization; using Xunit; - /// - /// Tests the struct. - /// public class PointTests { - /// - /// Tests the equality operators for equality. - /// [Fact] - public void AreEqual() + public void DefaultConstructorTest() { - Point first = new Point(100, 100); - Point second = new Point(100, 100); + Assert.Equal(Point.Empty, new Point()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(new Size(x, y)); + + Assert.Equal(p1, p2); + } + + [Theory] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + [InlineData(0)] + public void SingleIntConstructorTest(int x) + { + var p1 = new Point(x); + var p2 = new Point(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); + + Assert.Equal(p1, p2); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(Point.Empty.IsEmpty); + Assert.True(new Point().IsEmpty); + Assert.True(new Point(0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void IsEmptyRandomTest(int x, int y) + { + Assert.False(new Point(x, y).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void CoordinatesTest(int x, int y) + { + var p = new Point(x, y); + Assert.Equal(x, p.X); + Assert.Equal(y, p.Y); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void PointFConversionTest(int x, int y) + { + PointF p = new Point(x, y); + Assert.Equal(new PointF(x, y), p); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void SizeConversionTest(int x, int y) + { + var sz = (Size)new Point(x, y); + Assert.Equal(new Size(x, y), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(int x, int y) + { + Point addExpected, subExpected, p = new Point(x, y); + var s = new Size(y, x); + + unchecked + { + addExpected = new Point(x + y, y + x); + subExpected = new Point(x - y, y - x); + } - Assert.Equal(first, second); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, Point.Add(p, s)); + Assert.Equal(subExpected, Point.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void PointFMathematicalTest(float x, float y) + { + var pf = new PointF(x, y); + Point pCeiling, pTruncate, pRound; + + unchecked + { + pCeiling = new Point((int)MathF.Ceiling(x), (int)MathF.Ceiling(y)); + pTruncate = new Point((int)x, (int)y); + pRound = new Point((int)MathF.Round(x), (int)MathF.Round(y)); + } + + Assert.Equal(pCeiling, Point.Ceiling(pf)); + Assert.Equal(pRound, Point.Round(pf)); + Assert.Equal(pTruncate, (Point)pf); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void OffsetTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(y, x); + + p1.Offset(p2); + + Assert.Equal(unchecked(p2.X + p2.Y), p1.X); + Assert.Equal(p1.X, p1.Y); + + p2.Offset(x, y); + Assert.Equal(p1, p2); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(x / 2 - 1, y / 2 - 1); + var p3 = new Point(x, y); + + Assert.True(p1 == p3); + Assert.True(p1 != p2); + Assert.True(p2 != p3); + + Assert.True(p1.Equals(p3)); + Assert.False(p1.Equals(p2)); + Assert.False(p2.Equals(p3)); + + Assert.True(p1.Equals((object)p3)); + Assert.False(p1.Equals((object)p2)); + Assert.False(p2.Equals((object)p3)); + + Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); + } + + [Fact] + public static void EqualityTest_NotPoint() + { + var point = new Point(0, 0); + Assert.False(point.Equals(null)); + Assert.False(point.Equals(0)); + Assert.False(point.Equals(new PointF(0, 0))); } - /// - /// Tests the equality operators for inequality. - /// [Fact] - public void AreNotEqual() + public static void GetHashCodeTest() { - Point first = new Point(0, 100); - Point second = new Point(100, 100); + var point = new Point(10, 10); + Assert.Equal(point.GetHashCode(), new Point(10, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new Point(20, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new Point(10, 20).GetHashCode()); + } - Assert.NotEqual(first, second); + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(1, -2, 3, -4)] + public void ConversionTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + RectangleF rectF = rect; + Assert.Equal(x, rectF.X); + Assert.Equal(y, rectF.Y); + Assert.Equal(width, rectF.Width); + Assert.Equal(height, rectF.Height); + } + + [Fact] + public void ToStringTest() + { + var p = new Point(5, -5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Point [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); } - /// - /// Tests whether the point constructor correctly assign properties. - /// [Fact] - public void ConstructorAssignsProperties() + public void ToStringEmptyTest() { - Point first = new Point(4, 5); - Assert.Equal(4, first.X); - Assert.Equal(5, first.Y); + var p = new Point(0, 0); + Assert.Equal("Point [ Empty ]", p.ToString()); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/SizeFTests.cs b/tests/ImageSharp.Tests/Numerics/SizeFTests.cs new file mode 100644 index 000000000..dfeee762f --- /dev/null +++ b/tests/ImageSharp.Tests/Numerics/SizeFTests.cs @@ -0,0 +1,163 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Globalization; + using System.Reflection; + using Xunit; + + public class SizeFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(SizeF.Empty, new SizeF()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorAndDimensionsTest(float width, float height) + { + var s1 = new SizeF(width, height); + var p1 = new PointF(width, height); + var s2 = new SizeF(s1); + + Assert.Equal(s1, s2); + Assert.Equal(s1, new SizeF(p1)); + Assert.Equal(s2, new SizeF(p1)); + + Assert.Equal(width, s1.Width); + Assert.Equal(height, s1.Height); + + s1.Width = 10; + Assert.Equal(10, s1.Width); + + s1.Height = -10.123f; + Assert.Equal(-10.123, s1.Height, 3); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(SizeF.Empty.IsEmpty); + Assert.True(new SizeF().IsEmpty); + Assert.True(new SizeF(0, 0).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + public void IsEmptyRandomTest(float width, float height) + { + Assert.False(new SizeF(width, height).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(float width, float height) + { + var s1 = new SizeF(width, height); + var s2 = new SizeF(height, width); + var addExpected = new SizeF(width + height, width + height); + var subExpected = new SizeF(width - height, height - width); + + Assert.Equal(addExpected, s1 + s2); + Assert.Equal(addExpected, SizeF.Add(s1, s2)); + + Assert.Equal(subExpected, s1 - s2); + Assert.Equal(subExpected, SizeF.Subtract(s1, s2)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(float width, float height) + { + var sLeft = new SizeF(width, height); + var sRight = new SizeF(height, width); + + if (width == height) + { + Assert.True(sLeft == sRight); + Assert.False(sLeft != sRight); + Assert.True(sLeft.Equals(sRight)); + Assert.True(sLeft.Equals((object)sRight)); + Assert.Equal(sLeft.GetHashCode(), sRight.GetHashCode()); + return; + } + + Assert.True(sLeft != sRight); + Assert.False(sLeft == sRight); + Assert.False(sLeft.Equals(sRight)); + Assert.False(sLeft.Equals((object)sRight)); + } + + [Fact] + public static void EqualityTest_NotSizeF() + { + var size = new SizeF(0, 0); + Assert.False(size.Equals(null)); + Assert.False(size.Equals(0)); + + // If SizeF implements IEquatable (e.g in .NET Core), then classes that are implicitly + // convertible to SizeF can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToSizeF = typeof(IEquatable).IsAssignableFrom(size.GetType()); + Assert.Equal(expectsImplicitCastToSizeF, size.Equals(new Size(0, 0))); + + Assert.False(size.Equals((object)new Size(0, 0))); // No implicit cast + } + + [Fact] + public static void GetHashCodeTest() + { + var size = new SizeF(10, 10); + Assert.Equal(size.GetHashCode(), new SizeF(10, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new SizeF(20, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new SizeF(10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void ConversionTest(float width, float height) + { + var s1 = new SizeF(width, height); + var p1 = (PointF)s1; + var s2 = new Size(unchecked((int)width), unchecked((int)height)); + + Assert.Equal(new PointF(width, height), p1); + Assert.Equal(p1, (PointF)s1); + Assert.Equal(s2, (Size)s1); + } + + [Fact] + public void ToStringTest() + { + var sz = new SizeF(10, 5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "SizeF [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); + } + + [Fact] + public void ToStringTestEmpty() + { + var sz = new SizeF(0, 0); + Assert.Equal("SizeF [ Empty ]", sz.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/SizeTests.cs b/tests/ImageSharp.Tests/Numerics/SizeTests.cs index 29eb768d9..010cdad3c 100644 --- a/tests/ImageSharp.Tests/Numerics/SizeTests.cs +++ b/tests/ImageSharp.Tests/Numerics/SizeTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using System.Globalization; using Xunit; /// @@ -12,39 +13,183 @@ namespace ImageSharp.Tests /// public class SizeTests { - /// - /// Tests the equality operators for equality. - /// [Fact] - public void AreEqual() + public void DefaultConstructorTest() { - Size first = new Size(100, 100); - Size second = new Size(100, 100); + Assert.Equal(Size.Empty, new Size()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorTest(int width, int height) + { + var s1 = new Size(width, height); + var s2 = new Size(new Point(width, height)); - Assert.Equal(first, second); + Assert.Equal(s1, s2); + + s1.Width = 10; + Assert.Equal(10, s1.Width); + + s1.Height = -10; + Assert.Equal(-10, s1.Height); } - /// - /// Tests the equality operators for inequality. - /// [Fact] - public void AreNotEqual() + public void IsEmptyDefaultsTest() + { + Assert.True(Size.Empty.IsEmpty); + Assert.True(new Size().IsEmpty); + Assert.True(new Size(0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void IsEmptyRandomTest(int width, int height) + { + Assert.False(new Size(width, height).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void DimensionsTest(int width, int height) { - Size first = new Size(0, 100); - Size second = new Size(100, 100); + var p = new Size(width, height); + Assert.Equal(width, p.Width); + Assert.Equal(height, p.Height); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void PointFConversionTest(int width, int height) + { + SizeF sz = new Size(width, height); + Assert.Equal(new SizeF(width, height), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void SizeConversionTest(int width, int height) + { + var sz = (Point)new Size(width, height); + Assert.Equal(new Point(width, height), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(int width, int height) + { + var sz1 = new Size(width, height); + var sz2 = new Size(height, width); + Size addExpected, subExpected; + + unchecked + { + addExpected = new Size(width + height, height + width); + subExpected = new Size(width - height, height - width); + } + + Assert.Equal(addExpected, sz1 + sz2); + Assert.Equal(subExpected, sz1 - sz2); + Assert.Equal(addExpected, Size.Add(sz1, sz2)); + Assert.Equal(subExpected, Size.Subtract(sz1, sz2)); + } - Assert.NotEqual(first, second); + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void PointFMathematicalTest(float width, float height) + { + var szF = new SizeF(width, height); + Size pCeiling, pTruncate, pRound; + + unchecked + { + pCeiling = new Size((int)MathF.Ceiling(width), (int)MathF.Ceiling(height)); + pTruncate = new Size((int)width, (int)height); + pRound = new Size((int)MathF.Round(width), (int)MathF.Round(height)); + } + + Assert.Equal(pCeiling, Size.Ceiling(szF)); + Assert.Equal(pRound, Size.Round(szF)); + Assert.Equal(pTruncate, (Size)szF); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(int width, int height) + { + var p1 = new Size(width, height); + var p2 = new Size(unchecked(width - 1), unchecked(height - 1)); + var p3 = new Size(width, height); + + Assert.True(p1 == p3); + Assert.True(p1 != p2); + Assert.True(p2 != p3); + + Assert.True(p1.Equals(p3)); + Assert.False(p1.Equals(p2)); + Assert.False(p2.Equals(p3)); + + Assert.True(p1.Equals((object)p3)); + Assert.False(p1.Equals((object)p2)); + Assert.False(p2.Equals((object)p3)); + + Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); + } + + [Fact] + public static void EqualityTest_NotSize() + { + var size = new Size(0, 0); + Assert.False(size.Equals(null)); + Assert.False(size.Equals(0)); + Assert.False(size.Equals(new SizeF(0, 0))); + } + + [Fact] + public static void GetHashCodeTest() + { + var size = new Size(10, 10); + Assert.Equal(size.GetHashCode(), new Size(10, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new Size(20, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new Size(10, 20).GetHashCode()); + } + + [Fact] + public void ToStringTest() + { + var sz = new Size(10, 5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Size [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); } - /// - /// Tests whether the size constructor correctly assign properties. - /// [Fact] - public void ConstructorAssignsProperties() + public void ToStringTestEmpty() { - Size first = new Size(4, 5); - Assert.Equal(4, first.Width); - Assert.Equal(5, first.Height); + var sz = new Size(0, 0); + Assert.Equal("Size [ Empty ]", sz.ToString()); } } } \ No newline at end of file From acced54a1bc069b6fc9f73389af456f3aa2e54f1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 May 2017 10:25:15 +1000 Subject: [PATCH 2/6] Cleanup --- src/ImageSharp/Numerics/Point.cs | 120 ++++-------------- src/ImageSharp/Numerics/PointF.cs | 88 +++---------- src/ImageSharp/Numerics/Size.cs | 88 ++++--------- src/ImageSharp/Numerics/SizeF.cs | 72 ++--------- .../Processors/Overlays/GlowProcessor.cs | 2 +- .../Processors/Overlays/VignetteProcessor.cs | 2 +- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 2 +- 7 files changed, 80 insertions(+), 294 deletions(-) diff --git a/src/ImageSharp/Numerics/Point.cs b/src/ImageSharp/Numerics/Point.cs index 7c607885f..4d43a83af 100644 --- a/src/ImageSharp/Numerics/Point.cs +++ b/src/ImageSharp/Numerics/Point.cs @@ -79,30 +79,21 @@ namespace ImageSharp /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator PointF(Point point) - { - return new PointF(point.X, point.Y); - } + public static implicit operator PointF(Point point) => new PointF(point.X, point.Y); /// /// Creates a with the coordinates of the specified . /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2(Point point) - { - return new Vector2(point.X, point.Y); - } + public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y); /// /// Creates a with the coordinates of the specified . /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Size(Point point) - { - return new Size(point.X, point.Y); - } + public static explicit operator Size(Point point) => new Size(point.X, point.Y); /// /// Translates a by a given . @@ -113,10 +104,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point operator +(Point point, Size size) - { - return Add(point, size); - } + public static Point operator +(Point point, Size size) => Add(point, size); /// /// Translates a by the negative of a given . @@ -125,10 +113,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point operator -(Point point, Size size) - { - return Subtract(point, size); - } + public static Point operator -(Point point, Size size) => Subtract(point, size); /// /// Compares two objects for equality. @@ -139,10 +124,7 @@ namespace ImageSharp /// True if the current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Point left, Point right) - { - return left.Equals(right); - } + public static bool operator ==(Point left, Point right) => left.Equals(right); /// /// Compares two objects for inequality. @@ -153,10 +135,7 @@ namespace ImageSharp /// True if the current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Point left, Point right) - { - return !left.Equals(right); - } + public static bool operator !=(Point left, Point right) => !left.Equals(right); /// /// Translates a by the negative of a given . @@ -165,10 +144,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Add(Point point, Size size) - { - return new Point(point.X + size.Width, point.Y + size.Height); - } + public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height)); /// /// Translates a by the negative of a given . @@ -177,10 +153,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Subtract(Point point, Size size) - { - return new Point(point.X - size.Width, point.Y - size.Height); - } + public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height)); /// /// Converts a to a by performing a ceiling operation on all the coordinates. @@ -188,10 +161,7 @@ namespace ImageSharp /// The point /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Ceiling(PointF point) - { - return new Point((int)MathF.Ceiling(point.X), (int)MathF.Ceiling(point.Y)); - } + public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y))); /// /// Converts a to a by performing a round operation on all the coordinates. @@ -199,10 +169,15 @@ namespace ImageSharp /// The point /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Round(PointF point) - { - return new Point((int)MathF.Round(point.X), (int)MathF.Round(point.Y)); - } + public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); + + /// + /// Converts a to a by performing a truncate operation on all the coordinates. + /// + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y)); /// /// Converts a to a by performing a round operation on all the coordinates. @@ -210,10 +185,7 @@ namespace ImageSharp /// The vector /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Round(Vector2 vector) - { - return new Point((int)MathF.Round(vector.X), (int)MathF.Round(vector.Y)); - } + public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y))); /// /// Rotates a point around the given rotation matrix. @@ -222,10 +194,7 @@ namespace ImageSharp /// Rotation matrix used /// The rotated [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Rotate(Point point, Matrix3x2 rotation) - { - return Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); - } + public static Point Rotate(Point point, Matrix3x2 rotation) => Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); /// /// Skews a point using the given skew matrix. @@ -234,19 +203,7 @@ namespace ImageSharp /// Rotation matrix used /// The rotated [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Skew(Point point, Matrix3x2 skew) - { - return Round(Vector2.Transform(new Vector2(point.X, point.Y), skew)); - } - - /// - /// Gets a representation for this . - /// - /// A representation for this object. - public Vector2 ToVector2() - { - return new Vector2(this.X, this.Y); - } + public static Point Skew(Point point, Matrix3x2 skew) => Round(Vector2.Transform(new Vector2(point.X, point.Y), skew)); /// /// Translates this by the specified amount. @@ -265,16 +222,10 @@ namespace ImageSharp /// /// The used offset this . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Offset(Point point) - { - this.Offset(point.X, point.Y); - } + public void Offset(Point point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -288,33 +239,16 @@ namespace ImageSharp } /// - public override bool Equals(object obj) - { - if (obj is Point) - { - return this.Equals((Point)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is Point && this.Equals((Point)obj); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Point other) - { - return this.X == other.X && this.Y == other.Y; - } + public bool Equals(Point other) => this.X == other.X && this.Y == other.Y; private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); - private int GetHashCode(Point point) - { - unchecked - { - return point.X ^ point.Y; - } - } + private int GetHashCode(Point point) => point.X ^ point.Y; } } \ No newline at end of file diff --git a/src/ImageSharp/Numerics/PointF.cs b/src/ImageSharp/Numerics/PointF.cs index 1b76badb6..cbe5c7f48 100644 --- a/src/ImageSharp/Numerics/PointF.cs +++ b/src/ImageSharp/Numerics/PointF.cs @@ -71,10 +71,7 @@ namespace ImageSharp /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator PointF(Vector2 vector) - { - return new PointF(vector.X, vector.Y); - } + public static implicit operator PointF(Vector2 vector) => new PointF(vector.X, vector.Y); /// /// Creates a with the coordinates of the specified . @@ -84,10 +81,7 @@ namespace ImageSharp /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2(PointF point) - { - return new Vector2(point.X, point.Y); - } + public static implicit operator Vector2(PointF point) => new Vector2(point.X, point.Y); /// /// Creates a with the coordinates of the specified by truncating each of the coordinates. @@ -97,10 +91,7 @@ namespace ImageSharp /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Point(PointF point) - { - return new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); - } + public static explicit operator Point(PointF point) => Point.Truncate(point); /// /// Translates a by a given . @@ -111,10 +102,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PointF operator +(PointF point, SizeF size) - { - return Add(point, size); - } + public static PointF operator +(PointF point, SizeF size) => Add(point, size); /// /// Translates a by the negative of a given . @@ -123,10 +111,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PointF operator -(PointF point, SizeF size) - { - return Subtract(point, size); - } + public static PointF operator -(PointF point, SizeF size) => Subtract(point, size); /// /// Compares two objects for equality. @@ -141,10 +126,7 @@ namespace ImageSharp /// True if the current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(PointF left, PointF right) - { - return left.Equals(right); - } + public static bool operator ==(PointF left, PointF right) => left.Equals(right); /// /// Compares two objects for inequality. @@ -159,10 +141,7 @@ namespace ImageSharp /// True if the current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(PointF left, PointF right) - { - return !left.Equals(right); - } + public static bool operator !=(PointF left, PointF right) => !left.Equals(right); /// /// Translates a by the negative of a given . @@ -171,10 +150,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PointF Add(PointF point, SizeF size) - { - return new PointF(point.X + size.Width, point.Y + size.Height); - } + public static PointF Add(PointF point, SizeF size) => new PointF(point.X + size.Width, point.Y + size.Height); /// /// Translates a by the negative of a given . @@ -183,10 +159,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PointF Subtract(PointF point, SizeF size) - { - return new PointF(point.X - size.Width, point.Y - size.Height); - } + public static PointF Subtract(PointF point, SizeF size) => new PointF(point.X - size.Width, point.Y - size.Height); /// /// Rotates a point around the given rotation matrix. @@ -195,10 +168,7 @@ namespace ImageSharp /// Rotation matrix used /// The rotated [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PointF Rotate(PointF point, Matrix3x2 rotation) - { - return Vector2.Transform(new Vector2(point.X, point.Y), rotation); - } + public static PointF Rotate(PointF point, Matrix3x2 rotation) => Vector2.Transform(new Vector2(point.X, point.Y), rotation); /// /// Skews a point using the given skew matrix. @@ -207,10 +177,7 @@ namespace ImageSharp /// Rotation matrix used /// The rotated [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PointF Skew(PointF point, Matrix3x2 skew) - { - return Vector2.Transform(new Vector2(point.X, point.Y), skew); - } + public static PointF Skew(PointF point, Matrix3x2 skew) => Vector2.Transform(new Vector2(point.X, point.Y), skew); /// /// Translates this by the specified amount. @@ -229,16 +196,10 @@ namespace ImageSharp /// /// The used offset this . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Offset(PointF point) - { - this.Offset(point.X, point.Y); - } + public void Offset(PointF point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -252,22 +213,11 @@ namespace ImageSharp } /// - public override bool Equals(object obj) - { - if (obj is PointF) - { - return this.Equals((PointF)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is PointF && this.Equals((PointF)obj); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(PointF other) - { - return this.X.Equals(other.X) && this.Y.Equals(other.Y); - } + public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); /// /// Returns the hash code for this instance. @@ -278,12 +228,6 @@ namespace ImageSharp /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(PointF point) - { - unchecked - { - return point.X.GetHashCode() ^ point.Y.GetHashCode(); - } - } + private int GetHashCode(PointF point) => point.X.GetHashCode() ^ point.Y.GetHashCode(); } } \ No newline at end of file diff --git a/src/ImageSharp/Numerics/Size.cs b/src/ImageSharp/Numerics/Size.cs index c46247307..79ee1ddea 100644 --- a/src/ImageSharp/Numerics/Size.cs +++ b/src/ImageSharp/Numerics/Size.cs @@ -87,20 +87,14 @@ namespace ImageSharp /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator SizeF(Size size) - { - return new SizeF(size.Width, size.Height); - } + public static implicit operator SizeF(Size size) => new SizeF(size.Width, size.Height); /// /// Converts the given into a . /// /// The size [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Point(Size size) - { - return new Point(size.Width, size.Height); - } + public static explicit operator Point(Size size) => new Point(size.Width, size.Height); /// /// Computes the sum of adding two sizes. @@ -111,10 +105,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size operator +(Size left, Size right) - { - return Add(left, right); - } + public static Size operator +(Size left, Size right) => Add(left, right); /// /// Computes the difference left by subtracting one size from another. @@ -125,10 +116,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size operator -(Size left, Size right) - { - return Subtract(left, right); - } + public static Size operator -(Size left, Size right) => Subtract(left, right); /// /// Compares two objects for equality. @@ -143,10 +131,7 @@ namespace ImageSharp /// True if the current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Size left, Size right) - { - return left.Equals(right); - } + public static bool operator ==(Size left, Size right) => left.Equals(right); /// /// Compares two objects for inequality. @@ -161,10 +146,7 @@ namespace ImageSharp /// True if the current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Size left, Size right) - { - return !left.Equals(right); - } + public static bool operator !=(Size left, Size right) => !left.Equals(right); /// /// Performs vector addition of two objects. @@ -173,10 +155,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Add(Size left, Size right) - { - return new Size(left.Width + right.Width, left.Height + right.Height); - } + public static Size Add(Size left, Size right) => new Size(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height)); /// /// Contracts a by another @@ -185,10 +164,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Subtract(Size left, Size right) - { - return new Size(left.Width - right.Width, left.Height - right.Height); - } + public static Size Subtract(Size left, Size right) => new Size(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height)); /// /// Converts a to a by performing a ceiling operation on all the dimensions. @@ -196,10 +172,7 @@ namespace ImageSharp /// The size /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Ceiling(SizeF size) - { - return new Size((int)MathF.Ceiling(size.Width), (int)MathF.Ceiling(size.Height)); - } + public static Size Ceiling(SizeF size) => new Size(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height))); /// /// Converts a to a by performing a round operation on all the dimensions. @@ -207,16 +180,18 @@ namespace ImageSharp /// The size /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Round(SizeF size) - { - return new Size((int)MathF.Round(size.Width), (int)MathF.Round(size.Height)); - } + public static Size Round(SizeF size) => new Size(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height))); + + /// + /// Converts a to a by performing a round operation on all the dimensions. + /// + /// The size + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Truncate(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -230,22 +205,11 @@ namespace ImageSharp } /// - public override bool Equals(object obj) - { - if (obj is Size) - { - return this.Equals((Size)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is Size && this.Equals((Size)obj); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Size other) - { - return this.Width == other.Width && this.Height == other.Height; - } + public bool Equals(Size other) => this.Width == other.Width && this.Height == other.Height; /// /// Returns the hash code for this instance. @@ -256,12 +220,6 @@ namespace ImageSharp /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(Size size) - { - unchecked - { - return size.Width ^ size.Height; - } - } + private int GetHashCode(Size size) => size.Width ^ size.Height; } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/SizeF.cs b/src/ImageSharp/Numerics/SizeF.cs index f6b852cfa..78078fd01 100644 --- a/src/ImageSharp/Numerics/SizeF.cs +++ b/src/ImageSharp/Numerics/SizeF.cs @@ -79,20 +79,14 @@ namespace ImageSharp /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Size(SizeF size) - { - return new Size(unchecked((int)size.Width), unchecked((int)size.Height)); - } + public static explicit operator Size(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); /// /// Converts the given into a . /// /// The size [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator PointF(SizeF size) - { - return new PointF(size.Width, size.Height); - } + public static explicit operator PointF(SizeF size) => new PointF(size.Width, size.Height); /// /// Computes the sum of adding two sizes. @@ -103,10 +97,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SizeF operator +(SizeF left, SizeF right) - { - return Add(left, right); - } + public static SizeF operator +(SizeF left, SizeF right) => Add(left, right); /// /// Computes the difference left by subtracting one size from another. @@ -117,10 +108,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SizeF operator -(SizeF left, SizeF right) - { - return Subtract(left, right); - } + public static SizeF operator -(SizeF left, SizeF right) => Subtract(left, right); /// /// Compares two objects for equality. @@ -131,10 +119,7 @@ namespace ImageSharp /// True if the current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(SizeF left, SizeF right) - { - return left.Equals(right); - } + public static bool operator ==(SizeF left, SizeF right) => left.Equals(right); /// /// Compares two objects for inequality. @@ -145,10 +130,7 @@ namespace ImageSharp /// True if the current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(SizeF left, SizeF right) - { - return !left.Equals(right); - } + public static bool operator !=(SizeF left, SizeF right) => !left.Equals(right); /// /// Performs vector addition of two objects. @@ -157,10 +139,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SizeF Add(SizeF left, SizeF right) - { - return new SizeF(left.Width + right.Width, left.Height + right.Height); - } + public static SizeF Add(SizeF left, SizeF right) => new SizeF(left.Width + right.Width, left.Height + right.Height); /// /// Contracts a by another @@ -169,10 +148,7 @@ namespace ImageSharp /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SizeF Subtract(SizeF left, SizeF right) - { - return new SizeF(left.Width - right.Width, left.Height - right.Height); - } + public static SizeF Subtract(SizeF left, SizeF right) => new SizeF(left.Width - right.Width, left.Height - right.Height); /// public override int GetHashCode() @@ -192,38 +168,12 @@ namespace ImageSharp } /// - public override bool Equals(object obj) - { - if (obj is SizeF) - { - return this.Equals((SizeF)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is SizeF && this.Equals((SizeF)obj); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(SizeF other) - { - return this.Width.Equals(other.Width) && this.Height.Equals(other.Height); - } + public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); - /// - /// 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(SizeF size) - { - unchecked - { - return size.Width.GetHashCode() ^ size.Height.GetHashCode(); - } - } + private int GetHashCode(SizeF size) => size.Width.GetHashCode() ^ size.Height.GetHashCode(); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 23fea94e9..4e4a36a38 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -52,7 +52,7 @@ namespace ImageSharp.Processing.Processors int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; - var centre = Rectangle.Center(sourceRectangle).ToVector2(); + Vector2 centre = Rectangle.Center(sourceRectangle); float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 4dfa41989..23ba5afe0 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -58,7 +58,7 @@ namespace ImageSharp.Processing.Processors int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel vignetteColor = this.VignetteColor; - var centre = Rectangle.Center(sourceRectangle).ToVector2(); + Vector2 centre = Rectangle.Center(sourceRectangle); float rX = this.RadiusX > 0 ? MathF.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; float rY = this.RadiusY > 0 ? MathF.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 7608d3065..be4929d9a 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -79,7 +79,7 @@ namespace ImageSharp.Benchmarks int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; - Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); + Vector2 centre = Rectangle.Center(sourceRectangle); float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. From 05716295ff71ade8c0687bb543cd108ce7328b91 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 May 2017 12:39:15 +1000 Subject: [PATCH 3/6] Rectangle and RectangleF --- src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs | 4 +- .../Processors/DrawPathProcessor.cs | 3 +- src/ImageSharp/Numerics/Point.cs | 7 +- src/ImageSharp/Numerics/Rectangle.cs | 440 ++++++++++++------ src/ImageSharp/Numerics/RectangleF.cs | 422 ++++++++++------- .../Numerics/RectangleFTests.cs | 266 +++++++++++ .../Numerics/RectangleTests.cs | 318 +++++++++++-- 7 files changed, 1097 insertions(+), 363 deletions(-) create mode 100644 tests/ImageSharp.Tests/Numerics/RectangleFTests.cs diff --git a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs index 53a3c8c99..a5639e49a 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs @@ -132,7 +132,7 @@ namespace ImageSharp.Drawing.Pens { this.brush = brush.CreateApplicator(sourcePixels, region, options); this.halfWidth = width / 2; - this.RequiredRegion = RectangleF.Outset(region, width); + this.RequiredRegion = RectangleF.Inflate(region, width, width); } public override RectangleF RequiredRegion @@ -185,7 +185,7 @@ namespace ImageSharp.Drawing.Pens this.pattern[i + 1] = this.totalLength; } - this.RequiredRegion = RectangleF.Outset(region, width); + this.RequiredRegion = RectangleF.Inflate(region, width, width); } public override RectangleF RequiredRegion diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 860c4c4f0..d40c71f7e 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Drawing.Processors { using System; - using System.Numerics; using System.Threading.Tasks; using ImageSharp.Memory; @@ -58,7 +57,7 @@ namespace ImageSharp.Drawing.Processors { using (PenApplicator applicator = this.Pen.CreateApplicator(source, this.Path.Bounds, this.Options)) { - Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); + var rect = Rectangle.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; diff --git a/src/ImageSharp/Numerics/Point.cs b/src/ImageSharp/Numerics/Point.cs index 4d43a83af..9bff27d2a 100644 --- a/src/ImageSharp/Numerics/Point.cs +++ b/src/ImageSharp/Numerics/Point.cs @@ -213,8 +213,11 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(int dx, int dy) { - this.X += dx; - this.Y += dy; + unchecked + { + this.X += dx; + this.Y += dy; + } } /// diff --git a/src/ImageSharp/Numerics/Rectangle.cs b/src/ImageSharp/Numerics/Rectangle.cs index b651eff71..d16b22920 100644 --- a/src/ImageSharp/Numerics/Rectangle.cs +++ b/src/ImageSharp/Numerics/Rectangle.cs @@ -8,6 +8,7 @@ namespace ImageSharp using System; using System.ComponentModel; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Stores a set of four integers that represent the location and size of a rectangle. @@ -23,11 +24,6 @@ namespace ImageSharp /// public static readonly Rectangle Empty = default(Rectangle); - /// - /// The backing vector for SIMD support. - /// - private Vector4 backingVector; - /// /// Initializes a new instance of the struct. /// @@ -37,7 +33,10 @@ namespace ImageSharp /// The height of the rectangle. public Rectangle(int x, int y, int width, int height) { - this.backingVector = new Vector4(x, y, width, height); + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; } /// @@ -51,197 +50,325 @@ namespace ImageSharp /// public Rectangle(Point point, Size size) { - this.backingVector = new Vector4(point.X, point.Y, size.Width, size.Height); + this.X = point.X; + this.Y = point.Y; + this.Width = size.Width; + this.Height = size.Height; } /// - /// Initializes a new instance of the struct. + /// Gets or sets the x-coordinate of this . /// - /// - /// The which specifies the rectangles top left point in a two-dimensional plane. - /// - /// - /// The which specifies the rectangles bottom right point in a two-dimensional plane. - /// - public Rectangle(Point topLeft, Point bottomRight) - { - this.backingVector = new Vector4(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); - } + public int X { get; set; } /// - /// Initializes a new instance of the struct. + /// Gets or sets the y-coordinate of this . /// - /// The vector. - public Rectangle(Vector4 vector) - { - this.backingVector = vector; - } + public int Y { get; set; } /// - /// Gets or sets the x-coordinate of this . + /// Gets or sets the width of this . + /// + public int Width { get; set; } + + /// + /// Gets or sets the height of this . /// - public int X + public int Height { get; set; } + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Point Location { - get - { - return (int)this.backingVector.X; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Point(this.X, this.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.X = value; + this.X = value.X; + this.Y = value.Y; } } /// - /// Gets or sets the y-coordinate of this . + /// Gets or sets the size of this . /// - public int Y + [EditorBrowsable(EditorBrowsableState.Never)] + public Size Size { - get - { - return (int)this.backingVector.Y; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Size(this.Width, this.Height); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.Y = value; + this.Width = value.Width; + this.Height = value.Height; } } /// - /// Gets or sets the width of this . + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Gets the y-coordinate of the top edge of this . /// - public int Width + public int Top { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return (int)this.backingVector.Z; + return this.Y; } + } - set + /// + /// Gets the x-coordinate of the right edge of this . + /// + public int Right + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - this.backingVector.Z = value; + return unchecked(this.X + this.Width); } } /// - /// Gets or sets the height of this . + /// Gets the y-coordinate of the bottom edge of this . /// - public int Height + public int Bottom { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return (int)this.backingVector.W; + return unchecked(this.Y + this.Height); } + } - set + /// + /// Gets the x-coordinate of the left edge of this . + /// + public int Left + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - this.backingVector.W = value; + return this.X; } } /// - /// Gets the size of this . + /// Creates a with the coordinates of the specified . /// - public Size Size => new Size(this.Width, this.Height); + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator RectangleF(Rectangle rectangle) => new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); /// - /// Gets a value indicating whether this is empty. + /// Creates a with the coordinates of the specified . /// - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector4(Rectangle rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); /// - /// Gets the y-coordinate of the top edge of this . + /// Compares two objects for equality. /// - public int Top => this.Y; + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right); /// - /// Gets the x-coordinate of the right edge of this . + /// Compares two objects for inequality. /// - public int Right => this.X + this.Width; + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right); /// - /// Gets the y-coordinate of the bottom edge of this . + /// Creates a new with the specified location and size. + /// The left coordinate of the rectangle + /// The top coordinate of the rectangle + /// The right coordinate of the rectangle + /// The bottom coordinate of the rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + // ReSharper disable once InconsistentNaming + public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); + + /// + /// Returns the center point of the given /// - public int Bottom => this.Y + this.Height; + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Center(Rectangle rectangle) => new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); /// - /// Gets the x-coordinate of the left edge of this . + /// Creates a rectangle that represents the intersection between and + /// . If there is no intersection, an empty rectangle is returned. /// - public int Left => this.X; + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Intersect(Rectangle a, Rectangle b) + { + int x1 = Math.Max(a.X, b.X); + int x2 = Math.Min(a.Right, b.Right); + int y1 = Math.Max(a.Y, b.Y); + int y2 = Math.Min(a.Bottom, b.Bottom); + + if (x2 >= x1 && y2 >= y1) + { + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + return Empty; + } /// - /// Computes the sum of adding two rectangles. + /// Creates a that is inflated by the specified amount. /// - /// 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) + /// The rectangle + /// The amount to inflate the width by + /// The amount to inflate the height by + /// A new + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Inflate(Rectangle rectangle, int x, int y) { - return new Rectangle(left.backingVector + right.backingVector); + Rectangle r = rectangle; + r.Inflate(x, y); + return r; } /// - /// Computes the difference left by subtracting one rectangle from another. + /// Converts a to a by performing a ceiling operation on all the coordinates. /// - /// 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) + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Ceiling(RectangleF rectangle) { - return new Rectangle(left.backingVector - right.backingVector); + unchecked + { + return new Rectangle( + (int)MathF.Ceiling(rectangle.X), + (int)MathF.Ceiling(rectangle.Y), + (int)MathF.Ceiling(rectangle.Width), + (int)MathF.Ceiling(rectangle.Height)); + } } /// - /// Compares two objects for equality. + /// Converts a to a by performing a truncate operation on all the coordinates. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(Rectangle left, Rectangle right) + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Truncate(RectangleF rectangle) { - return left.Equals(right); + unchecked + { + return new Rectangle( + (int)rectangle.X, + (int)rectangle.Y, + (int)rectangle.Width, + (int)rectangle.Height); + } } /// - /// Compares two objects for inequality. + /// Converts a to a by performing a round operation on all the coordinates. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(Rectangle left, Rectangle right) + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Round(RectangleF rectangle) { - return !left.Equals(right); + unchecked + { + return new Rectangle( + (int)MathF.Round(rectangle.X), + (int)MathF.Round(rectangle.Y), + (int)MathF.Round(rectangle.Width), + (int)MathF.Round(rectangle.Height)); + } } /// - /// Returns the center point of the given + /// Creates a rectangle that represents the union between and . + /// + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Union(Rectangle a, Rectangle b) + { + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.Right, b.Right); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Bottom, b.Bottom); + + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + /// + /// Creates a Rectangle that represents the intersection between this Rectangle and the . /// /// The rectangle - /// - public static Point Center(Rectangle rectangle) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Intersect(Rectangle rectangle) { - return new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + Rectangle result = Intersect(rectangle, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; } + /// + /// Inflates this by the specified amount. + /// + /// The width + /// The height + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(int width, int height) + { + unchecked + { + this.X -= width; + this.Y -= height; + + this.Width += 2 * width; + this.Height += 2 * height; + } + } + + /// + /// Inflates this by the specified amount. + /// + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(Size size) => this.Inflate(size.Width, size.Height); + /// /// Determines if the specfied point is contained within the rectangular region defined by /// this . @@ -249,33 +376,63 @@ namespace ImageSharp /// The x-coordinate of the given point. /// The y-coordinate of the given point. /// The - public bool Contains(int x, int y) - { - // TODO: SIMD? - return this.X <= x - && x < this.Right - && this.Y <= y - && y < this.Bottom; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(int x, int y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this . + /// + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(Point point) => this.Contains(point.X, point.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained + /// within the rectangular region represented by this . + /// + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(Rectangle rectangle) => + (this.X <= rectangle.X) && (rectangle.Right <= this.Right) && + (this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom); /// /// Determines if the specfied intersects the rectangular region defined by /// this . /// - /// The other Rectange + /// The other Rectange /// The - public bool Intersects(Rectangle rect) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IntersectsWith(Rectangle rectangle) => + (rectangle.X < this.Right) && (this.X < rectangle.Right) && + (rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(Point point) => this.Offset(point.X, point.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(int dx, int dy) { - return rect.Left <= this.Right && rect.Right >= this.Left - && - rect.Top <= this.Bottom && rect.Bottom >= this.Top; + unchecked + { + this.X += dx; + this.Y += dy; + } } /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -285,39 +442,26 @@ namespace ImageSharp return "Rectangle [ Empty ]"; } - return - $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; + return $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; } /// - public override bool Equals(object obj) - { - if (obj is Rectangle) - { - return this.Equals((Rectangle)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is Rectangle && this.Equals((Rectangle)obj); /// - public bool Equals(Rectangle other) - { - return this.backingVector.Equals(other.backingVector); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rectangle other) => this.X == other.X && this.Y == other.Y && this.Width == other.Width && this.Height == other.Height; - /// - /// 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(Rectangle rectangle) { - return rectangle.backingVector.GetHashCode(); + unchecked + { + int hashCode = rectangle.X; + hashCode = (hashCode * 397) ^ rectangle.Y; + hashCode = (hashCode * 397) ^ rectangle.Width; + hashCode = (hashCode * 397) ^ rectangle.Height; + return hashCode; + } } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/RectangleF.cs b/src/ImageSharp/Numerics/RectangleF.cs index 2ed57c841..28d887c5d 100644 --- a/src/ImageSharp/Numerics/RectangleF.cs +++ b/src/ImageSharp/Numerics/RectangleF.cs @@ -8,9 +8,10 @@ namespace ImageSharp using System; using System.ComponentModel; using System.Numerics; + using System.Runtime.CompilerServices; /// - /// Stores a set of four integers that represent the location and size of a rectangle. + /// Stores a set of four single precision floating points that represent the location and size of a rectangle. /// /// /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, @@ -19,15 +20,10 @@ namespace ImageSharp public struct RectangleF : IEquatable { /// - /// Represents a that has X, Y, Width, and Height values set to zero. + /// Represents a that has X, Y, Width, and Height values set to zero. /// public static readonly RectangleF Empty = default(RectangleF); - /// - /// The backing vector for SIMD support. - /// - private Vector4 backingVector; - /// /// Initializes a new instance of the struct. /// @@ -37,221 +33,291 @@ namespace ImageSharp /// The height of the rectangle. public RectangleF(float x, float y, float width, float height) { - this.backingVector = new Vector4(x, y, width, height); + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; } /// /// Initializes a new instance of the struct. /// - /// The vector. - public RectangleF(Vector4 vector) + /// + /// The which specifies the rectangles point in a two-dimensional plane. + /// + /// + /// The which specifies the rectangles height and width. + /// + public RectangleF(PointF point, SizeF size) { - this.backingVector = vector; + this.X = point.X; + this.Y = point.Y; + this.Width = size.Width; + this.Height = size.Height; } /// /// Gets or sets the x-coordinate of this . /// - public float X + public float X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public float Y { get; set; } + + /// + /// Gets or sets the width of this . + /// + public float Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public float Height { get; set; } + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public PointF Location { - get - { - return this.backingVector.X; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new PointF(this.X, this.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.X = value; + this.X = value.X; + this.Y = value.Y; } } /// - /// Gets or sets the y-coordinate of this . + /// Gets or sets the size of this . /// - public float Y + [EditorBrowsable(EditorBrowsableState.Never)] + public SizeF Size { - get - { - return this.backingVector.Y; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new SizeF(this.Width, this.Height); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.Y = value; + this.Width = value.Width; + this.Height = value.Height; } } /// - /// Gets or sets the width of this . + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => (this.Width <= 0) || (this.Height <= 0); + + /// + /// Gets the y-coordinate of the top edge of this . /// - public float Width + public float Top { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return this.backingVector.Z; - } - - set - { - this.backingVector.Z = value; + return this.Y; } } /// - /// Gets or sets the height of this . + /// Gets the x-coordinate of the right edge of this . /// - public float Height + public float Right { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return this.backingVector.W; + return this.X + this.Width; } + } - set + /// + /// Gets the y-coordinate of the bottom edge of this . + /// + public float Bottom + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - this.backingVector.W = value; + return this.Y + this.Height; } } /// - /// Gets a value indicating whether this is empty. + /// Gets the x-coordinate of the left edge of this . /// - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public float Left + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.X; + } + } /// - /// Gets the y-coordinate of the top edge of this . + /// Creates a with the coordinates of the specified . /// - public float Top => this.Y; + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator RectangleF(Vector4 vector) => new RectangleF(vector.X, vector.Y, vector.Z, vector.W); /// - /// Gets the x-coordinate of the right edge of this . + /// Creates a with the coordinates of the specified . /// - public float Right => this.X + this.Width; + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector4(RectangleF rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); /// - /// Gets the y-coordinate of the bottom edge of this . + /// Creates a with the coordinates of the specified by truncating each coordinate. /// - public float Bottom => this.Y + this.Height; + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Rectangle(RectangleF rectangle) => Rectangle.Truncate(rectangle); /// - /// Gets the x-coordinate of the left edge of this . + /// Compares two objects for equality. /// - public float Left => this.X; + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(RectangleF left, RectangleF right) => left.Equals(right); /// - /// Performs an implicit conversion from to . + /// Compares two objects for inequality. /// - /// The d. + /// The on the left side of the operand. + /// The on the right side of the operand. /// - /// The result of the conversion. + /// True if the current left is unequal to the parameter; otherwise, false. /// - public static implicit operator RectangleF(Rectangle d) - { - return new RectangleF(d.Left, d.Top, d.Width, d.Height); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(RectangleF left, RectangleF right) => !left.Equals(right); /// - /// Computes the sum of adding two rectangles. + /// Creates a new with the specified location and size. + /// The left coordinate of the rectangle + /// The top coordinate of the rectangle + /// The right coordinate of the rectangle + /// The bottom coordinate of the rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + // ReSharper disable once InconsistentNaming + public static RectangleF FromLTRB(float left, float top, float right, float bottom) => new RectangleF(left, top, right - left, bottom - top); + + /// + /// Returns the center point of the given /// - /// The rectangle on the left hand of the operand. - /// The rectangle on the right hand of the operand. - /// - /// The - /// - public static RectangleF operator +(RectangleF left, RectangleF right) - { - return new RectangleF(left.backingVector + right.backingVector); - } + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Center(RectangleF rectangle) => new PointF(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); /// - /// Computes the difference left by subtracting one rectangle from another. + /// Creates a rectangle that represents the intersection between and + /// . If there is no intersection, an empty rectangle is returned. /// - /// The rectangle on the left hand of the operand. - /// The rectangle on the right hand of the operand. - /// - /// The - /// - public static RectangleF operator -(RectangleF left, RectangleF right) + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Intersect(RectangleF a, RectangleF b) { - return new RectangleF(left.backingVector - right.backingVector); + float x1 = MathF.Max(a.X, b.X); + float x2 = MathF.Min(a.Right, b.Right); + float y1 = MathF.Max(a.Y, b.Y); + float y2 = MathF.Min(a.Bottom, b.Bottom); + + if (x2 >= x1 && y2 >= y1) + { + return new RectangleF(x1, y1, x2 - x1, y2 - y1); + } + + return Empty; } /// - /// Compares two objects for equality. + /// Creates a that is inflated by the specified amount. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(RectangleF left, RectangleF right) + /// The rectangle + /// The amount to inflate the width by + /// The amount to inflate the height by + /// A new + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Inflate(RectangleF rectangle, float x, float y) { - return left.Equals(right); + RectangleF r = rectangle; + r.Inflate(x, y); + return r; } /// - /// Compares two objects for inequality. + /// Creates a rectangle that represents the union between and . /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(RectangleF left, RectangleF right) + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Union(RectangleF a, RectangleF b) { - return !left.Equals(right); + float x1 = MathF.Min(a.X, b.X); + float x2 = MathF.Max(a.Right, b.Right); + float y1 = MathF.Min(a.Y, b.Y); + float y2 = MathF.Max(a.Bottom, b.Bottom); + + return new RectangleF(x1, y1, x2 - x1, y2 - y1); } /// - /// Returns the center point of the given + /// Creates a RectangleF that represents the intersection between this RectangleF and the . /// /// The rectangle - /// - public static Vector2 Center(RectangleF rectangle) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Intersect(RectangleF rectangle) { - return new Vector2(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + RectangleF result = Intersect(rectangle, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; } /// - /// Rounds the points away from the center this into a - /// by rounding the dimensions to the nerent integer ensuring that the new rectangle is - /// never smaller than the source + /// Inflates this by the specified amount. /// - /// The source area to round out - /// - /// The smallest that the will fit inside. - /// - public static Rectangle Ceiling(RectangleF source) + /// The width + /// The height + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(float width, float height) { - int y = (int)Math.Floor(source.Y); - int width = (int)Math.Ceiling(source.Width); - int x = (int)Math.Floor(source.X); - int height = (int)Math.Ceiling(source.Height); - return new Rectangle(x, y, width, height); + this.X -= width; + this.Y -= height; + + this.Width += 2 * width; + this.Height += 2 * height; } /// - /// Outsets the specified region. + /// Inflates this by the specified amount. /// - /// The region. - /// The width. - /// - /// The with all dimensions move away from the center by the offset. - /// - public static RectangleF Outset(RectangleF region, float width) - { - float dblWidth = width * 2; - return new RectangleF(region.X - width, region.Y - width, region.Width + dblWidth, region.Height + dblWidth); - } + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(SizeF size) => this.Inflate(size.Width, size.Height); /// /// Determines if the specfied point is contained within the rectangular region defined by @@ -260,75 +326,89 @@ namespace ImageSharp /// The x-coordinate of the given point. /// The y-coordinate of the given point. /// The - public bool Contains(float x, float y) - { - // TODO: SIMD? - return this.X <= x - && x < this.Right - && this.Y <= y - && y < this.Bottom; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(float x, float y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this . + /// + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(PointF point) => this.Contains(point.X, point.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained + /// within the rectangular region represented by this . + /// + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(RectangleF rectangle) => + (this.X <= rectangle.X) && (rectangle.Right <= this.Right) && + (this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom); /// - /// Determines if the specfied intersects the rectangular region defined by - /// this . + /// Determines if the specfied intersects the rectangular region defined by + /// this . /// - /// The other Rectange + /// The other Rectange /// The - public bool Intersects(RectangleF rect) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IntersectsWith(RectangleF rectangle) => + (rectangle.X < this.Right) && (this.X < rectangle.Right) && + (rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(PointF point) => this.Offset(point.X, point.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(float dx, float dy) { - return rect.Left <= this.Right && rect.Right >= this.Left - && - rect.Top <= this.Bottom && rect.Bottom >= this.Top; + this.X += dx; + this.Y += dy; } /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() { if (this.IsEmpty) { - return "Rectangle [ Empty ]"; + return "RectangleF [ Empty ]"; } - return - $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; + return $"RectangleF [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; } /// - public override bool Equals(object obj) - { - if (obj is RectangleF) - { - return this.Equals((RectangleF)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is RectangleF && this.Equals((RectangleF)obj); /// - public bool Equals(RectangleF other) - { - return this.backingVector.Equals(other.backingVector); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RectangleF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Width.Equals(other.Width) && this.Height.Equals(other.Height); - /// - /// 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(RectangleF rectangle) { - return rectangle.backingVector.GetHashCode(); + 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; + } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs b/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs new file mode 100644 index 000000000..e06cf707e --- /dev/null +++ b/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs @@ -0,0 +1,266 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Globalization; + using System.Reflection; + + using Xunit; + + /// + /// Tests the struct. + /// + public class RectangleFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(RectangleF.Empty, new RectangleF()); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void NonDefaultConstructorTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var p = new PointF(x, y); + var s = new SizeF(width, height); + var rect2 = new RectangleF(p, s); + + Assert.Equal(rect1, rect2); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void FromLTRBTest(float left, float top, float right, float bottom) + { + var expected = new RectangleF(left, top, right - left, bottom - top); + var actual = RectangleF.FromLTRB(left, top, right, bottom); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void DimensionsTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + var p = new PointF(x, y); + var s = new SizeF(width, height); + + Assert.Equal(p, rect.Location); + Assert.Equal(s, rect.Size); + Assert.Equal(x, rect.X); + Assert.Equal(y, rect.Y); + Assert.Equal(width, rect.Width); + Assert.Equal(height, rect.Height); + Assert.Equal(x, rect.Left); + Assert.Equal(y, rect.Top); + Assert.Equal(x + width, rect.Right); + Assert.Equal(y + height, rect.Bottom); + } + + [Fact] + public void IsEmptyTest() + { + Assert.True(RectangleF.Empty.IsEmpty); + Assert.True(new RectangleF().IsEmpty); + Assert.True(new RectangleF(1, -2, -10, 10).IsEmpty); + Assert.True(new RectangleF(1, -2, 10, -10).IsEmpty); + Assert.True(new RectangleF(1, -2, 0, 0).IsEmpty); + + Assert.False(new RectangleF(0, 0, 10, 10).IsEmpty); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(float.MaxValue, float.MinValue)] + public static void LocationSetTest(float x, float y) + { + var point = new PointF(x, y); + var rect = new RectangleF(10, 10, 10, 10) { Location = point }; + Assert.Equal(point, rect.Location); + Assert.Equal(point.X, rect.X); + Assert.Equal(point.Y, rect.Y); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(float.MaxValue, float.MinValue)] + public static void SizeSetTest(float x, float y) + { + var size = new SizeF(x, y); + var rect = new RectangleF(10, 10, 10, 10) { Size = size }; + Assert.Equal(size, rect.Size); + Assert.Equal(size.Width, rect.Width); + Assert.Equal(size.Height, rect.Height); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void EqualityTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var rect2 = new RectangleF(width, height, x, y); + + Assert.True(rect1 != rect2); + Assert.False(rect1 == rect2); + Assert.False(rect1.Equals(rect2)); + Assert.False(rect1.Equals((object)rect2)); + } + + [Fact] + public static void EqualityTestNotRectangleF() + { + var rectangle = new RectangleF(0, 0, 0, 0); + Assert.False(rectangle.Equals(null)); + Assert.False(rectangle.Equals(0)); + + // If RectangleF implements IEquatable (e.g. in .NET Core), then classes that are implicitly + // convertible to RectangleF can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToRectangleF = typeof(IEquatable).IsAssignableFrom(rectangle.GetType()); + Assert.Equal(expectsImplicitCastToRectangleF, rectangle.Equals(new Rectangle(0, 0, 0, 0))); + + Assert.False(rectangle.Equals((object)new Rectangle(0, 0, 0, 0))); // No implicit cast + } + + [Fact] + public static void GetHashCodeTest() + { + var rect1 = new RectangleF(10, 10, 10, 10); + var rect2 = new RectangleF(10, 10, 10, 10); + Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(20, 10, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 20, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 20, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void ContainsTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + float X = (x + width) / 2; + float Y = (y + height) / 2; + var p = new PointF(X, Y); + var r = new RectangleF(X, Y, width / 2, height / 2); + + Assert.False(rect.Contains(X, Y)); + Assert.False(rect.Contains(p)); + Assert.False(rect.Contains(r)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue / 2, float.MinValue / 2, float.MinValue / 2, float.MaxValue / 2)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void InflateTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + var inflatedRect = new RectangleF(x - width, y - height, width + 2 * width, height + 2 * height); + + rect.Inflate(width, height); + Assert.Equal(inflatedRect, rect); + + var s = new SizeF(x, y); + inflatedRect = RectangleF.Inflate(rect, x, y); + + rect.Inflate(s); + Assert.Equal(inflatedRect, rect); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue / 2, float.MinValue / 2)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void IntersectTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var rect2 = new RectangleF(y, x, width, height); + var expectedRect = RectangleF.Intersect(rect1, rect2); + rect1.Intersect(rect2); + Assert.Equal(expectedRect, rect1); + Assert.False(rect1.IntersectsWith(expectedRect)); + } + + [Fact] + public static void IntersectIntersectingRectsTest() + { + var rect1 = new RectangleF(0, 0, 5, 5); + var rect2 = new RectangleF(1, 1, 3, 3); + var expected = new RectangleF(1, 1, 3, 3); + + Assert.Equal(expected, RectangleF.Intersect(rect1, rect2)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void UnionTest(float x, float y, float width, float height) + { + var a = new RectangleF(x, y, width, height); + var b = new RectangleF(width, height, x, y); + + float x1 = Math.Min(a.X, b.X); + float x2 = Math.Max(a.X + a.Width, b.X + b.Width); + float y1 = Math.Min(a.Y, b.Y); + float y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + var expectedRectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1); + + Assert.Equal(expectedRectangle, RectangleF.Union(a, b)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void OffsetTest(float x, float y, float width, float height) + { + var r1 = new RectangleF(x, y, width, height); + var expectedRect = new RectangleF(x + width, y + height, width, height); + var p = new PointF(width, height); + + r1.Offset(p); + Assert.Equal(expectedRect, r1); + + expectedRect.Offset(p); + r1.Offset(width, height); + Assert.Equal(expectedRect, r1); + } + + [Fact] + public void ToStringTest() + { + var r = new RectangleF(5, -5, 0, 1); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); + } + + [Fact] + public void ToStringTestEmpty() + { + var r = new RectangleF(0, 0, 0, 0); + Assert.Equal("RectangleF [ Empty ]", r.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/RectangleTests.cs b/tests/ImageSharp.Tests/Numerics/RectangleTests.cs index 2f9ad3d37..1ef03f0c6 100644 --- a/tests/ImageSharp.Tests/Numerics/RectangleTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RectangleTests.cs @@ -5,6 +5,9 @@ namespace ImageSharp.Tests { + using System; + using System.Globalization; + using Xunit; /// @@ -12,55 +15,294 @@ namespace ImageSharp.Tests /// public class RectangleTests { - /// - /// Tests the equality operators for equality. - /// [Fact] - public void AreEqual() + public void DefaultConstructorTest() + { + Assert.Equal(Rectangle.Empty, new Rectangle()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void NonDefaultConstructorTest(int x, int y, int width, int height) + { + var rect1 = new Rectangle(x, y, width, height); + var rect2 = new Rectangle(new Point(x, y), new Size(width, height)); + + Assert.Equal(rect1, rect2); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void FromLTRBTest(int left, int top, int right, int bottom) { - Rectangle first = new Rectangle(1, 1, 100, 100); - Rectangle second = new Rectangle(1, 1, 100, 100); + var rect1 = new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); + var rect2 = Rectangle.FromLTRB(left, top, right, bottom); - Assert.Equal(first, second); + Assert.Equal(rect1, rect2); } - /// - /// Tests the equality operators for inequality. - /// [Fact] - public void AreNotEqual() + public void EmptyTest() + { + Assert.True(Rectangle.Empty.IsEmpty); + Assert.True(new Rectangle(0, 0, 0, 0).IsEmpty); + Assert.True(new Rectangle().IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void NonEmptyTest(int x, int y, int width, int height) + { + Assert.False(new Rectangle(x, y, width, height).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + public void DimensionsTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + Assert.Equal(new Point(x, y), rect.Location); + Assert.Equal(new Size(width, height), rect.Size); + + Assert.Equal(x, rect.X); + Assert.Equal(y, rect.Y); + Assert.Equal(width, rect.Width); + Assert.Equal(height, rect.Height); + Assert.Equal(x, rect.Left); + Assert.Equal(y, rect.Top); + Assert.Equal(unchecked(x + width), rect.Right); + Assert.Equal(unchecked(y + height), rect.Bottom); + + var p = new Point(width, height); + var s = new Size(x, y); + rect.Location = p; + rect.Size = s; + + Assert.Equal(p, rect.Location); + Assert.Equal(s, rect.Size); + + Assert.Equal(width, rect.X); + Assert.Equal(height, rect.Y); + Assert.Equal(x, rect.Width); + Assert.Equal(y, rect.Height); + Assert.Equal(width, rect.Left); + Assert.Equal(height, rect.Top); + Assert.Equal(unchecked(x + width), rect.Right); + Assert.Equal(unchecked(y + height), rect.Bottom); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(int.MaxValue, int.MinValue)] + public static void LocationSetTest(int x, int y) + { + var point = new Point(x, y); + var rect = new Rectangle(10, 10, 10, 10) { Location = point }; + Assert.Equal(point, rect.Location); + Assert.Equal(point.X, rect.X); + Assert.Equal(point.Y, rect.Y); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(int.MaxValue, int.MinValue)] + public static void SizeSetTest(int x, int y) { - Rectangle first = new Rectangle(1, 1, 0, 100); - Rectangle second = new Rectangle(1, 1, 100, 100); + var size = new Size(x, y); + var rect = new Rectangle(10, 10, 10, 10) { Size = size }; + Assert.Equal(size, rect.Size); + Assert.Equal(size.Width, rect.Width); + Assert.Equal(size.Height, rect.Height); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + public void EqualityTest(int x, int y, int width, int height) + { + var rect1 = new Rectangle(x, y, width, height); + var rect2 = new Rectangle(width / 2, height / 2, x, y); - Assert.NotEqual(first, second); + Assert.True(rect1 != rect2); + Assert.False(rect1 == rect2); + Assert.False(rect1.Equals(rect2)); + Assert.False(rect1.Equals((object)rect2)); } - /// - /// Tests whether the rectangle constructors correctly assign properties. - /// [Fact] - public void ConstructorAssignsProperties() - { - Rectangle first = new Rectangle(1, 1, 50, 100); - Assert.Equal(1, first.X); - Assert.Equal(1, first.Y); - Assert.Equal(50, first.Width); - Assert.Equal(100, first.Height); - Assert.Equal(1, first.Top); - Assert.Equal(51, first.Right); - Assert.Equal(101, first.Bottom); - Assert.Equal(1, first.Left); - - Rectangle second = new Rectangle(new Point(1, 1), new Size(50, 100)); - Assert.Equal(1, second.X); - Assert.Equal(1, second.Y); - Assert.Equal(50, second.Width); - Assert.Equal(100, second.Height); - Assert.Equal(1, second.Top); - Assert.Equal(51, second.Right); - Assert.Equal(101, second.Bottom); - Assert.Equal(1, second.Left); + public static void EqualityTestNotRectangle() + { + var rectangle = new Rectangle(0, 0, 0, 0); + Assert.False(rectangle.Equals(null)); + Assert.False(rectangle.Equals(0)); + Assert.False(rectangle.Equals(new RectangleF(0, 0, 0, 0))); + } + + [Fact] + public static void GetHashCodeTest() + { + var rect1 = new Rectangle(10, 10, 10, 10); + var rect2 = new Rectangle(10, 10, 10, 10); + Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(20, 10, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 20, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 20, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)] + [InlineData(0, 0, 0, 0)] + public void RectangleFConversionTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + Rectangle rCeiling, rTruncate, rRound; + + unchecked + { + rCeiling = new Rectangle((int)Math.Ceiling(x), (int)Math.Ceiling(y), + (int)Math.Ceiling(width), (int)Math.Ceiling(height)); + rTruncate = new Rectangle((int)x, (int)y, (int)width, (int)height); + rRound = new Rectangle((int)Math.Round(x), (int)Math.Round(y), + (int)Math.Round(width), (int)Math.Round(height)); + } + + Assert.Equal(rCeiling, Rectangle.Ceiling(rect)); + Assert.Equal(rTruncate, Rectangle.Truncate(rect)); + Assert.Equal(rRound, Rectangle.Round(rect)); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void ContainsTest(int x, int y, int width, int height) + { + var rect = new Rectangle(unchecked(2 * x - width), unchecked(2 * y - height), width, height); + var p = new Point(x, y); + var r = new Rectangle(x, y, width / 2, height / 2); + + Assert.False(rect.Contains(x, y)); + Assert.False(rect.Contains(p)); + Assert.False(rect.Contains(r)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void InflateTest(int x, int y, int width, int height) + { + Rectangle inflatedRect, rect = new Rectangle(x, y, width, height); + unchecked + { + inflatedRect = new Rectangle(x - width, y - height, width + 2 * width, height + 2 * height); + } + + Assert.Equal(inflatedRect, Rectangle.Inflate(rect, width, height)); + + rect.Inflate(width, height); + Assert.Equal(inflatedRect, rect); + + var s = new Size(x, y); + unchecked + { + inflatedRect = new Rectangle(rect.X - x, rect.Y - y, rect.Width + 2 * x, rect.Height + 2 * y); + } + + rect.Inflate(s); + Assert.Equal(inflatedRect, rect); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void IntersectTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + var expectedRect = Rectangle.Intersect(rect, rect); + rect.Intersect(rect); + Assert.Equal(expectedRect, rect); + Assert.False(rect.IntersectsWith(expectedRect)); + } + + [Fact] + public static void IntersectIntersectingRectsTest() + { + var rect1 = new Rectangle(0, 0, 5, 5); + var rect2 = new Rectangle(1, 1, 3, 3); + var expected = new Rectangle(1, 1, 3, 3); + + Assert.Equal(expected, Rectangle.Intersect(rect1, rect2)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, 0, 0, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void UnionTest(int x, int y, int width, int height) + { + var a = new Rectangle(x, y, width, height); + var b = new Rectangle(width, height, x, y); + + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.X + a.Width, b.X + b.Width); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + var expectedRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); + + Assert.Equal(expectedRectangle, Rectangle.Union(a, b)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, 0, 0, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void OffsetTest(int x, int y, int width, int height) + { + var r1 = new Rectangle(x, y, width, height); + var expectedRect = new Rectangle(x + width, y + height, width, height); + var p = new Point(width, height); + + r1.Offset(p); + Assert.Equal(expectedRect, r1); + + expectedRect.Offset(p); + r1.Offset(width, height); + Assert.Equal(expectedRect, r1); + } + + [Fact] + public void ToStringTest() + { + var r = new Rectangle(5, -5, 0, 1); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Rectangle [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); + } + + [Fact] + public void ToStringTestEmpty() + { + var r = new Rectangle(0, 0, 0, 0); + Assert.Equal("Rectangle [ Empty ]", r.ToString()); } } } \ No newline at end of file From 543e1fe58fce0b5a3c09e84e8eb4a0a2b67fbc6d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 May 2017 13:02:03 +1000 Subject: [PATCH 4/6] Fix RectangleF empty test --- tests/ImageSharp.Tests/Numerics/RectangleFTests.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs b/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs index e06cf707e..b20b660dd 100644 --- a/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs @@ -252,14 +252,15 @@ namespace ImageSharp.Tests [Fact] public void ToStringTest() { - var r = new RectangleF(5, -5, 0, 1); + var r = new RectangleF(5, 5.1F, 1.3F, 1); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); } - [Fact] - public void ToStringTestEmpty() + [InlineData(0, 0, 0, 0)] + [InlineData(5, -5, 0.2, -1.3)] + public void ToStringTestEmpty(float x, float y, float width, float height) { - var r = new RectangleF(0, 0, 0, 0); + var r = new RectangleF(x, y, width, height); Assert.Equal("RectangleF [ Empty ]", r.ToString()); } } From ecc9ad49ee93bec878f4e7e5f599a6e772a43bbb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 May 2017 14:07:22 +1000 Subject: [PATCH 5/6] Add Skew/Rotate tests --- .../ImageSharp.Tests/Numerics/PointFTests.cs | 22 ++++++++++++++++++ tests/ImageSharp.Tests/Numerics/PointTests.cs | 23 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tests/ImageSharp.Tests/Numerics/PointFTests.cs b/tests/ImageSharp.Tests/Numerics/PointFTests.cs index 33de60ca7..3b5a72a14 100644 --- a/tests/ImageSharp.Tests/Numerics/PointFTests.cs +++ b/tests/ImageSharp.Tests/Numerics/PointFTests.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Tests.Numerics { using System; using System.Globalization; + using System.Numerics; using System.Reflection; using Xunit; @@ -101,6 +102,27 @@ namespace ImageSharp.Tests.Numerics Assert.Equal(subExpected, PointF.Subtract(p, s)); } + [Fact] + public void RotateTest() + { + var p = new PointF(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateRotation(45, PointF.Empty); + + var pout = PointF.Rotate(p, matrix); + + Assert.Equal(new PointF(-2.82842732F, 21.2132034F), pout); + } + + [Fact] + public void SkewTest() + { + var p = new PointF(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateSkew(45, 45, PointF.Empty); + + var pout = PointF.Skew(p, matrix); + Assert.Equal(new PointF(30, 30), pout); + } + [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MaxValue)] diff --git a/tests/ImageSharp.Tests/Numerics/PointTests.cs b/tests/ImageSharp.Tests/Numerics/PointTests.cs index 8eb36b6e8..9f7abe067 100644 --- a/tests/ImageSharp.Tests/Numerics/PointTests.cs +++ b/tests/ImageSharp.Tests/Numerics/PointTests.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests { using System.Globalization; + using System.Numerics; + using Xunit; public class PointTests @@ -155,6 +157,27 @@ namespace ImageSharp.Tests Assert.Equal(p1, p2); } + [Fact] + public void RotateTest() + { + var p = new Point(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateRotation(45, Point.Empty); + + var pout = Point.Rotate(p, matrix); + + Assert.Equal(new Point(-3, 21), pout); + } + + [Fact] + public void SkewTest() + { + var p = new Point(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateSkew(45, 45, Point.Empty); + + var pout = Point.Skew(p, matrix); + Assert.Equal(new Point(30, 30), pout); + } + [Theory] [InlineData(int.MaxValue, int.MinValue)] [InlineData(int.MinValue, int.MinValue)] From 01d403ebbf07ad922d81bb615483a1a7fa332b06 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 May 2017 20:12:44 +1000 Subject: [PATCH 6/6] Remove Vector4 <-> RectangleF cast. --- src/ImageSharp/Numerics/RectangleF.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/ImageSharp/Numerics/RectangleF.cs b/src/ImageSharp/Numerics/RectangleF.cs index 28d887c5d..7611c9602 100644 --- a/src/ImageSharp/Numerics/RectangleF.cs +++ b/src/ImageSharp/Numerics/RectangleF.cs @@ -164,20 +164,6 @@ namespace ImageSharp } } - /// - /// Creates a with the coordinates of the specified . - /// - /// The rectangle - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator RectangleF(Vector4 vector) => new RectangleF(vector.X, vector.Y, vector.Z, vector.W); - - /// - /// Creates a with the coordinates of the specified . - /// - /// The rectangle - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector4(RectangleF rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); - /// /// Creates a with the coordinates of the specified by truncating each coordinate. ///