diff --git a/Terminal.Gui/Drawing/Region.cs b/Terminal.Gui/Drawing/Region.cs new file mode 100644 index 000000000..a1f6c889f --- /dev/null +++ b/Terminal.Gui/Drawing/Region.cs @@ -0,0 +1,269 @@ +/// +/// Represents a region composed of one or more rectangles, providing methods for union, intersection, exclusion, and +/// complement operations. +/// +public class Region : IDisposable +{ + private List _rectangles; + + /// + /// Initializes a new instance of the class. + /// + public Region () { _rectangles = new (); } + + /// + /// Initializes a new instance of the class with the specified rectangle. + /// + /// The initial rectangle for the region. + public Region (Rectangle rectangle) { _rectangles = new() { rectangle }; } + + /// + /// Adds the specified rectangle to the region. + /// + /// The rectangle to add to the region. + public void Union (Rectangle rectangle) + { + _rectangles.Add (rectangle); + _rectangles = MergeRectangles (_rectangles); + } + + /// + /// Adds the specified region to this region. + /// + /// The region to add to this region. + public void Union (Region region) + { + _rectangles.AddRange (region._rectangles); + _rectangles = MergeRectangles (_rectangles); + } + + /// + /// Updates the region to be the intersection of itself with the specified rectangle. + /// + /// The rectangle to intersect with the region. + public void Intersect (Rectangle rectangle) + { + _rectangles = _rectangles.Select (r => Rectangle.Intersect (r, rectangle)).Where (r => !r.IsEmpty).ToList (); + } + + /// + /// Updates the region to be the intersection of itself with the specified region. + /// + /// The region to intersect with this region. + public void Intersect (Region region) + { + List intersections = new List (); + + foreach (Rectangle rect1 in _rectangles) + { + foreach (Rectangle rect2 in region._rectangles) + { + Rectangle intersected = Rectangle.Intersect (rect1, rect2); + + if (!intersected.IsEmpty) + { + intersections.Add (intersected); + } + } + } + + _rectangles = intersections; + } + + /// + /// Removes the portion of the specified rectangle from the region. + /// + /// The rectangle to exclude from the region. + public void Exclude (Rectangle rectangle) { _rectangles = _rectangles.SelectMany (r => SubtractRectangle (r, rectangle)).ToList (); } + + /// + /// Removes the portion of the specified region from this region. + /// + /// The region to exclude from this region. + public void Exclude (Region region) + { + foreach (Rectangle rect in region._rectangles) + { + _rectangles = _rectangles.SelectMany (r => SubtractRectangle (r, rect)).ToList (); + } + } + + /// + /// Updates the region to be the complement of itself within the specified bounds. + /// + /// The bounding rectangle to use for complementing the region. + public void Complement (Rectangle bounds) + { + if (bounds.IsEmpty || _rectangles.Count == 0) + { + _rectangles.Clear (); + + return; + } + + List complementRectangles = new List { bounds }; + + foreach (Rectangle rect in _rectangles) + { + complementRectangles = complementRectangles.SelectMany (r => SubtractRectangle (r, rect)).ToList (); + } + + _rectangles = complementRectangles; + } + + /// + /// Creates an exact copy of the region. + /// + /// A new that is a copy of this instance. + public Region Clone () + { + var clone = new Region (); + clone._rectangles = new (_rectangles); + + return clone; + } + + /// + /// Gets a bounding rectangle for the entire region. + /// + /// A that bounds the region. + public Rectangle GetBounds () + { + if (_rectangles.Count == 0) + { + return Rectangle.Empty; + } + + int left = _rectangles.Min (r => r.Left); + int top = _rectangles.Min (r => r.Top); + int right = _rectangles.Max (r => r.Right); + int bottom = _rectangles.Max (r => r.Bottom); + + return new (left, top, right - left, bottom - top); + } + + /// + /// Determines whether the region is empty. + /// + /// true if the region is empty; otherwise, false. + public bool IsEmpty () { return !_rectangles.Any (); } + + /// + /// Determines whether the specified point is contained within the region. + /// + /// The x-coordinate of the point. + /// The y-coordinate of the point. + /// true if the point is contained within the region; otherwise, false. + public bool Contains (int x, int y) { return _rectangles.Any (r => r.Contains (x, y)); } + + /// + /// Determines whether the specified rectangle is contained within the region. + /// + /// The rectangle to check for containment. + /// true if the rectangle is contained within the region; otherwise, false. + public bool Contains (Rectangle rectangle) { return _rectangles.Any (r => r.Contains (rectangle)); } + + /// + /// Returns an array of rectangles that represent the region. + /// + /// An array of objects that make up the region. + public Rectangle [] GetRegionScans () { return _rectangles.ToArray (); } + + /// + /// Merges overlapping rectangles into a minimal set of non-overlapping rectangles. + /// + /// The list of rectangles to merge. + /// A list of merged rectangles. + private List MergeRectangles (List rectangles) + { + // Simplified merging logic: this does not handle all edge cases for merging overlapping rectangles. + // For a full implementation, a plane sweep algorithm or similar would be needed. + List merged = new List (rectangles); + bool mergedAny; + + do + { + mergedAny = false; + + for (var i = 0; i < merged.Count; i++) + { + for (int j = i + 1; j < merged.Count; j++) + { + if (merged [i].IntersectsWith (merged [j])) + { + merged [i] = Rectangle.Union (merged [i], merged [j]); + merged.RemoveAt (j); + mergedAny = true; + + break; + } + } + + if (mergedAny) + { + break; + } + } + } + while (mergedAny); + + return merged; + } + + /// + /// Subtracts the specified rectangle from the original rectangle, returning the resulting rectangles. + /// + /// The original rectangle. + /// The rectangle to subtract from the original. + /// An enumerable collection of resulting rectangles after subtraction. + private IEnumerable SubtractRectangle (Rectangle original, Rectangle subtract) + { + if (!original.IntersectsWith (subtract)) + { + yield return original; + + yield break; + } + + // Top segment + if (original.Top < subtract.Top) + { + yield return new (original.Left, original.Top, original.Width, subtract.Top - original.Top); + } + + // Bottom segment + if (original.Bottom > subtract.Bottom) + { + yield return new (original.Left, subtract.Bottom, original.Width, original.Bottom - subtract.Bottom); + } + + // Left segment + if (original.Left < subtract.Left) + { + int top = Math.Max (original.Top, subtract.Top); + int bottom = Math.Min (original.Bottom, subtract.Bottom); + + if (bottom > top) + { + yield return new (original.Left, top, subtract.Left - original.Left, bottom - top); + } + } + + // Right segment + if (original.Right > subtract.Right) + { + int top = Math.Max (original.Top, subtract.Top); + int bottom = Math.Min (original.Bottom, subtract.Bottom); + + if (bottom > top) + { + yield return new (subtract.Right, top, original.Right - subtract.Right, bottom - top); + } + } + } + + /// + /// Releases all resources used by the . + /// + public void Dispose () { _rectangles.Clear (); } +} diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs index e9b7232e2..c94e39695 100644 --- a/Terminal.Gui/View/View.Drawing.cs +++ b/Terminal.Gui/View/View.Drawing.cs @@ -1,7 +1,7 @@ #nullable enable +#define HACK_DRAW_OVERLAPPED using System.ComponentModel; using System.Diagnostics; -using Microsoft.CodeAnalysis.Operations; namespace Terminal.Gui; @@ -430,23 +430,25 @@ public partial class View // Drawing APIs return; } +#if HACK_DRAW_OVERLAPPED IEnumerable subviewsNeedingDraw = _subviews.Where (view => (view.Visible && (view.NeedsDraw || view.SubViewNeedsDraw)) - );//|| view.Arrangement.HasFlag (ViewArrangement.Overlapped)); + || view.Arrangement.HasFlag (ViewArrangement.Overlapped)); +#else + IEnumerable subviewsNeedingDraw = _subviews.Where (view => (view.Visible && (view.NeedsDraw || view.SubViewNeedsDraw))); +#endif foreach (View view in subviewsNeedingDraw) { +#if HACK_DRAW_OVERLAPPED if (view.Arrangement.HasFlag (ViewArrangement.Overlapped)) { - //view.SetNeedsDraw (); - } - view.Draw (); - } - //var overlapped = subviewsNeedingDraw; - //foreach (View view in overlapped) - //{ - // view.Draw (); - //} + view.SetNeedsDraw (); + } +#endif + view.Draw (); + + } } #endregion DrawSubviews diff --git a/UnitTests/Drawing/RegionTests.cs b/UnitTests/Drawing/RegionTests.cs new file mode 100644 index 000000000..4015f167d --- /dev/null +++ b/UnitTests/Drawing/RegionTests.cs @@ -0,0 +1,2147 @@ +namespace Terminal.Gui.DrawingTests; + + + +public class RegionTests +{ + [Fact] + public void Constructor_EmptyRegion_IsEmpty () + { + var region = new Region (); + Assert.True (region.IsEmpty ()); + } + + [Fact] + public void Constructor_WithRectangle_IsNotEmpty () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + Assert.False (region.IsEmpty ()); + } + + [Fact] + public void Union_Rectangle_AddsToRegion () + { + var region = new Region (); + region.Union (new Rectangle (10, 10, 50, 50)); + Assert.False (region.IsEmpty ()); + Assert.True (region.Contains (20, 20)); + } + + [Fact] + public void Union_Region_MergesRegions () + { + var region1 = new Region (new Rectangle (10, 10, 50, 50)); + var region2 = new Region (new Rectangle (30, 30, 50, 50)); + region1.Union (region2); + Assert.True (region1.Contains (20, 20)); + Assert.True (region1.Contains (40, 40)); + } + + [Fact] + public void Intersect_Rectangle_IntersectsRegion () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + region.Intersect (new Rectangle (30, 30, 50, 50)); + Assert.False (region.Contains (20, 20)); + Assert.True (region.Contains (40, 40)); + } + + [Fact] + public void Intersect_Region_IntersectsRegions () + { + var region1 = new Region (new Rectangle (10, 10, 50, 50)); + var region2 = new Region (new Rectangle (30, 30, 50, 50)); + region1.Intersect (region2); + Assert.False (region1.Contains (20, 20)); + Assert.True (region1.Contains (40, 40)); + } + + [Fact] + public void Exclude_Rectangle_ExcludesFromRegion () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + region.Exclude (new Rectangle (20, 20, 20, 20)); + Assert.False (region.Contains (25, 25)); + Assert.True (region.Contains (15, 15)); + } + + [Fact] + public void Exclude_Region_ExcludesRegions () + { + var region1 = new Region (new Rectangle (10, 10, 50, 50)); + var region2 = new Region (new Rectangle (20, 20, 20, 20)); + region1.Exclude (region2); + Assert.False (region1.Contains (25, 25)); + Assert.True (region1.Contains (15, 15)); + } + + [Fact] + public void Complement_Rectangle_ComplementsRegion () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + region.Complement (new Rectangle (0, 0, 100, 100)); + Assert.True (region.Contains (5, 5)); + Assert.False (region.Contains (20, 20)); + } + + [Fact] + public void Clone_CreatesExactCopy () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + var clone = region.Clone (); + Assert.True (clone.Contains (20, 20)); + Assert.Equal (region.GetRegionScans (), clone.GetRegionScans ()); + } + + [Fact] + public void GetBounds_ReturnsBoundingRectangle () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + region.Union (new Rectangle (100, 100, 20, 20)); + var bounds = region.GetBounds (); + Assert.Equal (new Rectangle (10, 10, 110, 110), bounds); + } + + [Fact] + public void IsEmpty_EmptyRegion_ReturnsTrue () + { + var region = new Region (); + Assert.True (region.IsEmpty ()); + } + + [Fact] + public void Contains_PointInsideRegion_ReturnsTrue () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + Assert.True (region.Contains (20, 20)); + } + + [Fact] + public void Contains_RectangleInsideRegion_ReturnsTrue () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + Assert.True (region.Contains (new Rectangle (20, 20, 10, 10))); + } + + [Fact] + public void GetRegionScans_ReturnsAllRectangles () + { + var region = new Region (new Rectangle (10, 10, 50, 50)); + region.Union (new Rectangle (100, 100, 20, 20)); + var scans = region.GetRegionScans (); + Assert.Equal (2, scans.Length); + Assert.Contains (new Rectangle (10, 10, 50, 50), scans); + Assert.Contains (new Rectangle (100, 100, 20, 20), scans); + } + [Fact] + public void Union_WithRectangle_AddsRectangle () + { + var region = new Region (); + var rect = new Rectangle (10, 10, 50, 50); + + region.Union (rect); + + Assert.True (region.Contains (20, 20)); + Assert.False (region.Contains (100, 100)); + } + + [Fact] + public void Intersect_WithRectangle_IntersectsRectangles () + { + var region = new Region (new (10, 10, 50, 50)); + var rect = new Rectangle (30, 30, 50, 50); + + region.Intersect (rect); + + Assert.True (region.Contains (35, 35)); + Assert.False (region.Contains (20, 20)); + } + + [Fact] + public void Exclude_WithRectangle_ExcludesRectangle () + { + var region = new Region (new (10, 10, 50, 50)); + var rect = new Rectangle (30, 30, 50, 50); + + region.Exclude (rect); + + Assert.True (region.Contains (20, 20)); + Assert.False (region.Contains (35, 35)); + } + + [Fact] + public void Contains_Point_ReturnsCorrectResult () + { + var region = new Region (new (10, 10, 50, 50)); + + Assert.True (region.Contains (20, 20)); + Assert.False (region.Contains (100, 100)); + } + + [Fact] + public void IsEmpty_ReturnsCorrectResult () + { + var region = new Region (); + + Assert.True (region.IsEmpty ()); + + region.Union (new Rectangle(10, 10, 50, 50)); + + Assert.False (region.IsEmpty ()); + } + + [Fact] + public void GetBounds_ReturnsCorrectBounds () + { + var region = new Region (); + region.Union (new Rectangle (10, 10, 50, 50)); + region.Union (new Rectangle (30, 30, 50, 50)); + + Rectangle bounds = region.GetBounds (); + + Assert.Equal (new (10, 10, 70, 70), bounds); + } + + [Fact] + public void Dispose_ClearsRectangles () + { + var region = new Region (new (10, 10, 50, 50)); + region.Dispose (); + + Assert.True (region.IsEmpty ()); + } + + [Fact] + public void Union_WithRegion_AddsRegion () + { + var region1 = new Region (new (10, 10, 50, 50)); + var region2 = new Region (new (30, 30, 50, 50)); + + region1.Union (region2.GetBounds ()); + + Assert.True (region1.Contains (20, 20)); + Assert.True (region1.Contains (40, 40)); + } + + [Fact] + public void Intersect_WithRegion_IntersectsRegions () + { + var region1 = new Region (new (10, 10, 50, 50)); + var region2 = new Region (new (30, 30, 50, 50)); + + region1.Intersect (region2.GetBounds ()); + + Assert.True (region1.Contains (35, 35)); + Assert.False (region1.Contains (20, 20)); + } + + [Fact] + public void Exclude_WithRegion_ExcludesRegion () + { + var region1 = new Region (new (10, 10, 50, 50)); + var region2 = new Region (new (30, 30, 50, 50)); + + region1.Exclude (region2.GetBounds ()); + + Assert.True (region1.Contains (20, 20)); + Assert.False (region1.Contains (35, 35)); + } + + //[Fact] + //public void Complement_WithRectangle_ComplementsRegion () + //{ + // var region = new Region (new (10, 10, 50, 50)); + // var rect = new Rectangle (30, 30, 50, 50); + + // region.Complement (rect); + + // Assert.True (region.Contains (35, 35)); + // Assert.False (region.Contains (20, 20)); + //} + + //[Fact] + //public void Complement_WithRegion_ComplementsRegion () + //{ + // var region1 = new Region (new (10, 10, 50, 50)); + // var region2 = new Region (new (30, 30, 50, 50)); + + // region1.Complement (region2.GetBounds ()); + + // Assert.True (region1.Contains (35, 35)); + // Assert.False (region1.Contains (20, 20)); + //} + + private static Region CreateDisposedRegion () + { + Region region = new (); + region.Dispose (); + + return region; + } + + public static IEnumerable Region_TestData () + { + yield return new object [] { new Region () }; + yield return new object [] { new Region (new Rectangle (0, 0, 0, 0)) }; + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)) }; + } + + public static IEnumerable Complement_TestData () + { + yield return new object [] + { + new Region (new Rectangle (10, 10, 100, 100)), + new Rectangle [] { new (40, 60, 100, 20) }, + new Rectangle [] { new (110, 60, 30, 20) } + }; + + yield return new object [] + { + new Region (new Rectangle (70, 10, 100, 100)), + new Rectangle [] { new (40, 60, 100, 20) }, + new Rectangle [] { new (40, 60, 30, 20) } + }; + + yield return new object [] + { + new Region (new Rectangle (40, 100, 100, 100)), + new Rectangle [] { new (70, 80, 50, 40) }, + new Rectangle [] { new (70, 80, 50, 20) } + }; + + yield return new object [] + { + new Region (new Rectangle (40, 10, 100, 100)), + new Rectangle [] { new (70, 80, 50, 40) }, + new Rectangle [] { new (70, 110, 50, 10) } + }; + + yield return new object [] + { + new Region (new Rectangle (30, 30, 80, 80)), + new Rectangle [] + { + new (45, 45, 200, 200), + new (160, 260, 10, 10), + new (170, 260, 10, 10), + }, + new Rectangle [] { new (170, 260, 10, 10) } + }; + + yield return new object [] + { + new Region (), + new Rectangle [] { Rectangle.Empty }, + new Rectangle[0] + }; + + yield return new object [] + { + new Region (), + new Rectangle [] { new (1, 2, 3, 4) }, + new Rectangle[0] + }; + } + + [Theory] + [MemberData (nameof (Complement_TestData))] + public void Complement_Region_Success (Region region, Rectangle [] rectangles, Rectangle [] expectedScans) + { + using (region) + { + foreach (Rectangle rect in rectangles) + { + region.Complement (rect); + } + + var actualScans = region.GetRegionScans (); + Assert.Equal (expectedScans, actualScans); + } + } + +} +#if x + + [Fact] + public void Complement_UnionRegion_Success () + { + using Region region = new (new Rectangle (20, 20, 20, 20)); + using Region other = new (new Rectangle (20, 80, 20, 10)); + using Matrix matrix = new (); + other.Union (new Rectangle (60, 60, 30, 10)); + + region.Complement (other); + + Assert.Equal ( + [ + new (60, 60, 30, 10), + new (20, 80, 20, 10) + ], + region.GetRegionScans (matrix)); + } + + [Fact] + public void Complement_InfiniteAndWithIntersectRegion_Success () + { + using Region region = new (); + using Matrix matrix = new (); + region.Intersect (new Rectangle (5, 5, -10, -10)); + region.Complement (new Rectangle (-5, -5, 12, 12)); + + Assert.False (region.IsEmpty (s_graphic)); + Assert.False (region.IsInfinite (s_graphic)); + + Assert.Equal ( + [ + new (5, -5, 2, 10), + new (-5, 5, 12, 2) + ], + region.GetRegionScans (matrix)); + } + + [Fact] + public void Complement_InfiniteRegion_Success () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + using Matrix matrix = new (); + using Region other = new (); + region.Complement (other); + + Assert.Equal ( + [ + new (-4194304, -4194304, 8388608, 4194306), + new (-4194304, 2, 4194305, 4), + new (4, 2, 4194300, 4), + new (-4194304, 6, 8388608, 4194298) + ], + region.GetRegionScans (matrix)); + } + + [Fact] + public void Complement_NullRegion_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("region", () => region.Complement ((Region)null)); + } + + + [Fact] + public void Complement_SameRegion_ThrowsInvalidOperationException () + { + using Region region = new (); + Assert.Throws (() => region.Complement (region)); + } + + [Theory] + [MemberData (nameof (Complement_TestData))] + public void Complement_Rectangle_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Complement (new Rectangle ((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Complement_TestData))] + public void Complement_RectangleF_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Complement (rect); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Complement_TestData))] + public void Complement_GraphicsPath_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using GraphicsPath path = new (); + path.AddRectangle (rect); + region.Complement (path); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Complement_GraphicsPathWithMultipleRectangles_Success () + { + Rectangle rect1 = new (20, 30, 60, 80); + Rectangle rect2 = new (50, 40, 60, 80); + + using Graphics graphics = Graphics.FromImage (new Bitmap (600, 800)); + using Region region1 = new (rect1); + using Region region2 = new (rect2); + using Matrix matrix = new (); + graphics.DrawRectangle (Pens.Green, rect1); + graphics.DrawRectangle (Pens.Red, rect2); + + region1.Complement (region2); + graphics.FillRegion (Brushes.Blue, region1); + graphics.DrawRectangles (Pens.Yellow, region1.GetRegionScans (matrix)); + + Assert.Equal ( + [ + new (80, 40, 30, 70), + new (50, 110, 60, 10) + ], + region1.GetRegionScans (matrix)); + } + + + public static IEnumerable Equals_TestData () + { + static Region Empty () + { + Region emptyRegion = new (); + emptyRegion.MakeEmpty (); + + return emptyRegion; + } + + Region createdRegion = new (); + + yield return new object [] { createdRegion, createdRegion, true }; + yield return new object [] { new Region (), new Region (), true }; + yield return new object [] { new Region (), Empty (), false }; + yield return new object [] { new Region (), new Region (new Rectangle (1, 2, 3, 4)), false }; + + yield return new object [] { Empty (), Empty (), true }; + yield return new object [] { Empty (), new Region (new Rectangle (0, 0, 0, 0)), true }; + yield return new object [] { Empty (), new Region (new Rectangle (1, 2, 3, 3)), false }; + + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)), new Region (new Rectangle (1, 2, 3, 4)), true }; + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)), new Region (new RectangleF (1, 2, 3, 4)), true }; + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)), new Region (new Rectangle (2, 2, 3, 4)), false }; + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)), new Region (new Rectangle (1, 3, 3, 4)), false }; + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)), new Region (new Rectangle (1, 2, 4, 4)), false }; + yield return new object [] { new Region (new Rectangle (1, 2, 3, 4)), new Region (new Rectangle (1, 2, 3, 5)), false }; + + GraphicsPath graphics1 = new (); + graphics1.AddRectangle (new Rectangle (1, 2, 3, 4)); + + GraphicsPath graphics2 = new (); + graphics2.AddRectangle (new Rectangle (1, 2, 3, 4)); + + GraphicsPath graphics3 = new (); + graphics3.AddRectangle (new Rectangle (2, 2, 3, 4)); + + GraphicsPath graphics4 = new (); + graphics4.AddRectangle (new Rectangle (1, 3, 3, 4)); + + GraphicsPath graphics5 = new (); + graphics5.AddRectangle (new Rectangle (1, 2, 4, 4)); + + GraphicsPath graphics6 = new (); + graphics6.AddRectangle (new Rectangle (1, 2, 3, 5)); + + yield return new object [] { new Region (graphics1), new Region (graphics1), true }; + yield return new object [] { new Region (graphics1), new Region (graphics2), true }; + yield return new object [] { new Region (graphics1), new Region (graphics3), false }; + yield return new object [] { new Region (graphics1), new Region (graphics4), false }; + yield return new object [] { new Region (graphics1), new Region (graphics5), false }; + yield return new object [] { new Region (graphics1), new Region (graphics6), false }; + } + + [Theory] + [MemberData (nameof (Equals_TestData))] + public void Equals_Valid_ReturnsExpected (Region region, Region other, bool expected) + { + using (region) + using (other) + { + Assert.Equal (expected, region.Equals (other, s_graphic)); + } + } + + [Fact] + public void Equals_NullRegion_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("region", () => region.Equals (null, s_graphic)); + } + + [Fact] + public void Equals_NullGraphics_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("g", () => region.Equals (region, null)); + } + + [Fact] + public void Equals_DisposedGraphics_ThrowsArgumentException () + { + using Region region = new (); + using Region other = new (); + using Bitmap image = new (10, 10); + var graphics = Graphics.FromImage (image); + graphics.Dispose (); + Assert.Throws (null, () => region.Equals (region, graphics)); + } + + [Fact] + public void Equals_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + Assert.Throws (null, () => disposedRegion.Equals (new Region (), s_graphic)); + Assert.Throws (null, () => new Region ().Equals (disposedRegion, s_graphic)); + } + + public static IEnumerable Exclude_TestData () + { + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { new (500, 30, 60, 80) }, + new RectangleF[0] + }; + + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { RectangleF.Empty }, + new RectangleF [] { new (500, 30, 60, 80) } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { new (520, 40, 60, 80) }, + new RectangleF [] + { + new (-4194304, -4194304, 8388608, 4194344), + new (-4194304, 40, 4194824, 80), + new (580, 40, 4193724, 80), + new (-4194304, 120, 8388608, 4194184) + } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { RectangleF.Empty }, + new RectangleF [] { new Rectangle (-4194304, -4194304, 8388608, 8388608) } + }; + + // Intersecting from the right. + yield return new object [] + { + new Region (new Rectangle (10, 10, 100, 100)), + new RectangleF [] { new (40, 60, 100, 20) }, + new RectangleF [] + { + new (10, 10, 100, 50), + new (10, 60, 30, 20), + new (10, 80, 100, 30) + } + }; + + // Intersecting from the left. + yield return new object [] + { + new Region (new Rectangle (70, 10, 100, 100)), + new RectangleF [] { new (40, 60, 100, 20) }, + new RectangleF [] + { + new (70, 10, 100, 50), + new (140, 60, 30, 20), + new (70, 80, 100, 30) + } + }; + + // Intersecting from the top. + yield return new object [] + { + new Region (new Rectangle (40, 100, 100, 100)), + new RectangleF [] { new (70, 80, 50, 40) }, + new RectangleF [] + { + new (40, 100, 30, 20), + new (120, 100, 20, 20), + new (40, 120, 100, 80) + } + }; + + // Intersecting from the bottom. + yield return new object [] + { + new Region (new Rectangle (40, 10, 100, 100)), + new RectangleF [] { new (70, 80, 50, 40) }, + new RectangleF [] + { + new (40, 10, 100, 70), + new (40, 80, 30, 30), + new (120, 80, 20, 30) + } + }; + + // Multiple regions. + yield return new object [] + { + new Region (new Rectangle (30, 30, 80, 80)), + new RectangleF [] + { + new (45, 45, 200, 200), + new (160, 260, 10, 10), + new (170, 260, 10, 10) + }, + new RectangleF [] + { + new (30, 30, 80, 15), + new (30, 45, 15, 65) + } + }; + + // Intersecting from the top with a larger rect. + yield return new object [] + { + new Region (new Rectangle (50, 100, 100, 100)), + new RectangleF [] { new (30, 70, 150, 40) }, + new RectangleF [] { new (50, 110, 100, 90) } + }; + + // Intersecting from the right with a larger rect. + yield return new object [] + { + new Region (new Rectangle (70, 60, 100, 70)), + new RectangleF [] { new (40, 10, 100, 150) }, + new RectangleF [] { new (140, 60, 30, 70) } + }; + + // Intersecting from the left with a larger rect. + yield return new object [] + { + new Region (new Rectangle (70, 60, 100, 70)), + new RectangleF [] { new (100, 10, 100, 150) }, + new RectangleF [] { new (70, 60, 30, 70) } + }; + + // Intersecting from the bottom with a larger rect. + yield return new object [] + { + new Region (new Rectangle (20, 20, 100, 100)), + new RectangleF [] { new (10, 80, 140, 150) }, + new RectangleF [] { new (20, 20, 100, 60) } + }; + + yield return new object [] + { + new Region (new Rectangle (130, 30, 60, 80)), + new RectangleF [] { new (170, 40, 60, 80) }, + new RectangleF [] + { + new (130, 30, 60, 10), + new (130, 40, 40, 70) + } + }; + } + + [Theory] + [MemberData (nameof (Exclude_TestData))] + public void Exclude_Region_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using Region other = new (rect); + region.Exclude (other); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Exclude_UnionRegion_Success () + { + using Region region = new (new RectangleF (20, 20, 20, 20)); + using Region union = new (new RectangleF (20, 80, 20, 10)); + using Matrix matrix = new (); + union.Union (new RectangleF (60, 60, 30, 10)); + region.Exclude (union); + Assert.Equal ([new (20, 20, 20, 20)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Exclude_InfiniteRegion_Success () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + using Region other = new (); + using Matrix matrix = new (); + region.Exclude (other); + Assert.Equal ([], region.GetRegionScans (matrix)); + } + + [Fact] + public void Exclude_NullRegion_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("region", () => region.Exclude ((Region)null)); + } + + [Fact] + public void Exclude_DisposedRegion_ThrowsArgumentException () + { + Assert.Throws (null, () => new Region ().Exclude (CreateDisposedRegion ())); + } + + [Fact] + public void Exclude_SameRegion_ThrowsInvalidOperationException () + { + using Region region = new (); + Assert.Throws (() => region.Exclude (region)); + } + + [Theory] + [MemberData (nameof (Exclude_TestData))] + public void Exclude_Rectangle_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Exclude (new Rectangle ((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Exclude_TestData))] + public void Exclude_RectangleF_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Exclude (rect); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Exclude_TestData))] + public void Exclude_GraphicsPath_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using GraphicsPath path = new (); + path.AddRectangle (rect); + region.Exclude (path); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Exclude_EmptyPathWithInfiniteRegion_MakesInfinite () + { + using Region region = new (); + using GraphicsPath graphicsPath = new (); + region.Exclude (graphicsPath); + Assert.True (region.IsInfinite (s_graphic)); + } + + [Fact] + public void Exclude_NullGraphicsPath_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("path", () => region.Exclude ((GraphicsPath)null)); + } + + [Fact] + public void Exclude_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + using GraphicsPath graphicsPath = new (); + using Region other = new (); + Assert.Throws (null, () => disposedRegion.Exclude (graphicsPath)); + Assert.Throws (null, () => disposedRegion.Exclude (new Rectangle ())); + Assert.Throws (null, () => disposedRegion.Exclude (new RectangleF ())); + Assert.Throws (null, () => disposedRegion.Exclude (other)); + } + + [Fact] + public void FromHrgn_ValidHrgn_ReturnsExpected () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + IntPtr handle1 = region.GetHrgn (s_graphic); + IntPtr handle2 = region.GetHrgn (s_graphic); + Assert.NotEqual (IntPtr.Zero, handle1); + Assert.NotEqual (handle1, handle2); + + Region newRegion = Region.FromHrgn (handle1); + IntPtr handle3 = newRegion.GetHrgn (s_graphic); + Assert.NotEqual (handle3, handle1); + Assert.Equal (new RectangleF (1, 2, 3, 4), newRegion.GetBounds (s_graphic)); + + region.ReleaseHrgn (handle1); + region.ReleaseHrgn (handle2); + newRegion.ReleaseHrgn (handle3); + } + + [Fact] + public void FromHrgn_ZeroHrgn_ThrowsArgumentException () { Assert.Throws (null, () => Region.FromHrgn (IntPtr.Zero)); } + + [Fact] + public void GetHrgn_Infinite_ReturnsZero () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + IntPtr handle = region.GetHrgn (s_graphic); + Assert.NotEqual (IntPtr.Zero, handle); + region.ReleaseHrgn (handle); + + region.MakeInfinite (); + Assert.Equal (IntPtr.Zero, region.GetHrgn (s_graphic)); + } + + [Fact] + public void GetHrgn_Empty_ReturnsNonZero () + { + using Region region = new (); + Assert.Equal (IntPtr.Zero, region.GetHrgn (s_graphic)); + + region.MakeEmpty (); + IntPtr handle = region.GetHrgn (s_graphic); + Assert.NotEqual (IntPtr.Zero, handle); + region.ReleaseHrgn (handle); + } + + [Fact] + public void GetHrgn_NullGraphics_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("g", () => region.GetHrgn (null)); + } + + [Fact] + public void GetHrgn_Disposed_ThrowsArgumentException () + { + Assert.Throws (null, () => CreateDisposedRegion ().GetHrgn (s_graphic)); + } + + [Fact] + public void ReleaseHrgn_ZeroHandle_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("regionHandle", () => region.ReleaseHrgn (IntPtr.Zero)); + } + + [Fact] + public void GetBounds_NullGraphics_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("g", () => region.GetBounds (null)); + } + + [Fact] + public void GetBounds_DisposedGraphics_ThrowsArgumentException () + { + using Region region = new (); + using Bitmap image = new (10, 10); + var graphics = Graphics.FromImage (image); + graphics.Dispose (); + Assert.Throws (null, () => region.GetBounds (graphics)); + } + + [Fact] + public void GetBounds_Disposed_ThrowsArgumentException () + { + Assert.Throws (null, () => CreateDisposedRegion ().GetBounds (s_graphic)); + } + + [Fact] + public void GetRegionData_Disposed_ThrowsArgumentException () + { + Assert.Throws (null, () => CreateDisposedRegion ().GetRegionData ()); + } + + [Fact] + public void GetRegionScans_CustomMatrix_TransformsRegionScans () + { + using Matrix matrix = new (); + using Region region = new (new Rectangle (1, 2, 3, 4)); + using Matrix emptyMatrix = new (); + matrix.Translate (10, 11); + matrix.Scale (5, 6); + + Assert.Equal ([new (1, 2, 3, 4)], region.GetRegionScans (emptyMatrix)); + Assert.Equal ([new (15, 23, 15, 24)], region.GetRegionScans (matrix)); + } + + [Fact] + public void GetRegionScans_NullMatrix_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("matrix", () => region.GetRegionScans (null)); + } + + [Fact] + public void GetRegionScans_Disposed_ThrowsArgumentException () + { + using Matrix matrix = new (); + Assert.Throws (null, () => CreateDisposedRegion ().GetRegionScans (matrix)); + } + + [Fact] + public void GetRegionScans_DisposedMatrix_ThrowsArgumentException () + { + using Region region = new (); + Matrix matrix = new (); + matrix.Dispose (); + Assert.Throws (null, () => region.GetRegionScans (matrix)); + } + + [Fact] + public void Intersect_SmallerRect_Success () + { + using Region clipRegion = new (); + using Matrix matrix = new (); + Rectangle smaller = new (5, 5, -10, -10); + + clipRegion.Intersect (smaller); + Assert.False (clipRegion.IsEmpty (s_graphic)); + Assert.False (clipRegion.IsInfinite (s_graphic)); + + RectangleF [] rects = clipRegion.GetRegionScans (matrix); + Assert.Equal (1, rects.Length); + Assert.Equal (new RectangleF (-5, -5, 10, 10), rects [0]); + } + + public static IEnumerable Intersect_TestData () + { + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { new (500, 30, 60, 80) }, + new RectangleF [] { new (500, 30, 60, 80) } + }; + + yield return new object [] + { + new Region (new Rectangle (0, 0, 0, 0)), + new RectangleF [] { new (500, 30, 60, 80) }, + new RectangleF[0] + }; + + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { RectangleF.Empty }, + new RectangleF[0] + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { new (520, 40, 60, 80) }, + new RectangleF [] { new Rectangle (520, 40, 60, 80) } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { RectangleF.Empty }, + new RectangleF[0] + }; + + yield return new object [] + { + new Region (new RectangleF (260, 30, 60, 80)), + new RectangleF [] { new (290, 40, 60, 90) }, + new RectangleF [] { new (290, 40, 30, 70) } + }; + + yield return new object [] + { + new Region (new RectangleF (20, 330, 40, 50)), + new RectangleF [] + { + new (50, 340, 40, 50), + new (70, 360, 30, 50), + new (80, 400, 30, 10) + }, + new RectangleF[0] + }; + } + + [Theory] + [MemberData (nameof (Intersect_TestData))] + public void Intersect_Region_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using Region rectangleRegion = new (rect); + region.Intersect (rectangleRegion); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Intersect_InfiniteRegion_Success () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + using Matrix matrix = new (); + using Region infiniteRegion = new (); + region.Intersect (infiniteRegion); + + Assert.Equal ([new Rectangle (1, 2, 3, 4)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Intersect_NullRegion_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("region", () => region.Intersect ((Region)null)); + } + + [Fact] + public void Intersect_DisposedRegion_ThrowsArgumentException () + { + Assert.Throws (null, () => new Region ().Intersect (CreateDisposedRegion ())); + } + + [Fact] + public void Intersect_SameRegion_ThrowsInvalidOperationException () + { + using Region region = new (); + Assert.Throws (() => region.Intersect (region)); + } + + [Theory] + [MemberData (nameof (Intersect_TestData))] + public void Intersect_Rectangle_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Intersect (new Rectangle ((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Intersect_InfiniteRegionWithSmallerRectangle_Success () + { + using Region region = new (); + using Matrix matrix = new (); + region.Intersect (new Rectangle (5, 5, -10, -10)); + + Assert.False (region.IsEmpty (s_graphic)); + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal ([new (-5, -5, 10, 10)], region.GetRegionScans (matrix)); + } + + [Theory] + [MemberData (nameof (Intersect_TestData))] + public void Intersect_RectangleF_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Intersect (rect); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Intersect_InfiniteRegionWithSmallerRectangleF_Success () + { + using Region region = new (); + using Matrix matrix = new (); + region.Intersect (new RectangleF (5, 5, -10, -10)); + + Assert.False (region.IsEmpty (s_graphic)); + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal ([new (-5, -5, 10, 10)], region.GetRegionScans (matrix)); + } + + [Theory] + [MemberData (nameof (Intersect_TestData))] + public void Intersect_GraphicsPath_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using GraphicsPath path = new (); + path.AddRectangle (rect); + region.Intersect (path); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Intersect_EmptyPathWithInfiniteRegion_MakesEmpty () + { + using Region region = new (); + using GraphicsPath graphicsPath = new (); + region.Intersect (graphicsPath); + Assert.True (region.IsEmpty (s_graphic)); + } + + [Fact] + public void Intersect_NullGraphicsPath_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("path", () => region.Intersect ((GraphicsPath)null)); + } + + [Fact] + public void Intersect_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + using GraphicsPath graphicsPath = new (); + using Region other = new (); + Assert.Throws (null, () => disposedRegion.Intersect (graphicsPath)); + Assert.Throws (null, () => disposedRegion.Intersect (new Rectangle ())); + Assert.Throws (null, () => disposedRegion.Intersect (new RectangleF ())); + Assert.Throws (null, () => disposedRegion.Intersect (other)); + } + + [Fact] + public void IsEmpty_NullGraphics_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("g", () => region.IsEmpty (null)); + } + + [Fact] + public void IsEmpty_Disposed_ThrowsArgumentException () + { + Assert.Throws (null, () => CreateDisposedRegion ().IsEmpty (s_graphic)); + } + + [Fact] + public void IsInfinite_NullGraphics_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("g", () => region.IsInfinite (null)); + } + + [Fact] + public void IsInfinite_DisposedGraphics_ThrowsArgumentException () + { + using Region region = new (); + using Bitmap image = new (10, 10); + var graphics = Graphics.FromImage (image); + graphics.Dispose (); + Assert.Throws (null, () => region.IsInfinite (graphics)); + } + + [Fact] + public void IsInfinite_Disposed_ThrowsArgumentException () + { + Assert.Throws (null, () => CreateDisposedRegion ().IsInfinite (s_graphic)); + } + + public static IEnumerable IsVisible_Rectangle_TestData () + { + Region infiniteExclude = new (); + infiniteExclude.Exclude (new Rectangle (387, 292, 189, 133)); + infiniteExclude.Exclude (new Rectangle (387, 66, 189, 133)); + + yield return new object [] { infiniteExclude, new Rectangle (66, 292, 189, 133), true }; + yield return new object [] { new Region (), Rectangle.Empty, false }; + + yield return new object [] { new Region (new Rectangle (0, 0, 10, 10)), new Rectangle (0, 0, 0, 1), false }; + yield return new object [] { new Region (new Rectangle (500, 30, 60, 80)), new Rectangle (500, 30, 60, 80), true }; + yield return new object [] { new Region (new Rectangle (500, 30, 60, 80)), new Rectangle (520, 40, 60, 80), true }; + + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (1, 1, 2, 1), true }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (1, 1, 2, 2), true }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (1, 1, 10, 10), true }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (1, 1, 1, 1), true }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (2, 2, 1, 1), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (0, 0, 1, 1), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Rectangle (3, 3, 1, 1), false }; + } + + [Theory] + [MemberData (nameof (IsVisible_Rectangle_TestData))] + public void IsVisible_Rectangle_ReturnsExpected (Region region, Rectangle rectangle, bool expected) + { + using (region) + using (Bitmap image = new (10, 10)) + { + var disposedGraphics = Graphics.FromImage (image); + disposedGraphics.Dispose (); + + Assert.Equal (expected, region.IsVisible (rectangle)); + Assert.Equal (expected, region.IsVisible ((RectangleF)rectangle)); + Assert.Equal (expected, region.IsVisible (rectangle, s_graphic)); + Assert.Equal (expected, region.IsVisible (rectangle, disposedGraphics)); + Assert.Equal (expected, region.IsVisible (rectangle, null)); + Assert.Equal (expected, region.IsVisible ((RectangleF)rectangle, s_graphic)); + Assert.Equal (expected, region.IsVisible ((RectangleF)rectangle, disposedGraphics)); + Assert.Equal (expected, region.IsVisible ((RectangleF)rectangle, null)); + + Assert.Equal (expected, region.IsVisible (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)); + Assert.Equal (expected, region.IsVisible ((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)); + Assert.Equal (expected, region.IsVisible (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, s_graphic)); + Assert.Equal (expected, region.IsVisible (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, disposedGraphics)); + Assert.Equal (expected, region.IsVisible (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, null)); + Assert.Equal (expected, region.IsVisible ((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, s_graphic)); + Assert.Equal (expected, region.IsVisible ((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, disposedGraphics)); + Assert.Equal (expected, region.IsVisible ((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, null)); + } + } + + public static IEnumerable IsVisible_Point_TestData () + { + Region infiniteExclude = new (); + infiniteExclude.Exclude (new Rectangle (387, 292, 189, 133)); + infiniteExclude.Exclude (new Rectangle (387, 66, 189, 133)); + + yield return new object [] { infiniteExclude, new Point (66, 292), true }; + yield return new object [] { new Region (), Point.Empty, true }; + + yield return new object [] { new Region (new Rectangle (500, 30, 60, 80)), new Point (500, 29), false }; + yield return new object [] { new Region (new Rectangle (500, 30, 60, 80)), new Point (500, 30), true }; + + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (0, 1), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (1, 0), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (2, 0), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (3, 0), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (1, 1), true }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (2, 1), true }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (3, 1), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (0, 2), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (2, 2), false }; + yield return new object [] { new Region (new Rectangle (1, 1, 2, 1)), new Point (3, 2), false }; + } + + [Theory] + [MemberData (nameof (IsVisible_Point_TestData))] + public void IsVisible_Point_ReturnsExpected (Region region, Point point, bool expected) + { + using (region) + using (Bitmap image = new (10, 10)) + { + var disposedGraphics = Graphics.FromImage (image); + disposedGraphics.Dispose (); + + Assert.Equal (expected, region.IsVisible (point)); + Assert.Equal (expected, region.IsVisible ((PointF)point)); + Assert.Equal (expected, region.IsVisible (point, s_graphic)); + Assert.Equal (expected, region.IsVisible (point, disposedGraphics)); + Assert.Equal (expected, region.IsVisible (point, null)); + Assert.Equal (expected, region.IsVisible ((PointF)point, s_graphic)); + Assert.Equal (expected, region.IsVisible ((PointF)point, disposedGraphics)); + Assert.Equal (expected, region.IsVisible ((PointF)point, null)); + + Assert.Equal (expected, region.IsVisible (point.X, point.Y)); + Assert.Equal (expected, region.IsVisible (point.X, point.Y, s_graphic)); + Assert.Equal (expected, region.IsVisible (point.X, point.Y, disposedGraphics)); + Assert.Equal (expected, region.IsVisible (point.X, point.Y, null)); + + Assert.Equal (expected, region.IsVisible (point.X, point.Y, s_graphic)); + Assert.Equal (expected, region.IsVisible (point.X, point.Y, disposedGraphics)); + Assert.Equal (expected, region.IsVisible (point.X, point.Y, null)); + Assert.Equal (expected, region.IsVisible ((float)point.X, point.Y, s_graphic)); + Assert.Equal (expected, region.IsVisible ((float)point.X, point.Y, disposedGraphics)); + Assert.Equal (expected, region.IsVisible ((float)point.X, point.Y, null)); + } + } + + [Fact] + public void IsVisible_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + Assert.Throws (null, () => disposedRegion.IsVisible (1f, 2f)); + Assert.Throws (null, () => disposedRegion.IsVisible (new PointF (1, 2))); + Assert.Throws (null, () => disposedRegion.IsVisible (new Point (1, 2))); + + Assert.Throws (null, () => disposedRegion.IsVisible (1f, 2f, s_graphic)); + Assert.Throws (null, () => disposedRegion.IsVisible (new PointF (1, 2), s_graphic)); + Assert.Throws (null, () => disposedRegion.IsVisible (new Point (1, 2), s_graphic)); + + Assert.Throws (null, () => disposedRegion.IsVisible (1f, 2f, 3f, 4f)); + Assert.Throws (null, () => disposedRegion.IsVisible (new Rectangle (1, 2, 3, 4))); + Assert.Throws (null, () => disposedRegion.IsVisible (new RectangleF (1, 2, 3, 4))); + + Assert.Throws (null, () => disposedRegion.IsVisible (1f, 2f, 3f, 4f, s_graphic)); + Assert.Throws (null, () => disposedRegion.IsVisible (new Rectangle (1, 2, 3, 4), s_graphic)); + Assert.Throws (null, () => disposedRegion.IsVisible (new RectangleF (1, 2, 3, 4), s_graphic)); + + Assert.Throws (null, () => disposedRegion.IsVisible (1, 2, s_graphic)); + Assert.Throws (null, () => disposedRegion.IsVisible (1, 2, 3, 4)); + Assert.Throws (null, () => disposedRegion.IsVisible (1, 2, 3, 4, s_graphic)); + } + + [Theory] + [MemberData (nameof (Region_TestData))] + public void MakeEmpty_NonEmpty_Success (Region region) + { + using (region) + { + region.MakeEmpty (); + Assert.True (region.IsEmpty (s_graphic)); + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal (RectangleF.Empty, region.GetBounds (s_graphic)); + + using (Matrix matrix = new ()) + { + Assert.Empty (region.GetRegionScans (matrix)); + } + + region.MakeEmpty (); + Assert.True (region.IsEmpty (s_graphic)); + } + } + + [Fact] + public void MakeEmpty_Disposed_ThrowsArgumentException () { Assert.Throws (null, () => CreateDisposedRegion ().MakeEmpty ()); } + + [Theory] + [MemberData (nameof (Region_TestData))] + public void MakeInfinite_NonInfinity_Success (Region region) + { + using (region) + { + region.MakeInfinite (); + Assert.False (region.IsEmpty (s_graphic)); + Assert.True (region.IsInfinite (s_graphic)); + Assert.Equal (new RectangleF (-4194304, -4194304, 8388608, 8388608), region.GetBounds (s_graphic)); + + region.MakeInfinite (); + Assert.False (region.IsEmpty (s_graphic)); + Assert.True (region.IsInfinite (s_graphic)); + } + } + + [Fact] + public void MakeInfinite_Disposed_ThrowsArgumentException () + { + Assert.Throws (null, () => CreateDisposedRegion ().MakeInfinite ()); + } + + public static IEnumerable Union_TestData () + { + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { new (500, 30, 60, 80) }, + new RectangleF [] { new (500, 30, 60, 80) } + }; + + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { RectangleF.Empty }, + new RectangleF [] { new (500, 30, 60, 80) } + }; + + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { new (520, 30, 60, 80) }, + new RectangleF [] { new (500, 30, 80, 80) } + }; + + yield return new object [] + { + new Region (new Rectangle (500, 30, 60, 80)), + new RectangleF [] { new (520, 40, 60, 80) }, + new RectangleF [] + { + new (500, 30, 60, 10), + new (500, 40, 80, 70), + new (520, 110, 60, 10), + } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { new (520, 40, 60, 80) }, + new RectangleF [] { new Rectangle (-4194304, -4194304, 8388608, 8388608) } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { RectangleF.Empty }, + new RectangleF [] { new Rectangle (-4194304, -4194304, 8388608, 8388608) } + }; + + // No intersecting rects. + yield return new object [] + { + new Region (new Rectangle (20, 20, 20, 20)), + new RectangleF [] + { + new (20, 80, 20, 10), + new (60, 60, 30, 10) + }, + new RectangleF [] + { + new (20, 20, 20, 20), + new (60, 60, 30, 10), + new (20, 80, 20, 10) + } + }; + + yield return new object [] + { + new Region (new Rectangle (20, 180, 40, 50)), + new RectangleF [] + { + new (50, 190, 40, 50), + new (70, 210, 30, 50) + }, + new RectangleF [] + { + new (20, 180, 40, 10), + new (20, 190, 70, 20), + new (20, 210, 80, 20), + new (50, 230, 50, 10), + new (70, 240, 30, 20) + } + }; + + yield return new object [] + { + new Region (new Rectangle (20, 330, 40, 50)), + new RectangleF [] + { + new (50, 340, 40, 50), + new (70, 360, 30, 50), + new (80, 400, 30, 10) + }, + new RectangleF [] + { + new (20, 330, 40, 10), + new (20, 340, 70, 20), + new (20, 360, 80, 20), + new (50, 380, 50, 10), + new (70, 390, 30, 10), + new (70, 400, 40, 10) + } + }; + + yield return new object [] + { + new Region (new Rectangle (10, 20, 50, 50)), + new RectangleF [] + { + new (100, 100, 60, 60), + new (200, 200, 80, 80) + }, + new RectangleF [] + { + new (10, 20, 50, 50), + new (100, 100, 60, 60), + new (200, 200, 80, 80) + } + }; + + // Intersecting from the right. + yield return new object [] + { + new Region (new Rectangle (10, 10, 100, 100)), + new RectangleF [] { new (40, 60, 100, 20) }, + new RectangleF [] + { + new (10, 10, 100, 50), + new (10, 60, 130, 20), + new (10, 80, 100, 30) + } + }; + + // Intersecting from the left. + yield return new object [] + { + new Region (new Rectangle (70, 10, 100, 100)), + new RectangleF [] { new (40, 60, 100, 20) }, + new RectangleF [] + { + new (70, 10, 100, 50), + new (40, 60, 130, 20), + new (70, 80, 100, 30) + } + }; + + // Intersecting from the top. + yield return new object [] + { + new Region (new Rectangle (40, 100, 100, 100)), + new RectangleF [] { new (70, 80, 50, 40) }, + new RectangleF [] + { + new (70, 80, 50, 20), + new (40, 100, 100, 100) + } + }; + + // Intersecting from the bottom. + yield return new object [] + { + new Region (new Rectangle (40, 10, 100, 100)), + new RectangleF [] { new (70, 80, 50, 40) }, + new RectangleF [] + { + new (40, 10, 100, 100), + new (70, 110, 50, 10) + } + }; + + // Multiple regions separated by 0 pixels. + yield return new object [] + { + new Region (new Rectangle (30, 30, 80, 80)), + new RectangleF [] + { + new (45, 45, 200, 200), + new (160, 260, 10, 10), + new (170, 260, 10, 10) + }, + new RectangleF [] + { + new (30, 30, 80, 15), + new (30, 45, 215, 65), + new (45, 110, 200, 135), + new (160, 260, 20, 10) + } + }; + } + + [Theory] + [MemberData (nameof (Union_TestData))] + public void Union_Region_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using Region other = new (rect); + region.Union (other); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Union_InfiniteRegion_Success () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + using Region other = new (); + using Matrix matrix = new (); + region.Union (other); + + Assert.Equal ([new Rectangle (-4194304, -4194304, 8388608, 8388608)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Union_NullRegion_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("region", () => region.Union ((Region)null)); + } + + [Fact] + public void Union_DisposedRegion_ThrowsArgumentException () + { + using Region region = new (); + Assert.Throws (null, () => region.Union (CreateDisposedRegion ())); + } + + [Fact] + public void Union_SameRegion_ThrowsInvalidOperationException () + { + using Region region = new (); + Assert.Throws (() => region.Union (region)); + } + + [Theory] + [MemberData (nameof (Union_TestData))] + public void Union_Rectangle_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Union (new Rectangle ((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Union_TestData))] + public void Union_RectangleF_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Union (rect); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Union_TestData))] + public void Union_GraphicsPath_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using GraphicsPath path = new (); + path.AddRectangle (rect); + region.Union (path); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Union_EmptyPathWithInfiniteRegion_MakesInfinite () + { + using Region region = new (); + using GraphicsPath graphicsPath = new (); + region.Union (graphicsPath); + Assert.True (region.IsInfinite (s_graphic)); + } + + [Fact] + public void Union_NullGraphicsPath_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("path", () => region.Union ((GraphicsPath)null)); + } + + [Fact] + public void Union_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + using GraphicsPath graphicsPath = new (); + using Region other = new (); + Assert.Throws (null, () => disposedRegion.Union (graphicsPath)); + Assert.Throws (null, () => disposedRegion.Union (new Rectangle ())); + Assert.Throws (null, () => disposedRegion.Union (new RectangleF ())); + Assert.Throws (null, () => disposedRegion.Union (disposedRegion)); + } + + [Fact] + public void Transform_EmptyMatrix_Nop () + { + using Region region = new (new RectangleF (1, 2, 3, 4)); + using Matrix matrix = new (); + region.Transform (matrix); + Assert.Equal ([new (1, 2, 3, 4)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Transform_CustomMatrix_Success () + { + using Region region = new (new RectangleF (1, 2, 3, 4)); + using Matrix matrix = new (); + using Matrix emptyMatrix = new (); + matrix.Translate (10, 11); + matrix.Scale (5, 6); + + region.Transform (matrix); + Assert.Equal ([new (15, 23, 15, 24)], region.GetRegionScans (emptyMatrix)); + } + + [Theory] + [InlineData (0, 0, 0)] + [InlineData (2, 2, 0)] + [InlineData (0.5, 0.5, 0)] + [InlineData (1, 1, 45)] + public void Transform_Infinity_Nop (float scaleX, float scaleY, int angle) + { + using Region region = new (); + using Matrix matrix = new (); + using Matrix emptyMatrix = new (); + matrix.Translate (10, 11); + matrix.Scale (scaleX, scaleY); + matrix.Rotate (angle); + + region.Transform (matrix); + Assert.True (region.IsInfinite (s_graphic)); + Assert.Equal ([new (-4194304, -4194304, 8388608, 8388608)], region.GetRegionScans (emptyMatrix)); + } + + [Fact] + public void Transform_InfinityIntersectScale_Success () + { + using Region region = new (); + using Matrix matrix = new (); + using Matrix emptyMatrix = new (); + matrix.Scale (2, 0.5f); + + region.Intersect (new Rectangle (-10, -10, 20, 20)); + region.Transform (matrix); + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal ([new (-20, -5, 40, 10)], region.GetRegionScans (emptyMatrix)); + } + + [Fact] + public void Transform_InfinityIntersectTransform_Success () + { + using Region region = new (); + using Matrix matrix = new (2, 0, 0, 0.5f, 10, 10); + using Matrix emptyMatrix = new (); + region.Intersect (new Rectangle (-10, -10, 20, 20)); + region.Transform (matrix); + + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal ([new (-10, 5, 40, 10)], region.GetRegionScans (emptyMatrix)); + } + + [Fact] + public void Transform_NullMatrix_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("matrix", () => region.Transform (null)); + } + + [Fact] + public void Transform_Disposed_ThrowsArgumentException () + { + using Matrix matrix = new (); + Assert.Throws (null, () => CreateDisposedRegion ().Transform (matrix)); + } + + [Theory] + [InlineData (0, 0)] + [InlineData (2, 3)] + [InlineData (-2, -3)] + public void Translate_Int_Success (float dx, float dy) + { + using Region region = new (new RectangleF (1, 2, 3, 4)); + using Matrix matrix = new (); + region.Translate (dx, dy); + Assert.Equal ([new (1 + dx, 2 + dy, 3, 4)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Translate_IntInfinityIntersect_Success () + { + using Region region = new (); + using Matrix matrix = new (); + region.Intersect (new Rectangle (-10, -10, 20, 20)); + region.Translate (10, 10); + + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal ([new (0, 0, 20, 20)], region.GetRegionScans (matrix)); + } + + [Theory] + [InlineData (0, 0)] + [InlineData (2, 3)] + public void Translate_Float_Success (int dx, int dy) + { + using Region region = new (new RectangleF (1, 2, 3, 4)); + using Matrix matrix = new (); + region.Translate (dx, dy); + Assert.Equal ([new (1 + dx, 2 + dy, 3, 4)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Translate_FloatInfinityIntersect_Success () + { + using Region region = new (); + using Matrix matrix = new (); + region.Intersect (new Rectangle (-10, -10, 20, 20)); + region.Translate (10f, 10f); + + Assert.False (region.IsInfinite (s_graphic)); + Assert.Equal ([new (0, 0, 20, 20)], region.GetRegionScans (matrix)); + } + + [Fact] + public void Translate_Infinity_Nop () + { + using Region region = new (); + using Matrix matrix = new (); + region.Translate (10, 10); + region.Translate (10f, 10f); + + Assert.True (region.IsInfinite (s_graphic)); + Assert.Equal ([new (-4194304, -4194304, 8388608, 8388608)], region.GetRegionScans (matrix)); + } + + [Theory] + [InlineData (float.MaxValue)] + [InlineData (float.MinValue)] + [InlineData (float.NaN)] + [InlineData (float.PositiveInfinity)] + [InlineData (float.NegativeInfinity)] + public void Translate_InvalidFloatValue_EmptiesRegion (float f) + { + using Region region = new (new RectangleF (1, 2, 3, 4)); + using Matrix matrix = new (); + region.Translate (f, 0); + + Assert.True (region.IsEmpty (s_graphic)); + Assert.False (region.IsInfinite (s_graphic)); + Assert.Empty (region.GetRegionScans (matrix)); + } + + [Fact] + public void Translate_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + Assert.Throws (null, () => disposedRegion.Translate (1, 2)); + Assert.Throws (null, () => disposedRegion.Translate (1f, 2f)); + } + + public static IEnumerable Xor_TestData () + { + yield return new object [] + { + new Region (new RectangleF (500, 30, 60, 80)), + new RectangleF [] { new (500, 30, 60, 80) }, + new RectangleF[0] + }; + + yield return new object [] + { + new Region (new RectangleF (500, 30, 60, 80)), + new RectangleF [] { RectangleF.Empty }, + new RectangleF [] { new (500, 30, 60, 80) } + }; + + yield return new object [] + { + new Region (new RectangleF (0, 0, 0, 0)), + new RectangleF [] { new (500, 30, 60, 80) }, + new RectangleF [] { new (500, 30, 60, 80) } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { new (520, 40, 60, 80) }, + new RectangleF [] + { + new (-4194304, -4194304, 8388608, 4194344), + new (-4194304, 40, 4194824, 80), + new (580, 40, 4193724, 80), + new (-4194304, 120, 8388608, 4194184) + } + }; + + yield return new object [] + { + new Region (), + new RectangleF [] { RectangleF.Empty }, + new RectangleF [] { new Rectangle (-4194304, -4194304, 8388608, 8388608) } + }; + + yield return new object [] + { + new Region (new RectangleF (380, 30, 60, 80)), + new RectangleF [] { new (410, 40, 60, 80) }, + new RectangleF [] + { + new (380, 30, 60, 10), + new (380, 40, 30, 70), + new (440, 40, 30, 70), + new (410, 110, 60, 10) + } + }; + } + + [Theory] + [MemberData (nameof (Xor_TestData))] + public void Xor_Region_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using Region other = new (rect); + region.Xor (other); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Xor_InfiniteRegion_Success () + { + using Region region = new (new Rectangle (1, 2, 3, 4)); + using Region other = new (); + using Matrix matrix = new (); + region.Xor (other); + + Assert.Equal ( + [ + new (-4194304, -4194304, 8388608, 4194306), + new (-4194304, 2, 4194305, 4), + new (4, 2, 4194300, 4), + new (-4194304, 6, 8388608, 4194298) + ], + region.GetRegionScans (matrix)); + } + + [Fact] + public void Xor_NullRegion_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("region", () => region.Xor ((Region)null)); + } + + [Fact] + public void Xor_DisposedRegion_ThrowsArgumentException () + { + using Region region = new (); + Assert.Throws (null, () => region.Xor (CreateDisposedRegion ())); + } + + [Fact] + public void Xor_SameRegion_ThrowsInvalidOperationException () + { + using Region region = new (); + Assert.Throws (() => region.Xor (region)); + } + + [Theory] + [MemberData (nameof (Xor_TestData))] + public void Xor_Rectangle_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Xor (new Rectangle ((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Xor_TestData))] + public void Xor_RectangleF_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Xor (rect); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Theory] + [MemberData (nameof (Xor_TestData))] + public void Xor_GraphicsPath_Success (Region region, RectangleF [] rectangles, RectangleF [] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using GraphicsPath path = new (); + path.AddRectangle (rect); + region.Xor (path); + } + + using Matrix matrix = new (); + Assert.Equal (expectedScans, region.GetRegionScans (matrix)); + } + } + + [Fact] + public void Xor_EmptyPathWithInfiniteRegion_MakesInfinite () + { + using Region region = new (); + using GraphicsPath graphicsPath = new (); + region.Xor (graphicsPath); + Assert.True (region.IsInfinite (s_graphic)); + } + + [Fact] + public void Xor_NullGraphicsPath_ThrowsArgumentNullException () + { + using Region region = new (); + Assert.Throws ("path", () => region.Xor ((GraphicsPath)null)); + } + + [Fact] + public void Xor_Disposed_ThrowsArgumentException () + { + Region disposedRegion = CreateDisposedRegion (); + + using GraphicsPath graphicsPath = new (); + using Region other = new (); + Assert.Throws (null, () => disposedRegion.Xor (graphicsPath)); + Assert.Throws (null, () => disposedRegion.Xor (new Rectangle ())); + Assert.Throws (null, () => disposedRegion.Xor (new RectangleF ())); + Assert.Throws (null, () => disposedRegion.Xor (other)); + } + +} +#endif \ No newline at end of file