PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Data/Layer.cs

https://bitbucket.org/tuldok89/openpdn
C# | 629 lines | 414 code | 84 blank | 131 comment | 46 complexity | cea54dcc2f2f4b546cfcb0d5e85d120d MD5 | raw 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;
  10. using System.Collections.Generic;
  11. using System.Collections.Specialized;
  12. using System.Drawing;
  13. using System.Runtime.Serialization;
  14. using System.Threading;
  15. using System.Windows.Forms;
  16. namespace PaintDotNet
  17. {
  18. /// <summary>
  19. /// A layer's properties are immutable. That is, you can modify the surface
  20. /// of a layer all you want, but to change its dimensions requires creating
  21. /// a new layer.
  22. /// </summary>
  23. [Serializable]
  24. public abstract class Layer
  25. : ICloneable,
  26. IDisposable,
  27. IThumbnailProvider
  28. {
  29. private readonly int _width;
  30. private readonly int _height;
  31. /// <summary>
  32. /// The background layer is generally opaque although it doesn't *have* to be. For
  33. /// example, the Canvas Size action distinguishes between background and non-background
  34. /// layers such that it fills the background layer with opaque and the non-background
  35. /// layers with transparency.
  36. /// The value of this property should not be used to disallow the user from performing
  37. /// an action.
  38. /// </summary>
  39. public bool IsBackground
  40. {
  41. get
  42. {
  43. return _properties.isBackground;
  44. }
  45. set
  46. {
  47. bool oldValue = _properties.isBackground;
  48. if (oldValue == value) return;
  49. OnPropertyChanging(LayerProperties.IsBackgroundName);
  50. _properties.isBackground = value;
  51. OnPropertyChanged(LayerProperties.IsBackgroundName);
  52. }
  53. }
  54. /// <summary>
  55. /// If this value is non-0, then the PropertyChanged event will be
  56. /// suppressed. This is in place so that the Layer Properties dialog
  57. /// can tweak the properties without them filling up the undo stack.
  58. /// </summary>
  59. [NonSerialized]
  60. private int _suppressPropertyChanges;
  61. /// <summary>
  62. /// Encapsulates the mutable properties of the Layer class.
  63. /// </summary>
  64. [Serializable]
  65. internal sealed class LayerProperties
  66. : ICloneable,
  67. ISerializable
  68. {
  69. public string name;
  70. public NameValueCollection UserMetaData;
  71. public bool visible;
  72. public bool isBackground;
  73. public byte opacity;
  74. private const string NameTag = "name";
  75. private const string UserMetaDataTag = "userMetaData";
  76. private const string VisibleTag = "visible";
  77. private const string IsBackgroundTag = "isBackground";
  78. private const string OpacityTag = "opacity";
  79. public static string IsBackgroundName
  80. {
  81. get
  82. {
  83. return PdnResources.GetString("Layer.Properties.IsBackground.Name");
  84. }
  85. }
  86. public static string NameName
  87. {
  88. get
  89. {
  90. return PdnResources.GetString("Layer.Properties.Name.Name");
  91. }
  92. }
  93. public static string VisibleName
  94. {
  95. get
  96. {
  97. return PdnResources.GetString("Layer.Properties.Visible.Name");
  98. }
  99. }
  100. public static string OpacityName
  101. {
  102. get
  103. {
  104. return PdnResources.GetString("Layer.Properties.Opacity.Name");
  105. }
  106. }
  107. public LayerProperties(string name, NameValueCollection userMetaData, bool visible, bool isBackground, byte opacity)
  108. {
  109. this.name = name;
  110. UserMetaData = new NameValueCollection(userMetaData);
  111. this.visible = visible;
  112. this.isBackground = isBackground;
  113. this.opacity = opacity;
  114. }
  115. public LayerProperties(LayerProperties copyMe)
  116. {
  117. name = copyMe.name;
  118. UserMetaData = new NameValueCollection(copyMe.UserMetaData);
  119. visible = copyMe.visible;
  120. isBackground = copyMe.isBackground;
  121. opacity = copyMe.opacity;
  122. }
  123. public object Clone()
  124. {
  125. return new LayerProperties(this);
  126. }
  127. public LayerProperties(SerializationInfo info, StreamingContext context)
  128. {
  129. name = info.GetString(NameTag);
  130. UserMetaData = (NameValueCollection)info.GetValue(UserMetaDataTag, typeof(NameValueCollection));
  131. visible = info.GetBoolean(VisibleTag);
  132. isBackground = info.GetBoolean(IsBackgroundTag);
  133. // This property was added with v2.1. So as to allow loading old .PDN files,
  134. // this is an optional item.
  135. // (Historical note: this property was actually moved from the BitmapLayer
  136. // properties to the base class because it was found to be a rather important
  137. // property for rendering regardless of layer "type")
  138. try
  139. {
  140. opacity = info.GetByte(OpacityTag);
  141. }
  142. catch (SerializationException)
  143. {
  144. opacity = 255;
  145. }
  146. }
  147. public void GetObjectData(SerializationInfo info, StreamingContext context)
  148. {
  149. info.AddValue(NameTag, name);
  150. info.AddValue(UserMetaDataTag, UserMetaData);
  151. info.AddValue(VisibleTag, visible);
  152. info.AddValue(IsBackgroundTag, isBackground);
  153. info.AddValue(OpacityTag, opacity);
  154. }
  155. }
  156. private LayerProperties _properties;
  157. public byte Opacity
  158. {
  159. get
  160. {
  161. if (disposed)
  162. {
  163. throw new ObjectDisposedException("Layer");
  164. }
  165. return _properties.opacity;
  166. }
  167. set
  168. {
  169. if (disposed)
  170. {
  171. throw new ObjectDisposedException("Layer");
  172. }
  173. if (_properties.opacity == value) return;
  174. OnPropertyChanging(LayerProperties.OpacityName);
  175. _properties.opacity = value;
  176. OnPropertyChanged(LayerProperties.OpacityName);
  177. Invalidate();
  178. }
  179. }
  180. /// <summary>
  181. /// Allows you to save the mutable properties of the layer so you can restore them later
  182. /// (esp. important for undo!). Mutable properties include the layer's name, whether it's
  183. /// visible, and the metadata. This list might expand later.
  184. /// </summary>
  185. /// <returns>
  186. /// An object that can be used later in a call to LoadProperties.
  187. /// </returns>
  188. /// <remarks>
  189. /// It is important that derived classes call this in the correct fashion so as to 'chain'
  190. /// the properties list together. The following is the correct pattern:
  191. ///
  192. /// public override object SaveProperties()
  193. /// {
  194. /// object baseProperties = base.SaveProperties();
  195. /// return new List(properties.Clone(), new List(baseProperties, null));
  196. /// }
  197. /// </remarks>
  198. public virtual object SaveProperties()
  199. {
  200. return _properties.Clone();
  201. }
  202. public void LoadProperties(object oldState)
  203. {
  204. LoadProperties(oldState, false);
  205. }
  206. public virtual void LoadProperties(object oldState, bool suppressEvents)
  207. {
  208. var lp = (LayerProperties)oldState;
  209. var changed = new List<String>();
  210. if (!suppressEvents)
  211. {
  212. if (lp.name != _properties.name)
  213. {
  214. changed.Add(LayerProperties.NameName);
  215. }
  216. if (lp.isBackground != _properties.isBackground)
  217. {
  218. changed.Add(LayerProperties.IsBackgroundName);
  219. }
  220. if (lp.visible != _properties.visible)
  221. {
  222. changed.Add(LayerProperties.VisibleName);
  223. }
  224. if (lp.opacity != _properties.opacity)
  225. {
  226. changed.Add(LayerProperties.OpacityName);
  227. }
  228. }
  229. foreach (string propertyName in changed)
  230. {
  231. OnPropertyChanging(propertyName);
  232. }
  233. _properties = (LayerProperties)((LayerProperties)oldState).Clone();
  234. Invalidate();
  235. foreach (string propertyName in changed)
  236. {
  237. OnPropertyChanged(propertyName);
  238. }
  239. }
  240. public int Width
  241. {
  242. get
  243. {
  244. return _width;
  245. }
  246. }
  247. public int Height
  248. {
  249. get
  250. {
  251. return _height;
  252. }
  253. }
  254. public Size Size
  255. {
  256. get
  257. {
  258. return new Size(Width, Height);
  259. }
  260. }
  261. public Rectangle Bounds
  262. {
  263. get
  264. {
  265. return new Rectangle(new Point(0, 0), Size);
  266. }
  267. }
  268. public void PushSuppressPropertyChanged()
  269. {
  270. Interlocked.Increment(ref _suppressPropertyChanges);
  271. }
  272. public void PopSuppressPropertyChanged()
  273. {
  274. if (0 > Interlocked.Decrement(ref _suppressPropertyChanges))
  275. {
  276. throw new InvalidProgramException("suppressPreviewChanged is less than zero");
  277. }
  278. }
  279. [field: NonSerialized]
  280. public event EventHandler PreviewChanged;
  281. protected virtual void OnPreviewChanged()
  282. {
  283. if (PreviewChanged != null)
  284. {
  285. PreviewChanged(this, EventArgs.Empty);
  286. }
  287. }
  288. /// <summary>
  289. /// This event is raised before a property is changed. Note that the name given
  290. /// in the PropertyEventArgs is for descriptive (UI) purposes only and serves no
  291. /// programmatic purpose. When this event is raised you should not make any
  292. /// assumptions about which property was changed based on this description.
  293. /// </summary>
  294. [field: NonSerialized]
  295. public event PropertyEventHandler PropertyChanging;
  296. protected virtual void OnPropertyChanging(string propertyName)
  297. {
  298. if (_suppressPropertyChanges != 0) return;
  299. if (PropertyChanging != null)
  300. {
  301. PropertyChanging(this, new PropertyEventArgs(propertyName));
  302. }
  303. }
  304. /// <summary>
  305. /// This event is raised after a property is changed. Note that the name given
  306. /// in the PropertyEventArgs is for descriptive (UI) purposes only and serves no
  307. /// programmatic purpose. When this event is raised you should not make any
  308. /// assumptions about which property was changed based on this description.
  309. /// </summary>
  310. [field: NonSerialized]
  311. public event PropertyEventHandler PropertyChanged;
  312. protected virtual void OnPropertyChanged(string propertyName)
  313. {
  314. if (_suppressPropertyChanges != 0) return;
  315. if (PropertyChanged != null)
  316. {
  317. PropertyChanged(this, new PropertyEventArgs(propertyName));
  318. }
  319. }
  320. /// <summary>
  321. /// You can call this to raise the PropertyChanged event. Note that is will
  322. /// raise the event with an empty string for the property name description.
  323. /// Thus it is useful only for syncing up UI elements that require notification
  324. /// of events but that otherwise don't really track it.
  325. /// </summary>
  326. public void PerformPropertyChanged()
  327. {
  328. OnPropertyChanged(string.Empty);
  329. }
  330. /// <summary>
  331. /// A user-definable name.
  332. /// </summary>
  333. public string Name
  334. {
  335. get
  336. {
  337. return _properties.name;
  338. }
  339. set
  340. {
  341. if (_properties.name == value) return;
  342. OnPropertyChanging(LayerProperties.NameName);
  343. _properties.name = value;
  344. OnPropertyChanged(LayerProperties.NameName);
  345. }
  346. }
  347. [NonSerialized]
  348. private Metadata _metadata;
  349. public Metadata Metadata
  350. {
  351. get { return _metadata ?? (_metadata = new Metadata(_properties.UserMetaData)); }
  352. }
  353. /// <summary>
  354. /// Determines whether the layer is part of a document's composition. If this
  355. /// property is false, the composition engine will ignore this layer.
  356. /// </summary>
  357. public bool Visible
  358. {
  359. get
  360. {
  361. return _properties.visible;
  362. }
  363. set
  364. {
  365. bool oldValue = _properties.visible;
  366. if (oldValue == value) return;
  367. OnPropertyChanging(LayerProperties.VisibleName);
  368. _properties.visible = value;
  369. OnPropertyChanged(LayerProperties.VisibleName);
  370. Invalidate();
  371. }
  372. }
  373. /// <summary>
  374. /// Determines whether a rectangle is fully in bounds or not. This is determined by checking
  375. /// to make sure the left, top, right, and bottom edges are within bounds.
  376. /// </summary>
  377. /// <param name="roi"></param>
  378. /// <returns></returns>
  379. private bool IsInBounds(Rectangle roi)
  380. {
  381. return ((((roi.Left >= 0 && roi.Top >= 0) && roi.Left < Width) && roi.Top < Height) && roi.Right <= Width) && roi.Bottom <= Height;
  382. }
  383. /// <summary>
  384. /// Implements IThumbnailProvider.RenderThumbnail().
  385. /// </summary>
  386. public abstract Surface RenderThumbnail(int maxEdgeLength);
  387. /// <summary>
  388. /// Causes the layer to render a given rectangle of interest (roi) to the given destination surface.
  389. /// </summary>
  390. /// <param name="args">Contains information about which objects to use for rendering</param>
  391. /// <param name="roi">The rectangular region to be rendered.</param>
  392. public void Render(RenderArgs args, Rectangle roi)
  393. {
  394. // the bitmap we're rendering to must match the size of the layer we're rendering from
  395. if (args.Surface.Width != Width || args.Surface.Height != Height)
  396. {
  397. throw new ArgumentException();
  398. }
  399. // the region of interest can not be out of bounds!
  400. if (!IsInBounds(roi))
  401. {
  402. throw new ArgumentOutOfRangeException("roi");
  403. }
  404. RenderImpl(args, roi);
  405. }
  406. /// <summary>
  407. /// Causes the layer to render a given region of interest (roi) to the given destination surface.
  408. /// </summary>
  409. /// <param name="args">Contains information about which objects to use for rendering</param>
  410. /// <param name="roi">The region to be rendered.</param>
  411. public void Render(RenderArgs args, PdnRegion roi)
  412. {
  413. Rectangle roiBounds = roi.GetBoundsInt();
  414. if (!IsInBounds(roiBounds))
  415. {
  416. throw new ArgumentOutOfRangeException("roi");
  417. }
  418. Rectangle[] rects = roi.GetRegionScansReadOnlyInt();
  419. RenderImpl(args, rects);
  420. }
  421. public void RenderUnchecked(RenderArgs args, Rectangle[] roi, int startIndex, int length)
  422. {
  423. RenderImpl(args, roi, startIndex, length);
  424. }
  425. /// <summary>
  426. /// Override this method to provide your layer's rendering capabilities.
  427. /// </summary>
  428. /// <param name="args">Contains information about which objects to use for rendering</param>
  429. /// <param name="roi">The rectangular region to be rendered.</param>
  430. protected abstract void RenderImpl(RenderArgs args, Rectangle roi);
  431. protected void RenderImpl(RenderArgs args, Rectangle[] roi)
  432. {
  433. RenderImpl(args, roi, 0, roi.Length);
  434. }
  435. protected virtual void RenderImpl(RenderArgs args, Rectangle[] roi, int startIndex, int length)
  436. {
  437. for (int i = startIndex; i < startIndex + length; ++i)
  438. {
  439. RenderImpl(args, roi[i]);
  440. }
  441. }
  442. [field: NonSerialized]
  443. public event InvalidateEventHandler Invalidated;
  444. protected virtual void OnInvalidated(InvalidateEventArgs e)
  445. {
  446. if (Invalidated != null)
  447. {
  448. Invalidated(this, e);
  449. }
  450. }
  451. /// <summary>
  452. /// Causes the entire layer surface to be invalidated.
  453. /// </summary>
  454. public void Invalidate()
  455. {
  456. var rect = new Rectangle(0, 0, Width, Height);
  457. OnInvalidated(new InvalidateEventArgs(rect));
  458. }
  459. /// <summary>
  460. /// Causes a portion of the layer surface to be invalidated.
  461. /// </summary>
  462. /// <param name="roi">The region of interest to be invalidated.</param>
  463. public void Invalidate(PdnRegion roi)
  464. {
  465. foreach (Rectangle rect in roi.GetRegionScansReadOnlyInt())
  466. {
  467. Invalidate(rect);
  468. }
  469. }
  470. /// <summary>
  471. /// Causes a portion of the layer surface to be invalidated.
  472. /// </summary>
  473. /// <param name="roi">The region of interest to be invalidated.</param>
  474. public void Invalidate(RectangleF[] roi)
  475. {
  476. foreach (RectangleF rectF in roi)
  477. {
  478. Invalidate(Rectangle.Truncate(rectF));
  479. }
  480. }
  481. /// <summary>
  482. /// Causes a portion of the layer surface to be invalidated.
  483. /// </summary>
  484. /// <param name="roi">The rectangle of interest to be invalidated.</param>
  485. public void Invalidate(Rectangle roi)
  486. {
  487. Rectangle rect = Rectangle.Intersect(roi, Bounds);
  488. // TODO: this is horrible for performance w.r.t. complex invalidation regions. Lots of heap pollution.
  489. // fix that!
  490. OnInvalidated(new InvalidateEventArgs(rect));
  491. }
  492. protected Layer(int width, int height)
  493. {
  494. _width = width;
  495. _height = height;
  496. _properties = new LayerProperties(null, new NameValueCollection(), true, false, 255);
  497. }
  498. protected Layer(Layer copyMe)
  499. {
  500. _width = copyMe._width;
  501. _height = copyMe._height;
  502. _properties = (LayerProperties)copyMe._properties.Clone();
  503. }
  504. // TODO: add "name" parameter, keep this for legacy and fill it in with "Background"
  505. // goal is to put complete burden of loc on the client
  506. public static BitmapLayer CreateBackgroundLayer(int width, int height)
  507. {
  508. // set colors to 0xffffffff
  509. // note: we use alpha of 255 here so that "invert colors" works as expected
  510. // that is, for just 1 layer we invert the initial white->black
  511. // but on subsequent layers we invert transparent white -> transparent black, which shows up as white for the most part
  512. var layer = new BitmapLayer(width, height, ColorBgra.White)
  513. {
  514. Name = PdnResources.GetString("Layer.Background.Name"),
  515. _properties = {isBackground = true}
  516. };
  517. // tag it as a background layer
  518. return layer;
  519. }
  520. /// <summary>
  521. /// This allows a layer to provide a dialog for configuring
  522. /// the layer's properties.
  523. /// </summary>
  524. public abstract PdnBaseForm CreateConfigDialog();
  525. public abstract object Clone();
  526. ~Layer()
  527. {
  528. Dispose(false);
  529. }
  530. public void Dispose()
  531. {
  532. Dispose(true);
  533. GC.SuppressFinalize(this);
  534. }
  535. private bool disposed = false;
  536. protected virtual void Dispose(bool disposing)
  537. {
  538. if (disposed) return;
  539. disposed = true;
  540. if (disposing)
  541. {
  542. }
  543. }
  544. }
  545. }