2
0

BitmapManager.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using PixiEditor.Helpers;
  2. using PixiEditor.Models.DataHolders;
  3. using PixiEditor.Models.Events;
  4. using PixiEditor.Models.Layers;
  5. using PixiEditor.Models.Position;
  6. using PixiEditor.Models.Tools;
  7. using PixiEditor.Models.Tools.Tools;
  8. using PixiEditor.ViewModels.SubViewModels.Main;
  9. using SkiaSharp;
  10. using System;
  11. using System.Collections.ObjectModel;
  12. using System.Diagnostics;
  13. using System.Windows;
  14. namespace PixiEditor.Models.Controllers
  15. {
  16. [DebuggerDisplay("{Documents.Count} Document(s)")]
  17. public class BitmapManager : NotifyableObject
  18. {
  19. private ToolSessionController ToolSessionController { get; set; }
  20. public ICanvasInputTarget InputTarget => ToolSessionController;
  21. public BitmapOperationsUtility BitmapOperations { get; set; }
  22. public ObservableCollection<Document> Documents { get; set; } = new ObservableCollection<Document>();
  23. private Document activeDocument;
  24. public Document ActiveDocument
  25. {
  26. get => activeDocument;
  27. set
  28. {
  29. if (activeDocument == value)
  30. return;
  31. activeDocument?.UpdatePreviewImage();
  32. Document oldDoc = activeDocument;
  33. activeDocument = value;
  34. activeDocument?.UpdatePreviewImage();
  35. RaisePropertyChanged(nameof(ActiveDocument));
  36. ActiveWindow = value;
  37. DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
  38. }
  39. }
  40. private object activeWindow;
  41. public object ActiveWindow
  42. {
  43. get => activeWindow;
  44. set
  45. {
  46. if (activeWindow == value)
  47. return;
  48. activeWindow = value;
  49. RaisePropertyChanged(nameof(ActiveWindow));
  50. if (activeWindow is Document doc)
  51. ActiveDocument = doc;
  52. }
  53. }
  54. public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
  55. public event EventHandler StopUsingTool;
  56. public Layer ActiveLayer => ActiveDocument.ActiveLayer;
  57. public SKColor PrimaryColor { get; set; }
  58. private bool hideReferenceLayer;
  59. public bool HideReferenceLayer
  60. {
  61. get => hideReferenceLayer;
  62. set => SetProperty(ref hideReferenceLayer, value);
  63. }
  64. private bool onlyReferenceLayer;
  65. public bool OnlyReferenceLayer
  66. {
  67. get => onlyReferenceLayer;
  68. set => SetProperty(ref onlyReferenceLayer, value);
  69. }
  70. private readonly ToolsViewModel _tools;
  71. private int previewLayerSize;
  72. private int halfSize;
  73. private SKColor _highlightColor;
  74. private PenTool _highlightPen;
  75. private ToolSession activeSession = null;
  76. public BitmapManager(ToolsViewModel tools, UndoViewModel undo)
  77. {
  78. _tools = tools;
  79. ToolSessionController = new ToolSessionController();
  80. ToolSessionController.SessionStarted += OnSessionStart;
  81. ToolSessionController.SessionEnded += OnSessionEnd;
  82. ToolSessionController.PixelMousePositionChanged += OnPixelMousePositionChange;
  83. ToolSessionController.PreciseMousePositionChanged += OnPreciseMousePositionChange;
  84. ToolSessionController.KeyStateChanged += (_, _) => UpdateActionDisplay(_tools.ActiveTool);
  85. BitmapOperations = new BitmapOperationsUtility(this, tools);
  86. undo.UndoRedoCalled += (_, _) => ToolSessionController.ForceStopActiveSessionIfAny();
  87. DocumentChanged += BitmapManager_DocumentChanged;
  88. _highlightPen = new PenTool(this)
  89. {
  90. AutomaticallyResizeCanvas = false
  91. };
  92. _highlightColor = new SKColor(0, 0, 0, 77);
  93. }
  94. public void CloseDocument(Document document)
  95. {
  96. int nextIndex = 0;
  97. if (document == ActiveDocument)
  98. {
  99. nextIndex = Documents.Count > 1 ? Documents.IndexOf(document) : -1;
  100. nextIndex += nextIndex > 0 ? -1 : 0;
  101. }
  102. Documents.Remove(document);
  103. ActiveDocument = nextIndex >= 0 ? Documents[nextIndex] : null;
  104. document.Dispose();
  105. }
  106. public void UpdateActionDisplay(Tool tool)
  107. {
  108. tool?.UpdateActionDisplay(ToolSessionController.IsCtrlDown, ToolSessionController.IsShiftDown, ToolSessionController.IsAltDown);
  109. }
  110. private void OnSessionStart(object sender, ToolSession e)
  111. {
  112. activeSession = e;
  113. ActiveDocument.PreviewLayer.Reset();
  114. ExecuteTool();
  115. }
  116. private void OnSessionEnd(object sender, ToolSession e)
  117. {
  118. activeSession = null;
  119. if (e.Tool is BitmapOperationTool operationTool && operationTool.RequiresPreviewLayer)
  120. {
  121. BitmapOperations.ApplyPreviewLayer();
  122. }
  123. ActiveDocument.PreviewLayer.Reset();
  124. HighlightPixels(ToolSessionController.LastPixelPosition);
  125. StopUsingTool?.Invoke(this, EventArgs.Empty);
  126. }
  127. private void OnPreciseMousePositionChange(object sender, (double, double) e)
  128. {
  129. if (activeSession == null || !activeSession.Tool.RequiresPreciseMouseData)
  130. return;
  131. ExecuteTool();
  132. }
  133. private void OnPixelMousePositionChange(object sender, MouseMovementEventArgs e)
  134. {
  135. if (activeSession != null)
  136. {
  137. if (activeSession.Tool.RequiresPreciseMouseData)
  138. return;
  139. ExecuteTool();
  140. return;
  141. }
  142. else
  143. {
  144. HighlightPixels(e.NewPosition);
  145. }
  146. }
  147. private void ExecuteTool()
  148. {
  149. if (activeSession == null)
  150. throw new Exception("Can't execute tool's Use outside a session");
  151. if (activeSession.Tool is BitmapOperationTool operationTool)
  152. {
  153. BitmapOperations.UseTool(activeSession.MouseMovement, operationTool, PrimaryColor);
  154. }
  155. else if (activeSession.Tool is ReadonlyTool readonlyTool)
  156. {
  157. readonlyTool.Use(activeSession.MouseMovement);
  158. }
  159. else
  160. {
  161. throw new InvalidOperationException($"'{activeSession.Tool.GetType().Name}' is either not a Tool or can't inherit '{nameof(Tool)}' directly.\nChanges the base type to either '{nameof(BitmapOperationTool)}' or '{nameof(ReadonlyTool)}'");
  162. }
  163. }
  164. private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
  165. {
  166. e.NewDocument?.GeneratePreviewLayer();
  167. if (e.OldDocument != e.NewDocument)
  168. ToolSessionController.ForceStopActiveSessionIfAny();
  169. }
  170. public void UpdateHighlightIfNecessary(bool forceHide = false)
  171. {
  172. if (activeSession != null)
  173. return;
  174. HighlightPixels(forceHide ? new(-1, -1) : ToolSessionController.LastPixelPosition);
  175. }
  176. private void HighlightPixels(Coordinates newPosition)
  177. {
  178. if (ActiveDocument == null || ActiveDocument.Layers.Count == 0)
  179. {
  180. return;
  181. }
  182. var previewLayer = ActiveDocument.PreviewLayer;
  183. if (newPosition.X > ActiveDocument.Width
  184. || newPosition.Y > ActiveDocument.Height
  185. || newPosition.X < 0 || newPosition.Y < 0
  186. || _tools.ActiveTool.HideHighlight)
  187. {
  188. previewLayer.Reset();
  189. previewLayerSize = -1;
  190. return;
  191. }
  192. if (_tools.ToolSize != previewLayerSize || previewLayer.IsReset)
  193. {
  194. previewLayerSize = _tools.ToolSize;
  195. halfSize = (int)Math.Floor(_tools.ToolSize / 2f);
  196. previewLayer.CreateNewBitmap(_tools.ToolSize, _tools.ToolSize);
  197. Coordinates cords = new Coordinates(halfSize, halfSize);
  198. previewLayer.Offset = new Thickness(0, 0, 0, 0);
  199. _highlightPen.Draw(previewLayer, cords, cords, _highlightColor, _tools.ToolSize);
  200. }
  201. AdjustOffset(newPosition, previewLayer);
  202. previewLayer.InvokeLayerBitmapChange();
  203. }
  204. private void AdjustOffset(Coordinates newPosition, Layer previewLayer)
  205. {
  206. Coordinates start = newPosition - halfSize;
  207. previewLayer.Offset = new Thickness(start.X, start.Y, 0, 0);
  208. }
  209. }
  210. }