PageRenderTime 65ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Data/Document.cs

https://bitbucket.org/tuldok89/openpdn
C# | 1777 lines | 1241 code | 247 blank | 289 comment | 223 complexity | bda577f229112c8b436096fe432eeb7d 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.Collections.Generic;
  10. using PaintDotNet.Base;
  11. using PaintDotNet.SystemLayer;
  12. using System;
  13. using System.Collections.Specialized;
  14. using System.ComponentModel;
  15. using System.Drawing;
  16. using System.Drawing.Drawing2D;
  17. using System.Drawing.Imaging;
  18. using System.IO;
  19. using System.IO.Compression;
  20. using System.Reflection;
  21. using System.Runtime.Serialization;
  22. using System.Runtime.Serialization.Formatters.Binary;
  23. using System.Text;
  24. using System.Windows.Forms;
  25. using System.Xml;
  26. namespace PaintDotNet
  27. {
  28. [Serializable]
  29. public sealed class Document
  30. : IDeserializationCallback,
  31. IDisposable,
  32. ICloneable
  33. {
  34. private readonly LayerList _layers;
  35. private readonly int _width;
  36. private readonly int _height;
  37. private readonly NameValueCollection _userMetaData;
  38. [NonSerialized]
  39. private Threading.ThreadPool _threadPool = new Threading.ThreadPool();
  40. [NonSerialized]
  41. private InvalidateEventHandler _layerInvalidatedDelegate;
  42. // TODO: the document class should not manage its own update region, its owner should
  43. [NonSerialized]
  44. private Vector<Rectangle> _updateRegion;
  45. [NonSerialized]
  46. private bool _dirty;
  47. private Version _savedWith;
  48. [NonSerialized]
  49. private Metadata _metadata;
  50. [NonSerialized]
  51. private XmlDocument _headerXml;
  52. private const string HeaderXmlSkeleton = "<pdnImage><custom></custom></pdnImage>";
  53. private XmlDocument HeaderXml
  54. {
  55. get
  56. {
  57. if (_disposed)
  58. {
  59. throw new ObjectDisposedException("Document");
  60. }
  61. if (_headerXml == null)
  62. {
  63. _headerXml = new XmlDocument();
  64. _headerXml.LoadXml(HeaderXmlSkeleton);
  65. }
  66. return _headerXml;
  67. }
  68. }
  69. public string Header
  70. {
  71. get
  72. {
  73. if (_disposed)
  74. {
  75. throw new ObjectDisposedException("Document");
  76. }
  77. return HeaderXml.OuterXml;
  78. }
  79. }
  80. public string CustomHeaders
  81. {
  82. get
  83. {
  84. if (_disposed)
  85. {
  86. throw new ObjectDisposedException("Document");
  87. }
  88. return HeaderXml != null ? HeaderXml.SelectSingleNode("/pdnImage/custom").InnerXml : null;
  89. }
  90. set
  91. {
  92. if (_disposed)
  93. {
  94. throw new ObjectDisposedException("Document");
  95. }
  96. if (HeaderXml != null) HeaderXml.SelectSingleNode("/pdnImage/custom").InnerXml = value;
  97. Dirty = true;
  98. }
  99. }
  100. /// <summary>
  101. /// Gets or sets the units that are used for measuring the document's physical (printed) size.
  102. /// </summary>
  103. /// <remarks>
  104. /// If this property is set to MeasurementUnit.Pixel, then Dpu will be reset to 1.
  105. /// If this property has not been set in the image's metadata, its default value
  106. /// will be MeasurementUnit.Inch.
  107. /// If the EXIF data for the image is invalid (such as "ResolutionUnit = 0" or something),
  108. /// then the default DpuUnit will be returned.
  109. /// </remarks>
  110. public MeasurementUnit DpuUnit
  111. {
  112. get
  113. {
  114. PropertyItem[] pis = Metadata.GetExifValues(ExifTagID.ResolutionUnit);
  115. if (pis.Length == 0)
  116. {
  117. DpuUnit = DefaultDpuUnit;
  118. return DefaultDpuUnit;
  119. }
  120. try
  121. {
  122. ushort unit = Exif.DecodeShortValue(pis[0]);
  123. // Guard against bad data in the EXIF store
  124. switch ((MeasurementUnit)unit)
  125. {
  126. case MeasurementUnit.Centimeter:
  127. case MeasurementUnit.Inch:
  128. case MeasurementUnit.Pixel:
  129. return (MeasurementUnit)unit;
  130. default:
  131. Metadata.RemoveExifValues(ExifTagID.ResolutionUnit);
  132. return DpuUnit; // recursive call
  133. }
  134. }
  135. catch (Exception)
  136. {
  137. Metadata.RemoveExifValues(ExifTagID.ResolutionUnit);
  138. return DpuUnit; // recursive call
  139. }
  140. }
  141. set
  142. {
  143. PropertyItem pi = Exif.CreateShort(ExifTagID.ResolutionUnit, (ushort)value);
  144. Metadata.ReplaceExifValues(ExifTagID.ResolutionUnit, new[] { pi });
  145. if (value == MeasurementUnit.Pixel)
  146. {
  147. DpuX = 1.0;
  148. DpuY = 1.0;
  149. }
  150. Dirty = true;
  151. }
  152. }
  153. public static MeasurementUnit DefaultDpuUnit
  154. {
  155. get
  156. {
  157. return MeasurementUnit.Inch;
  158. }
  159. }
  160. #if false
  161. [Obsolete("Use DefaultDpuUnit property instead.")]
  162. public static MeasurementUnit GetDefaultDpuUnit()
  163. {
  164. return DefaultDpuUnit;
  165. }
  166. #endif
  167. private const double defaultDpi = 96.0;
  168. public static double DefaultDpi
  169. {
  170. get
  171. {
  172. return defaultDpi;
  173. }
  174. }
  175. public const double CmPerInch = 2.54;
  176. private const double defaultDpcm = defaultDpi / CmPerInch;
  177. public static double DefaultDpcm
  178. {
  179. get
  180. {
  181. return defaultDpcm;
  182. }
  183. }
  184. public const double MinimumDpu = 0.01;
  185. public const double MaximumDpu = 32767.0;
  186. public static double InchesToCentimeters(double inches)
  187. {
  188. return inches * CmPerInch;
  189. }
  190. public static double CentimetersToInches(double centimeters)
  191. {
  192. return centimeters / CmPerInch;
  193. }
  194. public static double DotsPerInchToDotsPerCm(double dpi)
  195. {
  196. return dpi / CmPerInch;
  197. }
  198. public static double DotsPerCmToDotsPerInch(double dpcm)
  199. {
  200. return dpcm * CmPerInch;
  201. }
  202. public static double GetDefaultDpu(MeasurementUnit units)
  203. {
  204. double dpu;
  205. switch (units)
  206. {
  207. case MeasurementUnit.Inch:
  208. dpu = defaultDpi;
  209. break;
  210. case MeasurementUnit.Centimeter:
  211. dpu = defaultDpcm;
  212. break;
  213. case MeasurementUnit.Pixel:
  214. dpu = 1.0;
  215. break;
  216. default:
  217. throw new InvalidEnumArgumentException("DpuUnit", (int)units, typeof(MeasurementUnit));
  218. }
  219. return dpu;
  220. }
  221. /// <summary>
  222. /// Ensures that the document's DpuX, DpuY, and DpuUnits properties are set.
  223. /// If they are not already set, they are initialized to their default values (96, 96 , inches).
  224. /// </summary>
  225. private void InitializeDpu()
  226. {
  227. DpuUnit = DpuUnit;
  228. DpuX = DpuX;
  229. DpuY = DpuY;
  230. }
  231. private static byte[] GetDoubleAsRationalExifData(double value)
  232. {
  233. uint numerator;
  234. uint denominator;
  235. if (Math.IEEERemainder(value, 1.0) == 0)
  236. {
  237. numerator = (uint)value;
  238. denominator = 1;
  239. }
  240. else
  241. {
  242. double s = value * 1000.0;
  243. numerator = (uint)Math.Floor(s);
  244. denominator = 1000;
  245. }
  246. return Exif.EncodeRationalValue(numerator, denominator);
  247. }
  248. /// <summary>
  249. /// Gets or sets the Document's dots-per-unit scale in the X direction.
  250. /// </summary>
  251. /// <remarks>
  252. /// If DpuUnit is equal to MeasurementUnit.Pixel, then this property may not be set
  253. /// to any value other than 1.0. Setting DpuUnit to MeasurementUnit.Pixel will reset
  254. /// this property to 1.0. This property may only be set to a value greater than 0.
  255. /// One dot is always equal to one pixel. This property will not return a value less
  256. /// than MinimumDpu, nor a value larger than MaximumDpu.
  257. /// </remarks>
  258. public double DpuX
  259. {
  260. get
  261. {
  262. PropertyItem[] pis = Metadata.GetExifValues(ExifTagID.XResolution);
  263. if (pis.Length == 0)
  264. {
  265. double defaultDpu = GetDefaultDpu(DpuUnit);
  266. DpuX = defaultDpu;
  267. return defaultDpu;
  268. }
  269. try
  270. {
  271. uint numerator;
  272. uint denominator;
  273. Exif.DecodeRationalValue(pis[0], out numerator, out denominator);
  274. if (denominator == 0)
  275. {
  276. throw new DivideByZeroException(); // will be caught by the below catch{}
  277. }
  278. return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator));
  279. }
  280. catch
  281. {
  282. Metadata.RemoveExifValues(ExifTagID.XResolution);
  283. return DpuX; // recursive call;
  284. }
  285. }
  286. set
  287. {
  288. if (value <= 0.0)
  289. {
  290. throw new ArgumentOutOfRangeException("value", value, "must be > 0.0");
  291. }
  292. if (DpuUnit == MeasurementUnit.Pixel && value != 1.0)
  293. {
  294. throw new ArgumentOutOfRangeException("value", value, "if DpuUnit == Pixel, then value must equal 1.0");
  295. }
  296. byte[] data = GetDoubleAsRationalExifData(value);
  297. PropertyItem pi = Exif.CreatePropertyItem(ExifTagID.XResolution, ExifTagType.Rational, data);
  298. Metadata.ReplaceExifValues(ExifTagID.XResolution, new[] { pi });
  299. Dirty = true;
  300. }
  301. }
  302. /// <summary>
  303. /// Gets or sets the Document's dots-per-unit scale in the Y direction.
  304. /// </summary>
  305. /// <remarks>
  306. /// If DpuUnit is equal to MeasurementUnit.Pixel, then this property may not be set
  307. /// to any value other than 1.0. Setting DpuUnit to MeasurementUnit.Pixel will reset
  308. /// this property to 1.0. This property may only be set to a value greater than 0.
  309. /// One dot is always equal to one pixel. This property will not return a value less
  310. /// than MinimumDpu, nor a value larger than MaximumDpu.
  311. /// </remarks>
  312. public double DpuY
  313. {
  314. get
  315. {
  316. PropertyItem[] pis = Metadata.GetExifValues(ExifTagID.YResolution);
  317. if (pis.Length == 0)
  318. {
  319. // If there's no DpuY setting, default to the DpuX setting
  320. double dpu = DpuX;
  321. DpuY = dpu;
  322. return dpu;
  323. }
  324. try
  325. {
  326. uint numerator;
  327. uint denominator;
  328. Exif.DecodeRationalValue(pis[0], out numerator, out denominator);
  329. if (denominator == 0)
  330. {
  331. throw new DivideByZeroException(); // will be caught by the below catch{}
  332. }
  333. return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator));
  334. }
  335. catch
  336. {
  337. Metadata.RemoveExifValues(ExifTagID.YResolution);
  338. return DpuY; // recursive call;
  339. }
  340. }
  341. set
  342. {
  343. if (value <= 0.0)
  344. {
  345. throw new ArgumentOutOfRangeException("value", value, "must be > 0.0");
  346. }
  347. if (DpuUnit == MeasurementUnit.Pixel && value != 1.0)
  348. {
  349. throw new ArgumentOutOfRangeException("value", value, "if DpuUnit == Pixel, then value must equal 1.0");
  350. }
  351. byte[] data = GetDoubleAsRationalExifData(value);
  352. PropertyItem pi = Exif.CreatePropertyItem(ExifTagID.YResolution, ExifTagType.Rational, data);
  353. Metadata.ReplaceExifValues(ExifTagID.YResolution, new[] { pi });
  354. Dirty = true;
  355. }
  356. }
  357. /// <summary>
  358. /// Gets the Document's measured physical width based on the DpuUnit and DpuX properties.
  359. /// </summary>
  360. public double PhysicalWidth
  361. {
  362. get
  363. {
  364. return Width / DpuX;
  365. }
  366. }
  367. /// <summary>
  368. /// Gets the Document's measured physical height based on the DpuUnit and DpuY properties.
  369. /// </summary>
  370. public double PhysicalHeight
  371. {
  372. get
  373. {
  374. return Height / DpuY;
  375. }
  376. }
  377. //
  378. // Conversion Matrix:
  379. //
  380. // GetPhysical[X|Y](x, unit), where dpu = this.dpuX or dpuY
  381. //
  382. // dpu | px | in | cm |
  383. // unit | | | |
  384. // -------------+------+------+------------+
  385. // px | x | x | x |
  386. // -------------+------+------+------------+
  387. // in | x / | x / | x / |
  388. // | 96 | dpuX | (dpuX*2.54)|
  389. // -------------+------+------+------------+
  390. // cm | x / |x*2.54| x / dpuX |
  391. // | 37.8| /dpuX| |
  392. // -------------+------+------+------------+
  393. public static double PixelToPhysical(double pixel, MeasurementUnit resultUnit, MeasurementUnit dpuUnit, double dpu)
  394. {
  395. double result;
  396. if (resultUnit == MeasurementUnit.Pixel)
  397. {
  398. result = pixel;
  399. }
  400. else
  401. {
  402. if (resultUnit == dpuUnit)
  403. {
  404. result = pixel / dpu;
  405. }
  406. else if (dpuUnit == MeasurementUnit.Pixel)
  407. {
  408. double defaultDpu = GetDefaultDpu(dpuUnit);
  409. result = pixel / defaultDpu;
  410. }
  411. else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
  412. {
  413. result = pixel / (CmPerInch * dpu);
  414. }
  415. else // if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
  416. {
  417. result = (pixel * CmPerInch) / dpu;
  418. }
  419. }
  420. return result;
  421. }
  422. public double PixelToPhysicalX(double pixel, MeasurementUnit resultUnit)
  423. {
  424. double result;
  425. if (resultUnit == MeasurementUnit.Pixel)
  426. {
  427. result = pixel;
  428. }
  429. else
  430. {
  431. MeasurementUnit dpuUnit = DpuUnit;
  432. if (resultUnit == dpuUnit)
  433. {
  434. result = pixel / DpuX;
  435. }
  436. else if (dpuUnit == MeasurementUnit.Pixel)
  437. {
  438. double defaultDpuX = GetDefaultDpu(dpuUnit);
  439. result = pixel / defaultDpuX;
  440. }
  441. else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
  442. {
  443. result = pixel / (CmPerInch * DpuX);
  444. }
  445. else //if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
  446. {
  447. result = (pixel * CmPerInch) / DpuX;
  448. }
  449. }
  450. return result;
  451. }
  452. public double PixelToPhysicalY(double pixel, MeasurementUnit resultUnit)
  453. {
  454. double result;
  455. if (resultUnit == MeasurementUnit.Pixel)
  456. {
  457. result = pixel;
  458. }
  459. else
  460. {
  461. MeasurementUnit dpuUnit = DpuUnit;
  462. if (resultUnit == dpuUnit)
  463. {
  464. result = pixel / DpuY;
  465. }
  466. else if (dpuUnit == MeasurementUnit.Pixel)
  467. {
  468. double defaultDpuY = GetDefaultDpu(dpuUnit);
  469. result = pixel / defaultDpuY;
  470. }
  471. else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
  472. {
  473. result = pixel / (CmPerInch * DpuY);
  474. }
  475. else //if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
  476. {
  477. result = (pixel * CmPerInch) / DpuY;
  478. }
  479. }
  480. return result;
  481. }
  482. private static bool IsValidMeasurementUnit(MeasurementUnit unit)
  483. {
  484. switch (unit)
  485. {
  486. case MeasurementUnit.Pixel:
  487. case MeasurementUnit.Centimeter:
  488. case MeasurementUnit.Inch:
  489. return true;
  490. default:
  491. return false;
  492. }
  493. }
  494. public static double ConvertMeasurement(
  495. double sourceLength,
  496. MeasurementUnit sourceUnits,
  497. MeasurementUnit basisDpuUnits,
  498. double basisDpu,
  499. MeasurementUnit resultDpuUnits)
  500. {
  501. // Validation
  502. if (!IsValidMeasurementUnit(sourceUnits))
  503. {
  504. throw new InvalidEnumArgumentException("sourceUnits", (int)sourceUnits, typeof(MeasurementUnit));
  505. }
  506. if (!IsValidMeasurementUnit(basisDpuUnits))
  507. {
  508. throw new InvalidEnumArgumentException("basisDpuUnits", (int)basisDpuUnits, typeof(MeasurementUnit));
  509. }
  510. if (!IsValidMeasurementUnit(resultDpuUnits))
  511. {
  512. throw new InvalidEnumArgumentException("resultDpuUnits", (int)resultDpuUnits, typeof(MeasurementUnit));
  513. }
  514. if (basisDpuUnits == MeasurementUnit.Pixel && basisDpu != 1.0)
  515. {
  516. throw new ArgumentOutOfRangeException("basisDpuUnits, basisDpu", "if basisDpuUnits is Pixel, then basisDpu must equal 1.0");
  517. }
  518. // Case 1. No conversion is necessary if they want the same units out.
  519. if (sourceUnits == resultDpuUnits)
  520. {
  521. return sourceLength;
  522. }
  523. // Case 2. Simple inches -> centimeters
  524. if (sourceUnits == MeasurementUnit.Inch && resultDpuUnits == MeasurementUnit.Centimeter)
  525. {
  526. return InchesToCentimeters(sourceLength);
  527. }
  528. // Case 3. Simple centimeters -> inches.
  529. if (sourceUnits == MeasurementUnit.Centimeter && resultDpuUnits == MeasurementUnit.Inch)
  530. {
  531. return CentimetersToInches(sourceLength);
  532. }
  533. // At this point we know we are converting from non-pixels to pixels, or from pixels
  534. // to non-pixels.
  535. // Cases 4 through 8 cover conversion from non-pixels to pixels.
  536. // Cases 9 through 11 cover conversion from pixels to non-pixels.
  537. // Case 4. Conversion from pixels to inches/centimeters when basis is in pixels too.
  538. // This means we must use the default DPU for the desired result measurement.
  539. // No need to compare lengthUnits != resultDpuUnits, since we already know this to
  540. // be true from case 1.
  541. if (sourceUnits == MeasurementUnit.Pixel && basisDpuUnits == MeasurementUnit.Pixel)
  542. {
  543. double dpu = GetDefaultDpu(resultDpuUnits);
  544. double lengthInOrCm = sourceLength / dpu;
  545. return lengthInOrCm;
  546. }
  547. // Case 5. Conversion from inches/centimeters to pixels when basis is in pixels too.
  548. // This means we must use the default DPU for the given input measurement.
  549. if (sourceUnits != MeasurementUnit.Pixel && basisDpuUnits == MeasurementUnit.Pixel)
  550. {
  551. double dpu = GetDefaultDpu(sourceUnits);
  552. double resultPx = sourceLength * dpu;
  553. return resultPx;
  554. }
  555. // Case 6. Conversion from inches/centimeters to pixels, when basis is in same units as input.
  556. if (sourceUnits == basisDpuUnits && resultDpuUnits == MeasurementUnit.Pixel)
  557. {
  558. double resultPx = sourceLength * basisDpu;
  559. return resultPx;
  560. }
  561. // Case 7. Conversion from inches to pixels, when basis is in centimeters.
  562. if (sourceUnits == MeasurementUnit.Inch && basisDpuUnits == MeasurementUnit.Centimeter)
  563. {
  564. double dpi = DotsPerCmToDotsPerInch(basisDpu);
  565. double resultPx = sourceLength * dpi;
  566. return resultPx;
  567. }
  568. // Case 8. Conversion from centimeters to pixels, when basis is in inches.
  569. if (sourceUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Inch)
  570. {
  571. double dpcm = DotsPerInchToDotsPerCm(basisDpu);
  572. double resultPx = sourceLength * dpcm;
  573. return resultPx;
  574. }
  575. // Case 9. Converting from pixels to inches/centimeters, when the basis and result
  576. // units are the same.
  577. if (basisDpuUnits == resultDpuUnits)
  578. {
  579. double resultInOrCm = sourceLength / basisDpu;
  580. return resultInOrCm;
  581. }
  582. // Case 10. Converting from pixels to centimeters, when the basis is in inches.
  583. if (resultDpuUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Inch)
  584. {
  585. double dpcm = DotsPerInchToDotsPerCm(basisDpu);
  586. double resultCm = sourceLength / dpcm;
  587. return resultCm;
  588. }
  589. // Case 11. Converting from pixels to inches, when the basis is in centimeters.
  590. if (resultDpuUnits == MeasurementUnit.Inch && basisDpuUnits == MeasurementUnit.Centimeter)
  591. {
  592. double dpi = DotsPerCmToDotsPerInch(basisDpu);
  593. double resultIn = sourceLength / dpi;
  594. return resultIn;
  595. }
  596. // Should not be possible to get here, but must appease the compiler.
  597. throw new InvalidOperationException();
  598. }
  599. public double PixelAreaToPhysicalArea(double area, MeasurementUnit resultUnit)
  600. {
  601. double xScale = PixelToPhysicalX(1.0, resultUnit);
  602. double yScale = PixelToPhysicalY(1.0, resultUnit);
  603. return area * xScale * yScale;
  604. }
  605. private static string GetUnitsAbbreviation(MeasurementUnit units)
  606. {
  607. string result;
  608. switch (units)
  609. {
  610. case MeasurementUnit.Pixel:
  611. result = string.Empty;
  612. break;
  613. case MeasurementUnit.Centimeter:
  614. result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
  615. break;
  616. case MeasurementUnit.Inch:
  617. result = PdnResources.GetString("MeasurementUnit.Inch.Abbreviation");
  618. break;
  619. default:
  620. throw new InvalidEnumArgumentException("MeasurementUnit was invalid");
  621. }
  622. return result;
  623. }
  624. public void CoordinatesToStrings(MeasurementUnit units, int x, int y, out string xString, out string yString, out string unitsString)
  625. {
  626. string unitsAbbreviation = GetUnitsAbbreviation(units);
  627. unitsString = GetUnitsAbbreviation(units);
  628. if (units == MeasurementUnit.Pixel)
  629. {
  630. xString = x.ToString();
  631. yString = y.ToString();
  632. }
  633. else
  634. {
  635. double physicalX = PixelToPhysicalX(x, units);
  636. xString = physicalX.ToString("F2");
  637. double physicalY = PixelToPhysicalY(y, units);
  638. yString = physicalY.ToString("F2");
  639. }
  640. }
  641. /// <summary>
  642. /// This is provided for future use.
  643. /// If you want to add new stuff that must be serialized, create a new class,
  644. /// then point 'tag' to a new instance of this class that is initialized
  645. /// during construction. Make sure the new class has a 'tag' variable as well.
  646. /// We effectively set up a 'linked list' where new versions of the code
  647. /// can open old versions of the document, as .NET serialization is fickle in
  648. /// certain areas. You might also add a new property to simplify using
  649. /// this stuff...
  650. /// public DocumentVersion2Data DocV2Data { get { return (DocumentVersion2Data)tag; } }
  651. /// </summary>
  652. // In practice, this has never been used, and .NET 2.0+ has better facilities for adding
  653. // new data to a serialization schema. Therefore, marking as obsolete.
  654. //[Obsolete]
  655. // private object _tag = null; COMMENTED OUT AS IT IS OBSOLETE
  656. /// <summary>
  657. /// Reports the version of Paint.NET that this file was saved with.
  658. /// This is reset when SaveToStream is used. This can be used to
  659. /// determine file format compatibility if necessary.
  660. /// </summary>
  661. public Version SavedWithVersion
  662. {
  663. get
  664. {
  665. if (_disposed)
  666. {
  667. throw new ObjectDisposedException("Document");
  668. }
  669. return _savedWith ?? (_savedWith = PdnInfo.GetVersion());
  670. }
  671. }
  672. [field: NonSerialized]
  673. public event EventHandler DirtyChanged;
  674. private void OnDirtyChanged()
  675. {
  676. if (DirtyChanged != null)
  677. {
  678. DirtyChanged(this, EventArgs.Empty);
  679. }
  680. }
  681. /// <summary>
  682. /// Keeps track of whether the document has changed at all since it was last opened
  683. /// or saved. This is something that is not reset to true by any method in the Document
  684. /// class, but is set to false anytime anything is changed.
  685. /// This way we can prompt the user to save a changed document when they go to quit.
  686. /// </summary>
  687. public bool Dirty
  688. {
  689. get
  690. {
  691. if (_disposed)
  692. {
  693. throw new ObjectDisposedException("Document");
  694. }
  695. return _dirty;
  696. }
  697. set
  698. {
  699. if (_disposed)
  700. {
  701. throw new ObjectDisposedException("Document");
  702. }
  703. if (_dirty == value) return;
  704. _dirty = value;
  705. OnDirtyChanged();
  706. }
  707. }
  708. /// <summary>
  709. /// Exposes a collection for access to the layers, and for manipulation of
  710. /// the way the document contains the layers (add/remove/move).
  711. /// </summary>
  712. public LayerList Layers
  713. {
  714. get
  715. {
  716. if (_disposed)
  717. {
  718. throw new ObjectDisposedException("Document");
  719. }
  720. return _layers;
  721. }
  722. }
  723. /// <summary>
  724. /// Width of the document, in pixels. All contained layers must be this wide as well.
  725. /// </summary>
  726. public int Width
  727. {
  728. get
  729. {
  730. return _width;
  731. }
  732. }
  733. /// <summary>
  734. /// Height of the document, in pixels. All contained layers must be this tall as well.
  735. /// </summary>
  736. public int Height
  737. {
  738. get
  739. {
  740. return _height;
  741. }
  742. }
  743. /// <summary>
  744. /// The size of the document, in pixels. This is a convenience property that wraps up
  745. /// the Width and Height properties in one Size structure.
  746. /// </summary>
  747. public Size Size
  748. {
  749. get
  750. {
  751. return new Size(Width, Height);
  752. }
  753. }
  754. public Rectangle Bounds
  755. {
  756. get
  757. {
  758. return new Rectangle(0, 0, Width, Height);
  759. }
  760. }
  761. public Metadata Metadata
  762. {
  763. get { return _metadata ?? (_metadata = new Metadata(_userMetaData)); }
  764. }
  765. public void ReplaceMetaDataFrom(Document other)
  766. {
  767. Metadata.ReplaceWithDataFrom(other.Metadata);
  768. }
  769. public void ClearMetaData()
  770. {
  771. Metadata.Clear();
  772. }
  773. /* [Obsolete("don't use this property; implementors should expose type-safe properties instead", false)]
  774. // Note, we can not remove this property because then the compiler complains that 'tag' is unused.
  775. public object Tag
  776. {
  777. get
  778. {
  779. return _tag; COMMENTED OUT AS IT IS OBSOLETE
  780. }
  781. set
  782. {
  783. _tag = value;
  784. }
  785. }*/
  786. /// <summary>
  787. /// Clears a portion of a surface to transparent.
  788. /// </summary>
  789. /// <param name="surface">The surface to partially clear</param>
  790. /// <param name="roi">The rectangle to clear</param>
  791. private static unsafe void ClearBackground(Surface surface, Rectangle roi)
  792. {
  793. roi.Intersect(surface.Bounds);
  794. for (int y = roi.Top; y < roi.Bottom; y++)
  795. {
  796. ColorBgra *ptr = surface.GetPointAddressUnchecked(roi.Left, y);
  797. Memory.SetToZero(ptr, (ulong)roi.Width * ColorBgra.SizeOf);
  798. }
  799. }
  800. /// <summary>
  801. /// Clears a portion of a surface to transparent.
  802. /// </summary>
  803. /// <param name="surface">The surface to partially clear</param>
  804. /// <param name="rois">The array of Rectangles designating the areas to clear</param>
  805. /// <param name="startIndex">The start index within the rois array to clear</param>
  806. /// <param name="length">The number of Rectangles in the rois array (staring with startIndex) to clear</param>
  807. private static void ClearBackground(Surface surface, IList<Rectangle> rois, int startIndex, int length)
  808. {
  809. for (int i = startIndex; i < startIndex + length; i++)
  810. {
  811. ClearBackground(surface, rois[i]);
  812. }
  813. }
  814. public void Render(RenderArgs args)
  815. {
  816. Render(args, args.Surface.Bounds);
  817. }
  818. public void Render(RenderArgs args, Rectangle roi)
  819. {
  820. Render(args, roi, false);
  821. }
  822. public void Render(RenderArgs args, bool clearBackground)
  823. {
  824. Render(args, args.Surface.Bounds, clearBackground);
  825. }
  826. /// <summary>
  827. /// Renders a requested region of the document. Will clear the background of the input
  828. /// before rendering if requested.
  829. /// </summary>
  830. /// <param name="args">Contains information used to control where rendering occurs.</param>
  831. /// <param name="roi">The rectangular region to render.</param>
  832. /// <param name="clearBackground">If true, 'args' will be cleared to zero before rendering.</param>
  833. public void Render(RenderArgs args, Rectangle roi, bool clearBackground)
  834. {
  835. int startIndex;
  836. if (clearBackground)
  837. {
  838. var layer0 = _layers[0] as BitmapLayer; //Change to BitmapLayer if compile error
  839. // Special case: if the first layer is a visible BitmapLayer with full opacity using
  840. // the default blend op, we can just copy the pixels straight over
  841. if (layer0 != null &&
  842. layer0.Visible &&
  843. layer0.Opacity == 255 &&
  844. layer0.BlendOp.GetType() == UserBlendOps.GetDefaultBlendOp())
  845. {
  846. args.Surface.CopySurface(layer0.Surface);
  847. startIndex = 1;
  848. }
  849. else
  850. {
  851. ClearBackground(args.Surface, roi);
  852. startIndex = 0;
  853. }
  854. }
  855. else
  856. {
  857. startIndex = 0;
  858. }
  859. for (int i = startIndex; i < _layers.Count; ++i)
  860. {
  861. var layer = (Layer)_layers[i];
  862. if (layer.Visible)
  863. {
  864. layer.Render(args, roi);
  865. }
  866. }
  867. }
  868. public void Render(RenderArgs args, Rectangle[] roi, bool clearBackground)
  869. {
  870. Render(args, roi, 0, roi.Length, clearBackground);
  871. }
  872. public void Render(RenderArgs args, Rectangle[] roi, int startIndex, int length, bool clearBackground)
  873. {
  874. int startLayerIndex;
  875. if (clearBackground)
  876. {
  877. var layer0 = _layers[0] as BitmapLayer;//Change to BitmapLayer if var fails
  878. // Special case: if the first layer is a visible BitmapLayer with full opacity using
  879. // the default blend op, we can just copy the pixels straight over
  880. if (layer0 != null &&
  881. layer0.Visible &&
  882. layer0.Opacity == 255 &&
  883. layer0.BlendOp.GetType() == UserBlendOps.GetDefaultBlendOp())
  884. {
  885. args.Surface.CopySurface(layer0.Surface, roi, startIndex, length);
  886. startLayerIndex = 1;
  887. }
  888. else
  889. {
  890. ClearBackground(args.Surface, roi, startIndex, length);
  891. startLayerIndex = 0;
  892. }
  893. }
  894. else
  895. {
  896. startLayerIndex = 0;
  897. }
  898. for (int i = startLayerIndex; i < _layers.Count; ++i)
  899. {
  900. var layer = (Layer)_layers[i];
  901. if (layer.Visible)
  902. {
  903. layer.RenderUnchecked(args, roi, startIndex, length);
  904. }
  905. }
  906. }
  907. private sealed class UpdateScansContext
  908. {
  909. private readonly Document _document;
  910. private readonly RenderArgs _dst;
  911. private readonly Rectangle[] _scans;
  912. private readonly int _startIndex;
  913. private readonly int _length;
  914. public void UpdateScans(object context)
  915. {
  916. _document.Render(_dst, _scans, _startIndex, _length, true);
  917. }
  918. public UpdateScansContext(Document document, RenderArgs dst, Rectangle[] scans, int startIndex, int length)
  919. {
  920. _document = document;
  921. _dst = dst;
  922. _scans = scans;
  923. _startIndex = startIndex;
  924. _length = length;
  925. }
  926. }
  927. /// <summary>
  928. /// Renders only the portions of the document that have changed (been Invalidated) since
  929. /// the last call to this function.
  930. /// </summary>
  931. /// <param name="args">Contains information used to control where rendering occurs.</param>
  932. /// <param name="dst"></param>
  933. /// <returns>true if any rendering was done (the update list was non-empty), false otherwise</returns>
  934. public bool Update(RenderArgs dst)
  935. {
  936. if (_disposed)
  937. {
  938. throw new ObjectDisposedException("Document");
  939. }
  940. Rectangle[] updateRects;
  941. int updateRectsLength;
  942. _updateRegion.GetArrayReadOnly(out updateRects, out updateRectsLength);
  943. if (updateRectsLength == 0)
  944. {
  945. return false;
  946. }
  947. PdnRegion region = Utility.RectanglesToRegion(updateRects, 0, updateRectsLength);
  948. Rectangle[] rectsOriginal = region.GetRegionScansReadOnlyInt();
  949. Rectangle[] rectsToUse;
  950. // Special case where we're drawing 1 big rectangle: split it up!
  951. // This case happens quite frequently, but we don't want to spend a lot of
  952. // time analyzing any other case that is more complicated.
  953. if (rectsOriginal.Length == 1 && rectsOriginal[0].Height > 1)
  954. {
  955. var rectsNew = new Rectangle[Processor.LogicalCpuCount]; //Change to Rectangle[] if var fails
  956. Utility.SplitRectangle(rectsOriginal[0], rectsNew);
  957. rectsToUse = rectsNew;
  958. }
  959. else
  960. {
  961. rectsToUse = rectsOriginal;
  962. }
  963. int cpuCount = Processor.LogicalCpuCount;
  964. for (int i = 0; i < cpuCount; ++i)
  965. {
  966. int start = (i * rectsToUse.Length) / cpuCount;
  967. int end = ((i + 1) * rectsToUse.Length) / cpuCount;
  968. var usc = new UpdateScansContext(this, dst, rectsToUse, start, end - start);
  969. if (i == cpuCount - 1)
  970. {
  971. // Reuse this thread for the last job -- no sense creating a new thread.
  972. usc.UpdateScans(usc);
  973. }
  974. else
  975. {
  976. _threadPool.QueueUserWorkItem(usc.UpdateScans, usc);
  977. }
  978. }
  979. _threadPool.Drain();
  980. Validate();
  981. return true;
  982. }
  983. /// <summary>
  984. /// Constructs a blank document (zero layers) of the given width and height.
  985. /// </summary>
  986. /// <param name="width"></param>
  987. /// <param name="height"></param>
  988. public Document(int width, int height)
  989. {
  990. _width = width;
  991. _height = height;
  992. Dirty = true;
  993. _updateRegion = new Vector<Rectangle>();
  994. _layers = new LayerList(this);
  995. SetupEvents();
  996. _userMetaData = new NameValueCollection();
  997. Invalidate();
  998. }
  999. public Document(Size size)
  1000. : this(size.Width, size.Height)
  1001. {
  1002. }
  1003. /// <summary>
  1004. /// Sets up event handling for contained objects.
  1005. /// </summary>
  1006. private void SetupEvents()
  1007. {
  1008. _layers.Changed += LayerListChangedHandler;
  1009. _layers.Changing += LayerListChangingHandler;
  1010. _layerInvalidatedDelegate = new InvalidateEventHandler(LayerInvalidatedHandler);
  1011. foreach (Layer layer in _layers)
  1012. {
  1013. layer.Invalidated += _layerInvalidatedDelegate;
  1014. }
  1015. }
  1016. /// <summary>
  1017. /// Called after deserialization occurs so that certain things that are non-serializable
  1018. /// can be set up.
  1019. /// </summary>
  1020. /// <param name="sender"></param>
  1021. public void OnDeserialization(object sender)
  1022. {
  1023. _updateRegion = new Vector<Rectangle>();
  1024. _updateRegion.Add(Bounds);
  1025. _threadPool = new Threading.ThreadPool();
  1026. SetupEvents();
  1027. Dirty = true;
  1028. }
  1029. [field: NonSerialized]
  1030. public event InvalidateEventHandler Invalidated;
  1031. /// <summary>
  1032. /// Raises the Invalidated event.
  1033. /// </summary>
  1034. /// <param name="e"></param>
  1035. private void OnInvalidated(InvalidateEventArgs e)
  1036. {
  1037. if (Invalidated != null)
  1038. {
  1039. Invalidated(this, e);
  1040. }
  1041. }
  1042. /// <summary>
  1043. /// Handles the Changing event that is raised from the contained LayerList.
  1044. /// </summary>
  1045. /// <param name="sender"></param>
  1046. /// <param name="e"></param>
  1047. private void LayerListChangingHandler(object sender, EventArgs e)
  1048. {
  1049. if (_disposed)
  1050. {
  1051. throw new ObjectDisposedException("Document");
  1052. }
  1053. foreach (Layer layer in Layers)
  1054. {
  1055. layer.Invalidated -= _layerInvalidatedDelegate;
  1056. }
  1057. }
  1058. /// <summary>
  1059. /// Handles the Changed event that is raised from the contained LayerList.
  1060. /// </summary>
  1061. /// <param name="sender"></param>
  1062. /// <param name="e"></param>
  1063. private void LayerListChangedHandler(object sender, EventArgs e)
  1064. {
  1065. foreach (Layer layer in Layers)
  1066. {
  1067. layer.Invalidated += _layerInvalidatedDelegate;
  1068. }
  1069. Invalidate();
  1070. }
  1071. /// <summary>
  1072. /// Handles the Invalidated event that is raised from any contained Layer.
  1073. /// </summary>
  1074. /// <param name="sender"></param>
  1075. /// <param name="e"></param>
  1076. private void LayerInvalidatedHandler(object sender, InvalidateEventArgs e)
  1077. {
  1078. Invalidate(e.InvalidRect);
  1079. }
  1080. /// <summary>
  1081. /// Causes the whole document to be invalidated, forcing a full rerender on
  1082. /// the next call to Update.
  1083. /// </summary>
  1084. public void Invalidate()
  1085. {
  1086. Dirty = true;
  1087. var rect = new Rectangle(0, 0, Width, Height);
  1088. _updateRegion.Clear();
  1089. _updateRegion.Add(rect);
  1090. OnInvalidated(new InvalidateEventArgs(rect));
  1091. }
  1092. /// <summary>
  1093. /// Invalidates a portion of the document. The given region is then tagged
  1094. /// for rerendering during the next call to Update.
  1095. /// </summary>
  1096. /// <param name="roi">The region of interest to be invalidated.</param>
  1097. public void Invalidate(PdnRegion roi)
  1098. {
  1099. Dirty = true;
  1100. foreach (Rectangle rect in roi.GetRegionScansReadOnlyInt())
  1101. {
  1102. rect.Intersect(Bounds);
  1103. _updateRegion.Add(rect);
  1104. if (rect.IsEmpty) continue;
  1105. var iea = new InvalidateEventArgs(rect);
  1106. OnInvalidated(iea);
  1107. }
  1108. }
  1109. public void Invalidate(RectangleF[] roi)
  1110. {
  1111. foreach (RectangleF rectF in roi)
  1112. {
  1113. Invalidate(Rectangle.Truncate(rectF));
  1114. }
  1115. }
  1116. public void Invalidate(RectangleF roi)
  1117. {
  1118. Invalidate(Rectangle.Truncate(roi));
  1119. }
  1120. public void Invalidate(Rectangle[] roi)
  1121. {
  1122. foreach (Rectangle rect in roi)
  1123. {
  1124. Invalidate(rect);
  1125. }
  1126. }
  1127. /// <summary>
  1128. /// Invalidates a portion of the document. The given region is then tagged
  1129. /// for rerendering during the next call to Update.
  1130. /// </summary>
  1131. /// <param name="roi">The region of interest to be invalidated.</param>
  1132. public void Invalidate(Rectangle roi)
  1133. {
  1134. Dirty = true;
  1135. Rectangle rect = Rectangle.Intersect(roi, Bounds);
  1136. _updateRegion.Add(rect);
  1137. OnInvalidated(new InvalidateEventArgs(rect));
  1138. }
  1139. /// <summary>
  1140. /// Clears the document's update region. This is called at the end of the
  1141. /// Update method.
  1142. /// </summary>
  1143. private void Validate()
  1144. {
  1145. _updateRegion.Clear();
  1146. }
  1147. /// <summary>
  1148. /// Creates a document that consists of one BitmapLayer.
  1149. /// </summary>
  1150. /// <param name="image">The Image to make a copy of that will be the first layer ("Background") in the document.</param>
  1151. public static Document FromImage(Image image)
  1152. {
  1153. if (image == null)
  1154. {
  1155. throw new ArgumentNullException("image");
  1156. }
  1157. var document = new Document(image.Width, image.Height);
  1158. BitmapLayer layer = Layer.CreateBackgroundLayer(image.Width, image.Height);
  1159. layer.Surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
  1160. var asBitmap = image as Bitmap;
  1161. // Copy pixels
  1162. if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format32bppArgb)
  1163. {
  1164. unsafe
  1165. {
  1166. BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  1167. try
  1168. {
  1169. for (int y = 0; y < bData.Height; ++y)
  1170. {
  1171. var srcPtr = (uint*)((byte*)bData.Scan0.ToPointer() + (y * bData.Stride));
  1172. ColorBgra* dstPtr = layer.Surface.GetRowAddress(y);
  1173. for (int x = 0; x < bData.Width; ++x)
  1174. {
  1175. dstPtr->Bgra = *srcPtr;
  1176. ++srcPtr;
  1177. ++dstPtr;
  1178. }
  1179. }
  1180. }
  1181. finally
  1182. {
  1183. asBitmap.UnlockBits(bData);
  1184. bData = null;
  1185. }
  1186. }
  1187. }
  1188. else if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format24bppRgb)
  1189. {
  1190. unsafe
  1191. {
  1192. BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  1193. try
  1194. {
  1195. for (int y = 0; y < bData.Height; ++y)
  1196. {
  1197. byte* srcPtr = (byte*)bData.Scan0.ToPointer() + (y * bData.Stride);
  1198. ColorBgra* dstPtr = layer.Surface.GetRowAddress(y);
  1199. for (int x = 0; x < bData.Width; ++x)
  1200. {
  1201. byte b = *srcPtr;
  1202. byte g = *(srcPtr + 1);
  1203. byte r = *(srcPtr + 2);
  1204. const byte a = 255;
  1205. *dstPtr = ColorBgra.FromBgra(b, g, r, a);
  1206. srcPtr += 3;
  1207. ++dstPtr;
  1208. }
  1209. }
  1210. }
  1211. finally
  1212. {
  1213. asBitmap.UnlockBits(bData);
  1214. bData = null;
  1215. }
  1216. }
  1217. }
  1218. else
  1219. {
  1220. using (var args = new RenderArgs(layer.Surface))
  1221. {
  1222. args.Graphics.CompositingMode = CompositingMode.SourceCopy;
  1223. args.Graphics.SmoothingMode = SmoothingMode.None;
  1224. args.Graphics.DrawImage(image, args.Bounds, args.Bounds, Graphic

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