Document.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using PixiEditor.Helpers;
  2. using PixiEditor.Models.Controllers;
  3. using PixiEditor.Models.Enums;
  4. using PixiEditor.Models.Layers;
  5. using PixiEditor.Models.Layers.Utils;
  6. using PixiEditor.Models.Position;
  7. using PixiEditor.Models.Undo;
  8. using PixiEditor.ViewModels;
  9. using SkiaSharp;
  10. using System;
  11. using System.Buffers;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Diagnostics;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Windows;
  18. namespace PixiEditor.Models.DataHolders
  19. {
  20. [DebuggerDisplay("'{Name, nq}' {width}x{height} {Layers.Count} Layer(s)")]
  21. public partial class Document : NotifyableObject, IDisposable
  22. {
  23. private ViewModelMain xamlAccesibleViewModel = null;
  24. public ViewModelMain XamlAccesibleViewModel // Used to access ViewModelMain, without changing DataContext in XAML
  25. {
  26. get => xamlAccesibleViewModel;
  27. set
  28. {
  29. xamlAccesibleViewModel = value;
  30. RaisePropertyChanged(nameof(XamlAccesibleViewModel));
  31. }
  32. }
  33. public string Name
  34. {
  35. get => (string.IsNullOrEmpty(DocumentFilePath) ? "Untitled" : Path.GetFileName(DocumentFilePath))
  36. + (!ChangesSaved ? " *" : string.Empty);
  37. }
  38. private int width = 1;
  39. public int Width
  40. {
  41. get => width;
  42. private set
  43. {
  44. width = value;
  45. RaisePropertyChanged(nameof(Width));
  46. }
  47. }
  48. private int height = 1;
  49. public int Height
  50. {
  51. get => height;
  52. private set
  53. {
  54. height = value;
  55. RaisePropertyChanged("Height");
  56. }
  57. }
  58. private Selection selection;
  59. public Selection ActiveSelection
  60. {
  61. get => selection;
  62. set
  63. {
  64. selection = value;
  65. RaisePropertyChanged(nameof(ActiveSelection));
  66. }
  67. }
  68. private double mouseXonCanvas;
  69. public double MouseXOnCanvas // Mouse X coordinate relative to canvas
  70. {
  71. get => mouseXonCanvas;
  72. set
  73. {
  74. mouseXonCanvas = value;
  75. RaisePropertyChanged(nameof(MouseXOnCanvas));
  76. }
  77. }
  78. private double mouseYonCanvas;
  79. public double MouseYOnCanvas // Mouse Y coordinate relative to canvas
  80. {
  81. get => mouseYonCanvas;
  82. set
  83. {
  84. mouseYonCanvas = value;
  85. RaisePropertyChanged(nameof(MouseYOnCanvas));
  86. }
  87. }
  88. public bool Disposed { get; private set; } = false;
  89. public ExecutionTrigger<Size> CenterViewportTrigger { get; } = new();
  90. public ExecutionTrigger<double> ZoomViewportTrigger { get; } = new();
  91. public UndoManager UndoManager { get; set; }
  92. public ObservableCollection<SKColor> Swatches { get; set; } = new ObservableCollection<SKColor>();
  93. public void RaisePropertyChange(string name)
  94. {
  95. RaisePropertyChanged(name);
  96. }
  97. /// <summary>
  98. /// Resizes canvas, so it fits exactly the size of drawn content, without any transparent pixels outside.
  99. /// </summary>
  100. public void ClipCanvas()
  101. {
  102. DoubleCoords? maybePoints = GetEdgePoints(Layers);
  103. if (maybePoints == null)
  104. {
  105. //all layers are empty
  106. return;
  107. }
  108. DoubleCoords points = maybePoints.Value;
  109. int smallestX = points.Coords1.X;
  110. int smallestY = points.Coords1.Y;
  111. int biggestX = points.Coords2.X;
  112. int biggestY = points.Coords2.Y;
  113. int width = biggestX - smallestX;
  114. int height = biggestY - smallestY;
  115. Coordinates moveVector = new Coordinates(-smallestX, -smallestY);
  116. Thickness[] oldOffsets = Layers.Select(x => x.Offset).ToArray();
  117. int oldWidth = Width;
  118. int oldHeight = Height;
  119. StorageBasedChange change = new StorageBasedChange(this, Layers);
  120. object[] reverseArguments = { oldWidth, oldHeight };
  121. object[] processArguments = { Layers.Select(x => new Thickness(x.OffsetX - smallestX, x.OffsetY - smallestY, 0, 0)).ToArray(), width, height };
  122. ResizeCanvasProcess(processArguments);
  123. UndoManager.AddUndoChange(change.ToChange(
  124. RestoreDocumentLayersProcess,
  125. reverseArguments,
  126. ResizeCanvasProcess,
  127. processArguments,
  128. "Clip canvas"));
  129. }
  130. /// <summary>
  131. /// Centers selected, visible layers inside document.
  132. /// </summary>
  133. public void CenterContent()
  134. {
  135. var layersToCenter = Layers.Where(x => x.IsActive && LayerStructureUtils.GetFinalLayerIsVisible(x, LayerStructure)).ToList();
  136. if (layersToCenter.Count == 0)
  137. {
  138. return;
  139. }
  140. List<Int32Rect> oldBounds = layersToCenter.Select(x => x.Bounds).ToList();
  141. DoubleCoords? maybePoints = ClipLayersAndGetEdgePoints(layersToCenter);
  142. if (maybePoints == null)
  143. return;
  144. DoubleCoords points = maybePoints.Value;
  145. int smallestX = points.Coords1.X;
  146. int smallestY = points.Coords1.Y;
  147. int biggestX = points.Coords2.X;
  148. int biggestY = points.Coords2.Y;
  149. Coordinates contentCenter = CoordinatesCalculator.GetCenterPoint(points.Coords1, points.Coords2);
  150. Coordinates documentCenter = CoordinatesCalculator.GetCenterPoint(
  151. new Coordinates(0, 0),
  152. new Coordinates(Width, Height));
  153. Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
  154. List<Int32Rect> emptyBounds = Enumerable.Repeat(Int32Rect.Empty, layersToCenter.Count).ToList();
  155. MoveOffsets(layersToCenter, emptyBounds, moveVector);
  156. List<Guid> guids = layersToCenter.Select(x => x.GuidValue).ToList();
  157. UndoManager.AddUndoChange(
  158. new Change(
  159. MoveOffsetsProcess,
  160. new object[] { guids, oldBounds, new Coordinates(-moveVector.X, -moveVector.Y) },
  161. MoveOffsetsProcess,
  162. new object[] { guids, emptyBounds, moveVector },
  163. "Center content"));
  164. }
  165. public void Dispose()
  166. {
  167. if (Disposed)
  168. return;
  169. Disposed = true;
  170. DisposeLayerBitmaps();
  171. UndoManager.Dispose();
  172. GC.SuppressFinalize(this);
  173. }
  174. private void SetAsActiveOnClick(object obj)
  175. {
  176. if (XamlAccesibleViewModel?.BitmapManager?.ActiveDocument != this)
  177. {
  178. XamlAccesibleViewModel.BitmapManager.ActiveDocument = this;
  179. }
  180. }
  181. private void RequestCloseDocument(object parameter)
  182. {
  183. ViewModelMain.Current.DocumentSubViewModel.RequestCloseDocument(this);
  184. }
  185. private int GetOffsetXForAnchor(int srcWidth, int destWidth, AnchorPoint anchor)
  186. {
  187. if (anchor.HasFlag(AnchorPoint.Center))
  188. {
  189. return (destWidth / 2) - (srcWidth / 2);
  190. }
  191. if (anchor.HasFlag(AnchorPoint.Right))
  192. {
  193. return destWidth - srcWidth;
  194. }
  195. return 0;
  196. }
  197. private int GetOffsetYForAnchor(int srcHeight, int destHeight, AnchorPoint anchor)
  198. {
  199. if (anchor.HasFlag(AnchorPoint.Middle))
  200. {
  201. return (destHeight / 2) - (srcHeight / 2);
  202. }
  203. if (anchor.HasFlag(AnchorPoint.Bottom))
  204. {
  205. return destHeight - srcHeight;
  206. }
  207. return 0;
  208. }
  209. private DoubleCoords? GetEdgePoints(IEnumerable<Layer> layers)
  210. {
  211. if (Layers.Count == 0)
  212. throw new ArgumentException("Not enough layers");
  213. int smallestX = int.MaxValue;
  214. int smallestY = int.MaxValue;
  215. int biggestX = int.MinValue;
  216. int biggestY = int.MinValue;
  217. bool allLayersSkipped = true;
  218. foreach (Layer layer in layers)
  219. {
  220. Int32Rect bounds = layer.TightBounds;
  221. if (layer.IsReset || !bounds.HasArea)
  222. continue;
  223. allLayersSkipped = false;
  224. if (layer.OffsetX + bounds.X < smallestX)
  225. smallestX = layer.OffsetX + bounds.X;
  226. if (layer.OffsetX + bounds.X + bounds.Width > biggestX)
  227. biggestX = layer.OffsetX + bounds.X + bounds.Width;
  228. if (layer.OffsetY + bounds.Y < smallestY)
  229. smallestY = layer.OffsetY + bounds.Y;
  230. if (layer.OffsetY + bounds.Y + bounds.Height > biggestY)
  231. biggestY = layer.OffsetY + bounds.Y + bounds.Height;
  232. }
  233. if (allLayersSkipped)
  234. return null;
  235. return new DoubleCoords(
  236. new Coordinates(smallestX, smallestY),
  237. new Coordinates(biggestX, biggestY));
  238. }
  239. private DoubleCoords? ClipLayersAndGetEdgePoints(IEnumerable<Layer> layers)
  240. {
  241. if (Layers.Count == 0)
  242. {
  243. throw new ArgumentException("Not enough layers");
  244. }
  245. int smallestX = int.MaxValue;
  246. int smallestY = int.MaxValue;
  247. int biggestX = int.MinValue;
  248. int biggestY = int.MinValue;
  249. bool allLayersSkipped = true;
  250. foreach (Layer layer in layers)
  251. {
  252. layer.ClipCanvas();
  253. if (layer.IsReset)
  254. continue;
  255. allLayersSkipped = false;
  256. if (layer.OffsetX < smallestX)
  257. {
  258. smallestX = layer.OffsetX;
  259. }
  260. if (layer.OffsetX + layer.Width > biggestX)
  261. {
  262. biggestX = layer.OffsetX + layer.Width;
  263. }
  264. if (layer.OffsetY < smallestY)
  265. {
  266. smallestY = layer.OffsetY;
  267. }
  268. if (layer.OffsetY + layer.Height > biggestY)
  269. {
  270. biggestY = layer.OffsetY + layer.Height;
  271. }
  272. }
  273. if (allLayersSkipped)
  274. return null;
  275. return new DoubleCoords(
  276. new Coordinates(smallestX, smallestY),
  277. new Coordinates(biggestX, biggestY));
  278. }
  279. }
  280. }