123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- using PixiEditor.Helpers;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Media;
- using System.Windows.Controls;
- using System.Windows.Input;
- using System.Windows.Media.Imaging;
- namespace PixiEditorDotNetCore3.Models.Tools
- {
- public class ToolSet
- {
- private Coordinates _activeCoordinates = new Coordinates();
- private bool _toolIsExecuting = false;
- private int _asyncDelay = 15;
- private WriteableBitmap _oldBitmap;
- /// <summary>
- /// Executes tool action
- /// </summary>
- /// <param name="layer">Layer to operate on.</param>
- /// <param name="startingCoords">Click coordinates.</param>
- /// <param name="color">Color that tool will use.</param>
- /// <param name="toolSize">Size/thickness of tool</param>
- /// <param name="tool">Tool to execute</param>
- /// <returns></returns>
- public Layer ExecuteTool(Layer layer, Coordinates startingCoords, Color color,int toolSize, ToolType tool)
- {
- if (toolSize < 1) return null;
- Layer cLayer = layer;
- _oldBitmap = new WriteableBitmap(layer.LayerBitmap);
- switch (tool)
- {
- case ToolType.Pen:
- cLayer.LayerBitmap = DrawPixel(cLayer.LayerBitmap, startingCoords, toolSize,color);
- break;
- case ToolType.Bucket:
- cLayer.LayerBitmap = FloodFill(cLayer.LayerBitmap, startingCoords, color);
- break;
- case ToolType.Line:
- if (_toolIsExecuting == false)
- {
- LineAsync(cLayer, startingCoords, color, toolSize);
- }
- break;
- case ToolType.Circle:
- if(_toolIsExecuting == false)
- {
- CircleAsync(cLayer, startingCoords, color);
- }
- break;
- case ToolType.Rectangle:
- if(_toolIsExecuting == false)
- {
- RectangleAsync(cLayer, startingCoords, color);
- }
- break;
- case ToolType.Earser:
- cLayer.LayerBitmap = DrawPixel(cLayer.LayerBitmap, startingCoords, toolSize, Colors.Transparent);
- break;
- case ToolType.Lighten:
- if(Mouse.LeftButton == MouseButtonState.Pressed)
- {
- cLayer.LayerBitmap = Lighten(cLayer.LayerBitmap, startingCoords);
- }
- else if(Mouse.RightButton == MouseButtonState.Pressed)
- {
- cLayer.LayerBitmap = Darken(cLayer.LayerBitmap, startingCoords);
- }
- break;
- }
- if (tool != ToolType.ColorPicker)
- {
- UndoManager.RecordChanges("ActiveLightLayer", new LightLayer(_oldBitmap.ToByteArray(), (int)_oldBitmap.Height, (int)_oldBitmap.Width),
- $"{tool.ToString()} Tool.");
- }
- return cLayer;
- }
- /// <summary>
- /// Not working yet.
- /// </summary>
- /// <param name="bitmap"></param>
- /// <param name="pixelCoordinates"></param>
- /// <param name="color"></param>
- /// <param name="highlightThickness"></param>
- public static void HighlightPixel(WriteableBitmap bitmap,Coordinates pixelCoordinates, Color color, int highlightThickness)
- {
- bitmap.Clear();
- bitmap.Blit(new Rect(new Size(bitmap.Width, bitmap.Height)), bitmap, new Rect(new Size(bitmap.Width, bitmap.Height)), WriteableBitmapExtensions.BlendMode.Additive);
- DCords centerCords = CalculateThicknessCenter(pixelCoordinates, highlightThickness);
- bitmap.FillRectangle(centerCords.Coords1.X, centerCords.Coords1.Y, centerCords.Coords2.X, centerCords.Coords2.Y, color);
- }
- /// <summary>
- /// Updates coordinates in order to some tools work
- /// </summary>
- /// <param name="cords">Current coordinates</param>
- public void UpdateCoordinates(Coordinates cords)
- {
- _activeCoordinates = cords;
- }
- /// <summary>
- /// Fills pixel(s) with choosen color
- /// </summary>
- /// <param name="canvas">Bitmap to operate on.</param>
- /// <param name="pixelPosition">Coordinates of pixel.</param>
- /// <param name="color">Color to be set.</param>
- private WriteableBitmap DrawPixel(WriteableBitmap canvas, Coordinates pixelPosition,int thickness,Color color)
- {
- WriteableBitmap bm = canvas;
- int x1, y1, x2, y2;
- DCords centeredCoords = CalculateThicknessCenter(pixelPosition, thickness);
- x1 = centeredCoords.Coords1.X;
- y1 = centeredCoords.Coords1.Y;
- x2 = centeredCoords.Coords2.X;
- y2 = centeredCoords.Coords2.Y;
- bm.FillRectangle(x1, y1, x2, y2, color);
- return bm;
- }
- /// <summary>
- /// Calculates center of thickness * thickness rectangle
- /// </summary>
- /// <param name="startPosition">Top left position of rectangle</param>
- /// <param name="thickness">Thickness of rectangle</param>
- /// <returns></returns>
- private static DCords CalculateThicknessCenter(Coordinates startPosition, int thickness)
- {
- int x1, x2, y1, y2;
- if (thickness % 2 == 0)
- {
- x2 = startPosition.X + thickness / 2;
- y2 = startPosition.Y + thickness / 2;
- x1 = x2 - thickness;
- y1 = y2 - thickness;
- }
- else
- {
- x2 = startPosition.X + (((thickness - 1) / 2) + 1);
- y2 = startPosition.Y + (((thickness - 1) / 2) + 1);
- x1 = x2 - thickness;
- y1 = y2 - thickness;
- }
- return new DCords(new Coordinates(x1, y1), new Coordinates(x2, y2));
- }
- /// <summary>
- /// Fills area with color (forest fire alghoritm)
- /// </summary>
- /// <param name="canvas">Bitmap to operate on</param>
- /// <param name="pixelPosition">Position of starting pixel</param>
- /// <param name="color">Fills area with this color</param>
- private WriteableBitmap FloodFill(WriteableBitmap canvas, Coordinates pixelPosition, Color color)
- {
- WriteableBitmap bm = canvas;
- Color colorToReplace = bm.GetPixel(pixelPosition.X, pixelPosition.Y);
- var stack = new Stack<Tuple<int, int>>();
- stack.Push(Tuple.Create(pixelPosition.X, pixelPosition.Y));
- while (stack.Count > 0)
- {
- var point = stack.Pop();
- if (point.Item1 < 0 || point.Item1 > bm.Height - 1) continue;
- if (point.Item2 < 0 || point.Item2 > bm.Width - 1) continue;
- if (bm.GetPixel(point.Item1, point.Item2) == color) continue;
- if (bm.GetPixel(point.Item1, point.Item2) == colorToReplace)
- {
- bm.SetPixel(point.Item1, point.Item2, color);
- stack.Push(Tuple.Create(point.Item1, point.Item2 - 1));
- stack.Push(Tuple.Create(point.Item1 + 1, point.Item2));
- stack.Push(Tuple.Create(point.Item1, point.Item2 + 1));
- stack.Push(Tuple.Create(point.Item1 - 1, point.Item2));
- }
- }
- return bm;
- }
- /// <summary>
- /// Draws line in canvas
- /// </summary>
- /// <param name="layer">Layer to operate on</param>
- /// <param name="coordinates">Starting coordinates, usually click point</param>
- /// <param name="color">Does it really need a description?</param>
- private async void LineAsync(Layer layer, Coordinates coordinates, Color color, int size)
- {
- WriteableBitmap wb = layer.LayerBitmap;
- _toolIsExecuting = true;
- //clones bitmap before line
- WriteableBitmap writeableBitmap = wb.Clone();
- //While Mouse buttons are pressed, clears current bitmap, pastes cloned bitmap and draws line, on each iteration
- while (Mouse.LeftButton == MouseButtonState.Pressed || Mouse.RightButton == MouseButtonState.Pressed)
- {
- wb.Clear();
- wb.Blit(new Rect(new Size(layer.Width, layer.Height)), writeableBitmap, new Rect(new Size(layer.Width, layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
- wb.DrawLineBresenham(coordinates.X, coordinates.Y, _activeCoordinates.X, _activeCoordinates.Y, color);
- await Task.Delay(_asyncDelay);
- }
- _toolIsExecuting = false;
- }
- /// <summary>
- /// Draws circle on bitmap.
- /// </summary>
- /// <param name="layer">Layer to operate on.</param>
- /// <param name="coordinates">Starting pixel coordinates.</param>
- /// <param name="color">Circle color.</param>
- private async void CircleAsync(Layer layer, Coordinates coordinates, Color color)
- {
- WriteableBitmap wb = layer.LayerBitmap;
- //Basically does the same like rectangle method, but with different shape
- _toolIsExecuting = true;
- WriteableBitmap bitmap = wb.Clone();
- while (Mouse.LeftButton == MouseButtonState.Pressed || Mouse.RightButton == MouseButtonState.Pressed)
- {
- wb.Clear();
- wb.Blit(new Rect(new Size(layer.Width, layer.Height)), bitmap, new Rect(new Size(layer.Width, layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
- if (coordinates.X > _activeCoordinates.X && coordinates.Y > _activeCoordinates.Y)
- {
- wb.DrawEllipse(_activeCoordinates.X, _activeCoordinates.Y, coordinates.X, coordinates.Y, color);
- }
- else if (coordinates.X < _activeCoordinates.X && coordinates.Y < _activeCoordinates.Y)
- {
- wb.DrawEllipse(coordinates.X, coordinates.Y, _activeCoordinates.X, _activeCoordinates.Y, color);
- }
- else if (coordinates.Y > _activeCoordinates.Y)
- {
- wb.DrawEllipse(coordinates.X, _activeCoordinates.Y, _activeCoordinates.X, coordinates.Y, color);
- }
- else
- {
- wb.DrawEllipse(_activeCoordinates.X, coordinates.Y, coordinates.X, _activeCoordinates.Y, color);
- }
- await Task.Delay(_asyncDelay);
- }
- _toolIsExecuting = false;
- }
- /// <summary>
- /// Draws rectangle on bitmap
- /// </summary>
- /// <param name="layer">Layer to operate on</param>
- /// <param name="coordinates">Starting pixel coordinate</param>
- /// <param name="color">Rectangle color</param>
- private async void RectangleAsync(Layer layer, Coordinates coordinates, Color color)
- {
- WriteableBitmap wb = layer.LayerBitmap;
- _toolIsExecuting = true;
- WriteableBitmap writeableBitmap = wb.Clone();
- while (Mouse.LeftButton == MouseButtonState.Pressed || Mouse.RightButton == MouseButtonState.Pressed)
- {
- //Two lines below are responsible for clearing last rectangle (on mouse move), to live show rectangle on bitmap
- wb.Clear();
- wb.Blit(new Rect(new Size(layer.Width, layer.Height)), writeableBitmap, new Rect(new Size(layer.Width, layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
- //Those ifs are changing direction of rectangle. In other words: flips rectangle on X and Y axis when needed
- if (coordinates.X > _activeCoordinates.X && coordinates.Y > _activeCoordinates.Y)
- {
- wb.DrawRectangle(_activeCoordinates.X, _activeCoordinates.Y, coordinates.X, coordinates.Y, color);
- }
- else if (coordinates.X < _activeCoordinates.X && coordinates.Y < _activeCoordinates.Y)
- {
- wb.DrawRectangle(coordinates.X, coordinates.Y, _activeCoordinates.X, _activeCoordinates.Y, color);
- }
- else if (coordinates.Y > _activeCoordinates.Y)
- {
- wb.DrawRectangle(coordinates.X, _activeCoordinates.Y, _activeCoordinates.X, coordinates.Y, color);
- }
- else
- {
- wb.DrawRectangle(_activeCoordinates.X, coordinates.Y, coordinates.X, _activeCoordinates.Y, color);
- }
- await Task.Delay(_asyncDelay);
- }
- _toolIsExecuting = false;
- }
- /// <summary>
- /// Returns color of pixel.
- /// </summary>
- /// <param name="layer">Layer in which bitmap with pixels are stored.</param>
- /// <param name="coordinates">Pixel coordinate.</param>
- /// <returns></returns>
- public static Color ColorPicker(Layer layer, Coordinates coordinates)
- {
- return layer.LayerBitmap.GetPixel(coordinates.X, coordinates.Y);
- }
- /// <summary>
- /// Ligtens pixel color.
- /// </summary>
- /// <param name="bitmap">Bitmap to work on.</param>
- /// <param name="coordinates">Pixel coordinates.</param>
- /// <returns></returns>
- private WriteableBitmap Lighten(WriteableBitmap bitmap, Coordinates coordinates)
- {
- WriteableBitmap wb = bitmap;
- Color pixel = wb.GetPixel(coordinates.X, coordinates.Y);
- Color newColor = ExColor.ChangeColorBrightness(System.Drawing.Color.FromArgb(pixel.R, pixel.G, pixel.B), 0.1f);
- wb.SetPixel(coordinates.X, coordinates.Y, newColor);
- return wb;
- }
- /// <summary>
- /// Darkens pixel color.
- /// </summary>
- /// <param name="bitmap">Bitmap to work on.</param>
- /// <param name="coordinates">Pixel coordinates.</param>
- /// <returns></returns>
- private WriteableBitmap Darken(WriteableBitmap bitmap, Coordinates coordinates)
- {
- WriteableBitmap wb = bitmap;
- Color pixel = wb.GetPixel(coordinates.X, coordinates.Y);
- Color newColor = ExColor.ChangeColorBrightness(System.Drawing.Color.FromArgb(pixel.R,pixel.G,pixel.B), -0.06f);
- wb.SetPixel(coordinates.X, coordinates.Y, newColor);
- return wb;
- }
- }
- }
|