PdnRegion.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using System.Linq;
  6. using System.Runtime.Serialization;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace SmartCoalApplication.Core
  10. {
  11. /// <summary>
  12. /// Designed as a proxy to the GDI+ Region class, while allowing for a
  13. /// replacement that won't break code. The main reason for having this
  14. /// right now is to work around some bugs in System.Drawing.Region,
  15. /// especially the memory leak in GetRegionScans().
  16. /// </summary>
  17. [Serializable]
  18. public sealed class PdnRegion : ISerializable, IDisposable
  19. {
  20. private object lockObject = new object();
  21. private Region gdiRegion;
  22. private bool changed = true;
  23. private int cachedArea = -1;
  24. private Rectangle cachedBounds = Rectangle.Empty;
  25. private RectangleF[] cachedRectsF = null;
  26. private Rectangle[] cachedRects = null;
  27. public object SyncRoot
  28. {
  29. get
  30. {
  31. return lockObject;
  32. }
  33. }
  34. public int GetArea()
  35. {
  36. lock (SyncRoot)
  37. {
  38. int theCachedArea = cachedArea;
  39. if (theCachedArea == -1)
  40. {
  41. int ourCachedArea = 0;
  42. foreach (Rectangle rect in GetRegionScansReadOnlyInt())
  43. {
  44. try
  45. {
  46. ourCachedArea += rect.Width * rect.Height;
  47. }
  48. catch (System.OverflowException)
  49. {
  50. ourCachedArea = int.MaxValue;
  51. break;
  52. }
  53. }
  54. cachedArea = ourCachedArea;
  55. return ourCachedArea;
  56. }
  57. else
  58. {
  59. return theCachedArea;
  60. }
  61. }
  62. }
  63. private bool IsChanged()
  64. {
  65. return this.changed;
  66. }
  67. private void Changed()
  68. {
  69. lock (SyncRoot)
  70. {
  71. this.changed = true;
  72. this.cachedArea = -1;
  73. this.cachedBounds = Rectangle.Empty;
  74. }
  75. }
  76. private void ResetChanged()
  77. {
  78. lock (SyncRoot)
  79. {
  80. this.changed = false;
  81. }
  82. }
  83. public PdnRegion()
  84. {
  85. this.gdiRegion = new Region();
  86. }
  87. public PdnRegion(GraphicsPath path)
  88. {
  89. this.gdiRegion = new Region(path);
  90. }
  91. public PdnRegion(Rectangle rect)
  92. {
  93. this.gdiRegion = new Region(rect);
  94. }
  95. public PdnRegion(RectangleF rectF)
  96. {
  97. this.gdiRegion = new Region(rectF);
  98. }
  99. public PdnRegion(RegionData regionData)
  100. {
  101. this.gdiRegion = new Region(regionData);
  102. }
  103. public PdnRegion(Region region, bool takeOwnership)
  104. {
  105. if (takeOwnership)
  106. {
  107. this.gdiRegion = region;
  108. }
  109. else
  110. {
  111. this.gdiRegion = region.Clone();
  112. }
  113. }
  114. public PdnRegion(Region region)
  115. : this(region, false)
  116. {
  117. }
  118. private PdnRegion(PdnRegion pdnRegion)
  119. {
  120. lock (pdnRegion.SyncRoot)
  121. {
  122. this.gdiRegion = pdnRegion.gdiRegion.Clone();
  123. this.changed = pdnRegion.changed;
  124. this.cachedArea = pdnRegion.cachedArea;
  125. this.cachedRectsF = pdnRegion.cachedRectsF;
  126. this.cachedRects = pdnRegion.cachedRects;
  127. }
  128. }
  129. // This constructor is used by WrapRegion. The boolean parameter is just
  130. // there because we already have a parameterless contructor
  131. private PdnRegion(bool sentinel)
  132. {
  133. }
  134. public static PdnRegion CreateEmpty()
  135. {
  136. PdnRegion region = new PdnRegion();
  137. region.MakeEmpty();
  138. return region;
  139. }
  140. public static PdnRegion WrapRegion(Region region)
  141. {
  142. PdnRegion pdnRegion = new PdnRegion(false);
  143. pdnRegion.gdiRegion = region;
  144. return pdnRegion;
  145. }
  146. public void GetObjectData(SerializationInfo info, StreamingContext context)
  147. {
  148. if (this.disposed)
  149. {
  150. throw new ObjectDisposedException("PdnRegion");
  151. }
  152. RegionData regionData;
  153. lock (SyncRoot)
  154. {
  155. regionData = this.gdiRegion.GetRegionData();
  156. }
  157. byte[] data = regionData.Data;
  158. info.AddValue("data", data);
  159. }
  160. public PdnRegion(SerializationInfo info, StreamingContext context)
  161. {
  162. byte[] data = (byte[])info.GetValue("data", typeof(byte[]));
  163. using (Region region = new Region())
  164. {
  165. RegionData regionData = region.GetRegionData();
  166. regionData.Data = data;
  167. this.gdiRegion = new Region(regionData);
  168. }
  169. this.lockObject = new object();
  170. this.cachedArea = -1;
  171. this.cachedBounds = Rectangle.Empty;
  172. this.changed = true;
  173. this.cachedRects = null;
  174. this.cachedRectsF = null;
  175. }
  176. public PdnRegion Clone()
  177. {
  178. return new PdnRegion(this);
  179. }
  180. ~PdnRegion()
  181. {
  182. Dispose(false);
  183. }
  184. private bool disposed = false;
  185. public void Dispose()
  186. {
  187. Dispose(true);
  188. GC.SuppressFinalize(this);
  189. }
  190. private void Dispose(bool disposing)
  191. {
  192. if (!disposed)
  193. {
  194. if (disposing)
  195. {
  196. lock (SyncRoot)
  197. {
  198. gdiRegion.Dispose();
  199. gdiRegion = null;
  200. }
  201. }
  202. disposed = true;
  203. }
  204. }
  205. public Region GetRegionReadOnly()
  206. {
  207. return this.gdiRegion;
  208. }
  209. public void Complement(GraphicsPath path)
  210. {
  211. lock (SyncRoot)
  212. {
  213. Changed();
  214. gdiRegion.Complement(path);
  215. }
  216. }
  217. public void Complement(Rectangle rect)
  218. {
  219. lock (SyncRoot)
  220. {
  221. Changed();
  222. gdiRegion.Complement(rect);
  223. }
  224. }
  225. public void Complement(RectangleF rectF)
  226. {
  227. lock (SyncRoot)
  228. {
  229. Changed();
  230. gdiRegion.Complement(rectF);
  231. }
  232. }
  233. public void Complement(Region region)
  234. {
  235. lock (SyncRoot)
  236. {
  237. Changed();
  238. gdiRegion.Complement(region);
  239. }
  240. }
  241. public void Complement(PdnRegion region2)
  242. {
  243. lock (SyncRoot)
  244. {
  245. Changed();
  246. gdiRegion.Complement(region2.gdiRegion);
  247. }
  248. }
  249. public void Exclude(GraphicsPath path)
  250. {
  251. lock (SyncRoot)
  252. {
  253. gdiRegion.Exclude(path);
  254. }
  255. }
  256. public void Exclude(Rectangle rect)
  257. {
  258. lock (SyncRoot)
  259. {
  260. gdiRegion.Exclude(rect);
  261. }
  262. }
  263. public void Exclude(RectangleF rectF)
  264. {
  265. lock (SyncRoot)
  266. {
  267. gdiRegion.Exclude(rectF);
  268. }
  269. }
  270. public void Exclude(Region region)
  271. {
  272. lock (SyncRoot)
  273. {
  274. gdiRegion.Exclude(region);
  275. }
  276. }
  277. public void Exclude(PdnRegion region2)
  278. {
  279. lock (SyncRoot)
  280. {
  281. gdiRegion.Exclude(region2.gdiRegion);
  282. }
  283. }
  284. public RectangleF GetBounds(Graphics graphics)
  285. {
  286. lock (SyncRoot)
  287. {
  288. return gdiRegion.GetBounds(graphics);
  289. }
  290. }
  291. public RectangleF GetBounds()
  292. {
  293. lock (SyncRoot)
  294. {
  295. using (SystemLayer.NullGraphics nullGraphics = new SystemLayer.NullGraphics())
  296. {
  297. return gdiRegion.GetBounds(nullGraphics.Graphics);
  298. }
  299. }
  300. }
  301. public Rectangle GetBoundsInt()
  302. {
  303. Rectangle bounds;
  304. lock (SyncRoot)
  305. {
  306. bounds = this.cachedBounds;
  307. if (bounds == Rectangle.Empty)
  308. {
  309. Rectangle[] rects = GetRegionScansReadOnlyInt();
  310. if (rects.Length == 0)
  311. {
  312. return Rectangle.Empty;
  313. }
  314. bounds = rects[0];
  315. for (int i = 1; i < rects.Length; ++i)
  316. {
  317. bounds = Rectangle.Union(bounds, rects[i]);
  318. }
  319. this.cachedBounds = bounds;
  320. }
  321. }
  322. return bounds;
  323. }
  324. public RegionData GetRegionData()
  325. {
  326. lock (SyncRoot)
  327. {
  328. return gdiRegion.GetRegionData();
  329. }
  330. }
  331. public RectangleF[] GetRegionScans()
  332. {
  333. return (RectangleF[])GetRegionScansReadOnly().Clone();
  334. }
  335. /// <summary>
  336. /// This is an optimized version of GetRegionScans that returns a reference to the array
  337. /// that is used to cache the region scans. This mitigates performance when this array
  338. /// is requested many times on an unmodified PdnRegion.
  339. /// Thus, by using this method you are promising to not modify the array that is returned.
  340. /// </summary>
  341. /// <returns></returns>
  342. public RectangleF[] GetRegionScansReadOnly()
  343. {
  344. lock (this.SyncRoot)
  345. {
  346. if (this.changed)
  347. {
  348. UpdateCachedRegionScans();
  349. }
  350. if (this.cachedRectsF == null)
  351. {
  352. this.cachedRectsF = new RectangleF[cachedRects.Length];
  353. for (int i = 0; i < this.cachedRectsF.Length; ++i)
  354. {
  355. this.cachedRectsF[i] = (RectangleF)this.cachedRects[i];
  356. }
  357. }
  358. return this.cachedRectsF;
  359. }
  360. }
  361. public Rectangle[] GetRegionScansInt()
  362. {
  363. return (Rectangle[])GetRegionScansReadOnlyInt().Clone();
  364. }
  365. public Rectangle[] GetRegionScansReadOnlyInt()
  366. {
  367. lock (this.SyncRoot)
  368. {
  369. if (this.changed)
  370. {
  371. UpdateCachedRegionScans();
  372. }
  373. return this.cachedRects;
  374. }
  375. }
  376. private unsafe void UpdateCachedRegionScans()
  377. {
  378. // Assumes we are in a lock(SyncRoot){} block
  379. SystemLayer.PdnGraphics.GetRegionScans(this.gdiRegion, out cachedRects, out cachedArea);
  380. this.cachedRectsF = null; // only update this when specifically asked for it
  381. }
  382. public void Intersect(GraphicsPath path)
  383. {
  384. lock (SyncRoot)
  385. {
  386. Changed();
  387. gdiRegion.Intersect(path);
  388. }
  389. }
  390. public void Intersect(Rectangle rect)
  391. {
  392. lock (SyncRoot)
  393. {
  394. Changed();
  395. gdiRegion.Intersect(rect);
  396. }
  397. }
  398. public void Intersect(RectangleF rectF)
  399. {
  400. lock (SyncRoot)
  401. {
  402. Changed();
  403. gdiRegion.Intersect(rectF);
  404. }
  405. }
  406. public void Intersect(Region region)
  407. {
  408. lock (SyncRoot)
  409. {
  410. Changed();
  411. gdiRegion.Intersect(region);
  412. }
  413. }
  414. public void Intersect(PdnRegion region2)
  415. {
  416. lock (SyncRoot)
  417. {
  418. Changed();
  419. gdiRegion.Intersect(region2.gdiRegion);
  420. }
  421. }
  422. public bool IsEmpty(Graphics graphics)
  423. {
  424. lock (SyncRoot)
  425. {
  426. return gdiRegion.IsEmpty(graphics);
  427. }
  428. }
  429. public bool IsEmpty()
  430. {
  431. return GetArea() == 0;
  432. }
  433. public bool IsInfinite(Graphics graphics)
  434. {
  435. lock (SyncRoot)
  436. {
  437. return gdiRegion.IsInfinite(graphics);
  438. }
  439. }
  440. public bool IsVisible(Point point)
  441. {
  442. lock (SyncRoot)
  443. {
  444. return gdiRegion.IsVisible(point);
  445. }
  446. }
  447. public bool IsVisible(PointF pointF)
  448. {
  449. lock (SyncRoot)
  450. {
  451. return gdiRegion.IsVisible(pointF);
  452. }
  453. }
  454. public bool IsVisible(Rectangle rect)
  455. {
  456. lock (SyncRoot)
  457. {
  458. return gdiRegion.IsVisible(rect);
  459. }
  460. }
  461. public bool IsVisible(RectangleF rectF)
  462. {
  463. lock (SyncRoot)
  464. {
  465. return gdiRegion.IsVisible(rectF);
  466. }
  467. }
  468. public bool IsVisible(Point point, Graphics graphics)
  469. {
  470. lock (SyncRoot)
  471. {
  472. return gdiRegion.IsVisible(point, graphics);
  473. }
  474. }
  475. public bool IsVisible(PointF pointF, Graphics graphics)
  476. {
  477. lock (SyncRoot)
  478. {
  479. return gdiRegion.IsVisible(pointF, graphics);
  480. }
  481. }
  482. public bool IsVisible(Rectangle rect, Graphics graphics)
  483. {
  484. lock (SyncRoot)
  485. {
  486. return gdiRegion.IsVisible(rect, graphics);
  487. }
  488. }
  489. public bool IsVisible(RectangleF rectF, Graphics graphics)
  490. {
  491. lock (SyncRoot)
  492. {
  493. return gdiRegion.IsVisible(rectF, graphics);
  494. }
  495. }
  496. public bool IsVisible(float x, float y)
  497. {
  498. lock (SyncRoot)
  499. {
  500. return gdiRegion.IsVisible(x, y);
  501. }
  502. }
  503. public bool IsVisible(int x, int y, Graphics graphics)
  504. {
  505. lock (SyncRoot)
  506. {
  507. return gdiRegion.IsVisible(x, y, graphics);
  508. }
  509. }
  510. public bool IsVisible(float x, float y, Graphics graphics)
  511. {
  512. lock (SyncRoot)
  513. {
  514. return gdiRegion.IsVisible(x, y, graphics);
  515. }
  516. }
  517. public bool IsVisible(int x, int y, int width, int height)
  518. {
  519. lock (SyncRoot)
  520. {
  521. return gdiRegion.IsVisible(x, y, width, height);
  522. }
  523. }
  524. public bool IsVisible(float x, float y, float width, float height)
  525. {
  526. lock (SyncRoot)
  527. {
  528. return gdiRegion.IsVisible(x, y, width, height);
  529. }
  530. }
  531. public bool IsVisible(int x, int y, int width, int height, Graphics graphics)
  532. {
  533. lock (SyncRoot)
  534. {
  535. return gdiRegion.IsVisible(x, y, width, height, graphics);
  536. }
  537. }
  538. public bool IsVisible(float x, float y, float width, float height, Graphics graphics)
  539. {
  540. lock (SyncRoot)
  541. {
  542. return gdiRegion.IsVisible(x, y, width, height, graphics);
  543. }
  544. }
  545. public void MakeEmpty()
  546. {
  547. lock (SyncRoot)
  548. {
  549. Changed();
  550. gdiRegion.MakeEmpty();
  551. }
  552. }
  553. public void MakeInfinite()
  554. {
  555. lock (SyncRoot)
  556. {
  557. Changed();
  558. gdiRegion.MakeInfinite();
  559. }
  560. }
  561. }
  562. }