BitmapManager.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. activeDocument?.UpdatePreviewImage();
  30. Document oldDoc = activeDocument;
  31. activeDocument = value;
  32. RaisePropertyChanged(nameof(ActiveDocument));
  33. DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
  34. }
  35. }
  36. public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
  37. public event EventHandler StopUsingTool;
  38. public Layer ActiveLayer => ActiveDocument.ActiveLayer;
  39. public SKColor PrimaryColor { get; set; }
  40. private bool hideReferenceLayer;
  41. public bool HideReferenceLayer
  42. {
  43. get => hideReferenceLayer;
  44. set => SetProperty(ref hideReferenceLayer, value);
  45. }
  46. private bool onlyReferenceLayer;
  47. public bool OnlyReferenceLayer
  48. {
  49. get => onlyReferenceLayer;
  50. set => SetProperty(ref onlyReferenceLayer, value);
  51. }
  52. private readonly ToolsViewModel _tools;
  53. private int previewLayerSize;
  54. private int halfSize;
  55. private SKColor _highlightColor;
  56. private PenTool _highlightPen;
  57. private ToolSession activeSession = null;
  58. public BitmapManager(ToolsViewModel tools)
  59. {
  60. _tools = tools;
  61. ToolSessionController = new ToolSessionController();
  62. ToolSessionController.SessionStarted += OnSessionStart;
  63. ToolSessionController.SessionEnded += OnSessionEnd;
  64. ToolSessionController.PixelMousePositionChanged += OnPixelMousePositionChange;
  65. ToolSessionController.PreciseMousePositionChanged += OnPreciseMousePositionChange;
  66. ToolSessionController.KeyStateChanged += (_, _) => UpdateActionDisplay(_tools.ActiveTool);
  67. BitmapOperations = new BitmapOperationsUtility(this, tools);
  68. DocumentChanged += BitmapManager_DocumentChanged;
  69. _highlightPen = new PenTool(this)
  70. {
  71. AutomaticallyResizeCanvas = false
  72. };
  73. _highlightColor = new SKColor(0, 0, 0, 77);
  74. }
  75. public void CloseDocument(Document document)
  76. {
  77. int nextIndex = 0;
  78. if (document == ActiveDocument)
  79. {
  80. nextIndex = Documents.Count > 1 ? Documents.IndexOf(document) : -1;
  81. nextIndex += nextIndex > 0 ? -1 : 0;
  82. }
  83. Documents.Remove(document);
  84. ActiveDocument = nextIndex >= 0 ? Documents[nextIndex] : null;
  85. document.Dispose();
  86. }
  87. public void UpdateActionDisplay(Tool tool)
  88. {
  89. tool?.UpdateActionDisplay(ToolSessionController.IsCtrlDown, ToolSessionController.IsShiftDown, ToolSessionController.IsAltDown);
  90. }
  91. private void OnSessionStart(object sender, ToolSession e)
  92. {
  93. activeSession = e;
  94. ActiveDocument.PreviewLayer.Reset();
  95. ExecuteTool();
  96. }
  97. private void OnSessionEnd(object sender, ToolSession e)
  98. {
  99. activeSession = null;
  100. if (e.Tool is BitmapOperationTool operationTool && operationTool.RequiresPreviewLayer)
  101. {
  102. BitmapOperations.ApplyPreviewLayer();
  103. }
  104. ActiveDocument.PreviewLayer.Reset();
  105. HighlightPixels(MousePositionConverter.CurrentCoordinates);
  106. StopUsingTool?.Invoke(this, EventArgs.Empty);
  107. }
  108. private void OnPreciseMousePositionChange(object sender, (double, double) e)
  109. {
  110. if (activeSession == null || !activeSession.Tool.RequiresPreciseMouseData)
  111. return;
  112. ExecuteTool();
  113. }
  114. private void OnPixelMousePositionChange(object sender, MouseMovementEventArgs e)
  115. {
  116. if (activeSession != null)
  117. {
  118. if (activeSession.Tool.RequiresPreciseMouseData)
  119. return;
  120. ExecuteTool();
  121. return;
  122. }
  123. else
  124. {
  125. HighlightPixels(e.NewPosition);
  126. }
  127. }
  128. private void ExecuteTool()
  129. {
  130. if (activeSession == null)
  131. throw new Exception("Can't execute tool's Use outside a session");
  132. if (activeSession.Tool is BitmapOperationTool operationTool)
  133. {
  134. BitmapOperations.UseTool(activeSession.MouseMovement, operationTool, PrimaryColor);
  135. }
  136. else if (activeSession.Tool is ReadonlyTool readonlyTool)
  137. {
  138. readonlyTool.Use(activeSession.MouseMovement);
  139. }
  140. else
  141. {
  142. 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)}'");
  143. }
  144. }
  145. private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
  146. {
  147. e.NewDocument?.GeneratePreviewLayer();
  148. if (e.OldDocument != e.NewDocument)
  149. ToolSessionController.ForceStopActiveSessionIfAny();
  150. }
  151. public void UpdateHighlightIfNecessary(bool forceHide = false)
  152. {
  153. if (activeSession != null)
  154. return;
  155. HighlightPixels(forceHide ? new(-1, -1) : ToolSessionController.LastPixelPosition);
  156. }
  157. private void HighlightPixels(Coordinates newPosition)
  158. {
  159. if (ActiveDocument == null || ActiveDocument.Layers.Count == 0)
  160. {
  161. return;
  162. }
  163. var previewLayer = ActiveDocument.PreviewLayer;
  164. if (newPosition.X > ActiveDocument.Width
  165. || newPosition.Y > ActiveDocument.Height
  166. || newPosition.X < 0 || newPosition.Y < 0
  167. || _tools.ActiveTool.HideHighlight)
  168. {
  169. previewLayer.Reset();
  170. previewLayerSize = -1;
  171. return;
  172. }
  173. if (_tools.ToolSize != previewLayerSize || previewLayer.IsReset)
  174. {
  175. previewLayerSize = _tools.ToolSize;
  176. halfSize = (int)Math.Floor(_tools.ToolSize / 2f);
  177. previewLayer.CreateNewBitmap(_tools.ToolSize, _tools.ToolSize);
  178. Coordinates cords = new Coordinates(halfSize, halfSize);
  179. previewLayer.Offset = new Thickness(0, 0, 0, 0);
  180. _highlightPen.Draw(previewLayer, cords, cords, _highlightColor, _tools.ToolSize);
  181. }
  182. AdjustOffset(newPosition, previewLayer);
  183. previewLayer.InvokeLayerBitmapChange();
  184. }
  185. private void AdjustOffset(Coordinates newPosition, Layer previewLayer)
  186. {
  187. Coordinates start = newPosition - halfSize;
  188. previewLayer.Offset = new Thickness(start.X, start.Y, 0, 0);
  189. }
  190. }
  191. }