ToolSet.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;
  4. using System.Windows;
  5. using System.Windows.Media;
  6. using System.Windows.Input;
  7. using System.Windows.Media.Imaging;
  8. namespace PixiEditorDotNetCore3.Models.Tools
  9. {
  10. public class ToolSet
  11. {
  12. public List<Tool> Tools { get; set; } = new List<Tool>();
  13. private Coordinates _activeCoordinates = new Coordinates();
  14. private bool _toolIsExecuting;
  15. private int _asyncDelay = 15;
  16. public ToolSet(List<Tool> tools)
  17. {
  18. Tools = tools;
  19. }
  20. /// <summary>
  21. /// Executes tool action
  22. /// </summary>
  23. /// <param name="layer">Layer to operate on.</param>
  24. /// <param name="startingCoords">Click coordinates.</param>
  25. /// <param name="color">Color that tool will use.</param>
  26. /// <param name="toolSize">Size/thickness of tool</param>
  27. /// <param name="tool">Tool to execute</param>
  28. /// <returns></returns>
  29. public void ExecuteTool(Layer layer, Coordinates startingCoords, Color color,int toolSize, ToolType tool)
  30. {
  31. if (toolSize < 1) return;
  32. BitmapPixelChanges changes;
  33. Tool selectedTool = Tools.Find(x => x.GetToolType == tool);
  34. changes = selectedTool.Use(layer, startingCoords, color, toolSize);
  35. if (tool != ToolType.ColorPicker)
  36. {
  37. UndoManager.RecordChanges("ActiveLightLayer", new LightLayer(layer.LayerBitmap.ToByteArray(), (int)layer.LayerBitmap.Height, (int)layer.LayerBitmap.Width),
  38. $"{tool.ToString()} Tool.");
  39. }
  40. if (selectedTool.ExecutesItself == false)
  41. {
  42. layer.ApplyPixels(changes, color);
  43. }
  44. }
  45. /// <summary>
  46. /// Updates coordinates in order to some tools work
  47. /// </summary>
  48. /// <param name="cords">Current coordinates</param>
  49. public void UpdateCoordinates(Coordinates cords)
  50. {
  51. _activeCoordinates = cords;
  52. }
  53. /// <summary>
  54. /// Fills area with color (forest fire alghoritm)
  55. /// </summary>
  56. /// <param name="canvas">Bitmap to operate on</param>
  57. /// <param name="pixelPosition">Position of starting pixel</param>
  58. /// <param name="color">Fills area with this color</param>
  59. private WriteableBitmap FloodFill(WriteableBitmap canvas, Coordinates pixelPosition, Color color)
  60. {
  61. WriteableBitmap bm = canvas;
  62. Color colorToReplace = bm.GetPixel(pixelPosition.X, pixelPosition.Y);
  63. var stack = new Stack<Tuple<int, int>>();
  64. stack.Push(Tuple.Create(pixelPosition.X, pixelPosition.Y));
  65. while (stack.Count > 0)
  66. {
  67. var point = stack.Pop();
  68. if (point.Item1 < 0 || point.Item1 > bm.Height - 1) continue;
  69. if (point.Item2 < 0 || point.Item2 > bm.Width - 1) continue;
  70. if (bm.GetPixel(point.Item1, point.Item2) == color) continue;
  71. if (bm.GetPixel(point.Item1, point.Item2) == colorToReplace)
  72. {
  73. bm.SetPixel(point.Item1, point.Item2, color);
  74. stack.Push(Tuple.Create(point.Item1, point.Item2 - 1));
  75. stack.Push(Tuple.Create(point.Item1 + 1, point.Item2));
  76. stack.Push(Tuple.Create(point.Item1, point.Item2 + 1));
  77. stack.Push(Tuple.Create(point.Item1 - 1, point.Item2));
  78. }
  79. }
  80. return bm;
  81. }
  82. /// <summary>
  83. /// Draws line in canvas
  84. /// </summary>
  85. /// <param name="layer">Layer to operate on</param>
  86. /// <param name="coordinates">Starting coordinates, usually click point</param>
  87. /// <param name="color">Does it really need a description?</param>
  88. private async void LineAsync(Layer layer, Coordinates coordinates, Color color, int size)
  89. {
  90. WriteableBitmap wb = layer.LayerBitmap;
  91. _toolIsExecuting = true;
  92. //clones bitmap before line
  93. WriteableBitmap writeableBitmap = wb.Clone();
  94. //While Mouse buttons are pressed, clears current bitmap, pastes cloned bitmap and draws line, on each iteration
  95. while (Mouse.LeftButton == MouseButtonState.Pressed || Mouse.RightButton == MouseButtonState.Pressed)
  96. {
  97. wb.Clear();
  98. wb.Blit(new Rect(new Size(layer.Width, layer.Height)), writeableBitmap, new Rect(new Size(layer.Width, layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
  99. wb.DrawLineBresenham(coordinates.X, coordinates.Y, _activeCoordinates.X, _activeCoordinates.Y, color);
  100. await Task.Delay(_asyncDelay);
  101. }
  102. _toolIsExecuting = false;
  103. }
  104. /// <summary>
  105. /// Draws circle on bitmap.
  106. /// </summary>
  107. /// <param name="layer">Layer to operate on.</param>
  108. /// <param name="coordinates">Starting pixel coordinates.</param>
  109. /// <param name="color">Circle color.</param>
  110. private async void CircleAsync(Layer layer, Coordinates coordinates, Color color)
  111. {
  112. WriteableBitmap wb = layer.LayerBitmap;
  113. //Basically does the same like rectangle method, but with different shape
  114. _toolIsExecuting = true;
  115. WriteableBitmap bitmap = wb.Clone();
  116. while (Mouse.LeftButton == MouseButtonState.Pressed || Mouse.RightButton == MouseButtonState.Pressed)
  117. {
  118. wb.Clear();
  119. wb.Blit(new Rect(new Size(layer.Width, layer.Height)), bitmap, new Rect(new Size(layer.Width, layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
  120. if (coordinates.X > _activeCoordinates.X && coordinates.Y > _activeCoordinates.Y)
  121. {
  122. wb.DrawEllipse(_activeCoordinates.X, _activeCoordinates.Y, coordinates.X, coordinates.Y, color);
  123. }
  124. else if (coordinates.X < _activeCoordinates.X && coordinates.Y < _activeCoordinates.Y)
  125. {
  126. wb.DrawEllipse(coordinates.X, coordinates.Y, _activeCoordinates.X, _activeCoordinates.Y, color);
  127. }
  128. else if (coordinates.Y > _activeCoordinates.Y)
  129. {
  130. wb.DrawEllipse(coordinates.X, _activeCoordinates.Y, _activeCoordinates.X, coordinates.Y, color);
  131. }
  132. else
  133. {
  134. wb.DrawEllipse(_activeCoordinates.X, coordinates.Y, coordinates.X, _activeCoordinates.Y, color);
  135. }
  136. await Task.Delay(_asyncDelay);
  137. }
  138. _toolIsExecuting = false;
  139. }
  140. /// <summary>
  141. /// Draws rectangle on bitmap
  142. /// </summary>
  143. /// <param name="layer">Layer to operate on</param>
  144. /// <param name="coordinates">Starting pixel coordinate</param>
  145. /// <param name="color">Rectangle color</param>
  146. private async void RectangleAsync(Layer layer, Coordinates coordinates, Color color)
  147. {
  148. WriteableBitmap wb = layer.LayerBitmap;
  149. _toolIsExecuting = true;
  150. WriteableBitmap writeableBitmap = wb.Clone();
  151. while (Mouse.LeftButton == MouseButtonState.Pressed || Mouse.RightButton == MouseButtonState.Pressed)
  152. {
  153. //Two lines below are responsible for clearing last rectangle (on mouse move), to live show rectangle on bitmap
  154. wb.Clear();
  155. wb.Blit(new Rect(new Size(layer.Width, layer.Height)), writeableBitmap, new Rect(new Size(layer.Width, layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
  156. //Those ifs are changing direction of rectangle. In other words: flips rectangle on X and Y axis when needed
  157. if (coordinates.X > _activeCoordinates.X && coordinates.Y > _activeCoordinates.Y)
  158. {
  159. wb.DrawRectangle(_activeCoordinates.X, _activeCoordinates.Y, coordinates.X, coordinates.Y, color);
  160. }
  161. else if (coordinates.X < _activeCoordinates.X && coordinates.Y < _activeCoordinates.Y)
  162. {
  163. wb.DrawRectangle(coordinates.X, coordinates.Y, _activeCoordinates.X, _activeCoordinates.Y, color);
  164. }
  165. else if (coordinates.Y > _activeCoordinates.Y)
  166. {
  167. wb.DrawRectangle(coordinates.X, _activeCoordinates.Y, _activeCoordinates.X, coordinates.Y, color);
  168. }
  169. else
  170. {
  171. wb.DrawRectangle(_activeCoordinates.X, coordinates.Y, coordinates.X, _activeCoordinates.Y, color);
  172. }
  173. await Task.Delay(_asyncDelay);
  174. }
  175. _toolIsExecuting = false;
  176. }
  177. /// <summary>
  178. /// Returns color of pixel.
  179. /// </summary>
  180. /// <param name="layer">Layer in which bitmap with pixels are stored.</param>
  181. /// <param name="coordinates">Pixel coordinate.</param>
  182. /// <returns></returns>
  183. public static Color ColorPicker(Layer layer, Coordinates coordinates)
  184. {
  185. return layer.LayerBitmap.GetPixel(coordinates.X, coordinates.Y);
  186. }
  187. /// <summary>
  188. /// Ligtens pixel color.
  189. /// </summary>
  190. /// <param name="bitmap">Bitmap to work on.</param>
  191. /// <param name="coordinates">Pixel coordinates.</param>
  192. /// <returns></returns>
  193. private WriteableBitmap Lighten(WriteableBitmap bitmap, Coordinates coordinates)
  194. {
  195. WriteableBitmap wb = bitmap;
  196. Color pixel = wb.GetPixel(coordinates.X, coordinates.Y);
  197. Color newColor = ExColor.ChangeColorBrightness(System.Drawing.Color.FromArgb(pixel.R, pixel.G, pixel.B), 0.1f);
  198. wb.SetPixel(coordinates.X, coordinates.Y, newColor);
  199. return wb;
  200. }
  201. /// <summary>
  202. /// Darkens pixel color.
  203. /// </summary>
  204. /// <param name="bitmap">Bitmap to work on.</param>
  205. /// <param name="coordinates">Pixel coordinates.</param>
  206. /// <returns></returns>
  207. private WriteableBitmap Darken(WriteableBitmap bitmap, Coordinates coordinates)
  208. {
  209. WriteableBitmap wb = bitmap;
  210. Color pixel = wb.GetPixel(coordinates.X, coordinates.Y);
  211. Color newColor = ExColor.ChangeColorBrightness(System.Drawing.Color.FromArgb(pixel.R,pixel.G,pixel.B), -0.06f);
  212. wb.SetPixel(coordinates.X, coordinates.Y, newColor);
  213. return wb;
  214. }
  215. }
  216. }