BitmapOperationsUtility.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. using PixiEditor.Models.DataHolders;
  2. using PixiEditor.Models.Layers;
  3. using PixiEditor.Models.Position;
  4. using PixiEditor.Models.Tools;
  5. using PixiEditor.Models.Tools.ToolSettings.Settings;
  6. using PixiEditor.ViewModels.SubViewModels.Main;
  7. using SkiaSharp;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Windows;
  11. using System.Windows.Input;
  12. namespace PixiEditor.Models.Controllers
  13. {
  14. public class BitmapOperationsUtility
  15. {
  16. private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
  17. private SizeSetting sizeSetting;
  18. public BitmapOperationsUtility(BitmapManager manager, ToolsViewModel tools)
  19. {
  20. Manager = manager;
  21. Tools = tools;
  22. }
  23. public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
  24. public BitmapManager Manager { get; set; }
  25. public ToolsViewModel Tools { get; set; }
  26. public void DeletePixels(Layer[] layers, Coordinates[] pixels)
  27. {
  28. if (Manager.ActiveDocument == null)
  29. {
  30. return;
  31. }
  32. // TODO: Fix
  33. //BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
  34. //Dictionary<Guid, SKColor[]> oldValues = BitmapUtils.GetPixelsForSelection(layers, pixels);
  35. //LayerChange[] old = new LayerChange[layers.Length];
  36. //LayerChange[] newChange = new LayerChange[layers.Length];
  37. //for (int i = 0; i < layers.Length; i++)
  38. //{
  39. // Guid guid = layers[i].LayerGuid;
  40. // old[i] = new LayerChange(
  41. // BitmapPixelChanges.FromArrays(pixels, oldValues[layers[i].LayerGuid]), guid);
  42. // newChange[i] = new LayerChange(changes, guid);
  43. // layers[i].SetPixels(changes);
  44. //}
  45. //Manager.ActiveDocument.UndoManager.AddUndoChange(new Change("UndoChanges", old, newChange, "Deleted pixels"));
  46. }
  47. /// <summary>
  48. /// Executes tool Use() method with given parameters. NOTE: [0] is a start point, [^1] is latest.
  49. /// </summary>
  50. /// <param name="newPos">Most recent coordinates.</param>
  51. /// <param name="mouseMove">Last mouse movement coordinates.</param>
  52. /// <param name="tool">Tool to execute.</param>
  53. public void ExecuteTool(Coordinates newPos, List<Coordinates> mouseMove, BitmapOperationTool tool)
  54. {
  55. if (Manager.ActiveDocument != null && tool != null)
  56. {
  57. if (Manager.ActiveDocument.Layers.Count == 0 || mouseMove.Count == 0)
  58. {
  59. return;
  60. }
  61. UseTool(mouseMove, tool, Manager.PrimaryColor);
  62. }
  63. }
  64. /// <summary>
  65. /// Applies pixels from preview layer to selected layer.
  66. /// </summary>
  67. public void ApplyPreviewLayer()
  68. {
  69. var previewLayer = Manager.ActiveDocument.PreviewLayer;
  70. var activeLayer = Manager.ActiveLayer;
  71. activeLayer.DynamicResizeAbsolute(previewLayer.OffsetX + previewLayer.Width, previewLayer.OffsetY + previewLayer.Height, previewLayer.OffsetX, previewLayer.OffsetY);
  72. previewLayer.LayerBitmap.SkiaSurface.Draw(
  73. activeLayer.LayerBitmap.SkiaSurface.Canvas,
  74. previewLayer.OffsetX - activeLayer.OffsetX,
  75. previewLayer.OffsetY - activeLayer.OffsetY,
  76. BlendingPaint
  77. );
  78. Manager.ActiveLayer.InvokeLayerBitmapChange(new Int32Rect(previewLayer.OffsetX, previewLayer.OffsetY, previewLayer.Width, previewLayer.Height));
  79. // Don't forget about firing BitmapChanged
  80. BitmapChanged?.Invoke(this, null);
  81. Manager.ActiveDocument.PreviewLayer.Reset();
  82. }
  83. private void UseTool(List<Coordinates> mouseMoveCords, BitmapOperationTool tool, SKColor color)
  84. {
  85. if (sizeSetting == null)
  86. {
  87. sizeSetting = tool.Toolbar.GetSetting<SizeSetting>("ToolSize");
  88. }
  89. int thickness = sizeSetting != null ? sizeSetting.Value : 1;
  90. bool shiftDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
  91. if (shiftDown && tool.UsesShift)
  92. {
  93. bool mouseInLine = DoCoordsFormLine(mouseMoveCords, thickness);
  94. if (!mouseInLine)
  95. {
  96. mouseMoveCords = GetSquareCoordiantes(mouseMoveCords);
  97. }
  98. else
  99. {
  100. mouseMoveCords = GetLineCoordinates(mouseMoveCords, thickness);
  101. }
  102. }
  103. if (!tool.RequiresPreviewLayer)
  104. {
  105. if (!Manager.ActiveDocument.PreviewLayer.IsReset)
  106. Manager.ActiveDocument.PreviewLayer.Reset();
  107. tool.Use(Manager.ActiveLayer, mouseMoveCords, color);
  108. BitmapChanged?.Invoke(this, null);
  109. }
  110. else
  111. {
  112. UseToolOnPreviewLayer(mouseMoveCords, tool.ClearPreviewLayerOnEachIteration);
  113. }
  114. }
  115. private bool DoCoordsFormLine(List<Coordinates> coords, int thickness)
  116. {
  117. var p1 = coords[0];
  118. var p2 = coords[^1];
  119. //find delta and mirror to first quadrant
  120. float dX = Math.Abs(p2.X - p1.X);
  121. float dY = Math.Abs(p2.Y - p1.Y);
  122. //normalize
  123. float length = (float)Math.Sqrt(dX * dX + dY * dY);
  124. if (length == 0)
  125. return false;
  126. dX = dX / length;
  127. dY = dY / length;
  128. return dX < 0.25f || dY < 0.25f; //angle < 15 deg or angle > 75 deg (sin 15 ~= 0.25)
  129. }
  130. private List<Coordinates> GetLineCoordinates(List<Coordinates> mouseMoveCords, int thickness)
  131. {
  132. int y = mouseMoveCords[0].Y;
  133. int x = mouseMoveCords[0].X;
  134. if (Math.Abs(mouseMoveCords[^1].X - mouseMoveCords[0].X) > Math.Abs(mouseMoveCords[^1].Y - mouseMoveCords[0].Y))
  135. {
  136. y = mouseMoveCords[^1].Y;
  137. }
  138. else
  139. {
  140. x = mouseMoveCords[^1].X;
  141. }
  142. mouseMoveCords[0] = new Coordinates(x, y);
  143. return mouseMoveCords;
  144. }
  145. /// <summary>
  146. /// Extracts square from rectangle mouse drag, used to draw symmetric shapes.
  147. /// </summary>
  148. private List<Coordinates> GetSquareCoordiantes(List<Coordinates> mouseMoveCords)
  149. {
  150. var p1 = mouseMoveCords[0];
  151. var p2 = mouseMoveCords[^1];
  152. //find delta and mirror to first quadrant
  153. var dX = Math.Abs(p2.X - p1.X);
  154. var dY = Math.Abs(p2.Y - p1.Y);
  155. float sqrt2 = (float)Math.Sqrt(2);
  156. //vector of length 1 at 45 degrees;
  157. float diagX, diagY;
  158. diagX = diagY = 1 / sqrt2;
  159. //dot product of delta and diag, returns length of [delta projected onto diag]
  160. float projectedLength = diagX * dX + diagY * dY;
  161. //project above onto axes
  162. float axisLength = projectedLength / sqrt2;
  163. //final coords
  164. float x = -Math.Sign(p2.X - p1.X) * axisLength;
  165. float y = -Math.Sign(p2.Y - p1.Y) * axisLength;
  166. mouseMoveCords[0] = new Coordinates((int)x + p2.X, (int)y + p2.Y);
  167. return mouseMoveCords;
  168. }
  169. private BitmapPixelChanges GetOldPixelsValues(Coordinates[] coordinates)
  170. {
  171. Dictionary<Coordinates, SKColor> values = new Dictionary<Coordinates, SKColor>();
  172. //using (Manager.ActiveLayer.LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
  173. {
  174. Coordinates[] relativeCoords = Manager.ActiveLayer.ConvertToRelativeCoordinates(coordinates);
  175. for (int i = 0; i < coordinates.Length; i++)
  176. {
  177. var cl = Manager.ActiveLayer.GetPixel(relativeCoords[i].X, relativeCoords[i].Y);
  178. values.Add(
  179. coordinates[i],
  180. cl);
  181. }
  182. }
  183. return new BitmapPixelChanges(values);
  184. }
  185. private void UseToolOnPreviewLayer(List<Coordinates> mouseMove, bool clearPreviewLayer = true)
  186. {
  187. if (mouseMove.Count > 0)
  188. {
  189. if (clearPreviewLayer)
  190. {
  191. Manager.ActiveDocument.PreviewLayer.ClearCanvas();
  192. }
  193. ((BitmapOperationTool)Tools.ActiveTool).Use(
  194. Manager.ActiveDocument.PreviewLayer,
  195. mouseMove,
  196. Manager.PrimaryColor);
  197. }
  198. }
  199. }
  200. }