Document.cs 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869
  1. using PaintDotNet.Base.SettingModel;
  2. using PaintDotNet.SystemLayer;
  3. using System;
  4. using System.Collections.Specialized;
  5. using System.ComponentModel;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. using System.Drawing.Imaging;
  9. using System.IO;
  10. using System.IO.Compression;
  11. using System.Reflection;
  12. using System.Runtime.InteropServices;
  13. using System.Runtime.Serialization;
  14. using System.Runtime.Serialization.Formatters.Binary;
  15. using System.Text;
  16. using System.Windows.Forms;
  17. using System.Xml;
  18. namespace PaintDotNet
  19. {
  20. [Serializable]
  21. public sealed class Document : IDeserializationCallback, IDisposable, ICloneable
  22. {
  23. public Surface surface;
  24. private int width;
  25. private int height;
  26. private NameValueCollection userMetaData;
  27. [NonSerialized]
  28. private Threading.ThreadPool threadPool = new Threading.ThreadPool();
  29. /*[NonSerialized]
  30. private InvalidateEventHandler layerInvalidatedDelegate;*/
  31. [NonSerialized]
  32. private Vector<Rectangle> updateRegion;
  33. [NonSerialized]
  34. private bool dirty;
  35. private Version savedWith;
  36. [NonSerialized]
  37. private Metadata metadata = null;
  38. [NonSerialized]
  39. private XmlDocument headerXml;
  40. private const string headerXmlSkeleton = "<pdnImage><custom></custom></pdnImage>";
  41. private XmlDocument HeaderXml
  42. {
  43. get
  44. {
  45. if (this.disposed)
  46. {
  47. throw new ObjectDisposedException("Document");
  48. }
  49. if (this.headerXml == null)
  50. {
  51. this.headerXml = new XmlDocument();
  52. this.headerXml.LoadXml(headerXmlSkeleton);
  53. }
  54. return this.headerXml;
  55. }
  56. }
  57. /// <summary>
  58. /// Gets or sets the units that are used for measuring the document's physical (printed) size.
  59. /// </summary>
  60. /// <remarks>
  61. /// If this property is set to MeasurementUnit.Pixel, then Dpu will be reset to 1.
  62. /// If this property has not been set in the image's metadata, its default value
  63. /// will be MeasurementUnit.Inch.
  64. /// If the EXIF data for the image is invalid (such as "ResolutionUnit = 0" or something),
  65. /// then the default DpuUnit will be returned.
  66. /// </remarks>
  67. public MeasurementUnit DpuUnit
  68. {
  69. get
  70. {
  71. PropertyItem[] pis = this.Metadata.GetExifValues(ExifTagID.ResolutionUnit);
  72. if (pis.Length == 0)
  73. {
  74. this.DpuUnit = DefaultDpuUnit;
  75. return DefaultDpuUnit;
  76. }
  77. else
  78. {
  79. try
  80. {
  81. ushort unit = Exif.DecodeShortValue(pis[0]);
  82. // Guard against bad data in the EXIF store
  83. switch ((MeasurementUnit)unit)
  84. {
  85. case MeasurementUnit.Pixel:
  86. case MeasurementUnit.Inch:
  87. case MeasurementUnit.Mil:
  88. case MeasurementUnit.Centimeter:
  89. case MeasurementUnit.Millimeter:
  90. case MeasurementUnit.Micron:
  91. case MeasurementUnit.Nano:
  92. return (MeasurementUnit)unit;
  93. default:
  94. this.Metadata.RemoveExifValues(ExifTagID.ResolutionUnit);
  95. return this.DpuUnit; // recursive call
  96. }
  97. }
  98. catch (Exception)
  99. {
  100. this.Metadata.RemoveExifValues(ExifTagID.ResolutionUnit);
  101. return this.DpuUnit; // recursive call
  102. }
  103. }
  104. }
  105. set
  106. {
  107. PropertyItem pi = Exif.CreateShort(ExifTagID.ResolutionUnit, (ushort)value);
  108. this.Metadata.ReplaceExifValues(ExifTagID.ResolutionUnit, new PropertyItem[1] { pi });
  109. if (value == MeasurementUnit.Pixel)
  110. {
  111. this.DpuX = 1.0;
  112. this.DpuY = 1.0;
  113. }
  114. Dirty = true;
  115. }
  116. }
  117. public static MeasurementUnit DefaultDpuUnit
  118. {
  119. get
  120. {
  121. return MeasurementUnit.Inch;
  122. }
  123. }
  124. public static double defaultDpi = 96;
  125. public static double DefaultDpi
  126. {
  127. get
  128. {
  129. return defaultDpi;
  130. }
  131. }
  132. /// <summary>
  133. /// 1英寸 = 2.54厘米
  134. /// </summary>
  135. public static double CmPerInch = 2.54;
  136. /// <summary>
  137. /// 默认值 = dpi / 单位
  138. /// </summary>
  139. public double defaultDpcm;
  140. public double DefaultDpcm
  141. {
  142. get
  143. {
  144. return defaultDpi / CmPerInch;
  145. }
  146. }
  147. public const double MinimumDpu = 0.01;
  148. public const double MaximumDpu = 32767.0;
  149. public static double InchesToCentimeters(double inches)
  150. {
  151. return inches * CmPerInch;
  152. }
  153. public static double MilsToCentimeters(double mils)
  154. {
  155. return mils * CmPerInch * 0.001;
  156. }
  157. public static double CentimetersToInches(double centimeters)
  158. {
  159. return centimeters / CmPerInch;
  160. }
  161. public static double CentimetersToMils(double centimeters)
  162. {
  163. return 1000 * centimeters / CmPerInch;
  164. }
  165. public static double DotsPerInchToDotsPerCm(double dpi)
  166. {
  167. return dpi / CmPerInch;
  168. }
  169. public static double DotsPerMilToDotsPerCm(double dpi)
  170. {
  171. return 0.001 * dpi / CmPerInch;
  172. }
  173. public static double DotsPerCmToDotsPerInch(double dpcm)
  174. {
  175. return dpcm * CmPerInch;
  176. }
  177. public static double DotsPerCmToDotsPerMil(double dpcm)
  178. {
  179. return dpcm * CmPerInch * 0.001;
  180. }
  181. public static double GetDefaultDpu(MeasurementUnit units)
  182. {
  183. double dpu;
  184. switch (units)
  185. {
  186. case MeasurementUnit.Inch:
  187. dpu = defaultDpi;
  188. break;
  189. case MeasurementUnit.Mil:
  190. dpu = defaultDpi / 1000;
  191. break;
  192. case MeasurementUnit.Centimeter:
  193. dpu = defaultDpi / CmPerInch;
  194. break;
  195. case MeasurementUnit.Millimeter:
  196. dpu = defaultDpi / CmPerInch / 10;
  197. break;
  198. case MeasurementUnit.Micron:
  199. dpu = defaultDpi / CmPerInch / 10000;
  200. break;
  201. case MeasurementUnit.Nano:
  202. dpu = defaultDpi / CmPerInch / 10000000;
  203. break;
  204. case MeasurementUnit.Pixel:
  205. dpu = 1.0;
  206. break;
  207. default:
  208. throw new InvalidEnumArgumentException("DpuUnit", (int)units, typeof(MeasurementUnit));
  209. }
  210. return dpu;
  211. }
  212. /// <summary>
  213. /// Ensures that the document's DpuX, DpuY, and DpuUnits properties are set.
  214. /// If they are not already set, they are initialized to their default values (96, 96 , inches).
  215. /// </summary>
  216. private void InitializeDpu()
  217. {
  218. this.DpuUnit = this.DpuUnit;
  219. this.DpuX = this.DpuX;
  220. this.DpuY = this.DpuY;
  221. }
  222. private byte[] GetDoubleAsRationalExifData(double value)
  223. {
  224. uint numerator;
  225. uint denominator;
  226. if (Math.IEEERemainder(value, 1.0) == 0)
  227. {
  228. numerator = (uint)value;
  229. denominator = 1;
  230. }
  231. else
  232. {
  233. double s = value * 1000.0;
  234. numerator = (uint)Math.Floor(s);
  235. denominator = 1000;
  236. }
  237. return Exif.EncodeRationalValue(numerator, denominator);
  238. }
  239. /// <summary>
  240. /// Gets or sets the Document's dots-per-unit scale in the X direction.
  241. /// </summary>
  242. /// <remarks>
  243. /// If DpuUnit is equal to MeasurementUnit.Pixel, then this property may not be set
  244. /// to any value other than 1.0. Setting DpuUnit to MeasurementUnit.Pixel will reset
  245. /// this property to 1.0. This property may only be set to a value greater than 0.
  246. /// One dot is always equal to one pixel. This property will not return a value less
  247. /// than MinimumDpu, nor a value larger than MaximumDpu.
  248. /// </remarks>
  249. public double DpuX
  250. {
  251. get
  252. {
  253. PropertyItem[] pis = this.Metadata.GetExifValues(ExifTagID.XResolution);
  254. if (pis.Length == 0)
  255. {
  256. double defaultDpu = GetDefaultDpu(this.DpuUnit);
  257. this.DpuX = defaultDpu;
  258. return defaultDpu;
  259. }
  260. else
  261. {
  262. try
  263. {
  264. uint numerator;
  265. uint denominator;
  266. Exif.DecodeRationalValue(pis[0], out numerator, out denominator);
  267. if (denominator == 0)
  268. {
  269. throw new DivideByZeroException(); // will be caught by the below catch{}
  270. }
  271. else
  272. {
  273. return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator));
  274. }
  275. }
  276. catch
  277. {
  278. this.Metadata.RemoveExifValues(ExifTagID.XResolution);
  279. return this.DpuX; // recursive call;
  280. }
  281. }
  282. }
  283. set
  284. {
  285. if (value <= 0.0)
  286. {
  287. throw new ArgumentOutOfRangeException("value", value, "must be > 0.0");
  288. }
  289. if (this.DpuUnit == MeasurementUnit.Pixel && value != 1.0)
  290. {
  291. throw new ArgumentOutOfRangeException("value", value, "if DpuUnit == Pixel, then value must equal 1.0");
  292. }
  293. byte[] data = GetDoubleAsRationalExifData(value);
  294. PropertyItem pi = Exif.CreatePropertyItem(ExifTagID.XResolution, ExifTagType.Rational, data);
  295. this.Metadata.ReplaceExifValues(ExifTagID.XResolution, new PropertyItem[1] { pi });
  296. Dirty = true;
  297. }
  298. }
  299. /// <summary>
  300. /// Gets or sets the Document's dots-per-unit scale in the Y direction.
  301. /// </summary>
  302. /// <remarks>
  303. /// If DpuUnit is equal to MeasurementUnit.Pixel, then this property may not be set
  304. /// to any value other than 1.0. Setting DpuUnit to MeasurementUnit.Pixel will reset
  305. /// this property to 1.0. This property may only be set to a value greater than 0.
  306. /// One dot is always equal to one pixel. This property will not return a value less
  307. /// than MinimumDpu, nor a value larger than MaximumDpu.
  308. /// </remarks>
  309. public double DpuY
  310. {
  311. get
  312. {
  313. PropertyItem[] pis = this.Metadata.GetExifValues(ExifTagID.YResolution);
  314. if (pis.Length == 0)
  315. {
  316. // If there's no DpuY setting, default to the DpuX setting
  317. double dpu = this.DpuX;
  318. this.DpuY = dpu;
  319. return dpu;
  320. }
  321. else
  322. {
  323. try
  324. {
  325. uint numerator;
  326. uint denominator;
  327. Exif.DecodeRationalValue(pis[0], out numerator, out denominator);
  328. if (denominator == 0)
  329. {
  330. throw new DivideByZeroException(); // will be caught by the below catch{}
  331. }
  332. else
  333. {
  334. return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator));
  335. }
  336. }
  337. catch
  338. {
  339. this.Metadata.RemoveExifValues(ExifTagID.YResolution);
  340. return this.DpuY; // recursive call;
  341. }
  342. }
  343. }
  344. set
  345. {
  346. if (value <= 0.0)
  347. {
  348. throw new ArgumentOutOfRangeException("value", value, "must be > 0.0");
  349. }
  350. if (this.DpuUnit == MeasurementUnit.Pixel && value != 1.0)
  351. {
  352. throw new ArgumentOutOfRangeException("value", value, "if DpuUnit == Pixel, then value must equal 1.0");
  353. }
  354. byte[] data = GetDoubleAsRationalExifData(value);
  355. PropertyItem pi = Exif.CreatePropertyItem(ExifTagID.YResolution, ExifTagType.Rational, data);
  356. this.Metadata.ReplaceExifValues(ExifTagID.YResolution, new PropertyItem[1] { pi });
  357. Dirty = true;
  358. }
  359. }
  360. /// <summary>
  361. /// Gets the Document's measured physical width based on the DpuUnit and DpuX properties.
  362. /// </summary>
  363. public double PhysicalWidth
  364. {
  365. get
  366. {
  367. return (double)this.Width / (double)this.DpuX;
  368. }
  369. }
  370. /// <summary>
  371. /// Gets the Document's measured physical height based on the DpuUnit and DpuY properties.
  372. /// </summary>
  373. public double PhysicalHeight
  374. {
  375. get
  376. {
  377. return (double)this.Height / (double)this.DpuY;
  378. }
  379. }
  380. //
  381. // Conversion Matrix:
  382. //
  383. // GetPhysical[X|Y](x, unit), where dpu = this.dpuX or dpuY
  384. //
  385. // dpu | px | in | cm |
  386. // unit | | | |
  387. // -------------+------+------+------------+
  388. // px | x | x | x |
  389. // -------------+------+------+------------+
  390. // in | x / | x / | x / |
  391. // | 96 | dpuX | (dpuX*2.54)|
  392. // -------------+------+------+------------+
  393. // cm | x / |x*2.54| x / dpuX |
  394. // | 37.8| /dpuX| |
  395. // -------------+------+------+------------+
  396. public static double PixelToPhysical(double pixel, MeasurementUnit resultUnit, MeasurementUnit dpuUnit, double dpu)
  397. {
  398. double result;
  399. if (resultUnit == MeasurementUnit.Pixel)
  400. {
  401. result = pixel;
  402. }
  403. else
  404. {
  405. if (resultUnit == dpuUnit)
  406. {
  407. result = pixel / dpu;
  408. }
  409. else if (dpuUnit == MeasurementUnit.Pixel)
  410. {
  411. double defaultDpu = GetDefaultDpu(dpuUnit);
  412. result = pixel / defaultDpu;
  413. }
  414. else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
  415. {
  416. result = pixel / (CmPerInch * dpu);
  417. }
  418. else // if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
  419. {
  420. result = (pixel * CmPerInch) / dpu;
  421. }
  422. }
  423. return result;
  424. }
  425. /// <summary>
  426. /// 像素换算到物理长度
  427. /// </summary>
  428. /// <param name="pixel"></param>
  429. /// <param name="resultUnit"></param>
  430. /// <returns></returns>
  431. public double PixelToPhysicalX(double pixel, MeasurementUnit resultUnit)
  432. {
  433. double result;
  434. if (resultUnit == MeasurementUnit.Pixel)
  435. {
  436. result = pixel;
  437. }
  438. else
  439. {
  440. MeasurementUnit dpuUnit = this.DpuUnit;
  441. if (resultUnit == dpuUnit)
  442. {
  443. result = pixel / this.DpuX;
  444. }
  445. else if (dpuUnit == MeasurementUnit.Pixel)
  446. {
  447. double defaultDpuX = GetDefaultDpu(dpuUnit);
  448. result = pixel / defaultDpuX;
  449. }
  450. else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
  451. {
  452. result = pixel / this.DpuX; // (CmPerInch * this.DpuX);
  453. }
  454. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Millimeter)
  455. {
  456. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Millimeter);
  457. result = pixel / defaultDpuY;
  458. }
  459. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Micron)
  460. {
  461. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Micron);
  462. result = pixel / defaultDpuY;
  463. }
  464. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Nano)
  465. {
  466. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Nano);
  467. result = pixel / defaultDpuY;
  468. }
  469. else
  470. {
  471. result = pixel / this.DpuX; //(pixel * CmPerInch) / this.DpuX;
  472. }
  473. }
  474. return result;
  475. }
  476. /// <summary>
  477. /// 像素换算到物理长度
  478. /// </summary>
  479. /// <param name="pixel"></param>
  480. /// <param name="resultUnit"></param>
  481. /// <returns></returns>
  482. public double PixelToPhysicalY(double pixel, MeasurementUnit resultUnit)
  483. {
  484. double result;
  485. if (resultUnit == MeasurementUnit.Pixel)
  486. {
  487. result = pixel;
  488. }
  489. else
  490. {
  491. MeasurementUnit dpuUnit = this.DpuUnit;
  492. if (resultUnit == dpuUnit)
  493. {
  494. double defaultDpuY = GetDefaultDpu(dpuUnit);
  495. result = pixel / defaultDpuY;
  496. //result = pixel / this.DpuY;
  497. }
  498. else if (dpuUnit == MeasurementUnit.Pixel)
  499. {
  500. double defaultDpuY = GetDefaultDpu(dpuUnit);
  501. result = pixel / defaultDpuY;
  502. }
  503. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Mil)
  504. {
  505. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Mil);
  506. result = pixel / defaultDpuY; // pixel / (CmPerInch * this.DpuY);
  507. }
  508. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
  509. {
  510. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Centimeter);
  511. result = pixel / defaultDpuY; // pixel / (CmPerInch * this.DpuY);
  512. }
  513. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Millimeter)
  514. {
  515. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Millimeter);
  516. result = pixel / defaultDpuY;
  517. }
  518. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Micron)
  519. {
  520. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Micron);
  521. result = pixel / defaultDpuY;
  522. }
  523. else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Nano)
  524. {
  525. double defaultDpuY = GetDefaultDpu(MeasurementUnit.Nano);
  526. result = pixel / defaultDpuY;
  527. }
  528. else
  529. {
  530. result = pixel / this.DpuX;//(pixel * CmPerInch) / this.DpuY;
  531. }
  532. }
  533. return result;
  534. }
  535. private static bool IsValidMeasurementUnit(MeasurementUnit unit)
  536. {
  537. switch (unit)
  538. {
  539. case MeasurementUnit.Pixel:
  540. case MeasurementUnit.Inch:
  541. case MeasurementUnit.Centimeter:
  542. case MeasurementUnit.Millimeter:
  543. case MeasurementUnit.Micron:
  544. case MeasurementUnit.Nano:
  545. return true;
  546. default:
  547. return false;
  548. }
  549. }
  550. public static double ConvertMeasurement(
  551. double sourceLength,
  552. MeasurementUnit sourceUnits,
  553. MeasurementUnit basisDpuUnits,
  554. double basisDpu,
  555. MeasurementUnit resultDpuUnits)
  556. {
  557. // Validation
  558. if (!IsValidMeasurementUnit(sourceUnits))
  559. {
  560. throw new InvalidEnumArgumentException("sourceUnits", (int)sourceUnits, typeof(MeasurementUnit));
  561. }
  562. if (!IsValidMeasurementUnit(basisDpuUnits))
  563. {
  564. throw new InvalidEnumArgumentException("basisDpuUnits", (int)basisDpuUnits, typeof(MeasurementUnit));
  565. }
  566. if (!IsValidMeasurementUnit(resultDpuUnits))
  567. {
  568. throw new InvalidEnumArgumentException("resultDpuUnits", (int)resultDpuUnits, typeof(MeasurementUnit));
  569. }
  570. if (basisDpuUnits == MeasurementUnit.Pixel && basisDpu != 1.0)
  571. {
  572. throw new ArgumentOutOfRangeException("basisDpuUnits, basisDpu", "if basisDpuUnits is Pixel, then basisDpu must equal 1.0");
  573. }
  574. // Case 1. No conversion is necessary if they want the same units out.
  575. if (sourceUnits == resultDpuUnits)
  576. {
  577. return sourceLength;
  578. }
  579. // Case 2. Simple inches -> centimeters
  580. if (sourceUnits == MeasurementUnit.Inch && resultDpuUnits == MeasurementUnit.Centimeter)
  581. {
  582. return InchesToCentimeters(sourceLength);
  583. }
  584. // Case 2. Simple mils -> centimeters
  585. if (sourceUnits == MeasurementUnit.Mil && resultDpuUnits == MeasurementUnit.Centimeter)
  586. {
  587. return MilsToCentimeters(sourceLength);
  588. }
  589. // Case 3. Simple centimeters -> inches.
  590. if (sourceUnits == MeasurementUnit.Centimeter && resultDpuUnits == MeasurementUnit.Inch)
  591. {
  592. return CentimetersToInches(sourceLength);
  593. }
  594. // Case 3. Simple centimeters -> inches.
  595. if (sourceUnits == MeasurementUnit.Centimeter && resultDpuUnits == MeasurementUnit.Mil)
  596. {
  597. return CentimetersToMils(sourceLength);
  598. }
  599. // At this point we know we are converting from non-pixels to pixels, or from pixels
  600. // to non-pixels.
  601. // Cases 4 through 8 cover conversion from non-pixels to pixels.
  602. // Cases 9 through 11 cover conversion from pixels to non-pixels.
  603. // Case 4. Conversion from pixels to inches/centimeters when basis is in pixels too.
  604. // This means we must use the default DPU for the desired result measurement.
  605. // No need to compare lengthUnits != resultDpuUnits, since we already know this to
  606. // be true from case 1.
  607. if (sourceUnits == MeasurementUnit.Pixel && basisDpuUnits == MeasurementUnit.Pixel)
  608. {
  609. double dpu = GetDefaultDpu(resultDpuUnits);
  610. double lengthInOrCm = sourceLength / dpu;
  611. return lengthInOrCm;
  612. }
  613. // Case 5. Conversion from inches/centimeters to pixels when basis is in pixels too.
  614. // This means we must use the default DPU for the given input measurement.
  615. if (sourceUnits != MeasurementUnit.Pixel && basisDpuUnits == MeasurementUnit.Pixel)
  616. {
  617. double dpu = GetDefaultDpu(sourceUnits);
  618. double resultPx = sourceLength * dpu;
  619. return resultPx;
  620. }
  621. // Case 6. Conversion from inches/centimeters to pixels, when basis is in same units as input.
  622. if (sourceUnits == basisDpuUnits && resultDpuUnits == MeasurementUnit.Pixel)
  623. {
  624. double resultPx = sourceLength * basisDpu;
  625. return resultPx;
  626. }
  627. // Case 7. Conversion from inches to pixels, when basis is in centimeters.
  628. if (sourceUnits == MeasurementUnit.Inch && basisDpuUnits == MeasurementUnit.Centimeter)
  629. {
  630. double dpi = DotsPerCmToDotsPerInch(basisDpu);
  631. double resultPx = sourceLength * dpi;
  632. return resultPx;
  633. }
  634. // Case 7. Conversion from inches to pixels, when basis is in centimeters.
  635. if (sourceUnits == MeasurementUnit.Mil && basisDpuUnits == MeasurementUnit.Centimeter)
  636. {
  637. double dpi = DotsPerCmToDotsPerMil(basisDpu);
  638. double resultPx = sourceLength * dpi;
  639. return resultPx;
  640. }
  641. // Case 8. Conversion from centimeters to pixels, when basis is in inches.
  642. if (sourceUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Inch)
  643. {
  644. double dpcm = DotsPerInchToDotsPerCm(basisDpu);
  645. double resultPx = sourceLength * dpcm;
  646. return resultPx;
  647. }
  648. // Case 8. Conversion from centimeters to pixels, when basis is in inches.
  649. if (sourceUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Mil)
  650. {
  651. double dpcm = DotsPerMilToDotsPerCm(basisDpu);
  652. double resultPx = sourceLength * dpcm;
  653. return resultPx;
  654. }
  655. // Case 9. Converting from pixels to inches/centimeters, when the basis and result
  656. // units are the same.
  657. if (basisDpuUnits == resultDpuUnits)
  658. {
  659. double resultInOrCm = sourceLength / basisDpu;
  660. return resultInOrCm;
  661. }
  662. // Case 10. Converting from pixels to centimeters, when the basis is in inches.
  663. if (resultDpuUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Inch)
  664. {
  665. double dpcm = DotsPerInchToDotsPerCm(basisDpu);
  666. double resultCm = sourceLength / dpcm;
  667. return resultCm;
  668. }
  669. // Case 11. Converting from pixels to inches, when the basis is in centimeters.
  670. if (resultDpuUnits == MeasurementUnit.Inch && basisDpuUnits == MeasurementUnit.Centimeter)
  671. {
  672. double dpi = DotsPerCmToDotsPerInch(basisDpu);
  673. double resultIn = sourceLength / dpi;
  674. return resultIn;
  675. }
  676. // Should not be possible to get here, but must appease the compiler.
  677. throw new InvalidOperationException();
  678. }
  679. public double PixelAreaToPhysicalArea(double area, MeasurementUnit resultUnit)
  680. {
  681. double xScale = PixelToPhysicalX(1.0, resultUnit);
  682. double yScale = PixelToPhysicalY(1.0, resultUnit);
  683. return area * xScale * yScale;
  684. }
  685. private static string GetUnitsAbbreviation(MeasurementUnit units)
  686. {
  687. string result;
  688. switch (units)
  689. {
  690. case MeasurementUnit.Pixel:
  691. result = string.Empty;
  692. break;
  693. case MeasurementUnit.Inch:
  694. result = PdnResources.GetString("MeasurementUnit.Inch.Abbreviation");
  695. break;
  696. case MeasurementUnit.Mil:
  697. result = PdnResources.GetString("MeasurementUnit.Mil.Abbreviation");
  698. break;
  699. case MeasurementUnit.Centimeter:
  700. result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
  701. break;
  702. case MeasurementUnit.Millimeter:
  703. result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
  704. break;
  705. case MeasurementUnit.Micron:
  706. result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
  707. break;
  708. case MeasurementUnit.Nano:
  709. result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
  710. break;
  711. default:
  712. throw new InvalidEnumArgumentException("MeasurementUnit was invalid");
  713. }
  714. return result;
  715. }
  716. public void CoordinatesToStrings(MeasurementUnit units, int x, int y, out string xString, out string yString, out string unitsString)
  717. {
  718. string unitsAbbreviation = GetUnitsAbbreviation(units);
  719. unitsString = GetUnitsAbbreviation(units);
  720. if (units == MeasurementUnit.Pixel)
  721. {
  722. xString = x.ToString();
  723. yString = y.ToString();
  724. }
  725. else
  726. {
  727. double physicalX = PixelToPhysicalX(x, units);
  728. xString = physicalX.ToString("F2");
  729. double physicalY = PixelToPhysicalY(y, units);
  730. yString = physicalY.ToString("F2");
  731. }
  732. }
  733. public Version SavedWithVersion
  734. {
  735. get
  736. {
  737. if (disposed)
  738. {
  739. throw new ObjectDisposedException("Document");
  740. }
  741. if (savedWith == null)
  742. {
  743. savedWith = PdnInfo.GetVersion();
  744. }
  745. return savedWith;
  746. }
  747. }
  748. [field: NonSerialized]
  749. public event EventHandler DirtyChanged;
  750. private void OnDirtyChanged()
  751. {
  752. if (DirtyChanged != null)
  753. {
  754. DirtyChanged(this, EventArgs.Empty);
  755. }
  756. }
  757. /// <summary>
  758. /// Keeps track of whether the document has changed at all since it was last opened
  759. /// or saved. This is something that is not reset to true by any method in the Document
  760. /// class, but is set to false anytime anything is changed.
  761. /// This way we can prompt the user to save a changed document when they go to quit.
  762. /// </summary>
  763. public bool Dirty
  764. {
  765. get
  766. {
  767. if (this.disposed)
  768. {
  769. throw new ObjectDisposedException("Document");
  770. }
  771. return this.dirty;
  772. }
  773. set
  774. {
  775. if (this.disposed)
  776. {
  777. throw new ObjectDisposedException("Document");
  778. }
  779. if (this.dirty != value)
  780. {
  781. this.dirty = value;
  782. OnDirtyChanged();
  783. }
  784. }
  785. }
  786. /// <summary>
  787. /// Exposes a collection for access to the layers, and for manipulation of
  788. /// the way the document contains the layers (add/remove/move).
  789. /// </summary>
  790. /*public LayerList Layers
  791. {
  792. get
  793. {
  794. return null;
  795. if (disposed)
  796. {
  797. throw new ObjectDisposedException("Document");
  798. }
  799. return layers;
  800. }
  801. }*/
  802. /// <summary>
  803. /// Width of the document, in pixels. All contained layers must be this wide as well.
  804. /// </summary>
  805. public int Width
  806. {
  807. get
  808. {
  809. return width;
  810. }
  811. }
  812. /// <summary>
  813. /// Height of the document, in pixels. All contained layers must be this tall as well.
  814. /// </summary>
  815. public int Height
  816. {
  817. get
  818. {
  819. return height;
  820. }
  821. }
  822. /// <summary>
  823. /// The size of the document, in pixels. This is a convenience property that wraps up
  824. /// the Width and Height properties in one Size structure.
  825. /// </summary>
  826. public Size Size
  827. {
  828. get
  829. {
  830. return new Size(Width, Height);
  831. }
  832. }
  833. public Rectangle Bounds
  834. {
  835. get
  836. {
  837. return new Rectangle(0, 0, Width, Height);
  838. }
  839. }
  840. public Metadata Metadata
  841. {
  842. get
  843. {
  844. if (metadata == null)
  845. {
  846. metadata = new Metadata(userMetaData);
  847. }
  848. return metadata;
  849. }
  850. }
  851. public void ReplaceMetaDataFrom(Document other)
  852. {
  853. this.Metadata.ReplaceWithDataFrom(other.Metadata);
  854. }
  855. public void ClearMetaData()
  856. {
  857. this.Metadata.Clear();
  858. }
  859. /// <summary>
  860. /// Clears a portion of a surface to transparent.
  861. /// </summary>
  862. /// <param name="surface">The surface to partially clear</param>
  863. /// <param name="roi">The rectangle to clear</param>
  864. private unsafe void ClearBackground(Surface surface, Rectangle roi)
  865. {
  866. roi.Intersect(surface.Bounds);
  867. for (int y = roi.Top; y < roi.Bottom; y++)
  868. {
  869. ColorBgra* ptr = surface.GetPointAddressUnchecked(roi.Left, y);
  870. Memory.SetToZero(ptr, (ulong)roi.Width * ColorBgra.SizeOf);
  871. }
  872. }
  873. /// <summary>
  874. /// Clears a portion of a surface to transparent.
  875. /// </summary>
  876. /// <param name="surface">The surface to partially clear</param>
  877. /// <param name="rois">The array of Rectangles designating the areas to clear</param>
  878. /// <param name="startIndex">The start index within the rois array to clear</param>
  879. /// <param name="length">The number of Rectangles in the rois array (staring with startIndex) to clear</param>
  880. private void ClearBackground(Surface surface, Rectangle[] rois, int startIndex, int length)
  881. {
  882. for (int i = startIndex; i < startIndex + length; i++)
  883. {
  884. ClearBackground(surface, rois[i]);
  885. }
  886. }
  887. public void Render(RenderArgs args, bool clearBackground)
  888. {
  889. Render(args, args.Surface.Bounds, clearBackground);
  890. }
  891. /// <summary>
  892. /// Renders a requested region of the document. Will clear the background of the input
  893. /// before rendering if requested.
  894. /// </summary>
  895. /// <param name="args">Contains information used to control where rendering occurs.</param>
  896. /// <param name="roi">The rectangular region to render.</param>
  897. /// <param name="clearBackground">If true, 'args' will be cleared to zero before rendering.</param>
  898. public void Render(RenderArgs args, Rectangle roi, bool clearBackground)
  899. {
  900. /*int startIndex;
  901. if (clearBackground)
  902. {
  903. BitmapLayer layer0;
  904. layer0 = this.layers[0] as BitmapLayer;
  905. // Special case: if the first layer is a visible BitmapLayer with full opacity using
  906. // the default blend op, we can just copy the pixels straight over
  907. if (layer0 != null &&
  908. layer0.Visible &&
  909. layer0.Opacity == 255 &&
  910. layer0.BlendOp.GetType() == UserBlendOps.GetDefaultBlendOp())
  911. {
  912. args.Surface.CopySurface(layer0.Surface);
  913. startIndex = 1;
  914. }
  915. else
  916. {
  917. ClearBackground(args.Surface, roi);
  918. startIndex = 0;
  919. }
  920. }
  921. else
  922. {
  923. startIndex = 0;
  924. }
  925. for (int i = startIndex; i < this.layers.Count; ++i)
  926. {
  927. Layer layer = (Layer)this.layers[i];
  928. if (layer.Visible)
  929. {
  930. layer.Render(args, roi);
  931. }
  932. }*/
  933. }
  934. public void Render(RenderArgs args, Rectangle[] roi, int startIndex, int length, bool clearBackground)
  935. {
  936. /*int startLayerIndex;
  937. if (clearBackground)
  938. {
  939. BitmapLayer layer0;
  940. layer0 = this.layers[0] as BitmapLayer;
  941. // Special case: if the first layer is a visible BitmapLayer with full opacity using
  942. // the default blend op, we can just copy the pixels straight over
  943. if (layer0 != null &&
  944. layer0.Visible &&
  945. layer0.Opacity == 255 &&
  946. layer0.BlendOp.GetType() == UserBlendOps.GetDefaultBlendOp())
  947. {
  948. args.Surface.CopySurface(layer0.Surface, roi, startIndex, length);
  949. startLayerIndex = 1;
  950. }
  951. else
  952. {
  953. ClearBackground(args.Surface, roi, startIndex, length);
  954. startLayerIndex = 0;
  955. }
  956. }
  957. else
  958. {
  959. startLayerIndex = 0;
  960. }
  961. for (int i = startLayerIndex; i < this.layers.Count; ++i)
  962. {
  963. Layer layer = (Layer)this.layers[i];
  964. if (layer.Visible)
  965. {
  966. layer.RenderUnchecked(args, roi, startIndex, length);
  967. }
  968. }*/
  969. }
  970. private sealed class UpdateScansContext
  971. {
  972. private Document document;
  973. private RenderArgs dst;
  974. private Rectangle[] scans;
  975. private int startIndex;
  976. private int length;
  977. public void UpdateScans(object context)
  978. {
  979. document.Render(dst, scans, startIndex, length, true);
  980. }
  981. public UpdateScansContext(Document document, RenderArgs dst, Rectangle[] scans, int startIndex, int length)
  982. {
  983. this.document = document;
  984. this.dst = dst;
  985. this.scans = scans;
  986. this.startIndex = startIndex;
  987. this.length = length;
  988. }
  989. }
  990. /// <summary>
  991. /// Constructs a blank document (zero layers) of the given width and height.
  992. /// </summary>
  993. /// <param name="width"></param>
  994. /// <param name="height"></param>
  995. public Document(int width, int height)
  996. {
  997. this.width = width;
  998. this.height = height;
  999. this.Dirty = true;
  1000. this.updateRegion = new Vector<Rectangle>();
  1001. //layers = new LayerList(this);
  1002. //SetupEvents();
  1003. userMetaData = new NameValueCollection();
  1004. Invalidate();
  1005. }
  1006. /// <summary>
  1007. /// Sets up event handling for contained objects.
  1008. /// </summary>
  1009. /*private void SetupEvents()
  1010. {
  1011. layers.Changed += new EventHandler(LayerListChangedHandler);
  1012. layers.Changing += new EventHandler(LayerListChangingHandler);
  1013. layerInvalidatedDelegate = new InvalidateEventHandler(LayerInvalidatedHandler);
  1014. foreach (Layer layer in layers)
  1015. {
  1016. layer.Invalidated += layerInvalidatedDelegate;
  1017. }
  1018. }*/
  1019. /// <summary>
  1020. /// Called after deserialization occurs so that certain things that are non-serializable
  1021. /// can be set up.
  1022. /// </summary>
  1023. /// <param name="sender"></param>
  1024. public void OnDeserialization(object sender)
  1025. {
  1026. this.updateRegion = new Vector<Rectangle>();
  1027. this.updateRegion.Add(this.Bounds);
  1028. this.threadPool = new PaintDotNet.Threading.ThreadPool();
  1029. //SetupEvents();
  1030. Dirty = true;
  1031. }
  1032. [field: NonSerialized]
  1033. public event InvalidateEventHandler Invalidated;
  1034. /// <summary>
  1035. /// Raises the Invalidated event.
  1036. /// </summary>
  1037. /// <param name="e"></param>
  1038. private void OnInvalidated(InvalidateEventArgs e)
  1039. {
  1040. if (Invalidated != null)
  1041. {
  1042. Invalidated(this, e);
  1043. }
  1044. }
  1045. /*/// <summary>
  1046. /// Handles the Changing event that is raised from the contained LayerList.
  1047. /// </summary>
  1048. /// <param name="sender"></param>
  1049. /// <param name="e"></param>
  1050. private void LayerListChangingHandler(object sender, EventArgs e)
  1051. {
  1052. if (disposed)
  1053. {
  1054. throw new ObjectDisposedException("Document");
  1055. }
  1056. foreach (Layer layer in Layers)
  1057. {
  1058. layer.Invalidated -= layerInvalidatedDelegate;
  1059. }
  1060. }*/
  1061. /*/// <summary>
  1062. /// Handles the Changed event that is raised from the contained LayerList.
  1063. /// </summary>
  1064. /// <param name="sender"></param>
  1065. /// <param name="e"></param>
  1066. private void LayerListChangedHandler(object sender, EventArgs e)
  1067. {
  1068. foreach (Layer layer in Layers)
  1069. {
  1070. layer.Invalidated += layerInvalidatedDelegate;
  1071. }
  1072. Invalidate();
  1073. }*/
  1074. /*/// <summary>
  1075. /// Handles the Invalidated event that is raised from any contained Layer.
  1076. /// </summary>
  1077. /// <param name="sender"></param>
  1078. /// <param name="e"></param>
  1079. private void LayerInvalidatedHandler(object sender, InvalidateEventArgs e)
  1080. {
  1081. Invalidate(e.InvalidRect);
  1082. }*/
  1083. /// <summary>
  1084. /// Causes the whole document to be invalidated, forcing a full rerender on
  1085. /// the next call to Update.
  1086. /// </summary>
  1087. public void Invalidate()
  1088. {
  1089. Dirty = true;
  1090. Rectangle rect = new Rectangle(0, 0, Width, Height);
  1091. updateRegion.Clear();
  1092. updateRegion.Add(rect);
  1093. OnInvalidated(new InvalidateEventArgs(rect));
  1094. }
  1095. /*/// <summary>
  1096. /// Invalidates a portion of the document. The given region is then tagged
  1097. /// for rerendering during the next call to Update.
  1098. /// </summary>
  1099. /// <param name="roi">The region of interest to be invalidated.</param>
  1100. public void Invalidate(Rectangle roi)
  1101. {
  1102. Dirty = true;
  1103. Rectangle rect = Rectangle.Intersect(roi, this.Bounds);
  1104. updateRegion.Add(rect);
  1105. OnInvalidated(new InvalidateEventArgs(rect));
  1106. }*/
  1107. /*/// <summary>
  1108. /// Clears the document's update region. This is called at the end of the
  1109. /// Update method.
  1110. /// </summary>
  1111. private void Validate()
  1112. {
  1113. updateRegion.Clear();
  1114. }*/
  1115. /// <summary>
  1116. /// Creates a document that consists of one BitmapLayer.
  1117. /// </summary>
  1118. /// <param name="image">The Image to make a copy of that will be the first layer ("Background") in the document.</param>
  1119. public static Document FromImage(Image image)
  1120. {
  1121. if (image == null)
  1122. {
  1123. throw new ArgumentNullException("image");
  1124. }
  1125. Document document = new Document(image.Width, image.Height);
  1126. Surface surface = new Surface(new Size(image.Width, image.Height));
  1127. surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
  1128. surface.PixelFormat = image.PixelFormat;
  1129. //BitmapLayer layer = Layer.CreateBackgroundLayer(image.Width, image.Height);
  1130. //layer.Surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
  1131. //layer.Surface.PixelFormat = image.PixelFormat;
  1132. Bitmap asBitmap = image as Bitmap;
  1133. // Copy pixels
  1134. if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format32bppArgb)
  1135. {
  1136. unsafe
  1137. {
  1138. BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  1139. try
  1140. {
  1141. for (int y = 0; y < bData.Height; ++y)
  1142. {
  1143. uint* srcPtr = (uint*)((byte*)bData.Scan0.ToPointer() + (y * bData.Stride));
  1144. ColorBgra* dstPtr = surface.GetRowAddress(y);
  1145. for (int x = 0; x < bData.Width; ++x)
  1146. {
  1147. dstPtr->Bgra = *srcPtr;
  1148. ++srcPtr;
  1149. ++dstPtr;
  1150. }
  1151. }
  1152. }
  1153. finally
  1154. {
  1155. asBitmap.UnlockBits(bData);
  1156. bData = null;
  1157. }
  1158. }
  1159. }
  1160. else if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format24bppRgb)
  1161. {
  1162. unsafe
  1163. {
  1164. BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  1165. try
  1166. {
  1167. for (int y = 0; y < bData.Height; ++y)
  1168. {
  1169. byte* srcPtr = (byte*)bData.Scan0.ToPointer() + (y * bData.Stride);
  1170. ColorBgra* dstPtr = surface.GetRowAddress(y);
  1171. for (int x = 0; x < bData.Width; ++x)
  1172. {
  1173. byte b = *srcPtr;
  1174. byte g = *(srcPtr + 1);
  1175. byte r = *(srcPtr + 2);
  1176. byte a = 255;
  1177. *dstPtr = ColorBgra.FromBgra(b, g, r, a);
  1178. srcPtr += 3;
  1179. ++dstPtr;
  1180. }
  1181. }
  1182. }
  1183. finally
  1184. {
  1185. asBitmap.UnlockBits(bData);
  1186. bData = null;
  1187. }
  1188. }
  1189. }
  1190. else
  1191. {
  1192. using (RenderArgs args = new RenderArgs(surface))
  1193. {
  1194. args.Graphics.CompositingMode = CompositingMode.SourceCopy;
  1195. args.Graphics.SmoothingMode = SmoothingMode.None;
  1196. args.Graphics.DrawImage(image, args.Bounds, args.Bounds, GraphicsUnit.Pixel);
  1197. }
  1198. }
  1199. // Console.Write(string.Format("Copy pixels end:{0};", DateTime.Now.Millisecond % 10000));
  1200. // Transfer metadata
  1201. // Sometimes GDI+ does not honor the resolution tags that we
  1202. // put in manually via the EXIF properties.
  1203. document.DpuUnit = MeasurementUnit.Inch;
  1204. document.DpuX = image.HorizontalResolution;
  1205. document.DpuY = image.VerticalResolution;
  1206. PropertyItem[] pis;
  1207. try
  1208. {
  1209. pis = image.PropertyItems;
  1210. }
  1211. catch (Exception ex)
  1212. {
  1213. Tracing.Ping("Exception while retreiving image's PropertyItems: " + ex.ToString());
  1214. pis = null;
  1215. }
  1216. if (pis != null)
  1217. {
  1218. for (int i = 0; i < pis.Length; ++i)
  1219. {
  1220. document.Metadata.AddExifValues(new PropertyItem[] { pis[i] });
  1221. }
  1222. }
  1223. //document.Layers.Add(layer);
  1224. document.surface = surface;
  1225. document.Invalidate();
  1226. return document;
  1227. }
  1228. public static Document FromMat(OpenCvSharp.Mat mat)
  1229. {
  1230. if (mat == null)
  1231. {
  1232. throw new ArgumentNullException("image");
  1233. }
  1234. Image image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat);
  1235. Bitmap asBitmap = image as Bitmap;
  1236. Document document = new Document(mat.Width, mat.Height);
  1237. Surface surface = new Surface(new Size(mat.Width, mat.Height));
  1238. surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
  1239. surface.PixelFormat = asBitmap.PixelFormat;
  1240. //BitmapLayer layer = Layer.CreateBackgroundLayer(mat.Width, mat.Height);
  1241. //layer.Surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
  1242. // Copy pixels
  1243. if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format32bppArgb)
  1244. {
  1245. unsafe
  1246. {
  1247. BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  1248. try
  1249. {
  1250. for (int y = 0; y < bData.Height; ++y)
  1251. {
  1252. uint* srcPtr = (uint*)((byte*)bData.Scan0.ToPointer() + (y * bData.Stride));
  1253. ColorBgra* dstPtr = surface.GetRowAddress(y);
  1254. for (int x = 0; x < bData.Width; ++x)
  1255. {
  1256. dstPtr->Bgra = *srcPtr;
  1257. ++srcPtr;
  1258. ++dstPtr;
  1259. }
  1260. }
  1261. }
  1262. finally
  1263. {
  1264. asBitmap.UnlockBits(bData);
  1265. bData = null;
  1266. }
  1267. }
  1268. }
  1269. else if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format24bppRgb)
  1270. {
  1271. unsafe
  1272. {
  1273. BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  1274. try
  1275. {
  1276. for (int y = 0; y < bData.Height; ++y)
  1277. {
  1278. byte* srcPtr = (byte*)bData.Scan0.ToPointer() + (y * bData.Stride);
  1279. ColorBgra* dstPtr = surface.GetRowAddress(y);
  1280. for (int x = 0; x < bData.Width; ++x)
  1281. {
  1282. byte b = *srcPtr;
  1283. byte g = *(srcPtr + 1);
  1284. byte r = *(srcPtr + 2);
  1285. byte a = 255;
  1286. *dstPtr = ColorBgra.FromBgra(b, g, r, a);
  1287. srcPtr += 3;
  1288. ++dstPtr;
  1289. }
  1290. }
  1291. }
  1292. finally
  1293. {
  1294. asBitmap.UnlockBits(bData);
  1295. bData = null;
  1296. }
  1297. }
  1298. }
  1299. else
  1300. {
  1301. using (RenderArgs args = new RenderArgs(surface))
  1302. {
  1303. args.Graphics.CompositingMode = CompositingMode.SourceCopy;
  1304. args.Graphics.SmoothingMode = SmoothingMode.None;
  1305. args.Graphics.DrawImage(image, args.Bounds, args.Bounds, GraphicsUnit.Pixel);
  1306. }
  1307. }
  1308. // Transfer metadata
  1309. // Sometimes GDI+ does not honor the resolution tags that we
  1310. // put in manually via the EXIF properties.
  1311. document.DpuUnit = MeasurementUnit.Inch;
  1312. document.DpuX = image.HorizontalResolution;
  1313. document.DpuY = image.VerticalResolution;
  1314. PropertyItem[] pis;
  1315. try
  1316. {
  1317. pis = image.PropertyItems;
  1318. }
  1319. catch (Exception ex)
  1320. {
  1321. Tracing.Ping("Exception while retreiving image's PropertyItems: " + ex.ToString());
  1322. pis = null;
  1323. }
  1324. if (pis != null)
  1325. {
  1326. for (int i = 0; i < pis.Length; ++i)
  1327. {
  1328. document.Metadata.AddExifValues(new PropertyItem[] { pis[i] });
  1329. }
  1330. }
  1331. document.surface = surface;
  1332. //document.Layers.Add(layer);
  1333. document.Invalidate();
  1334. return document;
  1335. }
  1336. public static Document FromByteArr(byte[] arr, int w, int h)
  1337. {
  1338. Document document = new Document(w, h);
  1339. Surface surface = new Surface(new Size(w, h), PixelFormat.Format24bppRgb);
  1340. Marshal.Copy(arr, 0, surface.Scan0.Pointer, arr.Length);
  1341. document.surface = surface;
  1342. document.Invalidate();
  1343. return document;
  1344. }
  1345. public static Document FromByteArr(OpenCvSharp.Mat mat)
  1346. {
  1347. if (mat.Channels() == 3)
  1348. {
  1349. OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGR2BGRA);
  1350. }
  1351. else if (mat.Channels() == 1)
  1352. {
  1353. OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.GRAY2BGRA);
  1354. }
  1355. Document document = new Document(mat.Width, mat.Height);
  1356. Surface surface = new Surface(new Size(mat.Width, mat.Height));
  1357. byte[] arr = new byte[mat.Total() * mat.ElemSize()];
  1358. Marshal.Copy(mat.Data, arr, 0, arr.Length);
  1359. Marshal.Copy(arr, 0, surface.Scan0.Pointer, arr.Length);
  1360. document.surface = surface;
  1361. document.Invalidate();
  1362. return document;
  1363. }
  1364. public static byte[] MagicBytes
  1365. {
  1366. get
  1367. {
  1368. return Encoding.UTF8.GetBytes("PDN3");
  1369. }
  1370. }
  1371. /// <summary>
  1372. /// Deserializes a Document from a stream.
  1373. /// </summary>
  1374. /// <param name="stream">The stream to deserialize from. This stream must be seekable.</param>
  1375. /// <returns>The Document that was stored in stream.</returns>
  1376. /// <remarks>
  1377. /// This is the only supported way to deserialize a Document instance from disk.
  1378. /// </remarks>
  1379. public static Document FromStream(Stream stream)
  1380. {
  1381. long oldPosition = stream.Position;
  1382. bool pdn21Format = true;
  1383. // Version 2.1+ file format:
  1384. // Starts with bytes as defined by MagicBytes
  1385. // Next three bytes are 24-bit unsigned int 'N' (first byte is low-word, second byte is middle-word, third byte is high word)
  1386. // The next N bytes are a string, this is the document header (it is XML, UTF-8 encoded)
  1387. // Important: 'N' indicates a byte count, not a character count. 'N' bytes may result in less than 'N' characters,
  1388. // depending on how the characters decode as per UTF8
  1389. // If the next 2 bytes are 0x00, 0x01: This signifies that non-compressed .NET serialized data follows.
  1390. // If the next 2 bytes are 0x1f, 0x8b: This signifies the start of the gzip compressed .NET serialized data
  1391. //
  1392. // Version 2.0 and previous file format:
  1393. // Starts with 0x1f, 0x8b: this signifies the start of the gzip compressed .NET serialized data.
  1394. // Read in the 'magic' bytes
  1395. for (int i = 0; i < MagicBytes.Length; ++i)
  1396. {
  1397. int theByte = stream.ReadByte();
  1398. if (theByte == -1)
  1399. {
  1400. throw new EndOfStreamException();
  1401. }
  1402. if (theByte != MagicBytes[i])
  1403. {
  1404. pdn21Format = false;
  1405. break;
  1406. }
  1407. }
  1408. // Read in the header if we found the 'magic' bytes identifying a PDN 2.1 file
  1409. XmlDocument headerXml = null;
  1410. if (pdn21Format)
  1411. {
  1412. int low = stream.ReadByte();
  1413. if (low == -1)
  1414. {
  1415. throw new EndOfStreamException();
  1416. }
  1417. int mid = stream.ReadByte();
  1418. if (mid == -1)
  1419. {
  1420. throw new EndOfStreamException();
  1421. }
  1422. int high = stream.ReadByte();
  1423. if (high == -1)
  1424. {
  1425. throw new EndOfStreamException();
  1426. }
  1427. int byteCount = low + (mid << 8) + (high << 16);
  1428. byte[] bytes = new byte[byteCount];
  1429. int bytesRead = Utility.ReadFromStream(stream, bytes, 0, byteCount);
  1430. if (bytesRead != byteCount)
  1431. {
  1432. throw new EndOfStreamException("expected " + byteCount + " bytes, but only got " + bytesRead);
  1433. }
  1434. string xml = Encoding.UTF8.GetString(bytes);
  1435. headerXml = new XmlDocument();
  1436. headerXml.LoadXml(xml);
  1437. }
  1438. else
  1439. {
  1440. stream.Position = oldPosition; // rewind and try as v2.0-or-earlier file
  1441. }
  1442. // Start reading the data section of the file. Determine if it's gzip or regular
  1443. long oldPosition2 = stream.Position;
  1444. int first = stream.ReadByte();
  1445. if (first == -1)
  1446. {
  1447. throw new EndOfStreamException();
  1448. }
  1449. int second = stream.ReadByte();
  1450. if (second == -1)
  1451. {
  1452. throw new EndOfStreamException();
  1453. }
  1454. Document document;
  1455. object docObject;
  1456. BinaryFormatter formatter = new BinaryFormatter();
  1457. SerializationFallbackBinder sfb = new SerializationFallbackBinder();
  1458. sfb.AddAssembly(Assembly.GetExecutingAssembly()); // first try PaintDotNet.Data.dll
  1459. sfb.AddAssembly(typeof(Utility).Assembly); // second, try PaintDotNet.Core.dll
  1460. sfb.AddAssembly(typeof(SystemLayer.Memory).Assembly); // third, try PaintDotNet.SystemLayer.dll
  1461. formatter.Binder = sfb;
  1462. if (first == 0 && second == 1)
  1463. {
  1464. DeferredFormatter deferred = new DeferredFormatter();
  1465. formatter.Context = new StreamingContext(formatter.Context.State, deferred);
  1466. docObject = formatter.UnsafeDeserialize(stream, null);
  1467. deferred.FinishDeserialization(stream);
  1468. }
  1469. else if (first == 0x1f && second == 0x8b)
  1470. {
  1471. stream.Position = oldPosition2; // rewind to the start of 0x1f, 0x8b
  1472. GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress, true);
  1473. docObject = formatter.UnsafeDeserialize(gZipStream, null);
  1474. }
  1475. else
  1476. {
  1477. throw new FormatException("file is not a valid document");
  1478. }
  1479. document = (Document)docObject;
  1480. document.Dirty = true;
  1481. document.headerXml = headerXml;
  1482. document.Invalidate();
  1483. return document;
  1484. }
  1485. /// <summary>
  1486. /// Saves the Document to the given Stream with only the default headers and no
  1487. /// IO completion callback.
  1488. /// </summary>
  1489. /// <param name="stream">The Stream to serialize the Document to.</param>
  1490. public void SaveToStream(Stream stream)
  1491. {
  1492. SaveToStream(stream, null);
  1493. }
  1494. /// <summary>
  1495. /// Saves the Document to the given Stream with the default and given headers, and
  1496. /// using the given IO completion callback.
  1497. /// </summary>
  1498. /// <param name="stream">The Stream to serialize the Document to.</param>
  1499. /// <param name="callback">
  1500. /// This can be used to keep track of the number of uncompressed bytes that are written. The
  1501. /// values reported through the IOEventArgs.Count+Offset will vary from 1 to approximately
  1502. /// Layers.Count*Width*Height*sizeof(ColorBgra). The final number will actually be higher
  1503. /// because of hierarchical overhead, so make sure to cap any progress reports to 100%. This
  1504. /// callback will be wired to the IOFinished event of a SiphonStream. Events may be raised
  1505. /// from any thread. May be null.
  1506. /// </param>
  1507. public void SaveToStream(Stream stream, IOEventHandler callback)
  1508. {
  1509. InitializeDpu();
  1510. PrepareHeader();
  1511. string headerText = this.HeaderXml.OuterXml;
  1512. // Write the header
  1513. byte[] magicBytes = Document.MagicBytes;
  1514. stream.Write(magicBytes, 0, magicBytes.Length);
  1515. byte[] headerBytes = Encoding.UTF8.GetBytes(headerText);
  1516. stream.WriteByte((byte)(headerBytes.Length & 0xff));
  1517. stream.WriteByte((byte)((headerBytes.Length & 0xff00) >> 8));
  1518. stream.WriteByte((byte)((headerBytes.Length & 0xff0000) >> 16));
  1519. stream.Write(headerBytes, 0, headerBytes.Length);
  1520. stream.Flush();
  1521. // Copy version info
  1522. this.savedWith = PdnInfo.GetVersion();
  1523. // Write 0x00, 0x01 to indicate normal .NET serialized data
  1524. stream.WriteByte(0x00);
  1525. stream.WriteByte(0x01);
  1526. // Write the remainder of the file (gzip compressed)
  1527. SiphonStream siphonStream = new SiphonStream(stream);
  1528. BinaryFormatter formatter = new BinaryFormatter();
  1529. DeferredFormatter deferred = new DeferredFormatter(true, null);
  1530. SaveProgressRelay relay = new SaveProgressRelay(deferred, callback);
  1531. formatter.Context = new StreamingContext(formatter.Context.State, deferred);
  1532. formatter.Serialize(siphonStream, this);
  1533. deferred.FinishSerialization(siphonStream);
  1534. stream.Flush();
  1535. }
  1536. private class SaveProgressRelay
  1537. {
  1538. private DeferredFormatter formatter;
  1539. private IOEventHandler ioCallback;
  1540. private long lastReportedBytes;
  1541. public SaveProgressRelay(DeferredFormatter formatter, IOEventHandler ioCallback)
  1542. {
  1543. this.formatter = formatter;
  1544. this.ioCallback = ioCallback;
  1545. this.formatter.ReportedBytesChanged += new EventHandler(Formatter_ReportedBytesChanged);
  1546. }
  1547. private void Formatter_ReportedBytesChanged(object sender, EventArgs e)
  1548. {
  1549. long reportedBytes = formatter.ReportedBytes;
  1550. bool raiseEvent;
  1551. long length = 0;
  1552. lock (this)
  1553. {
  1554. raiseEvent = (reportedBytes > lastReportedBytes);
  1555. if (raiseEvent)
  1556. {
  1557. length = reportedBytes - this.lastReportedBytes;
  1558. this.lastReportedBytes = reportedBytes;
  1559. }
  1560. }
  1561. if (raiseEvent && ioCallback != null)
  1562. {
  1563. ioCallback(this, new IOEventArgs(IOOperationType.Write, reportedBytes - length, (int)length));
  1564. }
  1565. }
  1566. }
  1567. private void PrepareHeader()
  1568. {
  1569. XmlDocument xd = this.HeaderXml;
  1570. XmlElement pdnImage = (XmlElement)xd.SelectSingleNode("/pdnImage");
  1571. pdnImage.SetAttribute("width", this.Width.ToString());
  1572. pdnImage.SetAttribute("height", this.Height.ToString());
  1573. pdnImage.SetAttribute("layers", "1");//this.Layers.Count.ToString()
  1574. pdnImage.SetAttribute("savedWithVersion", this.SavedWithVersion.ToString(4));
  1575. }
  1576. ~Document()
  1577. {
  1578. Dispose(false);
  1579. }
  1580. public void Dispose()
  1581. {
  1582. Dispose(true);
  1583. GC.SuppressFinalize(this);
  1584. }
  1585. private bool disposed = false;
  1586. private void Dispose(bool disposing)
  1587. {
  1588. if (!disposed)
  1589. {
  1590. if (disposing)
  1591. {
  1592. /*foreach (Layer layer in layers)
  1593. {
  1594. layer.Dispose();
  1595. }*/
  1596. }
  1597. disposed = true;
  1598. }
  1599. }
  1600. public Document Clone()
  1601. {
  1602. // I cheat.
  1603. MemoryStream stream = new MemoryStream();
  1604. SaveToStream(stream);
  1605. stream.Seek(0, SeekOrigin.Begin);
  1606. return (Document)Document.FromStream(stream);
  1607. }
  1608. object ICloneable.Clone()
  1609. {
  1610. return Clone();
  1611. }
  1612. }
  1613. }