1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Diagnostics.CodeAnalysis;
7 using System.Drawing.Drawing2D;
8 using System.Drawing.Internal;
9 using System.Globalization;
10 using System.Runtime.InteropServices;
11 
12 namespace System.Drawing
13 {
14     public sealed partial class Region : MarshalByRefObject, IDisposable
15     {
16 #if FINALIZATION_WATCH
17         private string allocationSite = Graphics.GetAllocationStack();
18 #endif
19 
Region()20         public Region()
21         {
22             IntPtr region = IntPtr.Zero;
23             int status = SafeNativeMethods.Gdip.GdipCreateRegion(out region);
24             SafeNativeMethods.Gdip.CheckStatus(status);
25 
26             SetNativeRegion(region);
27         }
28 
Region(RectangleF rect)29         public Region(RectangleF rect)
30         {
31             IntPtr region = IntPtr.Zero;
32             var gprectf = new GPRECTF(rect);
33             int status = SafeNativeMethods.Gdip.GdipCreateRegionRect(ref gprectf, out region);
34             SafeNativeMethods.Gdip.CheckStatus(status);
35 
36             SetNativeRegion(region);
37         }
38 
Region(Rectangle rect)39         public Region(Rectangle rect)
40         {
41             IntPtr region = IntPtr.Zero;
42             var gprect = new GPRECT(rect);
43             int status = SafeNativeMethods.Gdip.GdipCreateRegionRectI(ref gprect, out region);
44             SafeNativeMethods.Gdip.CheckStatus(status);
45 
46             SetNativeRegion(region);
47         }
48 
Region(GraphicsPath path)49         public Region(GraphicsPath path)
50         {
51             if (path == null)
52             {
53                 throw new ArgumentNullException(nameof(path));
54             }
55 
56             IntPtr region = IntPtr.Zero;
57             int status = SafeNativeMethods.Gdip.GdipCreateRegionPath(new HandleRef(path, path.nativePath), out region);
58             SafeNativeMethods.Gdip.CheckStatus(status);
59 
60             SetNativeRegion(region);
61         }
62 
Region(RegionData rgnData)63         public Region(RegionData rgnData)
64         {
65             if (rgnData == null)
66             {
67                 throw new ArgumentNullException(nameof(rgnData));
68             }
69 
70             IntPtr region = IntPtr.Zero;
71             int status = SafeNativeMethods.Gdip.GdipCreateRegionRgnData(rgnData.Data,
72                                                          rgnData.Data.Length,
73                                                          out region);
74             SafeNativeMethods.Gdip.CheckStatus(status);
75 
76             SetNativeRegion(region);
77         }
78 
79         internal Region(IntPtr nativeRegion) => SetNativeRegion(nativeRegion);
80 
FromHrgn(IntPtr hrgn)81         public static Region FromHrgn(IntPtr hrgn)
82         {
83             IntPtr region = IntPtr.Zero;
84             int status = SafeNativeMethods.Gdip.GdipCreateRegionHrgn(new HandleRef(null, hrgn), out region);
85             SafeNativeMethods.Gdip.CheckStatus(status);
86 
87             return new Region(region);
88         }
89 
SetNativeRegion(IntPtr nativeRegion)90         private void SetNativeRegion(IntPtr nativeRegion)
91         {
92             if (nativeRegion == IntPtr.Zero)
93             {
94                 throw new ArgumentNullException(nameof(nativeRegion));
95             }
96 
97             this._nativeRegion = nativeRegion;
98         }
99 
Clone()100         public Region Clone()
101         {
102             IntPtr region = IntPtr.Zero;
103             int status = SafeNativeMethods.Gdip.GdipCloneRegion(new HandleRef(this, _nativeRegion), out region);
104             SafeNativeMethods.Gdip.CheckStatus(status);
105 
106             return new Region(region);
107         }
108 
Dispose()109         public void Dispose()
110         {
111             Dispose(true);
112             GC.SuppressFinalize(this);
113         }
114 
Dispose(bool disposing)115         private void Dispose(bool disposing)
116         {
117 #if FINALIZATION_WATCH
118             if (!disposing && nativeRegion != IntPtr.Zero)
119                 Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite);
120 #endif
121             if (_nativeRegion != IntPtr.Zero)
122             {
123                 try
124                 {
125 #if DEBUG
126                     int status =
127 #endif
128                     SafeNativeMethods.Gdip.GdipDeleteRegion(new HandleRef(this, _nativeRegion));
129 #if DEBUG
130                     Debug.Assert(status == SafeNativeMethods.Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture));
131 #endif
132                 }
133                 catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex))
134                 {
135                 }
136                 finally
137                 {
138                     _nativeRegion = IntPtr.Zero;
139                 }
140             }
141         }
142 
~Region()143         ~Region() => Dispose(false);
144 
MakeInfinite()145         public void MakeInfinite()
146         {
147             int status = SafeNativeMethods.Gdip.GdipSetInfinite(new HandleRef(this, _nativeRegion));
148             SafeNativeMethods.Gdip.CheckStatus(status);
149         }
150 
MakeEmpty()151         public void MakeEmpty()
152         {
153             int status = SafeNativeMethods.Gdip.GdipSetEmpty(new HandleRef(this, _nativeRegion));
154             SafeNativeMethods.Gdip.CheckStatus(status);
155         }
156 
Intersect(RectangleF rect)157         public void Intersect(RectangleF rect)
158         {
159             var gprectf = new GPRECTF(rect);
160             int status = SafeNativeMethods.Gdip.GdipCombineRegionRect(new HandleRef(this, _nativeRegion), ref gprectf, CombineMode.Intersect);
161             SafeNativeMethods.Gdip.CheckStatus(status);
162         }
163 
Intersect(Rectangle rect)164         public void Intersect(Rectangle rect)
165         {
166             var gprect = new GPRECT(rect);
167             int status = SafeNativeMethods.Gdip.GdipCombineRegionRectI(new HandleRef(this, _nativeRegion), ref gprect, CombineMode.Intersect);
168             SafeNativeMethods.Gdip.CheckStatus(status);
169         }
170 
Intersect(GraphicsPath path)171         public void Intersect(GraphicsPath path)
172         {
173             if (path == null)
174             {
175                 throw new ArgumentNullException(nameof(path));
176             }
177 
178             int status = SafeNativeMethods.Gdip.GdipCombineRegionPath(new HandleRef(this, _nativeRegion), new HandleRef(path, path.nativePath), CombineMode.Intersect);
179             SafeNativeMethods.Gdip.CheckStatus(status);
180         }
181 
Intersect(Region region)182         public void Intersect(Region region)
183         {
184             if (region == null)
185             {
186                 throw new ArgumentNullException(nameof(region));
187             }
188 
189             int status = SafeNativeMethods.Gdip.GdipCombineRegionRegion(new HandleRef(this, _nativeRegion), new HandleRef(region, region._nativeRegion), CombineMode.Intersect);
190             SafeNativeMethods.Gdip.CheckStatus(status);
191         }
192 
Union(RectangleF rect)193         public void Union(RectangleF rect)
194         {
195             var gprectf = new GPRECTF(rect);
196             int status = SafeNativeMethods.Gdip.GdipCombineRegionRect(new HandleRef(this, _nativeRegion), ref gprectf, CombineMode.Union);
197             SafeNativeMethods.Gdip.CheckStatus(status);
198         }
199 
Union(Rectangle rect)200         public void Union(Rectangle rect)
201         {
202             var gprect = new GPRECT(rect);
203             int status = SafeNativeMethods.Gdip.GdipCombineRegionRectI(new HandleRef(this, _nativeRegion), ref gprect, CombineMode.Union);
204             SafeNativeMethods.Gdip.CheckStatus(status);
205         }
206 
Union(GraphicsPath path)207         public void Union(GraphicsPath path)
208         {
209             if (path == null)
210             {
211                 throw new ArgumentNullException(nameof(path));
212             }
213 
214             int status = SafeNativeMethods.Gdip.GdipCombineRegionPath(new HandleRef(this, _nativeRegion), new HandleRef(path, path.nativePath), CombineMode.Union);
215             SafeNativeMethods.Gdip.CheckStatus(status);
216         }
217 
Union(Region region)218         public void Union(Region region)
219         {
220             if (region == null)
221             {
222                 throw new ArgumentNullException(nameof(region));
223             }
224 
225             int status = SafeNativeMethods.Gdip.GdipCombineRegionRegion(new HandleRef(this, _nativeRegion), new HandleRef(region, region._nativeRegion), CombineMode.Union);
226             SafeNativeMethods.Gdip.CheckStatus(status);
227         }
228 
Xor(RectangleF rect)229         public void Xor(RectangleF rect)
230         {
231             var gprectf = new GPRECTF(rect);
232             int status = SafeNativeMethods.Gdip.GdipCombineRegionRect(new HandleRef(this, _nativeRegion), ref gprectf, CombineMode.Xor);
233             SafeNativeMethods.Gdip.CheckStatus(status);
234         }
235 
Xor(Rectangle rect)236         public void Xor(Rectangle rect)
237         {
238             var gprect = new GPRECT(rect);
239             int status = SafeNativeMethods.Gdip.GdipCombineRegionRectI(new HandleRef(this, _nativeRegion), ref gprect, CombineMode.Xor);
240             SafeNativeMethods.Gdip.CheckStatus(status);
241         }
242 
Xor(GraphicsPath path)243         public void Xor(GraphicsPath path)
244         {
245             if (path == null)
246             {
247                 throw new ArgumentNullException(nameof(path));
248             }
249 
250             int status = SafeNativeMethods.Gdip.GdipCombineRegionPath(new HandleRef(this, _nativeRegion), new HandleRef(path, path.nativePath), CombineMode.Xor);
251             SafeNativeMethods.Gdip.CheckStatus(status);
252         }
253 
Xor(Region region)254         public void Xor(Region region)
255         {
256             if (region == null)
257             {
258                 throw new ArgumentNullException(nameof(region));
259             }
260 
261             int status = SafeNativeMethods.Gdip.GdipCombineRegionRegion(new HandleRef(this, _nativeRegion), new HandleRef(region, region._nativeRegion), CombineMode.Xor);
262             SafeNativeMethods.Gdip.CheckStatus(status);
263         }
264 
Exclude(RectangleF rect)265         public void Exclude(RectangleF rect)
266         {
267             var gprectf = new GPRECTF(rect);
268             int status = SafeNativeMethods.Gdip.GdipCombineRegionRect(new HandleRef(this, _nativeRegion), ref gprectf, CombineMode.Exclude);
269             SafeNativeMethods.Gdip.CheckStatus(status);
270         }
271 
Exclude(Rectangle rect)272         public void Exclude(Rectangle rect)
273         {
274             var gprect = new GPRECT(rect);
275             int status = SafeNativeMethods.Gdip.GdipCombineRegionRectI(new HandleRef(this, _nativeRegion), ref gprect, CombineMode.Exclude);
276             SafeNativeMethods.Gdip.CheckStatus(status);
277         }
278 
Exclude(GraphicsPath path)279         public void Exclude(GraphicsPath path)
280         {
281             if (path == null)
282             {
283                 throw new ArgumentNullException(nameof(path));
284             }
285 
286             int status = SafeNativeMethods.Gdip.GdipCombineRegionPath(new HandleRef(this, _nativeRegion), new HandleRef(path, path.nativePath),
287                                                        CombineMode.Exclude);
288             SafeNativeMethods.Gdip.CheckStatus(status);
289         }
290 
Exclude(Region region)291         public void Exclude(Region region)
292         {
293             if (region == null)
294             {
295                 throw new ArgumentNullException(nameof(region));
296             }
297 
298             int status = SafeNativeMethods.Gdip.GdipCombineRegionRegion(new HandleRef(this, _nativeRegion), new HandleRef(region, region._nativeRegion),
299                                                          CombineMode.Exclude);
300             SafeNativeMethods.Gdip.CheckStatus(status);
301         }
302 
Complement(RectangleF rect)303         public void Complement(RectangleF rect)
304         {
305             var gprectf = new GPRECTF(rect);
306             int status = SafeNativeMethods.Gdip.GdipCombineRegionRect(new HandleRef(this, _nativeRegion), ref gprectf, CombineMode.Complement);
307             SafeNativeMethods.Gdip.CheckStatus(status);
308         }
309 
Complement(Rectangle rect)310         public void Complement(Rectangle rect)
311         {
312             var gprect = new GPRECT(rect);
313             int status = SafeNativeMethods.Gdip.GdipCombineRegionRectI(new HandleRef(this, _nativeRegion), ref gprect, CombineMode.Complement);
314             SafeNativeMethods.Gdip.CheckStatus(status);
315         }
316 
Complement(GraphicsPath path)317         public void Complement(GraphicsPath path)
318         {
319             if (path == null)
320             {
321                 throw new ArgumentNullException(nameof(path));
322             }
323 
324             int status = SafeNativeMethods.Gdip.GdipCombineRegionPath(new HandleRef(this, _nativeRegion), new HandleRef(path, path.nativePath), CombineMode.Complement);
325             SafeNativeMethods.Gdip.CheckStatus(status);
326         }
327 
Complement(Region region)328         public void Complement(Region region)
329         {
330             if (region == null)
331             {
332                 throw new ArgumentNullException(nameof(region));
333             }
334 
335             int status = SafeNativeMethods.Gdip.GdipCombineRegionRegion(new HandleRef(this, _nativeRegion), new HandleRef(region, region._nativeRegion), CombineMode.Complement);
336             SafeNativeMethods.Gdip.CheckStatus(status);
337         }
338 
Translate(float dx, float dy)339         public void Translate(float dx, float dy)
340         {
341             int status = SafeNativeMethods.Gdip.GdipTranslateRegion(new HandleRef(this, _nativeRegion), dx, dy);
342             SafeNativeMethods.Gdip.CheckStatus(status);
343         }
344 
Translate(int dx, int dy)345         public void Translate(int dx, int dy)
346         {
347             int status = SafeNativeMethods.Gdip.GdipTranslateRegionI(new HandleRef(this, _nativeRegion), dx, dy);
348             SafeNativeMethods.Gdip.CheckStatus(status);
349         }
350 
Transform(Matrix matrix)351         public void Transform(Matrix matrix)
352         {
353             if (matrix == null)
354             {
355                 throw new ArgumentNullException(nameof(matrix));
356             }
357 
358             int status = SafeNativeMethods.Gdip.GdipTransformRegion(new HandleRef(this, _nativeRegion),
359                                                      new HandleRef(matrix, matrix.nativeMatrix));
360             SafeNativeMethods.Gdip.CheckStatus(status);
361         }
362 
GetBounds(Graphics g)363         public RectangleF GetBounds(Graphics g)
364         {
365             if (g == null)
366             {
367                 throw new ArgumentNullException(nameof(g));
368             }
369 
370             var gprectf = new GPRECTF();
371             int status = SafeNativeMethods.Gdip.GdipGetRegionBounds(new HandleRef(this, _nativeRegion), new HandleRef(g, g.NativeGraphics), ref gprectf);
372             SafeNativeMethods.Gdip.CheckStatus(status);
373 
374             return gprectf.ToRectangleF();
375         }
376 
GetHrgn(Graphics g)377         public IntPtr GetHrgn(Graphics g)
378         {
379             if (g == null)
380             {
381                 throw new ArgumentNullException(nameof(g));
382             }
383 
384             IntPtr hrgn = IntPtr.Zero;
385             int status = SafeNativeMethods.Gdip.GdipGetRegionHRgn(new HandleRef(this, _nativeRegion), new HandleRef(g, g.NativeGraphics), out hrgn);
386             SafeNativeMethods.Gdip.CheckStatus(status);
387 
388             return hrgn;
389         }
390 
IsEmpty(Graphics g)391         public bool IsEmpty(Graphics g)
392         {
393             if (g == null)
394             {
395                 throw new ArgumentNullException(nameof(g));
396             }
397 
398             int isEmpty;
399             int status = SafeNativeMethods.Gdip.GdipIsEmptyRegion(new HandleRef(this, _nativeRegion), new HandleRef(g, g.NativeGraphics), out isEmpty);
400             SafeNativeMethods.Gdip.CheckStatus(status);
401 
402             return isEmpty != 0;
403         }
404 
IsInfinite(Graphics g)405         public bool IsInfinite(Graphics g)
406         {
407             if (g == null)
408             {
409                 throw new ArgumentNullException(nameof(g));
410             }
411 
412             int isInfinite;
413             int status = SafeNativeMethods.Gdip.GdipIsInfiniteRegion(new HandleRef(this, _nativeRegion), new HandleRef(g, g.NativeGraphics), out isInfinite);
414             SafeNativeMethods.Gdip.CheckStatus(status);
415 
416             return isInfinite != 0;
417         }
418 
Equals(Region region, Graphics g)419         public bool Equals(Region region, Graphics g)
420         {
421             if (g == null)
422             {
423                 throw new ArgumentNullException(nameof(g));
424             }
425 
426             if (region == null)
427             {
428                 throw new ArgumentNullException(nameof(region));
429             }
430 
431             int isEqual;
432             int status = SafeNativeMethods.Gdip.GdipIsEqualRegion(new HandleRef(this, _nativeRegion), new HandleRef(region, region._nativeRegion), new HandleRef(g, g.NativeGraphics), out isEqual);
433             SafeNativeMethods.Gdip.CheckStatus(status);
434 
435             return isEqual != 0;
436         }
437 
GetRegionData()438         public RegionData GetRegionData()
439         {
440             int regionSize = 0;
441             int status = SafeNativeMethods.Gdip.GdipGetRegionDataSize(new HandleRef(this, _nativeRegion), out regionSize);
442             SafeNativeMethods.Gdip.CheckStatus(status);
443 
444             if (regionSize == 0)
445             {
446                 return null;
447             }
448 
449             byte[] regionData = new byte[regionSize];
450             status = SafeNativeMethods.Gdip.GdipGetRegionData(new HandleRef(this, _nativeRegion), regionData, regionSize, out regionSize);
451             SafeNativeMethods.Gdip.CheckStatus(status);
452 
453             return new RegionData(regionData);
454         }
455 
IsVisible(float x, float y)456         public bool IsVisible(float x, float y) => IsVisible(new PointF(x, y), null);
457 
458         public bool IsVisible(PointF point) => IsVisible(point, null);
459 
IsVisible(float x, float y, Graphics g)460         public bool IsVisible(float x, float y, Graphics g) => IsVisible(new PointF(x, y), g);
461 
IsVisible(PointF point, Graphics g)462         public bool IsVisible(PointF point, Graphics g)
463         {
464             int isVisible;
465             int status = SafeNativeMethods.Gdip.GdipIsVisibleRegionPoint(new HandleRef(this, _nativeRegion), point.X, point.Y,
466                                                           new HandleRef(g, (g == null) ? IntPtr.Zero : g.NativeGraphics),
467                                                           out isVisible);
468             SafeNativeMethods.Gdip.CheckStatus(status);
469 
470             return isVisible != 0;
471         }
472 
IsVisible(float x, float y, float width, float height)473         public bool IsVisible(float x, float y, float width, float height) => IsVisible(new RectangleF(x, y, width, height), null);
474 
475         public bool IsVisible(RectangleF rect) => IsVisible(rect, null);
476 
IsVisible(float x, float y, float width, float height, Graphics g)477         public bool IsVisible(float x, float y, float width, float height, Graphics g) => IsVisible(new RectangleF(x, y, width, height), g);
478 
IsVisible(RectangleF rect, Graphics g)479         public bool IsVisible(RectangleF rect, Graphics g)
480         {
481             int isVisible = 0;
482             int status = SafeNativeMethods.Gdip.GdipIsVisibleRegionRect(new HandleRef(this, _nativeRegion), rect.X, rect.Y,
483                                                          rect.Width, rect.Height,
484                                                          new HandleRef(g, (g == null) ? IntPtr.Zero : g.NativeGraphics),
485                                                          out isVisible);
486             SafeNativeMethods.Gdip.CheckStatus(status);
487 
488             return isVisible != 0;
489         }
490 
IsVisible(int x, int y, Graphics g)491         public bool IsVisible(int x, int y, Graphics g) => IsVisible(new Point(x, y), g);
492 
493         public bool IsVisible(Point point) => IsVisible(point, null);
494 
IsVisible(Point point, Graphics g)495         public bool IsVisible(Point point, Graphics g)
496         {
497             int isVisible = 0;
498             int status = SafeNativeMethods.Gdip.GdipIsVisibleRegionPointI(new HandleRef(this, _nativeRegion), point.X, point.Y,
499                                                            new HandleRef(g, (g == null) ? IntPtr.Zero : g.NativeGraphics),
500                                                            out isVisible);
501             SafeNativeMethods.Gdip.CheckStatus(status);
502 
503             return isVisible != 0;
504         }
505 
IsVisible(int x, int y, int width, int height)506         public bool IsVisible(int x, int y, int width, int height) => IsVisible(new Rectangle(x, y, width, height), null);
507 
508         public bool IsVisible(Rectangle rect) => IsVisible(rect, null);
509 
IsVisible(int x, int y, int width, int height, Graphics g)510         public bool IsVisible(int x, int y, int width, int height, Graphics g) => IsVisible(new Rectangle(x, y, width, height), g);
511 
IsVisible(Rectangle rect, Graphics g)512         public bool IsVisible(Rectangle rect, Graphics g)
513         {
514             int isVisible = 0;
515             int status = SafeNativeMethods.Gdip.GdipIsVisibleRegionRectI(new HandleRef(this, _nativeRegion), rect.X, rect.Y,
516                                                           rect.Width, rect.Height,
517                                                           new HandleRef(g, (g == null) ? IntPtr.Zero : g.NativeGraphics),
518                                                           out isVisible);
519             SafeNativeMethods.Gdip.CheckStatus(status);
520 
521             return isVisible != 0;
522         }
523 
GetRegionScans(Matrix matrix)524         public RectangleF[] GetRegionScans(Matrix matrix)
525         {
526             if (matrix == null)
527             {
528                 throw new ArgumentNullException(nameof(matrix));
529             }
530 
531             int count = 0;
532             int status = SafeNativeMethods.Gdip.GdipGetRegionScansCount(new HandleRef(this, _nativeRegion),
533                                                          out count,
534                                                          new HandleRef(matrix, matrix.nativeMatrix));
535             SafeNativeMethods.Gdip.CheckStatus(status);
536 
537             int rectsize = (int)Marshal.SizeOf(typeof(GPRECTF));
538             IntPtr memoryRects = Marshal.AllocHGlobal(checked(rectsize * count));
539 
540             try
541             {
542                 status = SafeNativeMethods.Gdip.GdipGetRegionScans(new HandleRef(this, _nativeRegion),
543                     memoryRects,
544                     out count,
545                     new HandleRef(matrix, matrix.nativeMatrix));
546                 SafeNativeMethods.Gdip.CheckStatus(status);
547 
548                 var gprectf = new GPRECTF();
549 
550                 var rectangles = new RectangleF[count];
551                 for (int index = 0; index < count; index++)
552                 {
553                     gprectf = (GPRECTF)Marshal.PtrToStructure((IntPtr)(checked((long)memoryRects + rectsize * index)), typeof(GPRECTF));
554                     rectangles[index] = gprectf.ToRectangleF();
555                 }
556 
557                 return rectangles;
558             }
559             finally
560             {
561                 Marshal.FreeHGlobal(memoryRects);
562             }
563         }
564 
565         internal IntPtr _nativeRegion;
566     }
567 }
568