PdnRegion.cs 16 KB

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