Browse Source

Merge pull request #4 from flabbet/dev

Merge Dev to Master
Krzysztof Krysiński 5 years ago
parent
commit
d6cc1f7b34
26 changed files with 600 additions and 405 deletions
  1. 5 2
      PixiEditorDotNetCore3/Models/Change.cs
  2. 48 0
      PixiEditorDotNetCore3/Models/CoordinatesCalculator.cs
  3. 2 2
      PixiEditorDotNetCore3/Models/DoubleCords.cs
  4. 9 0
      PixiEditorDotNetCore3/Models/Layer.cs
  5. 3 1
      PixiEditorDotNetCore3/Models/MousePositionConverter.cs
  6. 1 1
      PixiEditorDotNetCore3/Models/StackEx.cs
  7. 19 0
      PixiEditorDotNetCore3/Models/Tools/BitmapPixelChanges.cs
  8. 41 0
      PixiEditorDotNetCore3/Models/Tools/ShapeTool.cs
  9. 14 0
      PixiEditorDotNetCore3/Models/Tools/Tool.cs
  10. 0 324
      PixiEditorDotNetCore3/Models/Tools/ToolSet.cs
  11. 1 1
      PixiEditorDotNetCore3/Models/Tools/ToolType.cs
  12. 30 0
      PixiEditorDotNetCore3/Models/Tools/Tools/BrightnessTool.cs
  13. 28 0
      PixiEditorDotNetCore3/Models/Tools/Tools/CircleTool.cs
  14. 18 0
      PixiEditorDotNetCore3/Models/Tools/Tools/EarserTool.cs
  15. 51 0
      PixiEditorDotNetCore3/Models/Tools/Tools/FloodFill.cs
  16. 22 0
      PixiEditorDotNetCore3/Models/Tools/Tools/LineTool.cs
  17. 29 0
      PixiEditorDotNetCore3/Models/Tools/Tools/PenTool.cs
  18. 22 0
      PixiEditorDotNetCore3/Models/Tools/Tools/RectangleTool.cs
  19. 114 0
      PixiEditorDotNetCore3/Models/Tools/ToolsManager.cs
  20. 20 7
      PixiEditorDotNetCore3/Models/UndoManager.cs
  21. 1 0
      PixiEditorDotNetCore3/ViewModels/NewFileMenuViewModel.cs
  22. 65 53
      PixiEditorDotNetCore3/ViewModels/ViewModelMain.cs
  23. 4 4
      PixiEditorDotNetCore3/Views/MainWindow.xaml
  24. 8 10
      PixiEditorTests/PixiEditorTests.csproj
  25. 25 0
      PixiEditorTests/WorkspaceTests/ImageGeneratorTests.cs
  26. 20 0
      PixiEditorTests/WorkspaceTests/ToolsTests/CoordinatesCalculatorTests.cs

+ 5 - 2
PixiEditorDotNetCore3/Models/Change.cs

@@ -13,6 +13,8 @@ namespace PixiEditorDotNetCore3.Models
     {
     {
         public object OldValue { get; set; }
         public object OldValue { get; set; }
 
 
+        public object NewValue { get; set; }
+
         public string Description { get; set; }
         public string Description { get; set; }
 
 
         public string Property { get; set; }
         public string Property { get; set; }
@@ -21,12 +23,13 @@ namespace PixiEditorDotNetCore3.Models
         {
         {
             Property = property;
             Property = property;
             OldValue = oldValue;
             OldValue = oldValue;
-            Description = description;
+            Description = description;
+            NewValue = OldValue;
         }
         }
 
 
         public Change()
         public Change()
         {
         {
-
+           
         }
         }
 
 
     }
     }

+ 48 - 0
PixiEditorDotNetCore3/Models/CoordinatesCalculator.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace PixiEditorDotNetCore3.Models
+{
+    public static class CoordinatesCalculator
+    {
+        /// <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>
+        public static DoubleCords 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 DoubleCords(new Coordinates(x1, y1), new Coordinates(x2, y2));
+        }
+
+        public static Coordinates[] RectangleToCoordinates(int x1, int y1, int x2, int y2)
+        {
+            List<Coordinates> coordinates = new List<Coordinates>();
+            for (int y = y1; y < y1 + (y2 - y1); y++)
+            {
+                for (int x = x1; x < x1 + (x2 - x1); x++)
+                {
+                    coordinates.Add(new Coordinates(x, y));
+                }
+            }
+            return coordinates.ToArray();
+        }
+    }
+}

+ 2 - 2
PixiEditorDotNetCore3/Models/DCords.cs → PixiEditorDotNetCore3/Models/DoubleCords.cs

@@ -6,12 +6,12 @@ using System.Threading.Tasks;
 
 
 namespace PixiEditorDotNetCore3.Models
 namespace PixiEditorDotNetCore3.Models
 {
 {
-    public class DCords
+    public class DoubleCords
     {
     {
         public Coordinates Coords1 { get; set; }
         public Coordinates Coords1 { get; set; }
         public Coordinates Coords2 { get; set; }
         public Coordinates Coords2 { get; set; }
         
         
-        public DCords(Coordinates cords1, Coordinates cords2)
+        public DoubleCords(Coordinates cords1, Coordinates cords2)
         {
         {
             Coords1 = cords1;
             Coords1 = cords1;
             Coords2 = cords2;
             Coords2 = cords2;

+ 9 - 0
PixiEditorDotNetCore3/Models/Layer.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
+using PixiEditorDotNetCore3.Models.Tools;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.ComponentModel;
@@ -41,5 +42,13 @@ namespace PixiEditorDotNetCore3.Models
             Width = (int) layerBitmap.Width;
             Width = (int) layerBitmap.Width;
             Height = (int) layerBitmap.Height;
             Height = (int) layerBitmap.Height;
         }
         }
+
+        public void ApplyPixels(BitmapPixelChanges pixels, Color color)
+        {
+            foreach (var coords in pixels.ChangedCoordinates)
+            {
+                LayerBitmap.SetPixel(Math.Clamp(coords.X, 0, Width - 1), Math.Clamp(coords.Y, 0, Height - 1), color);
+            }
+        }
     }
     }
 }
 }

+ 3 - 1
PixiEditorDotNetCore3/Models/MousePositionConverter.cs

@@ -11,11 +11,13 @@ namespace PixiEditorDotNetCore3.Models
 {
 {
     public static class MousePositionConverter
     public static class MousePositionConverter
     {
     {
+        public static Coordinates CurrentCoordinates { get; set; }
+
         public static Coordinates MousePositionToCoordinates(Layer baseLayer, Point mousePosition)
         public static Coordinates MousePositionToCoordinates(Layer baseLayer, Point mousePosition)
         {
         {
             int xCoord = (int)(mousePosition.X / baseLayer.Width);
             int xCoord = (int)(mousePosition.X / baseLayer.Width);
             int yCoord = (int)(mousePosition.Y / baseLayer.Height);
             int yCoord = (int)(mousePosition.Y / baseLayer.Height);
             return new Coordinates(xCoord, yCoord);
             return new Coordinates(xCoord, yCoord);
-        }
+        }       
     }
     }
 }
 }

+ 1 - 1
PixiEditorDotNetCore3/Models/StackEx.cs

@@ -44,7 +44,7 @@ namespace PixiEditorDotNetCore3.Models
                 return temp;
                 return temp;
             }
             }
             else
             else
-                return default(T);
+                return default;
         }
         }
 
 
         public void PushToBottom(T item)
         public void PushToBottom(T item)

+ 19 - 0
PixiEditorDotNetCore3/Models/Tools/BitmapPixelChanges.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools
+{
+     public struct BitmapPixelChanges
+    {
+        public Coordinates[] ChangedCoordinates { get; set; }
+        public Color PixelsColor { get; set; }
+
+        public BitmapPixelChanges(Coordinates[] changedCoordinates, Color color)
+        {
+            ChangedCoordinates = changedCoordinates;
+            PixelsColor = color;
+        }
+    }
+}

+ 41 - 0
PixiEditorDotNetCore3/Models/Tools/ShapeTool.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools
+{
+    public abstract class ShapeTool : Tool
+    {
+        public override abstract ToolType ToolType { get; }
+
+        public ShapeTool()
+        {
+            ExecutesItself = true;
+        }
+
+        public abstract override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize);
+
+        protected DoubleCords CalculateCoordinatesForShapeRotation(Coordinates startingCords)
+        {
+            Coordinates currentCoordinates = MousePositionConverter.CurrentCoordinates;
+
+            if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(currentCoordinates.X, currentCoordinates.Y), new Coordinates(startingCords.X, startingCords.Y));
+            }
+            else if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(startingCords.X, startingCords.Y), new Coordinates(currentCoordinates.X, currentCoordinates.Y));
+            }
+            else if (startingCords.Y > currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(startingCords.X, currentCoordinates.Y), new Coordinates(currentCoordinates.X, startingCords.Y));
+            }
+            else
+            {
+                return new DoubleCords(new Coordinates(currentCoordinates.X, startingCords.Y), new Coordinates(startingCords.X, currentCoordinates.Y));
+            }
+        }
+    }
+}

+ 14 - 0
PixiEditorDotNetCore3/Models/Tools/Tool.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools
+{
+    public abstract class Tool
+    {
+        public abstract BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize);
+        public abstract ToolType ToolType { get; }
+        public bool ExecutesItself = false;
+    }
+}

+ 0 - 324
PixiEditorDotNetCore3/Models/Tools/ToolSet.cs

@@ -1,324 +0,0 @@
-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;
-        }
-    }
-}

+ 1 - 1
PixiEditorDotNetCore3/Models/Tools/ToolType.cs

@@ -8,6 +8,6 @@ namespace PixiEditorDotNetCore3.Models.Tools
 {
 {
     public enum ToolType
     public enum ToolType
     {
     {
-        Pen = 0, Bucket, Line, Circle, Rectangle, ColorPicker, Earser, Lighten
+        Pen = 0, Bucket, Line, Circle, Rectangle, ColorPicker, Earser, Brightness
     }
     }
 }
 }

+ 30 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/BrightnessTool.cs

@@ -0,0 +1,30 @@
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class BrightnessTool : Tool
+    {
+        public override ToolType ToolType => ToolType.Brightness;
+        public const float DarkenFactor = -0.06f;
+        public const float LightenFactor = 0.1f;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            if(Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                return ChangeBrightness(layer, startingCoords, toolSize, LightenFactor);
+            }
+                return ChangeBrightness(layer, startingCoords, toolSize, DarkenFactor);
+        }       
+
+        private BitmapPixelChanges ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
+        {
+            PenTool pen = new PenTool();
+            Color pixel = layer.LayerBitmap.GetPixel(coordinates.X, coordinates.Y);
+            Color newColor = ExColor.ChangeColorBrightness(System.Drawing.Color.FromArgb(pixel.R, pixel.G, pixel.B), correctionFactor);
+            return pen.Draw(coordinates, newColor, toolSize);
+        }
+    }
+}

+ 28 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/CircleTool.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class CircleTool : ShapeTool
+    {
+        public override ToolType ToolType => ToolType.Circle;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            CreateCircle(layer, startingCoords, color, toolSize);
+            return new BitmapPixelChanges();
+        }
+
+        public void CreateCircle(Layer layer, Coordinates coordinates, Color color, int size)
+        {
+            DoubleCords calculatedCords = CalculateCoordinatesForShapeRotation(coordinates);
+            layer.LayerBitmap.DrawEllipse(calculatedCords.Coords1.X, calculatedCords.Coords1.Y, calculatedCords.Coords2.X,
+                calculatedCords.Coords2.Y, color);
+        }
+
+        
+    }
+}

+ 18 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/EarserTool.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class EarserTool : Tool
+    {
+        public override ToolType ToolType => ToolType.Earser;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            PenTool pen = new PenTool();
+            return pen.Draw(startingCoords, Colors.Transparent, toolSize);
+        }
+    }
+}

+ 51 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/FloodFill.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class FloodFill : Tool
+    {
+        public override ToolType ToolType => ToolType.Bucket;
+
+        public FloodFill()
+        {
+            ExecutesItself = true;
+        }
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            return ForestFire(layer, startingCoords, color);
+        }
+
+        public BitmapPixelChanges ForestFire(Layer layer, Coordinates startingCoords, Color newColor)
+        {
+            List<Coordinates> changedCoords = new List<Coordinates>();
+
+            Color colorToReplace = layer.LayerBitmap.GetPixel(startingCoords.X, startingCoords.Y);
+
+            var stack = new Stack<Tuple<int, int>>();
+            stack.Push(Tuple.Create(startingCoords.X, startingCoords.Y));
+
+            while (stack.Count > 0)
+            {
+                var point = stack.Pop();
+                if (point.Item1 < 0 || point.Item1 > layer.Height - 1) continue;
+                if (point.Item2 < 0 || point.Item2 > layer.Width - 1) continue;
+                if (layer.LayerBitmap.GetPixel(point.Item1, point.Item2) == newColor) continue;
+
+                if (layer.LayerBitmap.GetPixel(point.Item1, point.Item2) == colorToReplace)
+                {
+                    changedCoords.Add(new Coordinates(point.Item1, point.Item2));
+                    layer.LayerBitmap.SetPixel(point.Item1, point.Item2, newColor);
+                    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 new BitmapPixelChanges(changedCoords.ToArray(), newColor);
+        }
+    }
+}

+ 22 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/LineTool.cs

@@ -0,0 +1,22 @@
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class LineTool : ShapeTool
+    {
+        public override ToolType ToolType => ToolType.Line;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            CreateLine(layer, startingCoords, color, toolSize);
+            return new BitmapPixelChanges();
+        }
+
+        public void CreateLine(Layer layer, Coordinates coordinates, Color color, int size)
+        {           
+            layer.LayerBitmap.DrawLineBresenham(coordinates.X, coordinates.Y, MousePositionConverter.CurrentCoordinates.X,
+                MousePositionConverter.CurrentCoordinates.Y, color);
+        }
+    }
+}

+ 29 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/PenTool.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class PenTool : Tool
+    {
+        public override ToolType ToolType => ToolType.Pen;
+
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            return Draw(startingCoords, color, toolSize);
+        }
+
+        public BitmapPixelChanges Draw(Coordinates startingCoords, Color color, int toolSize)
+        {
+            int x1, y1, x2, y2;
+            DoubleCords centeredCoords = CoordinatesCalculator.CalculateThicknessCenter(startingCoords, toolSize);
+            x1 = centeredCoords.Coords1.X;
+            y1 = centeredCoords.Coords1.Y;
+            x2 = centeredCoords.Coords2.X;
+            y2 = centeredCoords.Coords2.Y;
+            return new BitmapPixelChanges(CoordinatesCalculator.RectangleToCoordinates(x1, y1, x2, y2), color);
+        }
+    }
+}

+ 22 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/RectangleTool.cs

@@ -0,0 +1,22 @@
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class RectangleTool : ShapeTool
+    {
+        public override ToolType ToolType => ToolType.Rectangle;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            CreateRectangle(layer,startingCoords,color,toolSize);
+            return new BitmapPixelChanges();
+        }
+
+        public void CreateRectangle(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            DoubleCords coordinates = CalculateCoordinatesForShapeRotation(startingCoords);
+            layer.LayerBitmap.DrawRectangle(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X, coordinates.Coords2.Y, color);
+        }
+    }
+}

+ 114 - 0
PixiEditorDotNetCore3/Models/Tools/ToolsManager.cs

@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
+using PixiEditor.ViewModels;
+using System.Timers;
+using System.Windows.Threading;
+using System.Threading;
+
+namespace PixiEditorDotNetCore3.Models.Tools
+{
+    public class ToolsManager
+    {
+        public List<Tool> Tools { get; set; } = new List<Tool>();
+        public Tool SelectedTool { get; private set; }
+        private bool _toolRecievedData = false;
+
+        private System.Timers.Timer _loopTimer;
+        private Layer _layer;
+        private WriteableBitmap _clonedBitmap;
+        private Coordinates _startCoordinates;
+        private Color _color;
+        private int _toolSzie;
+
+        public ToolsManager(List<Tool> tools)
+        {
+            Tools = tools;
+            _loopTimer = new System.Timers.Timer
+            {
+                Interval = 15,
+                Enabled = false,
+                AutoReset = true
+            };
+            _loopTimer.Elapsed += LoopTimer_Elapsed;
+        }
+
+        private void LoopTimer_Elapsed(object sender, ElapsedEventArgs e)
+        {
+            Application.Current.Dispatcher.Invoke(() =>
+            {              
+                if(_clonedBitmap != null)
+                {
+                    _layer.LayerBitmap.Clear();
+                    _layer.LayerBitmap.Blit(new Rect(new Size(_layer.Width, _layer.Height)), _clonedBitmap, new Rect(new Size(_layer.Width, _layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
+                }
+                BitmapPixelChanges changes = SelectedTool.Use(_layer, _startCoordinates, _color, _toolSzie);
+
+                if (!SelectedTool.ExecutesItself)
+                {
+                    _layer.ApplyPixels(changes, changes.PixelsColor);
+                }
+            });
+        }
+
+        public void SetTool(ToolType tool)
+        {
+              SelectedTool = Tools.Find(x => x.ToolType == tool);
+        }
+
+        public void StopExectuingTool()
+        {
+            _loopTimer.Enabled = false;
+            _toolRecievedData = false;
+            _clonedBitmap = null;
+        }
+
+        private void StartTimer()
+        {
+            _toolRecievedData = true;
+            _loopTimer.Enabled = true;
+        }
+
+        private void CloneBitmapIfToolIsShape()
+        {
+            if (SelectedTool.GetType().BaseType == typeof(ShapeTool))
+            {
+                _clonedBitmap = _layer.LayerBitmap.Clone();                
+            }
+        }
+
+        /// <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 void ExecuteTool(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            if (toolSize < 1)
+                return;
+
+            if(_toolRecievedData == false || (_toolRecievedData == true && SelectedTool.GetType().BaseType != typeof(ShapeTool)))
+            {
+                _startCoordinates = startingCoords;
+                _layer = layer;
+                _color = color;
+                _toolSzie = toolSize;
+            }
+
+            if (_loopTimer.Enabled == false)
+            {
+                StartTimer();
+                CloneBitmapIfToolIsShape();
+            }
+        }
+
+    }
+}

+ 20 - 7
PixiEditorDotNetCore3/Models/UndoManager.cs

@@ -17,8 +17,6 @@ namespace PixiEditorDotNetCore3.Models
 {
 {
     public static class UndoManager
     public static class UndoManager
     {
     {
-        private const int MaximumChangesInRam = 1;
-
         public static StackEx<Change> UndoStack { get; set; } = new StackEx<Change>(); 
         public static StackEx<Change> UndoStack { get; set; } = new StackEx<Change>(); 
         public static StackEx<Change> RedoStack { get; set; } = new StackEx<Change>();
         public static StackEx<Change> RedoStack { get; set; } = new StackEx<Change>();
         private static bool _stopRecording = false; 
         private static bool _stopRecording = false; 
@@ -61,10 +59,12 @@ namespace PixiEditorDotNetCore3.Models
         {
         {
             if (_stopRecording == false)
             if (_stopRecording == false)
             {
             {
-                if (_recordedChanges.Count < 2)
+                if (_recordedChanges.Count >= 2)
                 {
                 {
-                    _recordedChanges.Add(new Change(property, oldValue, undoDescription));
+                    _recordedChanges.RemoveAt(_recordedChanges.Count - 1);
                 }
                 }
+                _recordedChanges.Add(new Change(property, oldValue, undoDescription));
+
             }
             }
         }
         }
 
 
@@ -77,11 +77,23 @@ namespace PixiEditorDotNetCore3.Models
             if (_recordedChanges.Count > 0)
             if (_recordedChanges.Count > 0)
             {
             {
                 Change changeToSave = _recordedChanges[0];
                 Change changeToSave = _recordedChanges[0];
-                AddUndoChange(changeToSave.Property, changeToSave.OldValue, changeToSave.Description);
+                changeToSave.NewValue = _recordedChanges.Last().OldValue;
+                AddUndoChange(changeToSave);
                 _recordedChanges.Clear();
                 _recordedChanges.Clear();
             }
             }
             _stopRecording = false;
             _stopRecording = false;
         }
         }
+
+        public static void AddUndoChange(Change change)
+        {
+            if (_lastChangeWasUndo == false && RedoStack.Count > 0) //Cleares RedoStack if las move wasn't redo or undo and if redo stack is greater than 0
+            {
+                RedoStack.Clear();
+            }
+            _lastChangeWasUndo = false;
+            UndoStack.Push(change);
+            Debug.WriteLine("UndoStackCount: " + UndoStack.Count + " RedoStackCount: " + RedoStack.Count);
+        }
         /// <summary>
         /// <summary>
         /// Adds property change to UndoStack
         /// Adds property change to UndoStack
         /// </summary>
         /// </summary>
@@ -94,7 +106,7 @@ namespace PixiEditorDotNetCore3.Models
             if(_lastChangeWasUndo == false && RedoStack.Count > 0) //Cleares RedoStack if las move wasn't redo or undo and if redo stack is greater than 0
             if(_lastChangeWasUndo == false && RedoStack.Count > 0) //Cleares RedoStack if las move wasn't redo or undo and if redo stack is greater than 0
             {
             {
                 RedoStack.Clear();
                 RedoStack.Clear();
-            }
+            }            
             _lastChangeWasUndo = false;
             _lastChangeWasUndo = false;
             UndoStack.Push(new Change(property, oldValue, undoDescription));
             UndoStack.Push(new Change(property, oldValue, undoDescription));
             Debug.WriteLine("UndoStackCount: " + UndoStack.Count + " RedoStackCount: " + RedoStack.Count);
             Debug.WriteLine("UndoStackCount: " + UndoStack.Count + " RedoStackCount: " + RedoStack.Count);
@@ -118,7 +130,8 @@ namespace PixiEditorDotNetCore3.Models
         {
         {
             _lastChangeWasUndo = true;
             _lastChangeWasUndo = true;
             PropertyInfo propinfo = MainRoot.GetType().GetProperty(RedoStack.Peek().Property);
             PropertyInfo propinfo = MainRoot.GetType().GetProperty(RedoStack.Peek().Property);
-            propinfo.SetValue(MainRoot, RedoStack.Pop().OldValue);
+            propinfo.SetValue(MainRoot, RedoStack.Peek().NewValue);
+            UndoStack.Push(RedoStack.Pop());
 
 
         }
         }
     }
     }

+ 1 - 0
PixiEditorDotNetCore3/ViewModels/NewFileMenuViewModel.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
+using PixiEditorDotNetCore3.Models;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;

+ 65 - 53
PixiEditorDotNetCore3/ViewModels/ViewModelMain.cs

@@ -1,27 +1,17 @@
-using Microsoft.Win32;
-using PixiEditor.Helpers;
+using PixiEditor.Helpers;
 using PixiEditorDotNetCore3.Models.Enums;
 using PixiEditorDotNetCore3.Models.Enums;
 using PixiEditorDotNetCore3.Models.Tools;
 using PixiEditorDotNetCore3.Models.Tools;
-using PixiEditor.Views;
 using PixiEditorDotNetCore3.Models;
 using PixiEditorDotNetCore3.Models;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
-using System.Windows.Data;
 using System.Windows.Input;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
-using Xceed.Wpf.Toolkit.Zoombox;
-
+using PixiTools = PixiEditorDotNetCore3.Models.Tools.Tools;
+
 namespace PixiEditor.ViewModels
 namespace PixiEditor.ViewModels
 {
 {
     class ViewModelMain : ViewModelBase
     class ViewModelMain : ViewModelBase
@@ -63,13 +53,6 @@ namespace PixiEditor.ViewModels
         {
         {
             get { return _activeLayer; }
             get { return _activeLayer; }
             set {
             set {
-                if (_activeLayer != null)
-                {
-                    UndoManager.AddUndoChange("ActiveLightLayer",
-                        new LightLayer(_activeLayer.LayerBitmap.ToByteArray(), ActiveLayer.Height, ActiveLayer.Width),
-                        "Layer Changed");
-                }
-
                 _activeLayer = value;
                 _activeLayer = value;
                 RefreshImage();
                 RefreshImage();
                 RaisePropertyChanged("ActiveLayer");
                 RaisePropertyChanged("ActiveLayer");
@@ -78,7 +61,13 @@ namespace PixiEditor.ViewModels
 
 
         public LightLayer ActiveLightLayer
         public LightLayer ActiveLightLayer
         {
         {
-            get => new LightLayer(_activeLayer.LayerBitmap.ToByteArray(), ActiveLayer.Height, ActiveLayer.Width);
+            get 
+            {
+                if (_activeLayer != null)
+                    return new LightLayer(_activeLayer.LayerBitmap.ToByteArray(), ActiveLayer.Height, ActiveLayer.Width);
+                else
+                    return null;
+            }
             set => ActiveLayer = new Layer(BitmapConverter.BytesToWriteableBitmap(ActiveLayer.Width, ActiveLayer.Height,value.LayerBytes));
             set => ActiveLayer = new Layer(BitmapConverter.BytesToWriteableBitmap(ActiveLayer.Width, ActiveLayer.Height,value.LayerBytes));
         }
         }
 
 
@@ -121,14 +110,28 @@ namespace PixiEditor.ViewModels
             get { return _secondaryColor; }
             get { return _secondaryColor; }
             set { if (_secondaryColor != value) { _secondaryColor = value; RaisePropertyChanged("SecondaryColor"); } }
             set { if (_secondaryColor != value) { _secondaryColor = value; RaisePropertyChanged("SecondaryColor"); } }
         }
         }
-       
 
 
+        private Color _selectedColor = Colors.White;
+
+        public Color SelectedColor
+        {
+            get { return _selectedColor; }
+            set 
+            { 
+                if(_selectedColor != value) 
+                    _selectedColor = value;
+                RaisePropertyChanged("SelectedColor");
+            }
+        }
+
+
+
         private ToolType _selectedTool = ToolType.Pen;
         private ToolType _selectedTool = ToolType.Pen;
 
 
         public ToolType SelectedTool
         public ToolType SelectedTool
         {
         {
             get { return _selectedTool; }
             get { return _selectedTool; }
-            set { if (_selectedTool != value) { _selectedTool = value; RaisePropertyChanged("SelectedTool"); } }
+            set { if (_selectedTool != value) { _selectedTool = value; primaryToolSet.SetTool(SelectedTool); RaisePropertyChanged("SelectedTool"); } }
         }
         }
 
 
 
 
@@ -140,7 +143,7 @@ namespace PixiEditor.ViewModels
             set { if (_toolSize != value) { _toolSize = value; RaisePropertyChanged("ToolSize"); } }
             set { if (_toolSize != value) { _toolSize = value; RaisePropertyChanged("ToolSize"); } }
         }
         }
 
 
-        private ToolSet primaryToolSet;
+        private ToolsManager primaryToolSet;
 
 
         public ViewModelMain()
         public ViewModelMain()
         {
         {
@@ -155,8 +158,10 @@ namespace PixiEditor.ViewModels
             MouseUpCommand = new RelayCommand(MouseUp);
             MouseUpCommand = new RelayCommand(MouseUp);
             RecenterZoomboxCommand = new RelayCommand(RecenterZoombox);
             RecenterZoomboxCommand = new RelayCommand(RecenterZoombox);
             OpenFileCommand = new RelayCommand(OpenFile);
             OpenFileCommand = new RelayCommand(OpenFile);
-            primaryToolSet = new ToolSet();
+            primaryToolSet = new ToolsManager(new List<Tool> { new PixiTools.PenTool(), new PixiTools.FloodFill(), new PixiTools.LineTool(),
+            new PixiTools.CircleTool(), new PixiTools.RectangleTool(), new PixiTools.EarserTool(), new PixiTools.BrightnessTool()});
             UndoManager.SetMainRoot(this);
             UndoManager.SetMainRoot(this);
+            primaryToolSet.SetTool(SelectedTool);
         }
         }
 
 
         #region Undo/Redo
         #region Undo/Redo
@@ -212,6 +217,7 @@ namespace PixiEditor.ViewModels
         private void MouseUp(object parameter)
         private void MouseUp(object parameter)
         {
         {
             UndoManager.StopRecording();
             UndoManager.StopRecording();
+            primaryToolSet.StopExectuingTool();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -220,39 +226,45 @@ namespace PixiEditor.ViewModels
         /// <param name="parameter"></param>
         /// <param name="parameter"></param>
         private void MouseMoveOrClick(object parameter)
         private void MouseMoveOrClick(object parameter)
         {
         {
-            Color color;
             Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
             Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
-            if (Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                color = PrimaryColor;
-            }
-            else if(Mouse.RightButton == MouseButtonState.Pressed)
-            {
-                color = SecondaryColor;
+            MousePositionConverter.CurrentCoordinates = cords;
 
 
+            if (Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                SelectedColor = PrimaryColor;
             }
             }
-            else
-            {
-                return;
+            else if(Mouse.RightButton == MouseButtonState.Pressed)
+            {
+                SelectedColor = SecondaryColor;
             }
             }
-
-            if (SelectedTool != ToolType.ColorPicker)
-            {
-                primaryToolSet.UpdateCoordinates(cords);
-                primaryToolSet.ExecuteTool(ActiveLayer, cords, color, ToolSize,SelectedTool);
-                RefreshImage();
-            }
-            else
-            {
-                if (Mouse.LeftButton == MouseButtonState.Pressed)
-                {
-                    PrimaryColor = ToolSet.ColorPicker(ActiveLayer, cords);
-                }
-                else
-                {
-                    SecondaryColor = ToolSet.ColorPicker(ActiveLayer, cords);
-                }
+            else
+            {
+                return;
             }
             }
+
+            if (SelectedTool != ToolType.ColorPicker)
+            {
+                UndoManager.RecordChanges("ActiveLightLayer", new LightLayer(ActiveLayer.LayerBitmap.ToByteArray(), (int)ActiveLayer.LayerBitmap.Height, 
+                    (int)ActiveLayer.LayerBitmap.Width), $"Used {SelectedTool.ToString()}");
+                primaryToolSet.ExecuteTool(ActiveLayer, cords, SelectedColor, ToolSize);
+                RefreshImage();
+            }
+            else
+            {
+                ExecuteColorPicker(cords);
+            }
+        }
+
+        private void ExecuteColorPicker(Coordinates cords)
+        {
+            if (Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                PrimaryColor = ActiveLayer.LayerBitmap.GetPixel(cords.X, cords.Y);
+            }
+            else
+            {
+                SecondaryColor = ActiveLayer.LayerBitmap.GetPixel(cords.X, cords.Y);
+            }
         }
         }
 
 
         private void RefreshImage()
         private void RefreshImage()

+ 4 - 4
PixiEditorDotNetCore3/Views/MainWindow.xaml

@@ -11,7 +11,7 @@
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
         xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
         xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
         mc:Ignorable="d"
         mc:Ignorable="d"
-        Title="Pixi" Height="1000" Width="1600" Background="#FF252424" WindowStartupLocation="CenterScreen" WindowState="Maximized" DataContext="{DynamicResource ViewModelMain}">
+        Title="Pixi" Height="1000" Width="1600" Background="#FF252424" WindowStartupLocation="CenterScreen"  WindowState="Maximized" DataContext="{DynamicResource ViewModelMain}">
     <Window.Resources>
     <Window.Resources>
         <vm:ViewModelMain x:Key="ViewModelMain"/>
         <vm:ViewModelMain x:Key="ViewModelMain"/>
         <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
         <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
@@ -175,7 +175,7 @@
                     <ImageBrush ImageSource="/Images/EarserImage.png" Stretch="Uniform"/>
                     <ImageBrush ImageSource="/Images/EarserImage.png" Stretch="Uniform"/>
                 </Button.Background>
                 </Button.Background>
             </Button>
             </Button>
-            <Button Style="{StaticResource ToolButtonStyle}" Command="{Binding SelectToolCommand, Mode=OneWay}" CommandParameter="Lighten" ToolTip="Makes pixel brighter or darker pixel (U)">
+            <Button Style="{StaticResource ToolButtonStyle}" Command="{Binding SelectToolCommand, Mode=OneWay}" CommandParameter="Brightness" ToolTip="Makes pixel brighter or darker pixel (U)">
                 <Button.Background>
                 <Button.Background>
                     <ImageBrush ImageSource="/Images/LightenImage.png" Stretch="Uniform"/>
                     <ImageBrush ImageSource="/Images/LightenImage.png" Stretch="Uniform"/>
                 </Button.Background>
                 </Button.Background>
@@ -183,12 +183,12 @@
         </StackPanel>
         </StackPanel>
 
 
         <Grid VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="60" Grid.Column="0" Grid.Row="1" Margin="0,0,0,0" Height="65">
         <Grid VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="60" Grid.Column="0" Grid.Row="1" Margin="0,0,0,0" Height="65">
-            <Rectangle Height="40" Width="40" HorizontalAlignment="Left" VerticalAlignment="Top" Stroke="Black" StrokeThickness="1">
+            <Rectangle Height="40" Width="40" HorizontalAlignment="Left" VerticalAlignment="Top" Stroke="Black" StrokeThickness="1" Panel.ZIndex="1">
                 <Rectangle.Fill>
                 <Rectangle.Fill>
                     <SolidColorBrush Color="{Binding PrimaryColor, Mode=OneWay}"/>
                     <SolidColorBrush Color="{Binding PrimaryColor, Mode=OneWay}"/>
                 </Rectangle.Fill>
                 </Rectangle.Fill>
             </Rectangle>
             </Rectangle>
-            <xctk:ColorPicker Width="40" Height="40" VerticalAlignment="Top" HorizontalAlignment="Left" UsingAlphaChannel="True" AvailableColorsSortingMode="Alphabetical" ShowDropDownButton="False" Background="Transparent" BorderThickness="0" ShowRecentColors="True" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"></xctk:ColorPicker>
+            <xctk:ColorPicker Width="40" Panel.ZIndex="2" Height="40" VerticalAlignment="Top" HorizontalAlignment="Left" UsingAlphaChannel="True" AvailableColorsSortingMode="Alphabetical" ShowDropDownButton="False" Background="Transparent" BorderThickness="0" ShowRecentColors="True" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"></xctk:ColorPicker>
             <Rectangle Height="40" Width="40" HorizontalAlignment="Right" VerticalAlignment="Bottom" Stroke="Black" StrokeThickness="1" Margin="0,0,4,5">
             <Rectangle Height="40" Width="40" HorizontalAlignment="Right" VerticalAlignment="Bottom" Stroke="Black" StrokeThickness="1" Margin="0,0,4,5">
                 <Rectangle.Fill>
                 <Rectangle.Fill>
                     <SolidColorBrush Color="{Binding SecondaryColor, Mode=OneWay}"/>
                     <SolidColorBrush Color="{Binding SecondaryColor, Mode=OneWay}"/>

+ 8 - 10
PixiEditorTests/PixiEditorTests.csproj

@@ -7,13 +7,13 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="nunit" Version="3.11.0" />
-    <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="ToolsTests\" />
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.5">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
+    <PackageReference Include="nunit" Version="3.12.0" />
+    <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
@@ -21,9 +21,7 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <Reference Include="PresentationCore">
-      <HintPath>C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll</HintPath>
-    </Reference>
+    <Folder Include="ViewModelsTests\" />
   </ItemGroup>
   </ItemGroup>
 
 
 </Project>
 </Project>

+ 25 - 0
PixiEditorTests/WorkspaceTests/ImageGeneratorTests.cs

@@ -0,0 +1,25 @@
+using NUnit.Framework;
+using PixiEditorDotNetCore3.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace PixiEditorTests.ToolsTests
+{
+    [TestFixture, Apartment(System.Threading.ApartmentState.STA)]
+    public class ImageGeneratorTests
+    {
+        [TestCase(16,16)]
+        [TestCase(1024,12)]
+        [TestCase(50000,50000)]
+        public void ImageIsPixelArtReady(int width, int height)
+        {
+            Image img = ImageGenerator.GenerateForPixelArts(width, height);
+            
+            Assert.IsTrue(img.Stretch == Stretch.Uniform && RenderOptions.GetBitmapScalingMode(img) == BitmapScalingMode.NearestNeighbor
+                && RenderOptions.GetEdgeMode(img) == EdgeMode.Aliased && img.Width == width && img.Height == height);
+        }
+    }
+}

+ 20 - 0
PixiEditorTests/WorkspaceTests/ToolsTests/CoordinatesCalculatorTests.cs

@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using PixiEditorDotNetCore3.Models;
+using PixiEditorDotNetCore3.Models.Tools.Tools;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace PixiEditorTests.WorkspaceTests.ToolsTests
+{
+    [TestFixture]
+    public class CoordinatesCalculatorTests
+    {
+        [TestCase(0,0,2,2, ExpectedResult = 9)]
+        [TestCase(0,0,10,10, ExpectedResult = 121)]
+        public int RectangleToCoordinatesAmountTest(int x1, int y1, int x2, int y2)
+        {
+            return CoordinatesCalculator.RectangleToCoordinates(x1, y1, x2, y2).Length;
+        }
+    }
+}