PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Core/Utility.cs

https://bitbucket.org/tuldok89/openpdn
C# | 3143 lines | 2384 code | 493 blank | 266 comment | 337 complexity | ef47c96c16a4ad561e769079360fb56c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using System.Linq;
  10. using PaintDotNet.Base;
  11. using PaintDotNet.SystemLayer;
  12. using System;
  13. using System.Collections;
  14. using System.Collections.Generic;
  15. using System.ComponentModel;
  16. using System.Drawing;
  17. using System.Drawing.Drawing2D;
  18. using System.Drawing.Imaging;
  19. using System.IO;
  20. using System.Net;
  21. using System.Reflection;
  22. using System.Runtime.InteropServices;
  23. using System.Runtime.Serialization.Formatters.Binary;
  24. using System.Text;
  25. using System.Windows.Forms;
  26. namespace PaintDotNet
  27. {
  28. /// <summary>
  29. /// Defines miscellaneous constants and static functions.
  30. /// </summary>
  31. /// // TODO: refactor into mini static classes
  32. public static class Utility
  33. {
  34. internal static bool IsNumber(float x)
  35. {
  36. return x >= float.MinValue && x <= float.MaxValue;
  37. }
  38. internal static bool IsNumber(double x)
  39. {
  40. return x >= double.MinValue && x <= double.MaxValue;
  41. }
  42. internal static int Min(int val0, params int[] vals)
  43. {
  44. int min = val0;
  45. foreach (int t in vals)
  46. {
  47. if (t < min)
  48. {
  49. min = t;
  50. }
  51. }
  52. return min;
  53. }
  54. internal static int Max(int val0, params int[] vals)
  55. {
  56. int max = val0;
  57. foreach (int t in vals)
  58. {
  59. if (t > max)
  60. {
  61. max = t;
  62. }
  63. }
  64. return max;
  65. }
  66. public static PointF[] GetRgssOffsets(int quality)
  67. {
  68. unsafe
  69. {
  70. int sampleCount = quality * quality;
  71. var samplesArray = new PointF[sampleCount];
  72. fixed (PointF* pSamplesArray = samplesArray)
  73. {
  74. GetRgssOffsets(pSamplesArray, sampleCount, quality);
  75. }
  76. return samplesArray;
  77. }
  78. }
  79. public static unsafe void GetRgssOffsets(PointF* samplesArray, int sampleCount, int quality)
  80. {
  81. if (sampleCount < 1)
  82. {
  83. throw new ArgumentOutOfRangeException("sampleCount", "sampleCount must be [0, int.MaxValue]");
  84. }
  85. if (sampleCount != quality * quality)
  86. {
  87. throw new ArgumentOutOfRangeException("sampleCount != (quality * quality)");
  88. }
  89. if (sampleCount == 1)
  90. {
  91. samplesArray[0] = new PointF(0.0f, 0.0f);
  92. }
  93. else
  94. {
  95. for (int i = 0; i < sampleCount; ++i)
  96. {
  97. double y = (i + 1d) / (sampleCount + 1d);
  98. double x = y * quality;
  99. x -= (int)x;
  100. samplesArray[i] = new PointF((float)(x - 0.5d), (float)(y - 0.5d));
  101. }
  102. }
  103. }
  104. public static bool IsObsolete(Type type, bool inherit)
  105. {
  106. object[] attrs = type.GetCustomAttributes(typeof(ObsoleteAttribute), inherit);
  107. return (attrs.Length != 0);
  108. }
  109. public static void DrawDropShadow1Px(Graphics g, Rectangle rect)
  110. {
  111. Brush b0 = new SolidBrush(Color.FromArgb(15, Color.Black));
  112. Brush b1 = new SolidBrush(Color.FromArgb(47, Color.Black));
  113. var p2 = new Pen(Color.FromArgb(63, Color.Black));
  114. g.FillRectangle(b0, rect.Left, rect.Top, 1, 1);
  115. g.FillRectangle(b1, rect.Left + 1, rect.Top, 1, 1);
  116. g.FillRectangle(b1, rect.Left, rect.Top + 1, 1, 1);
  117. g.FillRectangle(b0, rect.Right - 1, rect.Top, 1, 1);
  118. g.FillRectangle(b1, rect.Right - 2, rect.Top, 1, 1);
  119. g.FillRectangle(b1, rect.Right - 1, rect.Top + 1, 1, 1);
  120. g.FillRectangle(b0, rect.Left, rect.Bottom - 1, 1, 1);
  121. g.FillRectangle(b1, rect.Left + 1, rect.Bottom - 1, 1, 1);
  122. g.FillRectangle(b1, rect.Left, rect.Bottom - 2, 1, 1);
  123. g.FillRectangle(b0, rect.Right - 1, rect.Bottom - 1, 1, 1);
  124. g.FillRectangle(b1, rect.Right - 2, rect.Bottom - 1, 1, 1);
  125. g.FillRectangle(b1, rect.Right - 1, rect.Bottom - 2, 1, 1);
  126. g.DrawLine(p2, rect.Left + 2, rect.Top, rect.Right - 3, rect.Top);
  127. g.DrawLine(p2, rect.Left, rect.Top + 2, rect.Left, rect.Bottom - 3);
  128. g.DrawLine(p2, rect.Left + 2, rect.Bottom - 1, rect.Right - 3, rect.Bottom - 1);
  129. g.DrawLine(p2, rect.Right - 1, rect.Top + 2, rect.Right - 1, rect.Bottom - 3);
  130. b0.Dispose();
  131. b0 = null;
  132. b1.Dispose();
  133. b1 = null;
  134. p2.Dispose();
  135. p2 = null;
  136. }
  137. public static Keys LetterOrDigitCharToKeys(char c)
  138. {
  139. if (c >= 'a' && c <= 'z')
  140. {
  141. return (Keys)(c - 'a' + (int)Keys.A);
  142. }
  143. if (c >= 'A' && c <= 'Z')
  144. {
  145. return (Keys)(c - 'A' + (int)Keys.A);
  146. }
  147. if (c >= '0' && c <= '9')
  148. {
  149. return (Keys)(c - '0' + (int)Keys.D0);
  150. }
  151. return Keys.None;
  152. }
  153. public static Control FindFocus()
  154. {
  155. return (from Form form in Application.OpenForms select FindFocus(form)).FirstOrDefault(focused => focused != null);
  156. }
  157. private static Control FindFocus(Control c)
  158. {
  159. return c.Focused ? c : (from Control child in c.Controls select FindFocus(child)).FirstOrDefault(f => f != null);
  160. }
  161. public static void DrawColorRectangle(Graphics g, Rectangle rect, Color color, bool drawBorder)
  162. {
  163. int inflateAmt = drawBorder ? -2 : 0;
  164. Rectangle colorRectangle = Rectangle.Inflate(rect, inflateAmt, inflateAmt);
  165. Brush colorBrush = new LinearGradientBrush(colorRectangle, Color.FromArgb(255, color), color, 90.0f, false);
  166. var backgroundBrush = new HatchBrush(HatchStyle.LargeCheckerBoard, Color.FromArgb(191, 191, 191), Color.FromArgb(255, 255, 255));
  167. if (drawBorder)
  168. {
  169. g.DrawRectangle(Pens.Black, rect.Left, rect.Top, rect.Width - 1, rect.Height - 1);
  170. g.DrawRectangle(Pens.White, rect.Left + 1, rect.Top + 1, rect.Width - 3, rect.Height - 3);
  171. }
  172. PixelOffsetMode oldPOM = g.PixelOffsetMode;
  173. g.PixelOffsetMode = PixelOffsetMode.Half;
  174. g.FillRectangle(backgroundBrush, colorRectangle);
  175. g.FillRectangle(colorBrush, colorRectangle);
  176. g.PixelOffsetMode = oldPOM;
  177. backgroundBrush.Dispose();
  178. colorBrush.Dispose();
  179. }
  180. public static Size ComputeThumbnailSize(Size originalSize, int maxEdgeLength)
  181. {
  182. Size thumbSize;
  183. if (originalSize.Width > originalSize.Height)
  184. {
  185. int longSide = Math.Min(originalSize.Width, maxEdgeLength);
  186. thumbSize = new Size(longSide, Math.Max(1, (originalSize.Height * longSide) / originalSize.Width));
  187. }
  188. else if (originalSize.Height > originalSize.Width)
  189. {
  190. int longSide = Math.Min(originalSize.Height, maxEdgeLength);
  191. thumbSize = new Size(Math.Max(1, (originalSize.Width * longSide) / originalSize.Height), longSide);
  192. }
  193. else // if (docSize.Width == docSize.Height)
  194. {
  195. int longSide = Math.Min(originalSize.Width, maxEdgeLength);
  196. thumbSize = new Size(longSide, longSide);
  197. }
  198. return thumbSize;
  199. }
  200. public static bool IsClipboardImageAvailable()
  201. {
  202. try
  203. {
  204. return System.Windows.Forms.Clipboard.ContainsImage() ||
  205. System.Windows.Forms.Clipboard.ContainsData(DataFormats.EnhancedMetafile);
  206. }
  207. catch (ExternalException)
  208. {
  209. return false;
  210. }
  211. }
  212. public static Font CreateFont(string name, float size, FontStyle style)
  213. {
  214. Font returnFont;
  215. try
  216. {
  217. returnFont = new Font(name, size, style);
  218. }
  219. catch (Exception)
  220. {
  221. returnFont = new Font(FontFamily.GenericSansSerif, size);
  222. }
  223. return returnFont;
  224. }
  225. public static Font CreateFont(string name, float size, string backupName, float backupSize, FontStyle style)
  226. {
  227. Font returnFont;
  228. try
  229. {
  230. returnFont = new Font(name, size, style);
  231. }
  232. catch (Exception)
  233. {
  234. returnFont = CreateFont(backupName, backupSize, style);
  235. }
  236. return returnFont;
  237. }
  238. public static readonly Color TransparentKey = Color.FromArgb(192, 192, 192);
  239. public static string WebExceptionToErrorMessage(WebException wex)
  240. {
  241. string errorMessage;
  242. switch (wex.Status)
  243. {
  244. case WebExceptionStatus.ProtocolError:
  245. string format = PdnResources.GetString("WebExceptionStatus.ProtocolError.Format");
  246. HttpStatusCode statusCode = ((HttpWebResponse)wex.Response).StatusCode;
  247. errorMessage = string.Format(format, statusCode, (int)statusCode);
  248. break;
  249. default:
  250. string stringName = "WebExceptionStatus." + wex.Status;
  251. errorMessage = PdnResources.GetString(stringName);
  252. break;
  253. }
  254. return errorMessage;
  255. }
  256. private static bool _allowGcFullCollect = true;
  257. public static bool AllowGCFullCollect
  258. {
  259. get
  260. {
  261. return _allowGcFullCollect;
  262. }
  263. set
  264. {
  265. _allowGcFullCollect = value;
  266. }
  267. }
  268. public static void GCFullCollect()
  269. {
  270. if (!AllowGCFullCollect) return;
  271. GC.Collect();
  272. GC.WaitForPendingFinalizers();
  273. GC.Collect();
  274. GC.WaitForPendingFinalizers();
  275. }
  276. private static int _defaultSimplificationFactor = 50;
  277. public static int DefaultSimplificationFactor
  278. {
  279. get
  280. {
  281. return _defaultSimplificationFactor;
  282. }
  283. set
  284. {
  285. _defaultSimplificationFactor = value;
  286. }
  287. }
  288. public static bool IsArrowKey(Keys keyData)
  289. {
  290. Keys key = keyData & Keys.KeyCode;
  291. return key == Keys.Up || key == Keys.Down || key == Keys.Left || key == Keys.Right;
  292. }
  293. public static bool DoesControlHaveMouseCaptured(Control control)
  294. {
  295. bool result = false;
  296. result |= control.Capture;
  297. return control.Controls.Cast<Control>().Aggregate(result, (current, c) => current | DoesControlHaveMouseCaptured(c));
  298. }
  299. public static void SplitRectangle(Rectangle rect, Rectangle[] rects)
  300. {
  301. int height = rect.Height;
  302. for (int i = 0; i < rects.Length; ++i)
  303. {
  304. Rectangle newRect = Rectangle.FromLTRB(rect.Left,
  305. rect.Top + ((height * i) / rects.Length),
  306. rect.Right,
  307. rect.Top + ((height * (i + 1)) / rects.Length));
  308. rects[i] = newRect;
  309. }
  310. }
  311. public static long TicksToMs(long ticks)
  312. {
  313. return ticks / 10000;
  314. }
  315. public static string GetStaticName(Type type)
  316. {
  317. PropertyInfo pi = type.GetProperty("StaticName", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty);
  318. return (string)pi.GetValue(null, null);
  319. }
  320. public static readonly float[][] Identity5X5F = new[]
  321. {
  322. new float[] { 1, 0, 0, 0, 0 },
  323. new float[] { 0, 1, 0, 0, 0 },
  324. new float[] { 0, 0, 1, 0, 0 },
  325. new float[] { 0, 0, 0, 1, 0 },
  326. new float[] { 0, 0, 0, 0, 1 }
  327. };
  328. public static readonly ColorMatrix IdentityColorMatrix = new ColorMatrix(Identity5X5F);
  329. [ThreadStatic]
  330. private static Matrix _identityMatrix;
  331. public static Matrix IdentityMatrix
  332. {
  333. get
  334. {
  335. if (_identityMatrix == null)
  336. {
  337. _identityMatrix = new Matrix();
  338. _identityMatrix.Reset();
  339. }
  340. return _identityMatrix;
  341. }
  342. }
  343. /// <summary>
  344. /// Rounds an integer to the smallest power of 2 that is greater
  345. /// than or equal to it.
  346. /// </summary>
  347. public static int Log2RoundUp(int x)
  348. {
  349. if (x == 0)
  350. {
  351. return 1;
  352. }
  353. if (x == 1)
  354. {
  355. return 1;
  356. }
  357. return 1 << (1 + HighestBit(x - 1));
  358. }
  359. private static int HighestBit(int x)
  360. {
  361. if (x == 0)
  362. {
  363. return 0;
  364. }
  365. int b = 0;
  366. int hi = 0;
  367. while (b <= 30)
  368. {
  369. if ((x & (1 << b)) != 0)
  370. {
  371. hi = b;
  372. }
  373. ++b;
  374. }
  375. return hi;
  376. }
  377. /*
  378. private int CountBits(int x)
  379. {
  380. var y = (uint)x;
  381. int count = 0;
  382. for (int bit = 0; bit < 32; ++bit)
  383. {
  384. if ((y & ((uint)1 << bit)) != 0)
  385. {
  386. ++count;
  387. }
  388. }
  389. return count; //POSSIBLE UNUSED METHOD
  390. }
  391. */
  392. public static string RemoveSpaces(string s)
  393. {
  394. var sb = new StringBuilder();
  395. foreach (char c in s.Where(c => !char.IsWhiteSpace(c)))
  396. {
  397. sb.Append(c);
  398. }
  399. return sb.ToString();
  400. }
  401. public static int Max(int[,] array)
  402. {
  403. int max = int.MinValue;
  404. for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i)
  405. {
  406. for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); ++j)
  407. {
  408. if (array[i,j] > max)
  409. {
  410. max = array[i,j];
  411. }
  412. }
  413. }
  414. return max;
  415. }
  416. public static int Sum(int[][] array)
  417. {
  418. int sum = 0;
  419. foreach (int[] row in array)
  420. {
  421. sum = row.Sum();
  422. }
  423. return sum;
  424. }
  425. // TODO: obsolete these NUD funcitons, move them into PdnNumericUpDown
  426. public static void ClipNumericUpDown(NumericUpDown upDown)
  427. {
  428. if (upDown.Value < upDown.Minimum)
  429. {
  430. upDown.Value = upDown.Minimum;
  431. }
  432. else if (upDown.Value > upDown.Maximum)
  433. {
  434. upDown.Value = upDown.Maximum;
  435. }
  436. }
  437. public static bool GetUpDownValueFromText(NumericUpDown nud, out double val)
  438. {
  439. if (nud.Text == string.Empty)
  440. {
  441. val = 0;
  442. return false;
  443. }
  444. try
  445. {
  446. val = nud.DecimalPlaces == 0 ? int.Parse(nud.Text) : double.Parse(nud.Text);
  447. }
  448. catch
  449. {
  450. val = 0;
  451. return false;
  452. }
  453. return true;
  454. }
  455. public static bool CheckNumericUpDown(NumericUpDown upDown)
  456. {
  457. int a;
  458. bool result = int.TryParse(upDown.Text, out a);
  459. return result && (a <= (int)upDown.Maximum) && (a >= (int)upDown.Minimum);
  460. }
  461. public static void SetNumericUpDownValue(NumericUpDown upDown, decimal newValue)
  462. {
  463. if (upDown.Value != newValue)
  464. {
  465. upDown.Value = newValue;
  466. }
  467. }
  468. public static void SetNumericUpDownValue(NumericUpDown upDown, int newValue)
  469. {
  470. SetNumericUpDownValue(upDown, (decimal)newValue);
  471. }
  472. public static string SizeStringFromBytes(long bytes)
  473. {
  474. var bytesDouble = (double)bytes;
  475. string toStringFormat;
  476. string formatString;
  477. if (bytesDouble > (1024 * 1024 * 1024))
  478. {
  479. // Gigs
  480. bytesDouble /= 1024 * 1024 * 1024;
  481. toStringFormat = "F1";
  482. formatString = PdnResources.GetString("Utility.SizeStringFromBytes.GBFormat");
  483. }
  484. else if (bytesDouble > (1024 * 1024))
  485. {
  486. // Megs
  487. bytesDouble /= 1024 * 1024;
  488. toStringFormat = "F1";
  489. formatString = PdnResources.GetString("Utility.SizeStringFromBytes.MBFormat");
  490. }
  491. else if (bytesDouble > (1024))
  492. {
  493. // K
  494. bytesDouble /= 1024;
  495. toStringFormat = "F1";
  496. formatString = PdnResources.GetString("Utility.SizeStringFromBytes.KBFormat");
  497. }
  498. else
  499. {
  500. // Bytes
  501. toStringFormat = "F0";
  502. formatString = PdnResources.GetString("Utility.SizeStringFromBytes.BytesFormat");
  503. }
  504. string bytesString = bytesDouble.ToString(toStringFormat);
  505. string sizeString = string.Format(formatString, bytesString);
  506. return sizeString;
  507. }
  508. public static void ShowWiaError(IWin32Window owner)
  509. {
  510. // WIA requires Windows XP SP1 or later, or Windows Server 2003
  511. // So if we know they're on WS2k3, we tell them to enable WIA.
  512. // If they're on XP or later, tell them that WIA isn't available.
  513. // Otherwise we tell them they need XP SP1 (for the Win2K folks).
  514. ErrorBox(owner,
  515. OS.Type == OSType.Server
  516. ? PdnResources.GetString("WIA.Error.EnableMe")
  517. : PdnResources.GetString("WIA.Error.UnableToLoad"));
  518. }
  519. public static void ShowNonAdminErrorBox(IWin32Window parent)
  520. {
  521. ErrorBox(parent, PdnResources.GetString("NonAdminErrorBox.Message"));
  522. }
  523. public static void ErrorBox(IWin32Window parent, string message)
  524. {
  525. MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OK, MessageBoxIcon.Error);
  526. }
  527. public static DialogResult ErrorBoxOkCancel(IWin32Window parent, string message)
  528. {
  529. return MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
  530. }
  531. public static void InfoBox(IWin32Window parent, string message)
  532. {
  533. MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OK, MessageBoxIcon.Information);
  534. }
  535. public static DialogResult InfoBoxOkCancel(IWin32Window parent, string message)
  536. {
  537. return MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
  538. }
  539. public static DialogResult AskOkCancel(IWin32Window parent, string question)
  540. {
  541. return MessageBox.Show(parent, question, PdnInfo.GetBareProductName(), MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
  542. }
  543. public static DialogResult AskYesNo(IWin32Window parent, string question)
  544. {
  545. return MessageBox.Show(parent, question, PdnInfo.GetBareProductName(), MessageBoxButtons.YesNo, MessageBoxIcon.Question);
  546. }
  547. public static DialogResult AskYesNoCancel(IWin32Window parent, string question)
  548. {
  549. return MessageBox.Show(parent, question, PdnInfo.GetBareProductName(), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
  550. }
  551. public static Icon ImageToIcon(Image image)
  552. {
  553. return ImageToIcon(image, TransparentKey);
  554. }
  555. public static Icon ImageToIcon(Image image, bool disposeImage)
  556. {
  557. return ImageToIcon(image, TransparentKey, disposeImage);
  558. }
  559. public static Icon ImageToIcon(Image image, Color seeThru)
  560. {
  561. return ImageToIcon(image, seeThru, false);
  562. }
  563. /// <summary>
  564. /// Converts an Image to an Icon.
  565. /// </summary>
  566. /// <param name="image">The Image to convert to an icon. Must be an appropriate icon size (32x32, 16x16, etc).</param>
  567. /// <param name="seeThru">The color that will be treated as transparent in the icon.</param>
  568. /// <param name="disposeImage">Whether or not to dispose the passed-in Image.</param>
  569. /// <returns>An Icon representation of the Image.</returns>
  570. public static Icon ImageToIcon(Image image, Color seeThru, bool disposeImage)
  571. {
  572. var bitmap = new Bitmap(image);
  573. for (int y = 0; y < bitmap.Height; ++y)
  574. {
  575. for (int x = 0; x < bitmap.Width; ++x)
  576. {
  577. if (bitmap.GetPixel(x, y) == seeThru)
  578. {
  579. bitmap.SetPixel(x, y, Color.FromArgb(0));
  580. }
  581. }
  582. }
  583. Icon icon = Icon.FromHandle(bitmap.GetHicon());
  584. bitmap.Dispose();
  585. if (disposeImage)
  586. {
  587. image.Dispose();
  588. }
  589. return icon;
  590. }
  591. public static Icon BitmapToIcon(Bitmap bitmap, bool disposeBitmap)
  592. {
  593. Icon icon = Icon.FromHandle(bitmap.GetHicon());
  594. if (disposeBitmap)
  595. {
  596. bitmap.Dispose();
  597. }
  598. return icon;
  599. }
  600. public static Icon SurfaceToIcon(Surface surface, bool disposeSurface)
  601. {
  602. Bitmap bitmap = surface.CreateAliasedBitmap();
  603. Icon icon = Icon.FromHandle(bitmap.GetHicon());
  604. bitmap.Dispose();
  605. if (disposeSurface)
  606. {
  607. surface.Dispose();
  608. }
  609. return icon;
  610. }
  611. public static Point GetRectangleCenter(Rectangle rect)
  612. {
  613. return new Point((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2);
  614. }
  615. public static PointF GetRectangleCenter(RectangleF rect)
  616. {
  617. return new PointF((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2);
  618. }
  619. public static Scanline[] GetRectangleScans(Rectangle rect)
  620. {
  621. var scans = new Scanline[rect.Height];
  622. for (int y = 0; y < rect.Height; ++y)
  623. {
  624. scans[y] = new Scanline(rect.X, rect.Y + y, rect.Width);
  625. }
  626. return scans;
  627. }
  628. public static Scanline[] GetRegionScans(Rectangle[] region)
  629. {
  630. int scanCount = region.Sum(t => t.Height);
  631. var scans = new Scanline[scanCount];
  632. int scanIndex = 0;
  633. foreach (Rectangle rect in region)
  634. {
  635. for (int y = 0; y < rect.Height; ++y)
  636. {
  637. scans[scanIndex] = new Scanline(rect.X, rect.Y + y, rect.Width);
  638. ++scanIndex;
  639. }
  640. }
  641. return scans;
  642. }
  643. public static Rectangle[] ScanlinesToRectangles(Scanline[] scans)
  644. {
  645. return ScanlinesToRectangles(scans, 0, scans.Length);
  646. }
  647. public static Rectangle[] ScanlinesToRectangles(Scanline[] scans, int startIndex, int length)
  648. {
  649. var rects = new Rectangle[length];
  650. for (int i = 0; i < length; ++i)
  651. {
  652. Scanline scan = scans[i + startIndex];
  653. rects[i] = new Rectangle(scan.X, scan.Y, scan.Length, 1);
  654. }
  655. return rects;
  656. }
  657. /// <summary>
  658. /// Found on Google Groups when searching for "Region.Union" while looking
  659. /// for bugs:
  660. /// ---
  661. /// Hello,
  662. ///
  663. /// I did not run your code, but I know Region.Union is flawed in both 1.0 and
  664. /// 1.1, so I assume it is in the gdi+ unmanged code dll. The best workaround,
  665. /// in terms of speed, is to use a PdnGraphicsPath, but it must be a path with
  666. /// FillMode = FillMode.Winding. You add the rectangles to the path, then you do
  667. /// union onto an empty region with the path. The important point is to do only
  668. /// one union call on a given empty region. We created a "super region" object
  669. /// to hide all these bugs and optimize clipping operations. In fact, it is much
  670. /// faster to use the path than to call Region.Union for each rectangle.
  671. ///
  672. /// Too bad about Region.Union. A lot of people will hit this bug, as it is
  673. /// essential in high-performance animation.
  674. ///
  675. /// Regards,
  676. /// Frank Hileman
  677. /// Prodige Software Corporation
  678. /// ---
  679. /// </summary>
  680. /// <param name="rectsF"></param>
  681. /// <param name="startIndex"></param>
  682. /// <param name="length"></param>
  683. /// <returns></returns>
  684. public static PdnRegion RectanglesToRegion(RectangleF[] rectsF, int startIndex, int length)
  685. {
  686. PdnRegion region;
  687. if (rectsF == null || rectsF.Length == 0 || length == 0)
  688. {
  689. region = PdnRegion.CreateEmpty();
  690. }
  691. else
  692. {
  693. using (var path = new PdnGraphicsPath())
  694. {
  695. path.FillMode = FillMode.Winding;
  696. if (startIndex == 0 && length == rectsF.Length)
  697. {
  698. path.AddRectangles(rectsF);
  699. }
  700. else
  701. {
  702. for (int i = startIndex; i < startIndex + length; ++i)
  703. {
  704. path.AddRectangle(rectsF[i]);
  705. }
  706. }
  707. region = new PdnRegion(path);
  708. }
  709. }
  710. return region;
  711. }
  712. public static PdnRegion RectanglesToRegion(RectangleF[] rectsF)
  713. {
  714. return RectanglesToRegion(rectsF, 0, rectsF != null ? rectsF.Length : 0);
  715. }
  716. public static PdnRegion RectanglesToRegion(RectangleF[] rectsF1, RectangleF[] rectsF2, params RectangleF[][] rectsFA)
  717. {
  718. using (var path = new PdnGraphicsPath())
  719. {
  720. path.FillMode = FillMode.Winding;
  721. if (rectsF1 != null && rectsF1.Length > 0)
  722. {
  723. path.AddRectangles(rectsF1);
  724. }
  725. if (rectsF2 != null && rectsF2.Length > 0)
  726. {
  727. path.AddRectangles(rectsF2);
  728. }
  729. foreach (RectangleF[] rectsF in rectsFA.Where(rectsF => rectsF != null && rectsF.Length > 0))
  730. {
  731. path.AddRectangles(rectsF);
  732. }
  733. return new PdnRegion(path);
  734. }
  735. }
  736. public static PdnRegion RectanglesToRegion(Rectangle[] rects, int startIndex, int length)
  737. {
  738. PdnRegion region;
  739. if (length == 0)
  740. {
  741. region = PdnRegion.CreateEmpty();
  742. }
  743. else
  744. {
  745. using (var path = new PdnGraphicsPath())
  746. {
  747. path.FillMode = FillMode.Winding;
  748. if (startIndex == 0 && length == rects.Length)
  749. {
  750. path.AddRectangles(rects);
  751. }
  752. else
  753. {
  754. for (int i = startIndex; i < startIndex + length; ++i)
  755. {
  756. path.AddRectangle(rects[i]);
  757. }
  758. }
  759. region = new PdnRegion(path);
  760. path.Dispose();
  761. }
  762. }
  763. return region;
  764. }
  765. public static PdnRegion RectanglesToRegion(Rectangle[] rects)
  766. {
  767. return RectanglesToRegion(rects, 0, rects.Length);
  768. }
  769. public static int GetRegionArea(RectangleF[] rectsF)
  770. {
  771. return rectsF.Select(rectF => Rectangle.Truncate(rectF)).Select(rect => rect.Width*rect.Height).Sum();
  772. }
  773. public static RectangleF RectangleFromCenter(PointF center, float halfSize)
  774. {
  775. var ret = new RectangleF(center.X, center.Y, 0, 0);
  776. ret.Inflate(halfSize, halfSize);
  777. return ret;
  778. }
  779. public static List<PointF> PointListToPointFList(List<Point> ptList)
  780. {
  781. var ret = new List<PointF>(ptList.Count);
  782. ret.AddRange(ptList.Select(t => (PointF) t));
  783. return ret;
  784. }
  785. public static PointF[] PointArrayToPointFArray(Point[] ptArray)
  786. {
  787. var ret = new PointF[ptArray.Length];
  788. for (int i = 0; i < ret.Length; ++i)
  789. {
  790. ret[i] = ptArray[i];
  791. }
  792. return ret;
  793. }
  794. public static Rectangle[] InflateRectangles(Rectangle[] rects, int amount)
  795. {
  796. var inflated = new Rectangle[rects.Length];
  797. for (int i = 0; i < rects.Length; ++i)
  798. {
  799. inflated[i] = Rectangle.Inflate(rects[i], amount, amount);
  800. }
  801. return inflated;
  802. }
  803. public static void InflateRectanglesInPlace(Rectangle[] rects, int amount)
  804. {
  805. for (int i = 0; i < rects.Length; ++i)
  806. {
  807. rects[i].Inflate(amount, amount);
  808. }
  809. }
  810. public static RectangleF[] InflateRectangles(RectangleF[] rectsF, int amount)
  811. {
  812. var inflated = new RectangleF[rectsF.Length];
  813. for (int i = 0; i < rectsF.Length; ++i)
  814. {
  815. inflated[i] = RectangleF.Inflate(rectsF[i], amount, amount);
  816. }
  817. return inflated;
  818. }
  819. public static void InflateRectanglesInPlace(RectangleF[] rectsF, float amount)
  820. {
  821. for (int i = 0; i < rectsF.Length; ++i)
  822. {
  823. rectsF[i].Inflate(amount, amount);
  824. }
  825. }
  826. public static Rectangle PointsToConstrainedRectangle(Point a, Point b)
  827. {
  828. Rectangle rect = PointsToRectangle(a, b);
  829. int minWH = Math.Min(rect.Width, rect.Height);
  830. rect.Width = minWH;
  831. rect.Height = minWH;
  832. if (rect.Y != a.Y)
  833. {
  834. rect.Location = new Point(rect.X, a.Y - minWH);
  835. }
  836. if (rect.X != a.X)
  837. {
  838. rect.Location = new Point(a.X - minWH, rect.Y);
  839. }
  840. return rect;
  841. }
  842. public static RectangleF PointsToConstrainedRectangle(PointF a, PointF b)
  843. {
  844. RectangleF rect = PointsToRectangle(a, b);
  845. float minWH = Math.Min(rect.Width, rect.Height);
  846. rect.Width = minWH;
  847. rect.Height = minWH;
  848. if (rect.Y != a.Y)
  849. {
  850. rect.Location = new PointF(rect.X, a.Y - minWH);
  851. }
  852. if (rect.X != a.X)
  853. {
  854. rect.Location = new PointF(a.X - minWH, rect.Y);
  855. }
  856. return rect;
  857. }
  858. /// <summary>
  859. /// Takes two points and creates a bounding rectangle from them.
  860. /// </summary>
  861. /// <param name="a">One corner of the rectangle.</param>
  862. /// <param name="b">The other corner of the rectangle.</param>
  863. /// <returns>A Rectangle instance that bounds the two points.</returns>
  864. public static Rectangle PointsToRectangle(Point a, Point b)
  865. {
  866. int x = Math.Min(a.X, b.X);
  867. int y = Math.Min(a.Y, b.Y);
  868. int width = Math.Abs(a.X - b.X) + 1;
  869. int height = Math.Abs(a.Y - b.Y) + 1;
  870. return new Rectangle(x, y, width, height);
  871. }
  872. public static RectangleF PointsToRectangle(PointF a, PointF b)
  873. {
  874. float x = Math.Min(a.X, b.X);
  875. float y = Math.Min(a.Y, b.Y);
  876. float width = Math.Abs(a.X - b.X) + 1;
  877. float height = Math.Abs(a.Y - b.Y) + 1;
  878. return new RectangleF(x, y, width, height);
  879. }
  880. public static Rectangle PointsToRectangleExclusive(Point a, Point b)
  881. {
  882. int x = Math.Min(a.X, b.X);
  883. int y = Math.Min(a.Y, b.Y);
  884. int width = Math.Abs(a.X - b.X);
  885. int height = Math.Abs(a.Y - b.Y);
  886. return new Rectangle(x, y, width, height);
  887. }
  888. public static RectangleF PointsToRectangleExclusive(PointF a, PointF b)
  889. {
  890. float x = Math.Min(a.X, b.X);
  891. float y = Math.Min(a.Y, b.Y);
  892. float width = Math.Abs(a.X - b.X);
  893. float height = Math.Abs(a.Y - b.Y);
  894. return new RectangleF(x, y, width, height);
  895. }
  896. public static RectangleF[] PointsToRectangles(PointF[] pointsF)
  897. {
  898. if (pointsF.Length == 0)
  899. {
  900. return new RectangleF[] { };
  901. }
  902. if (pointsF.Length == 1)
  903. {
  904. return new[] { new RectangleF(pointsF[0].X, pointsF[0].Y, 1, 1) };
  905. }
  906. var rectsF = new RectangleF[pointsF.Length - 1];
  907. for (int i = 0; i < pointsF.Length - 1; ++i)
  908. {
  909. rectsF[i] = PointsToRectangle(pointsF[i], pointsF[i + 1]);
  910. }
  911. return rectsF;
  912. }
  913. public static Rectangle[] PointsToRectangles(Point[] points)
  914. {
  915. if (points.Length == 0)
  916. {
  917. return new Rectangle[] { };
  918. }
  919. if (points.Length == 1)
  920. {
  921. return new[] { new Rectangle(points[0].X, points[0].Y, 1, 1) };
  922. }
  923. var rects = new Rectangle[points.Length - 1];
  924. for (int i = 0; i < points.Length - 1; ++i)
  925. {
  926. rects[i] = PointsToRectangle(points[i], points[i + 1]);
  927. }
  928. return rects;
  929. }
  930. /// <summary>
  931. /// Converts a RectangleF to RectangleF by rounding down the Location and rounding
  932. /// up the Size.
  933. /// </summary>
  934. public static Rectangle RoundRectangle(RectangleF rectF)
  935. {
  936. var left = (float)Math.Floor(rectF.Left);
  937. var top = (float)Math.Floor(rectF.Top);
  938. var right = (float)Math.Ceiling(rectF.Right);
  939. var bottom = (float)Math.Ceiling(rectF.Bottom);
  940. return Rectangle.Truncate(RectangleF.FromLTRB(left, top, right, bottom));
  941. }
  942. public static Stack Reverse(Stack reverseMe)
  943. {
  944. var reversed = new Stack();
  945. foreach (object o in reverseMe)
  946. {
  947. reversed.Push(o);
  948. }
  949. return reversed;
  950. }
  951. public static void SerializeObjectToStream(object graph, Stream stream)
  952. {
  953. new BinaryFormatter().Serialize(stream, graph);
  954. }
  955. public static object DeserializeObjectFromStream(Stream stream)
  956. {
  957. return new BinaryFormatter().Deserialize(stream);
  958. }
  959. [Obsolete("Use rect.Contains() instead", true)]
  960. public static bool IsPointInRectangle(Point pt, Rectangle rect)
  961. {
  962. return rect.Contains(pt);
  963. }
  964. [Obsolete("Use rect.Contains() instead", true)]
  965. public static bool IsPointInRectangle(int x, int y, Rectangle rect)
  966. {
  967. return rect.Contains(x, y);
  968. }
  969. public static Bitmap FullCloneBitmap(Bitmap cloneMe)
  970. {
  971. var bitmap = new Bitmap(cloneMe.Width, cloneMe.Height, cloneMe.PixelFormat);
  972. using (Graphics g = Graphics.FromImage(bitmap))
  973. {
  974. g.DrawImage(cloneMe, 0, 0, cloneMe.Width, cloneMe.Height);
  975. }
  976. return bitmap;
  977. }
  978. /// <summary>
  979. /// Allows you to find the bounding box for a Region object without requiring
  980. /// the presence of a Graphics object.
  981. /// (Region.GetBounds takes a Graphics instance as its only parameter.)
  982. /// </summary>
  983. /// <param name="region">The region you want to find a bounding box for.</param>
  984. /// <returns>A RectangleF structure that surrounds the Region.</returns>
  985. public static Rectangle GetRegionBounds(PdnRegion region)
  986. {
  987. Rectangle[] rects = region.GetRegionScansReadOnlyInt();
  988. return GetRegionBounds(rects, 0, rects.Length);
  989. }
  990. /// <summary>
  991. /// Allows you to find the bounding box for a "region" that is described as an
  992. /// array of bounding boxes.
  993. /// </summary>
  994. /// <param name="rectsF">The "region" you want to find a bounding box for.</param>
  995. /// <param name="startIndex"></param>
  996. /// <param name="length"></param>
  997. /// <returns>A RectangleF structure that surrounds the Region.</returns>
  998. public static RectangleF GetRegionBounds(RectangleF[] rectsF, int startIndex, int length)
  999. {
  1000. if (rectsF.Length == 0)
  1001. {
  1002. return RectangleF.Empty;
  1003. }
  1004. float left = rectsF[startIndex].Left;
  1005. float top = rectsF[startIndex].Top;
  1006. float right = rectsF[startIndex].Right;
  1007. float bottom = rectsF[startIndex].Bottom;
  1008. for (int i = startIndex + 1; i < startIndex + length; ++i)
  1009. {
  1010. RectangleF rectF = rectsF[i];
  1011. if (rectF.Left < left)
  1012. {
  1013. left = rectF.Left;
  1014. }
  1015. if (rectF.Top < top)
  1016. {
  1017. top = rectF.Top;
  1018. }
  1019. if (rectF.Right > right)
  1020. {
  1021. right = rectF.Right;
  1022. }
  1023. if (rectF.Bottom > bottom)
  1024. {
  1025. bottom = rectF.Bottom;
  1026. }
  1027. }
  1028. return RectangleF.FromLTRB(left, top, right, bottom);
  1029. }
  1030. public static RectangleF GetTraceBounds(PointF[] pointsF, int startIndex, int length)
  1031. {
  1032. if (pointsF.Length == 0)
  1033. {
  1034. return RectangleF.Empty;
  1035. }
  1036. float left = pointsF[startIndex].X;
  1037. float top = pointsF[startIndex].Y;
  1038. float right = 1 + pointsF[startIndex].X;
  1039. float bottom = 1 + pointsF[startIndex].Y;
  1040. for (int i = startIndex + 1; i < startIndex + length; ++i)
  1041. {
  1042. PointF pointF = pointsF[i];
  1043. if (pointF.X < left)
  1044. {
  1045. left = pointF.X;
  1046. }
  1047. if (pointF.Y < top)
  1048. {
  1049. top = pointF.Y;
  1050. }
  1051. if (pointF.X > right)
  1052. {
  1053. right = pointF.X;
  1054. }
  1055. if (pointF.Y > bottom)
  1056. {
  1057. bottom = pointF.Y;
  1058. }
  1059. }
  1060. return RectangleF.FromLTRB(left, top, right, bottom);
  1061. }
  1062. public static Rectangle GetTraceBounds(Point[] points, int startIndex, int length)
  1063. {
  1064. if (points.Length == 0)
  1065. {
  1066. return Rectangle.Empty;
  1067. }
  1068. int left = points[startIndex].X;
  1069. int top = points[startIndex].Y;
  1070. int right = 1 + points[startIndex].X;
  1071. int bottom = 1 + points[startIndex].Y;
  1072. for (int i = startIndex + 1; i < startIndex + length; ++i)
  1073. {
  1074. Point point = points[i];
  1075. if (point.X < left)
  1076. {
  1077. left = point.X;
  1078. }
  1079. if (point.Y < top)
  1080. {
  1081. top = point.Y;
  1082. }
  1083. if (point.X > right)
  1084. {
  1085. right = point.X;
  1086. }
  1087. if (point.Y > bottom)
  1088. {
  1089. bottom = point.Y;
  1090. }
  1091. }
  1092. return Rectangle.FromLTRB(left, top, right, bottom);
  1093. }
  1094. /// <summary>
  1095. /// Allows you to find the bounding box for a "region" that is described as an
  1096. /// array of bounding boxes.
  1097. /// </summary>
  1098. /// <param name="rectsF">The "region" you want to find a bounding box for.</param>
  1099. /// <param name="rects"></param>
  1100. /// <param name="startIndex"></param>
  1101. /// <param name="length"></param>
  1102. /// <returns>A RectangleF structure that surrounds the Region.</returns>
  1103. public static Rectangle GetRegionBounds(Rectangle[] rects, int startIndex, int length)
  1104. {
  1105. if (rects.Length == 0)
  1106. {
  1107. return Rectangle.Empty;
  1108. }
  1109. int left = rects[startIndex].Left;
  1110. int top = rects[startIndex].Top;
  1111. int right = rects[startIndex].Right;
  1112. int bottom = rects[startIndex].Bottom;
  1113. for (int i = startIndex + 1; i < startIndex + length; ++i)
  1114. {
  1115. Rectangle rect = rects[i];
  1116. if (rect.Left < left)
  1117. {
  1118. left = rect.Left;
  1119. }
  1120. if (rect.Top < top)
  1121. {
  1122. top = rect.Top;
  1123. }
  1124. if (rect.Right > right)
  1125. {
  1126. right = rect.Right;
  1127. }
  1128. if (rect.Bottom > bottom)
  1129. {
  1130. bottom = rect.Bottom;
  1131. }
  1132. }
  1133. return Rectangle.FromLTRB(left, top, right, bottom);
  1134. }
  1135. public static RectangleF GetRegionBounds(RectangleF[] rectsF)
  1136. {
  1137. return GetRegionBounds(rectsF, 0, rectsF.Length);
  1138. }
  1139. public static Rectangle GetRegionBounds(Rectangle[] rects)
  1140. {
  1141. return GetRegionBounds(rects, 0, rects.Length);
  1142. }
  1143. /*
  1144. private static float DistanceSquared(IList<RectangleF> rectsF, int indexA, int indexB)
  1145. {
  1146. var centerA = new PointF(rectsF[indexA].Left + (rectsF[indexA].Width / 2), rectsF[indexA].Top + (rectsF[indexA].Height / 2));
  1147. var centerB = new PointF(rectsF[indexB].Left + (rectsF[indexB].Width / 2), rectsF[indexB].Top + (rectsF[indexB].Height / 2));
  1148. return ((centerA.X - centerB.X) * (centerA.X - centerB.X)) +
  1149. ((centerA.Y - centerB.Y) * (centerA.Y - centerB.Y)); UNUSED METHOD
  1150. }
  1151. */
  1152. /// <summary>
  1153. /// Simplifies a Region into N number of bounding boxes.
  1154. /// </summary>
  1155. /// <param name="region">The Region to simplify.</param>
  1156. /// <param name="complexity">The maximum number of bounding boxes to return, or 0 for however many are necessary (equivalent to using Region.GetRegionScans).</param>
  1157. /// <returns></returns>
  1158. public static Rectangle[] SimplifyRegion(PdnRegion region, int complexity)
  1159. {
  1160. Rectangle[] rects = region.GetRegionScansReadOnlyInt();
  1161. return SimplifyRegion(rects, complexity);
  1162. }
  1163. public static Rectangle[] SimplifyRegion(Rectangle[] rects, int complexity)
  1164. {
  1165. if (complexity == 0 || rects.Length < complexity)
  1166. {
  1167. return (Rectangle[])rects.Clone();
  1168. }
  1169. var boxes = new Rectangle[complexity];
  1170. for (int i = 0; i < complexity; ++i)
  1171. {
  1172. int startIndex = (i * rects.Length) / complexity;
  1173. int length = Math.Min(rects.Length, ((i + 1) * rects.Length) / complexity) - startIndex;
  1174. boxes[i] = GetRegionBounds(rects, startIndex, length);
  1175. }
  1176. return boxes;
  1177. }
  1178. public static RectangleF[] SimplifyTrace(PointF[] pointsF, int complexity)
  1179. {
  1180. if (complexity == 0 ||
  1181. (pointsF.Length - 1) < complexity)
  1182. {
  1183. return PointsToRectangles(pointsF);
  1184. }
  1185. var boxes = new RectangleF[complexity];
  1186. int parLength = pointsF.Length - 1; // "(points as Rectangles).Length"
  1187. for (int i = 0; i < complexity; ++i)
  1188. {
  1189. int startIndex = (i * parLength) / complexity;
  1190. int length = Math.Min(parLength, ((i + 1) * parLength) / complexity) - startIndex;
  1191. boxes[i] = GetTraceBounds(pointsF, startIndex, length + 1);
  1192. }
  1193. return boxes;
  1194. }
  1195. public static Rectangle[] SimplifyTrace(PdnGraphicsPath trace, int complexity)
  1196. {
  1197. return SimplifyRegion(TraceToRectangles(trace), complexity);
  1198. }
  1199. public static Rectangle[] SimplifyTrace(PdnGraphicsPath trace)
  1200. {
  1201. return SimplifyTrace(trace, DefaultSimplificationFactor);
  1202. }
  1203. public static Rectangle[] TraceToRectangles(PdnGraphicsPath trace, int complexity)
  1204. {
  1205. int pointCount = trace.PointCount;
  1206. if (pointCount == 0)
  1207. {
  1208. return new Rectangle[0];
  1209. }
  1210. PointF[] pathPoints = trace.PathPoints;
  1211. byte[] pathTypes = trace.PathTypes;
  1212. int figureStart = 0;
  1213. // first get count of rectangles we'll need
  1214. var rects = new Rectangle[pointCount];
  1215. for (int i = 0; i < pointCount; ++i)
  1216. {
  1217. byte type = pathTypes[i];
  1218. Point a = Point.Truncate(pathPoints[i]);
  1219. Point b;
  1220. if ((type & (byte)PathPointType.CloseSubpath) != 0)
  1221. {
  1222. b = Point.Truncate(pathPoints[figureStart]);
  1223. figureStart = i + 1;
  1224. }
  1225. else
  1226. {
  1227. b = Point.Truncate(pathPoints[i + 1]);
  1228. }
  1229. rects[i] = PointsToRectangle(a, b);
  1230. }
  1231. return rects;
  1232. }
  1233. public static Rectangle[] TraceToRectangles(PdnGraphicsPath trace)
  1234. {
  1235. return TraceToRectangles(trace, DefaultSimplificationFactor);
  1236. }

Large files files are truncated, but you can click here to view the full file