BitmapOperationsUtility.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Windows.Input;
  6. using System.Windows.Media;
  7. using System.Windows.Media.Imaging;
  8. using PixiEditor.Helpers.Extensions;
  9. using PixiEditor.Models.DataHolders;
  10. using PixiEditor.Models.ImageManipulation;
  11. using PixiEditor.Models.Layers;
  12. using PixiEditor.Models.Position;
  13. using PixiEditor.Models.Tools;
  14. namespace PixiEditor.Models.Controllers
  15. {
  16. public class BitmapOperationsUtility
  17. {
  18. public List<LayerChange> PreviewLayerChanges => previewLayerChanges;
  19. private List<LayerChange> previewLayerChanges;
  20. private Coordinates lastMousePos;
  21. public BitmapOperationsUtility(BitmapManager manager)
  22. {
  23. Manager = manager;
  24. }
  25. public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
  26. public BitmapManager Manager { get; set; }
  27. public void DeletePixels(Layer[] layers, Coordinates[] pixels)
  28. {
  29. if (Manager.ActiveDocument == null)
  30. {
  31. return;
  32. }
  33. BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, Color.FromArgb(0, 0, 0, 0));
  34. Dictionary<Layer, Color[]> 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. int indexOfLayer = Manager.ActiveDocument.Layers.IndexOf(layers[i]);
  40. old[i] = new LayerChange(
  41. BitmapPixelChanges.FromArrays(pixels, oldValues[layers[i]]), indexOfLayer);
  42. newChange[i] = new LayerChange(changes, indexOfLayer);
  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: mouseMove is reversed inside function!.
  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. mouseMove.Reverse();
  62. UseTool(mouseMove, tool, Manager.PrimaryColor);
  63. lastMousePos = newPos;
  64. }
  65. }
  66. /// <summary>
  67. /// Applies pixels from preview layer to selected layer.
  68. /// </summary>
  69. public void ApplyPreviewLayer()
  70. {
  71. if (previewLayerChanges == null)
  72. {
  73. return;
  74. }
  75. foreach (var modifiedLayer in previewLayerChanges)
  76. {
  77. Layer layer = Manager.ActiveDocument.Layers[modifiedLayer.LayerIndex];
  78. BitmapPixelChanges oldValues = ApplyToLayer(layer, modifiedLayer).PixelChanges;
  79. BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
  80. modifiedLayer.PixelChanges,
  81. oldValues,
  82. modifiedLayer.LayerIndex));
  83. Manager.ActiveDocument.GeneratePreviewLayer();
  84. }
  85. previewLayerChanges = null;
  86. }
  87. private void UseTool(List<Coordinates> mouseMoveCords, BitmapOperationTool tool, Color color)
  88. {
  89. if (Keyboard.IsKeyDown(Key.LeftShift) && !MouseCordsNotInLine(mouseMoveCords))
  90. {
  91. mouseMoveCords = GetSquareCoordiantes(mouseMoveCords);
  92. }
  93. if (!tool.RequiresPreviewLayer)
  94. {
  95. LayerChange[] modifiedLayers = tool.Use(Manager.ActiveLayer, mouseMoveCords.ToArray(), color);
  96. LayerChange[] oldPixelsValues = new LayerChange[modifiedLayers.Length];
  97. for (int i = 0; i < modifiedLayers.Length; i++)
  98. {
  99. Layer layer = Manager.ActiveDocument.Layers[modifiedLayers[i].LayerIndex];
  100. oldPixelsValues[i] = ApplyToLayer(layer, modifiedLayers[i]);
  101. BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
  102. modifiedLayers[i].PixelChanges,
  103. oldPixelsValues[i].PixelChanges,
  104. modifiedLayers[i].LayerIndex));
  105. }
  106. }
  107. else
  108. {
  109. UseToolOnPreviewLayer(mouseMoveCords, tool.ClearPreviewLayerOnEachIteration);
  110. }
  111. }
  112. private LayerChange ApplyToLayer(Layer layer, LayerChange change)
  113. {
  114. layer.DynamicResize(change.PixelChanges);
  115. LayerChange oldPixelsValues = new LayerChange(
  116. GetOldPixelsValues(change.PixelChanges.ChangedPixels.Keys.ToArray()),
  117. change.LayerIndex);
  118. layer.SetPixels(change.PixelChanges, false);
  119. return oldPixelsValues;
  120. }
  121. private bool MouseCordsNotInLine(List<Coordinates> cords)
  122. {
  123. return cords[0].X == cords[^1].X || cords[0].Y == cords[^1].Y;
  124. }
  125. /// <summary>
  126. /// Extracts square from rectangle mouse drag, used to draw symmetric shapes.
  127. /// </summary>
  128. private List<Coordinates> GetSquareCoordiantes(List<Coordinates> mouseMoveCords)
  129. {
  130. int xLength = mouseMoveCords[0].Y - mouseMoveCords[^1].Y;
  131. int yLength = mouseMoveCords[0].Y - mouseMoveCords[^1].Y;
  132. if (mouseMoveCords[^1].Y > mouseMoveCords[0].Y)
  133. {
  134. xLength *= -1;
  135. }
  136. if (mouseMoveCords[^1].X > mouseMoveCords[0].X)
  137. {
  138. xLength *= -1;
  139. }
  140. mouseMoveCords[0] = new Coordinates(mouseMoveCords[^1].X + xLength, mouseMoveCords[^1].Y + yLength);
  141. return mouseMoveCords;
  142. }
  143. private BitmapPixelChanges GetOldPixelsValues(Coordinates[] coordinates)
  144. {
  145. Dictionary<Coordinates, Color> values = new Dictionary<Coordinates, Color>();
  146. using (Manager.ActiveLayer.LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
  147. {
  148. Coordinates[] relativeCoords = Manager.ActiveLayer.ConvertToRelativeCoordinates(coordinates);
  149. for (int i = 0; i < coordinates.Length; i++)
  150. {
  151. values.Add(
  152. coordinates[i],
  153. Manager.ActiveLayer.GetPixel(relativeCoords[i].X, relativeCoords[i].Y));
  154. }
  155. }
  156. return new BitmapPixelChanges(values);
  157. }
  158. private void UseToolOnPreviewLayer(List<Coordinates> mouseMove, bool clearPreviewLayer = true)
  159. {
  160. LayerChange[] modifiedLayers;
  161. if (mouseMove.Count > 0 && mouseMove[0] != lastMousePos)
  162. {
  163. if (clearPreviewLayer || Manager.ActiveDocument.PreviewLayer == null)
  164. {
  165. Manager.ActiveDocument.GeneratePreviewLayer();
  166. }
  167. modifiedLayers = ((BitmapOperationTool)Manager.SelectedTool).Use(
  168. Manager.ActiveDocument.ActiveLayer,
  169. mouseMove.ToArray(),
  170. Manager.PrimaryColor);
  171. BitmapPixelChanges[] changes = modifiedLayers.Select(x => x.PixelChanges).ToArray();
  172. Manager.ActiveDocument.PreviewLayer.SetPixels(BitmapPixelChanges.CombineOverride(changes));
  173. if (clearPreviewLayer || previewLayerChanges == null)
  174. {
  175. previewLayerChanges = new List<LayerChange>(modifiedLayers);
  176. }
  177. else
  178. {
  179. InjectPreviewLayerChanges(modifiedLayers);
  180. }
  181. }
  182. }
  183. private void InjectPreviewLayerChanges(LayerChange[] modifiedLayers)
  184. {
  185. for (int i = 0; i < modifiedLayers.Length; i++)
  186. {
  187. var layer = previewLayerChanges.First(x => x.LayerIndex == modifiedLayers[i].LayerIndex);
  188. layer.PixelChanges.ChangedPixels.AddRangeOverride(modifiedLayers[i].PixelChanges.ChangedPixels);
  189. layer.PixelChanges = layer.PixelChanges.WithoutTransparentPixels();
  190. }
  191. }
  192. }
  193. }