diff --git a/src/Avalonia.Visuals/Media/PixelPoint.cs b/src/Avalonia.Visuals/Media/PixelPoint.cs new file mode 100644 index 0000000000..995781ee9f --- /dev/null +++ b/src/Avalonia.Visuals/Media/PixelPoint.cs @@ -0,0 +1,201 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Globalization; +using Avalonia.Utilities; + +namespace Avalonia +{ + /// + /// Represents a point in device pixels. + /// + public readonly struct PixelPoint + { + /// + /// A point representing 0,0. + /// + public static readonly PixelPoint Origin = new PixelPoint(0, 0); + + /// + /// Initializes a new instance of the structure. + /// + /// The X co-ordinate. + /// The Y co-ordinate. + public PixelPoint(int x, int y) + { + X = x; + Y = y; + } + + /// + /// Gets the X co-ordinate. + /// + public int X { get; } + + /// + /// Gets the Y co-ordinate. + /// + public int Y { get; } + + /// + /// Checks for equality between two s. + /// + /// The first point. + /// The second point. + /// True if the points are equal; otherwise false. + public static bool operator ==(PixelPoint left, PixelPoint right) + { + return left.X == right.X && left.Y == right.Y; + } + + /// + /// Checks for inequality between two s. + /// + /// The first point. + /// The second point. + /// True if the points are unequal; otherwise false. + public static bool operator !=(PixelPoint left, PixelPoint right) + { + return !(left == right); + } + + /// + /// Parses a string. + /// + /// The string. + /// The . + public static PixelPoint Parse(string s) + { + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelPoint")) + { + return new PixelPoint( + tokenizer.ReadInt32(), + tokenizer.ReadInt32()); + } + } + + /// + /// Checks for equality between a point and an object. + /// + /// The object. + /// + /// True if is a point that equals the current point. + /// + public override bool Equals(object obj) + { + if (obj is PixelPoint other) + { + return this == other; + } + + return false; + } + + /// + /// Returns a hash code for a . + /// + /// The hash code. + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = (hash * 23) + X.GetHashCode(); + hash = (hash * 23) + Y.GetHashCode(); + return hash; + } + } + + /// + /// Returns a new with the same Y co-ordinate and the specified X co-ordinate. + /// + /// The X co-ordinate. + /// The new . + public PixelPoint WithX(int x) => new PixelPoint(x, Y); + + /// + /// Returns a new with the same X co-ordinate and the specified Y co-ordinate. + /// + /// The Y co-ordinate. + /// The new . + public PixelPoint WithY(int y) => new PixelPoint(X, y); + + /// + /// Converts the to a device-independent using the + /// specified scaling factor. + /// + /// The scaling factor. + /// The device-independent point. + public Point ToPoint(double scale) => new Point(X / scale, Y / scale); + + /// + /// Converts the to a device-independent using the + /// specified scaling factor. + /// + /// The scaling factor. + /// The device-independent point. + public Point ToPoint(Vector scale) => new Point(X / scale.X, Y / scale.Y); + + /// + /// Converts the to a device-independent using the + /// specified dots per inch (DPI). + /// + /// The dots per inch of the device. + /// The device-independent point. + public Point ToPointWithDpi(double dpi) => ToPoint(dpi / 96); + + /// + /// Converts the to a device-independent using the + /// specified dots per inch (DPI). + /// + /// The dots per inch of the device. + /// The device-independent point. + public Point ToPointWithDpi(Vector dpi) => ToPoint(new Vector(dpi.X / 96, dpi.Y / 96)); + + /// + /// Converts a to device pixels using the specified scaling factor. + /// + /// The point. + /// The scaling factor. + /// The device-independent point. + public static PixelPoint FromPoint(Point point, double scale) => new PixelPoint( + (int)(point.X * scale), + (int)(point.Y * scale)); + + /// + /// Converts a to device pixels using the specified scaling factor. + /// + /// The point. + /// The scaling factor. + /// The device-independent point. + public static PixelPoint FromPoint(Point point, Vector scale) => new PixelPoint( + (int)(point.X * scale.X), + (int)(point.Y * scale.Y)); + + /// + /// Converts a to device pixels using the specified dots per inch (DPI). + /// + /// The point. + /// The dots per inch of the device. + /// The device-independent point. + public static PixelPoint FromPointWithDpi(Point point, double dpi) => FromPoint(point, dpi / 96); + + /// + /// Converts a to device pixels using the specified dots per inch (DPI). + /// + /// The point. + /// The dots per inch of the device. + /// The device-independent point. + public static PixelPoint FromPointWithDpi(Point point, Vector dpi) => FromPoint(point, new Vector(dpi.X / 96, dpi.Y / 96)); + + /// + /// Returns the string representation of the point. + /// + /// The string representation of the point. + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", X, Y); + } + } +} diff --git a/src/Avalonia.Visuals/Media/PixelRect.cs b/src/Avalonia.Visuals/Media/PixelRect.cs new file mode 100644 index 0000000000..9c8e5ad1c4 --- /dev/null +++ b/src/Avalonia.Visuals/Media/PixelRect.cs @@ -0,0 +1,436 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Globalization; +using Avalonia.Utilities; + +namespace Avalonia +{ + /// + /// Represents a rectangle in device pixels. + /// + public readonly struct PixelRect + { + /// + /// An empty rectangle. + /// + public static readonly PixelRect Empty = default; + + /// + /// Initializes a new instance of the structure. + /// + /// The X position. + /// The Y position. + /// The width. + /// The height. + public PixelRect(int x, int y, int width, int height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The size of the rectangle. + public PixelRect(PixelSize size) + { + X = 0; + Y = 0; + Width = size.Width; + Height = size.Height; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The position of the rectangle. + /// The size of the rectangle. + public PixelRect(PixelPoint position, PixelSize size) + { + X = position.X; + Y = position.Y; + Width = size.Width; + Height = size.Height; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The top left position of the rectangle. + /// The bottom right position of the rectangle. + public PixelRect(PixelPoint topLeft, PixelPoint bottomRight) + { + X = topLeft.X; + Y = topLeft.Y; + Width = bottomRight.X - topLeft.X; + Height = bottomRight.Y - topLeft.Y; + } + + /// + /// Gets the X position. + /// + public int X { get; } + + /// + /// Gets the Y position. + /// + public int Y { get; } + + /// + /// Gets the width. + /// + public int Width { get; } + + /// + /// Gets the height. + /// + public int Height { get; } + + /// + /// Gets the position of the rectangle. + /// + public PixelPoint Position => new PixelPoint(X, Y); + + /// + /// Gets the size of the rectangle. + /// + public PixelSize Size => new PixelSize(Width, Height); + + /// + /// Gets the right position of the rectangle. + /// + public int Right => X + Width; + + /// + /// Gets the bottom position of the rectangle. + /// + public int Bottom => Y + Height; + + /// + /// Gets the top left point of the rectangle. + /// + public PixelPoint TopLeft => new PixelPoint(X, Y); + + /// + /// Gets the top right point of the rectangle. + /// + public PixelPoint TopRight => new PixelPoint(Right, Y); + + /// + /// Gets the bottom left point of the rectangle. + /// + public PixelPoint BottomLeft => new PixelPoint(X, Bottom); + + /// + /// Gets the bottom right point of the rectangle. + /// + public PixelPoint BottomRight => new PixelPoint(Right, Bottom); + + /// + /// Gets the center point of the rectangle. + /// + public PixelPoint Center => new PixelPoint(X + (Width / 2), Y + (Height / 2)); + + /// + /// Gets a value that indicates whether the rectangle is empty. + /// + public bool IsEmpty => Width == 0 && Height == 0; + + /// + /// Checks for equality between two s. + /// + /// The first rect. + /// The second rect. + /// True if the rects are equal; otherwise false. + public static bool operator ==(PixelRect left, PixelRect right) + { + return left.Position == right.Position && left.Size == right.Size; + } + + /// + /// Checks for inequality between two s. + /// + /// The first rect. + /// The second rect. + /// True if the rects are unequal; otherwise false. + public static bool operator !=(PixelRect left, PixelRect right) + { + return !(left == right); + } + + /// + /// Determines whether a point in in the bounds of the rectangle. + /// + /// The point. + /// true if the point is in the bounds of the rectangle; otherwise false. + public bool Contains(PixelPoint p) + { + return p.X >= X && p.X <= Right && p.Y >= Y && p.Y <= Bottom; + } + + /// + /// Determines whether the rectangle fully contains another rectangle. + /// + /// The rectangle. + /// true if the rectangle is fully contained; otherwise false. + public bool Contains(PixelRect r) + { + return Contains(r.TopLeft) && Contains(r.BottomRight); + } + + /// + /// Centers another rectangle in this rectangle. + /// + /// The rectangle to center. + /// The centered rectangle. + public PixelRect CenterRect(PixelRect rect) + { + return new PixelRect( + X + ((Width - rect.Width) / 2), + Y + ((Height - rect.Height) / 2), + rect.Width, + rect.Height); + } + + /// + /// Returns a boolean indicating whether the given object is equal to this rectangle. + /// + /// The object to compare against. + /// True if the object is equal to this rectangle; false otherwise. + public override bool Equals(object obj) + { + if (obj is PixelRect other) + { + return this == other; + } + + return false; + } + + /// + /// Returns the hash code for this instance. + /// + /// The hash code. + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = (hash * 23) + X.GetHashCode(); + hash = (hash * 23) + Y.GetHashCode(); + hash = (hash * 23) + Width.GetHashCode(); + hash = (hash * 23) + Height.GetHashCode(); + return hash; + } + } + + /// + /// Gets the intersection of two rectangles. + /// + /// The other rectangle. + /// The intersection. + public PixelRect Intersect(PixelRect rect) + { + var newLeft = (rect.X > X) ? rect.X : X; + var newTop = (rect.Y > Y) ? rect.Y : Y; + var newRight = (rect.Right < Right) ? rect.Right : Right; + var newBottom = (rect.Bottom < Bottom) ? rect.Bottom : Bottom; + + if ((newRight > newLeft) && (newBottom > newTop)) + { + return new PixelRect(newLeft, newTop, newRight - newLeft, newBottom - newTop); + } + else + { + return Empty; + } + } + + /// + /// Determines whether a rectangle intersects with this rectangle. + /// + /// The other rectangle. + /// + /// True if the specified rectangle intersects with this one; otherwise false. + /// + public bool Intersects(PixelRect rect) + { + return (rect.X < Right) && (X < rect.Right) && (rect.Y < Bottom) && (Y < rect.Bottom); + } + + /// + /// Gets the union of two rectangles. + /// + /// The other rectangle. + /// The union. + public PixelRect Union(PixelRect rect) + { + if (IsEmpty) + { + return rect; + } + else if (rect.IsEmpty) + { + return this; + } + else + { + var x1 = Math.Min(X, rect.X); + var x2 = Math.Max(Right, rect.Right); + var y1 = Math.Min(Y, rect.Y); + var y2 = Math.Max(Bottom, rect.Bottom); + + return new PixelRect(new PixelPoint(x1, y1), new PixelPoint(x2, y2)); + } + } + + /// + /// Returns a new with the specified X position. + /// + /// The x position. + /// The new . + public PixelRect WithX(int x) + { + return new PixelRect(x, Y, Width, Height); + } + + /// + /// Returns a new with the specified Y position. + /// + /// The y position. + /// The new . + public PixelRect WithY(int y) + { + return new PixelRect(X, y, Width, Height); + } + + /// + /// Returns a new with the specified width. + /// + /// The width. + /// The new . + public PixelRect WithWidth(int width) + { + return new PixelRect(X, Y, width, Height); + } + + /// + /// Returns a new with the specified height. + /// + /// The height. + /// The new . + public PixelRect WithHeight(int height) + { + return new PixelRect(X, Y, Width, Height); + } + + /// + /// Converts the to a device-independent using the + /// specified scaling factor. + /// + /// The scaling factor. + /// The device-independent rect. + public Rect ToRect(double scale) => new Rect(Position.ToPoint(scale), Size.ToSize(scale)); + + /// + /// Converts the to a device-independent using the + /// specified scaling factor. + /// + /// The scaling factor. + /// The device-independent rect. + public Rect ToRect(Vector scale) => new Rect(Position.ToPoint(scale), Size.ToSize(scale)); + + /// + /// Converts the to a device-independent using the + /// specified dots per inch (DPI). + /// + /// The dots per inch of the device. + /// The device-independent rect. + public Rect ToRectWithDpi(double dpi) => new Rect(Position.ToPointWithDpi(dpi), Size.ToSizeWithDpi(dpi)); + + /// + /// Converts the to a device-independent using the + /// specified dots per inch (DPI). + /// + /// The dots per inch of the device. + /// The device-independent rect. + public Rect ToRectWithDpi(Vector dpi) => new Rect(Position.ToPointWithDpi(dpi), Size.ToSizeWithDpi(dpi)); + + /// + /// Converts a to device pixels using the specified scaling factor. + /// + /// The rect. + /// The scaling factor. + /// The device-independent rect. + public static PixelRect FromRect(Rect rect, double scale) => new PixelRect( + PixelPoint.FromPoint(rect.Position, scale), + PixelSize.FromSize(rect.Size, scale)); + + /// + /// Converts a to device pixels using the specified scaling factor. + /// + /// The rect. + /// The scaling factor. + /// The device-independent point. + public static PixelRect FromRect(Rect rect, Vector scale) => new PixelRect( + PixelPoint.FromPoint(rect.Position, scale), + PixelSize.FromSize(rect.Size, scale)); + + /// + /// Converts a to device pixels using the specified dots per inch (DPI). + /// + /// The rect. + /// The dots per inch of the device. + /// The device-independent point. + public static PixelRect FromRectWithDpi(Rect rect, double dpi) => new PixelRect( + PixelPoint.FromPointWithDpi(rect.Position, dpi), + PixelSize.FromSizeWithDpi(rect.Size, dpi)); + + /// + /// Converts a to device pixels using the specified dots per inch (DPI). + /// + /// The rect. + /// The dots per inch of the device. + /// The device-independent point. + public static PixelRect FromRectWithDpi(Rect rect, Vector dpi) => new PixelRect( + PixelPoint.FromPointWithDpi(rect.Position, dpi), + PixelSize.FromSizeWithDpi(rect.Size, dpi)); + + /// + /// Returns the string representation of the rectangle. + /// + /// The string representation of the rectangle. + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, + "{0}, {1}, {2}, {3}", + X, + Y, + Width, + Height); + } + + /// + /// Parses a string. + /// + /// The string. + /// The parsed . + public static PixelRect Parse(string s) + { + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelRect")) + { + return new PixelRect( + tokenizer.ReadInt32(), + tokenizer.ReadInt32(), + tokenizer.ReadInt32(), + tokenizer.ReadInt32() + ); + } + } + } +}