flabbet 3 years ago
parent
commit
db791e800c
69 changed files with 997 additions and 1337 deletions
  1. 3 12
      PixiEditor/Helpers/Converters/BoolToBrushConverter.cs
  2. 4 4
      PixiEditor/Helpers/Converters/BoolToIntConverter.cs
  3. 7 26
      PixiEditor/Helpers/Converters/BrushTuple.cs
  4. 4 9
      PixiEditor/Helpers/Converters/DoubleToIntConverter.cs
  5. 7 15
      PixiEditor/Helpers/Converters/EmptyStringToVisibilityConverter.cs
  6. 3 8
      PixiEditor/Helpers/Converters/EqualityBoolToVisibilityConverter.cs
  7. 5 7
      PixiEditor/Helpers/Converters/FileExtensionToColorConverter.cs
  8. 12 28
      PixiEditor/Helpers/Converters/FinalIsVisibleToVisiblityConverter.cs
  9. 3 4
      PixiEditor/Helpers/Converters/FloorConverter.cs
  10. 13 33
      PixiEditor/Helpers/Converters/FormattedColorConverter.cs
  11. 4 7
      PixiEditor/Helpers/Converters/IndentConverter.cs
  12. 6 8
      PixiEditor/Helpers/Converters/IndexOfConverter.cs
  13. 4 4
      PixiEditor/Helpers/Converters/IntToPickerTypeConverter.cs
  14. 6 13
      PixiEditor/Helpers/Converters/IntToViewportRectConverter.cs
  15. 6 13
      PixiEditor/Helpers/Converters/InverseBooleanConverter.cs
  16. 3 7
      PixiEditor/Helpers/Converters/IsSpecifiedTypeConverter.cs
  17. 3 12
      PixiEditor/Helpers/Converters/KeyToStringConverter.cs
  18. 6 10
      PixiEditor/Helpers/Converters/LayerStructureToGroupsConverter.cs
  19. 7 20
      PixiEditor/Helpers/Converters/LayerToFinalOpacityConverter.cs
  20. 7 3
      PixiEditor/Helpers/Converters/LayersToStructuredLayersConverter.cs
  21. 26 0
      PixiEditor/Helpers/Converters/MarkupConverter.cs
  22. 19 0
      PixiEditor/Helpers/Converters/MultiValueMarkupConverter.cs
  23. 6 9
      PixiEditor/Helpers/Converters/NotNullToBoolConverter.cs
  24. 4 8
      PixiEditor/Helpers/Converters/NotNullToVisibilityConverter.cs
  25. 4 24
      PixiEditor/Helpers/Converters/NullToVisibilityConverter.cs
  26. 6 10
      PixiEditor/Helpers/Converters/OppositeVisibilityConverter.cs
  27. 23 0
      PixiEditor/Helpers/Converters/SingleInstanceConverter.cs
  28. 20 0
      PixiEditor/Helpers/Converters/SingleInstanceMultiValueConverter.cs
  29. 6 12
      PixiEditor/Helpers/Converters/ThresholdVisibilityConverter.cs
  30. 5 4
      PixiEditor/Helpers/Converters/ToolSizeToIntConverter.cs
  31. 3 7
      PixiEditor/Helpers/Converters/ZoomToViewportConverter.cs
  32. 23 124
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  33. 0 106
      PixiEditor/Models/Controllers/PixelChangesController.cs
  34. 0 24
      PixiEditor/Models/DataHolders/LayerChange.cs
  35. 31 10
      PixiEditor/Models/Layers/Layer.cs
  36. 14 0
      PixiEditor/Models/Position/CoordinatesCalculator.cs
  37. 18 30
      PixiEditor/Models/Tools/BitmapOperationTool.cs
  38. 7 6
      PixiEditor/Models/Tools/ShapeTool.cs
  39. 31 33
      PixiEditor/Models/Tools/Tool.cs
  40. 14 12
      PixiEditor/Models/Tools/Tools/BrightnessTool.cs
  41. 53 45
      PixiEditor/Models/Tools/Tools/CircleTool.cs
  42. 38 37
      PixiEditor/Models/Tools/Tools/EraserTool.cs
  43. 1 1
      PixiEditor/Models/Tools/Tools/FloodFill.cs
  44. 60 44
      PixiEditor/Models/Tools/Tools/LineTool.cs
  45. 225 225
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  46. 148 151
      PixiEditor/Models/Tools/Tools/PenTool.cs
  47. 30 39
      PixiEditor/Models/Tools/Tools/RectangleTool.cs
  48. 7 7
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  49. 5 29
      PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
  50. 0 6
      PixiEditor/ViewModels/ViewModelMain.cs
  51. 2 5
      PixiEditor/Views/Dialogs/HelloTherePopup.xaml
  52. 0 4
      PixiEditor/Views/Dialogs/ImportFilePopup.xaml
  53. 0 4
      PixiEditor/Views/Dialogs/NewFilePopup.xaml
  54. 0 5
      PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml
  55. 0 5
      PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml
  56. 2 6
      PixiEditor/Views/Dialogs/SaveFilePopup.xaml
  57. 3 4
      PixiEditor/Views/Dialogs/SettingsWindow.xaml
  58. 2 3
      PixiEditor/Views/Dialogs/ShortcutPopup.xaml
  59. 7 7
      PixiEditor/Views/MainWindow.xaml
  60. 8 9
      PixiEditor/Views/UserControls/DiscordRPPreview.xaml
  61. 1 4
      PixiEditor/Views/UserControls/EditableTextBlock.xaml
  62. 6 2
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml
  63. 1 4
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml
  64. 1 4
      PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml
  65. 16 10
      PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml
  66. 1 1
      PixiEditor/Views/UserControls/PreviewWindow.xaml
  67. 2 5
      PixiEditor/Views/UserControls/SizeInput.xaml
  68. 4 8
      PixiEditor/Views/UserControls/SmallColorPicker.xaml
  69. 1 1
      PixiEditor/Views/UserControls/SwatchesView.xaml

+ 3 - 12
PixiEditor/Helpers/Converters/BoolToBrushConverter.cs

@@ -1,24 +1,15 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class BoolToBrushConverter : IValueConverter
+    public class BoolToBrushConverter
+        : SingleInstanceConverter<BoolToBrushConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             BrushTuple tuple = (BrushTuple)parameter;
             return (bool)value ? tuple.FirstBrush : tuple.SecondBrush;
         }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
     }
 }

+ 4 - 4
PixiEditor/Helpers/Converters/BoolToIntConverter.cs

@@ -1,17 +1,17 @@
 using System;
 using System.Globalization;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class BoolToIntConverter : IValueConverter
+    public class BoolToIntConverter
+        : SingleInstanceConverter<BoolToIntConverter>
     {
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return value.ToString() == "0";
         }
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (value is bool boolean)
             {

+ 7 - 26
PixiEditor/Helpers/Converters/BrushTuple.cs

@@ -1,39 +1,24 @@
 using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
 using System.Windows.Media;
 
 namespace PixiEditor.Helpers.Converters
 {
     public class BrushTuple : NotifyableObject, ITuple
     {
-        public object this[int index]
+        public object this[int index] => index switch
         {
-            get
-            {
-                return index switch
-                {
-                    0 => FirstBrush,
-                    1 => SecondBrush,
-                    _ => throw new IndexOutOfRangeException("Index was out of range")
-                };
-            }
-        }
+            0 => FirstBrush,
+            1 => SecondBrush,
+            _ => throw new ArgumentOutOfRangeException(nameof(index))
+        };
 
         private Brush item1;
 
         public Brush FirstBrush
         {
             get => item1;
-            set
-            {
-                item1 = value;
-                RaisePropertyChanged(nameof(FirstBrush));
-            }
+            set => SetProperty(ref item1, value);
         }
 
         private Brush item2;
@@ -41,11 +26,7 @@ namespace PixiEditor.Helpers.Converters
         public Brush SecondBrush
         {
             get => item2;
-            set
-            {
-                item2 = value;
-                RaisePropertyChanged(nameof(SecondBrush));
-            }
+            set => SetProperty(ref item2, value);
         }
 
         public int Length => 2;

+ 4 - 9
PixiEditor/Helpers/Converters/DoubleToIntConverter.cs

@@ -1,14 +1,14 @@
 using System;
 using System.Globalization;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class DoubleToIntConverter : IValueConverter
+    public class DoubleToIntConverter :
+        SingleInstanceConverter<DoubleToIntConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            if (value is double || value is float)
+            if (value is double or float)
             {
                 double val = (double)value;
                 return (int)val;
@@ -16,10 +16,5 @@ namespace PixiEditor.Helpers.Converters
 
             return value;
         }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
     }
 }

+ 7 - 15
PixiEditor/Helpers/Converters/EmptyStringToVisibilityConverter.cs

@@ -1,26 +1,18 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Windows;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class EmptyStringToVisibilityConverter : IValueConverter
+    public class EmptyStringToVisibilityConverter :
+        SingleInstanceConverter<EmptyStringToVisibilityConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            string s = (string)value;
-
-            return !string.IsNullOrEmpty(s) ? Visibility.Visible : Visibility.Collapsed;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
+            return
+                string.IsNullOrEmpty((string)value)
+                ? Visibility.Collapsed
+                : Visibility.Visible;
         }
     }
 }

+ 3 - 8
PixiEditor/Helpers/Converters/EqualityBoolToVisibilityConverter.cs

@@ -1,20 +1,15 @@
 using System;
 using System.Globalization;
 using System.Windows;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class EqualityBoolToVisibilityConverter : IValueConverter
+    public class EqualityBoolToVisibilityConverter :
+        SingleInstanceConverter<EqualityBoolToVisibilityConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return value.Equals(parameter) ? Visibility.Visible : Visibility.Collapsed;
         }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
     }
 }

+ 5 - 7
PixiEditor/Helpers/Converters/FileExtensionToColorConverter.cs

@@ -1,11 +1,11 @@
 using System;
 using System.Globalization;
-using System.Windows.Data;
 using System.Windows.Media;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class FileExtensionToColorConverter : IValueConverter
+    public class FileExtensionToColorConverter :
+        SingleInstanceConverter<FileExtensionToColorConverter>
     {
         private static readonly SolidColorBrush PixiBrush = ColorBrush(226, 1, 45);
 
@@ -15,7 +15,7 @@ namespace PixiEditor.Helpers.Converters
 
         private static readonly SolidColorBrush UnknownBrush = ColorBrush(100, 100, 100);
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             string extension = (string)value;
 
@@ -35,11 +35,9 @@ namespace PixiEditor.Helpers.Converters
             return UnknownBrush;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        private static SolidColorBrush ColorBrush(byte r, byte g, byte b)
         {
-            throw new NotImplementedException();
+            return new SolidColorBrush(Color.FromRgb(r, g, b));
         }
-
-        private static SolidColorBrush ColorBrush(byte r, byte g, byte b) => new SolidColorBrush(Color.FromRgb(r, g, b));
     }
 }

+ 12 - 28
PixiEditor/Helpers/Converters/FinalIsVisibleToVisiblityConverter.cs

@@ -1,4 +1,5 @@
-using PixiEditor.Models.Layers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Layers;
 using PixiEditor.ViewModels;
 using System;
 using System.Globalization;
@@ -8,36 +9,19 @@ using System.Windows.Markup;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class FinalIsVisibleToVisiblityConverter : MarkupExtension, IMultiValueConverter
+    public class FinalIsVisibleToVisiblityConverter
+        : SingleInstanceMultiValueConverter<FinalIsVisibleToVisiblityConverter>
     {
-        private static FinalIsVisibleToVisiblityConverter converter = null;
-
-        public override object ProvideValue(IServiceProvider serviceProvider)
-        {
-            if (converter == null)
-            {
-                converter = new FinalIsVisibleToVisiblityConverter();
-            }
-
-            return converter;
-        }
-
-        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
         {
-            if (values[0] is Layer layer)
-            {
-                if (ViewModelMain.Current?.BitmapManager?.ActiveDocument != null)
-                {
-                    return ViewModelMain.Current.BitmapManager.ActiveDocument.GetFinalLayerIsVisible(layer) ? Visibility.Visible : Visibility.Collapsed;
-                }
-            }
-
-            return Visibility.Visible;
-        }
+            BitmapManager bitmapManager = ViewModelMain.Current?.BitmapManager;
 
-        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
+            return
+                (values[0] is not Layer layer ||
+                bitmapManager.ActiveDocument is null ||
+                bitmapManager.ActiveDocument.GetFinalLayerIsVisible(layer))
+                    ? Visibility.Visible
+                    : (object)Visibility.Collapsed;
         }
     }
 }

+ 3 - 4
PixiEditor/Helpers/Converters/FloorConverter.cs

@@ -1,17 +1,16 @@
 using System;
 using System.Globalization;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    class FloorConverter : IValueConverter
+    public class FloorConverter : SingleInstanceConverter<FloorConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return Math.Floor((double)value);
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return value;
         }

+ 13 - 33
PixiEditor/Helpers/Converters/FormattedColorConverter.cs

@@ -1,48 +1,28 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Data;
-using System.Windows.Markup;
 using System.Windows.Media;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class FormattedColorConverter : MarkupExtension, IMultiValueConverter
+    public class FormattedColorConverter
+        : SingleInstanceMultiValueConverter<FormattedColorConverter>
     {
-        private static FormattedColorConverter converter;
-        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
         {
-            if(values != null && values.Length > 1 && values[0] is Color color && values[1] is string format)
+            if (values == null ||
+                values.Length <= 1 ||
+                values[0] is not Color color ||
+                values[1] is not string format)
             {
-                switch (format.ToLower())
-                {
-                    case "hex":
-                        return color.ToString();
-                    case "rgba":
-                        return $"({color.R}, {color.G}, {color.B}, {color.A})";
-                    default:
-                        break;
-                }
+                return "";
             }
 
-            return "";
-        }
-
-        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
-
-        public override object ProvideValue(IServiceProvider serviceProvider)
-        {
-            if(converter == null)
+            return format.ToLowerInvariant() switch
             {
-                converter = new FormattedColorConverter();
-            }
-            return converter;
+                "hex" => color.ToString(),
+                "rgba" => $"({color.R}, {color.G}, {color.B}, {color.A})",
+                _ => "",
+            };
         }
     }
 }

+ 4 - 7
PixiEditor/Helpers/Converters/IndentConverter.cs

@@ -1,24 +1,21 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class IndentConverter : IValueConverter
+    public class IndentConverter
+        : SingleInstanceConverter<IndentConverter>
     {
         private const int IndentSize = 20;
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return new GridLength(((GridLength)value).Value + IndentSize);
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return Binding.DoNothing;
         }

+ 6 - 8
PixiEditor/Helpers/Converters/IndexOfConverter.cs

@@ -6,9 +6,10 @@ using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class IndexOfConverter : IValueConverter
+    public class IndexOfConverter
+        : SingleInstanceConverter<IndexOfConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (value is Layer layer && ViewModelMain.Current.BitmapManager.ActiveDocument != null)
             {
@@ -16,12 +17,9 @@ namespace PixiEditor.Helpers.Converters
                 return index;
             }
 
-            return Binding.DoNothing;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
+            return value is Layer layer && bitmapManager.ActiveDocument != null
+                   ? bitmapManager.ActiveDocument.Layers.IndexOf(layer)
+                   : Binding.DoNothing;
         }
     }
 }

+ 4 - 4
PixiEditor/Helpers/Converters/IntToPickerTypeConverter.cs

@@ -1,18 +1,18 @@
 using ColorPicker.Models;
 using System;
 using System.Globalization;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class IntToPickerTypeConverter : IValueConverter
+    public class IntToPickerTypeConverter
+        : SingleInstanceConverter<IntToPickerTypeConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return (PickerType)value;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return (int)value;
         }

+ 6 - 13
PixiEditor/Helpers/Converters/IntToViewportRectConverter.cs

@@ -5,21 +5,14 @@ using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class IntToViewportRectConverter : IValueConverter
+    public class IntToViewportRectConverter
+        : SingleInstanceConverter<IntToViewportRectConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            if (parameter is string and "vertical")
-            {
-                return new Rect(0, 0, 1d / (int)value, 1d);
-            }
-
-            return new Rect(0, 0, 1d, 1d / (int)value);
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
+            return parameter is string and "vertical"
+                   ? new Rect(0, 0, 1d / (int)value, 1d)
+                   : (object)new Rect(0, 0, 1d, 1d / (int)value);
         }
     }
 }

+ 6 - 13
PixiEditor/Helpers/Converters/InverseBooleanConverter.cs

@@ -4,21 +4,14 @@ using System.Windows.Data;
 namespace PixiEditor.Helpers.Converters
 {
     [ValueConversion(typeof(bool), typeof(bool))]
-    public class InverseBooleanConverter : IValueConverter
+    public class InverseBooleanConverter
+        : SingleInstanceConverter<InverseBooleanConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
-            if (targetType != typeof(bool))
-            {
-                throw new InvalidOperationException("The target must be a boolean");
-            }
-
-            return !(bool)value;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
-        {
-            throw new NotSupportedException();
+            return targetType != typeof(bool)
+                   ? throw new InvalidOperationException("The target must be a boolean")
+                   : !(bool)value;
         }
     }
 }

+ 3 - 7
PixiEditor/Helpers/Converters/IsSpecifiedTypeConverter.cs

@@ -5,17 +5,13 @@ using System.Windows.Data;
 namespace PixiEditor.Helpers.Converters
 {
     [ValueConversion(typeof(object), typeof(bool))]
-    public class IsSpecifiedTypeConverter : IValueConverter
+    public class IsSpecifiedTypeConverter : MarkupConverter
     {
         public Type SpecifiedType { get; set; }
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return value != null && value.GetType() == SpecifiedType;
-        }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            throw new NotImplementedException();
+            return value != null && value.GetType() == SpecifiedType;
         }
     }
 }

+ 3 - 12
PixiEditor/Helpers/Converters/KeyToStringConverter.cs

@@ -1,17 +1,13 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Data;
 using System.Windows.Input;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class KeyToStringConverter : IValueConverter
+    public class KeyToStringConverter
+        : SingleInstanceConverter<KeyToStringConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (value is Key key)
             {
@@ -26,10 +22,5 @@ namespace PixiEditor.Helpers.Converters
                 return string.Empty;
             }
         }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
     }
 }

+ 6 - 10
PixiEditor/Helpers/Converters/LayerStructureToGroupsConverter.cs

@@ -9,21 +9,17 @@ using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class LayerStructureToGroupsConverter : IMultiValueConverter
+    public class LayerStructureToGroupsConverter
+        : SingleInstanceMultiValueConverter<LayerStructureToGroupsConverter>
     {
-        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
         {
-            if (values[0] is LayerStructure structure)
+            if (values[0] is not LayerStructure structure)
             {
-                return GetSubGroups(structure.Groups);
+                return Binding.DoNothing;
             }
 
-            return Binding.DoNothing;
-        }
-
-        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
+            return GetSubGroups(structure.Groups);
         }
 
         private ObservableCollection<GuidStructureItem> GetSubGroups(IEnumerable<GuidStructureItem> groups)

+ 7 - 20
PixiEditor/Helpers/Converters/LayerToFinalOpacityConverter.cs

@@ -1,18 +1,15 @@
-using System;
-using System.Globalization;
-using System.Windows.Data;
-using System.Windows.Markup;
-using PixiEditor.Models.Layers;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers.Utils;
 using PixiEditor.ViewModels;
+using System;
+using System.Globalization;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class LayerToFinalOpacityConverter : MarkupExtension, IMultiValueConverter
+    public class LayerToFinalOpacityConverter
+        : SingleInstanceMultiValueConverter<LayerToFinalOpacityConverter>
     {
-        private static LayerToFinalOpacityConverter converter;
-
-        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
         {
             if (values.Length > 0 && values[0] is Layer layer && ViewModelMain.Current?.BitmapManager?.ActiveDocument != null)
             {
@@ -22,19 +19,9 @@ namespace PixiEditor.Helpers.Converters
             return null;
         }
 
-        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+        public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
         {
             return null;
         }
-
-        public override object ProvideValue(IServiceProvider serviceProvider)
-        {
-            if(converter == null)
-            {
-                converter = new LayerToFinalOpacityConverter();
-            }
-
-            return converter;
-        }
     }
 }

+ 7 - 3
PixiEditor/Helpers/Converters/LayersToStructuredLayersConverter.cs

@@ -10,14 +10,15 @@ using System.Windows.Data;
 namespace PixiEditor.Helpers.Converters
 {
     // TODO: Implement rebuilding only changed items instead whole tree
-    public class LayersToStructuredLayersConverter : IMultiValueConverter
+    public class LayersToStructuredLayersConverter
+        : MultiValueMarkupConverter
     {
         private static StructuredLayerTree cachedTree;
         private List<Guid> lastLayerGuids = new List<Guid>();
         private IList<Layer> lastLayers = new List<Layer>();
         private ObservableCollection<GuidStructureItem> lastStructure = new ObservableCollection<GuidStructureItem>();
 
-        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
         {
             if (values[0] is ObservableCollection<Layer> layers && values[1] is LayerStructure structure)
             {
@@ -42,10 +43,12 @@ namespace PixiEditor.Helpers.Converters
 
             return DependencyProperty.UnsetValue;
         }
-        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+
+        public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
         {
             throw new ArgumentException("Value is not a StructuredLayerTree");
         }
+
         private bool LayerOrderIsDifferent(IList<Layer> layers)
         {
             var guids = layers.Select(x => x.LayerGuid).ToArray();
@@ -65,6 +68,7 @@ namespace PixiEditor.Helpers.Converters
             }
             return false;
         }
+
         private bool TryFindStructureDifferences(LayerStructure structure)
         {
             bool structureModified = false;

+ 26 - 0
PixiEditor/Helpers/Converters/MarkupConverter.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using System.Windows.Markup;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public abstract class MarkupConverter : MarkupExtension, IValueConverter
+    {
+        public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
+
+        public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            return this;
+        }
+    }
+}

+ 19 - 0
PixiEditor/Helpers/Converters/MultiValueMarkupConverter.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Markup;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public abstract class MultiValueMarkupConverter : MarkupExtension, IMultiValueConverter
+    {
+        public abstract object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
+
+        public virtual object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override object ProvideValue(IServiceProvider serviceProvider) => this;
+    }
+}

+ 6 - 9
PixiEditor/Helpers/Converters/NotNullToBoolConverter.cs

@@ -5,20 +5,17 @@ using System.Windows.Data;
 namespace PixiEditor.Helpers.Converters
 {
     [ValueConversion(typeof(object), typeof(bool))]
-    public class NotNullToBoolConverter : IValueConverter
+    public class NotNullToBoolConverter
+        : SingleInstanceConverter<NotNullToBoolConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            bool result = value != null;
-            if (parameter != null)
-            {
-                return !result;
-            }
+            bool result = value is not null;
 
-            return result;
+            return parameter is null ? result : !result;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return value;
         }

+ 4 - 8
PixiEditor/Helpers/Converters/NotNullToVisibilityConverter.cs

@@ -6,13 +6,14 @@ using System.Windows.Data;
 namespace PixiEditor.Helpers.Converters
 {
     [ValueConversion(typeof(object), typeof(Visibility))]
-    public class NotNullToVisibilityConverter : IValueConverter
+    public class NotNullToVisibilityConverter
+        : MarkupConverter
     {
         public bool Inverted { get; set; }
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            bool isNull = value != null;
+            bool isNull = value is not null;
 
             if (Inverted)
             {
@@ -21,10 +22,5 @@ namespace PixiEditor.Helpers.Converters
 
             return isNull ? Visibility.Visible : Visibility.Collapsed;
         }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
     }
 }

+ 4 - 24
PixiEditor/Helpers/Converters/NullToVisibilityConverter.cs

@@ -1,35 +1,15 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Windows;
-using System.Windows.Data;
-using System.Windows.Markup;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class NullToVisibilityConverter : MarkupExtension, IValueConverter
+    public class NullToVisibilityConverter
+        : SingleInstanceConverter<NullToVisibilityConverter>
     {
-        private static NullToVisibilityConverter converter;
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            return value != null ? Visibility.Collapsed : Visibility.Visible;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
-
-        public override object ProvideValue(IServiceProvider serviceProvider)
-        {
-            if(converter == null)
-            {
-                converter = new NullToVisibilityConverter();
-            }
-            return converter;
+            return value is null ? Visibility.Visible : Visibility.Collapsed;
         }
     }
 }

+ 6 - 10
PixiEditor/Helpers/Converters/OppositeVisibilityConverter.cs

@@ -5,9 +5,10 @@ using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class OppositeVisibilityConverter : IValueConverter
+    public class OppositeVisibilityConverter
+        : SingleInstanceConverter<OppositeVisibilityConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (value.ToString().ToLower() == "visible")
             {
@@ -17,16 +18,11 @@ namespace PixiEditor.Helpers.Converters
             return Visibility.Visible;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            if (value is Visibility)
+            if (value is Visibility visibility)
             {
-                if ((Visibility)value == Visibility.Visible)
-                {
-                    return "Hidden";
-                }
-
-                return "Visible";
+                return visibility == Visibility.Visible ? "Hidden" : "Visible";
             }
 
             return null;

+ 23 - 0
PixiEditor/Helpers/Converters/SingleInstanceConverter.cs

@@ -0,0 +1,23 @@
+using System;
+
+namespace PixiEditor.Helpers.Converters
+{
+    /// <summary>
+    /// Use this if you want to share the same converter over the whole application. <para/> Do not use this if your converter has properties.
+    /// </summary>
+    public abstract class SingleInstanceConverter<TThis> : MarkupConverter
+        where TThis : SingleInstanceConverter<TThis>
+    {
+        private static SingleInstanceConverter<TThis> instance;
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            if (instance is null)
+            {
+                instance = this;
+            }
+
+            return instance;
+        }
+    }
+}

+ 20 - 0
PixiEditor/Helpers/Converters/SingleInstanceMultiValueConverter.cs

@@ -0,0 +1,20 @@
+using System;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public abstract class SingleInstanceMultiValueConverter<TThis> : MultiValueMarkupConverter
+        where TThis : SingleInstanceMultiValueConverter<TThis>
+    {
+        private static SingleInstanceMultiValueConverter<TThis> instance;
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            if (instance is null)
+            {
+                instance = this;
+            }
+
+            return instance;
+        }
+    }
+}

+ 6 - 12
PixiEditor/Helpers/Converters/ThresholdVisibilityConverter.cs

@@ -1,26 +1,20 @@
 using System;
 using System.Globalization;
 using System.Windows;
-using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    class ThresholdVisibilityConverter : IValueConverter
+    public class ThresholdVisibilityConverter
+        : MarkupConverter
     {
         public double Threshold { get; set; } = 100;
         public bool CheckIfLess { get; set; } = false;
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            if (CheckIfLess)
-                return (double)value < Threshold ? Visibility.Visible : Visibility.Hidden;
-            else
-                return (double)value >= Threshold ? Visibility.Visible : Visibility.Hidden;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
+            return CheckIfLess
+                   ? (double)value < Threshold ? Visibility.Visible : Visibility.Hidden
+                   : (double)value >= Threshold ? Visibility.Visible : Visibility.Hidden;
         }
     }
 }

+ 5 - 4
PixiEditor/Helpers/Converters/ToolSizeToIntConverter.cs

@@ -4,17 +4,18 @@ using System.Linq;
 using System.Text.RegularExpressions;
 using System.Windows.Data;
 
-namespace PixiEditor.Helpers
+namespace PixiEditor.Helpers.Converters
 {
     [ValueConversion(typeof(string), typeof(int))]
-    internal class ToolSizeToIntConverter : IValueConverter
+    internal class ToolSizeToIntConverter
+        : SingleInstanceConverter<ToolSizeToIntConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return string.Format("{0} {1}", value, "px");
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (string.IsNullOrWhiteSpace(value as string))
             {

+ 3 - 7
PixiEditor/Helpers/Converters/ZoomToViewportConverter.cs

@@ -5,9 +5,10 @@ using System.Windows.Data;
 
 namespace PixiEditor.Helpers.Converters
 {
-    public class ZoomToViewportConverter : IValueConverter
+    public class ZoomToViewportConverter
+        : SingleInstanceConverter<ZoomToViewportConverter>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (value is double scale)
             {
@@ -17,10 +18,5 @@ namespace PixiEditor.Helpers.Converters
 
             return Binding.DoNothing;
         }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
     }
 }

+ 23 - 124
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -16,10 +16,6 @@ namespace PixiEditor.Models.Controllers
 {
     public class BitmapOperationsUtility
     {
-        public List<LayerChange> PreviewLayerChanges => previewLayerChanges;
-
-        private List<LayerChange> previewLayerChanges;
-
         private Coordinates lastMousePos;
 
         private SizeSetting sizeSetting;
@@ -39,21 +35,21 @@ namespace PixiEditor.Models.Controllers
             {
                 return;
             }
-
-            BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
-            Dictionary<Guid, SKColor[]> oldValues = BitmapUtils.GetPixelsForSelection(layers, pixels);
-            LayerChange[] old = new LayerChange[layers.Length];
-            LayerChange[] newChange = new LayerChange[layers.Length];
-            for (int i = 0; i < layers.Length; i++)
-            {
-                Guid guid = layers[i].LayerGuid;
-                old[i] = new LayerChange(
-                    BitmapPixelChanges.FromArrays(pixels, oldValues[layers[i].LayerGuid]), guid);
-                newChange[i] = new LayerChange(changes, guid);
-                layers[i].SetPixels(changes);
-            }
-
-            Manager.ActiveDocument.UndoManager.AddUndoChange(new Change("UndoChanges", old, newChange, "Deleted pixels"));
+            // TODO: Fix
+            //BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
+            //Dictionary<Guid, SKColor[]> oldValues = BitmapUtils.GetPixelsForSelection(layers, pixels);
+            //LayerChange[] old = new LayerChange[layers.Length];
+            //LayerChange[] newChange = new LayerChange[layers.Length];
+            //for (int i = 0; i < layers.Length; i++)
+            //{
+            //    Guid guid = layers[i].LayerGuid;
+            //    old[i] = new LayerChange(
+            //        BitmapPixelChanges.FromArrays(pixels, oldValues[layers[i].LayerGuid]), guid);
+            //    newChange[i] = new LayerChange(changes, guid);
+            //    layers[i].SetPixels(changes);
+            //}
+
+            //Manager.ActiveDocument.UndoManager.AddUndoChange(new Change("UndoChanges", old, newChange, "Deleted pixels"));
         }
 
         /// <summary>
@@ -82,37 +78,8 @@ namespace PixiEditor.Models.Controllers
         /// </summary>
         public void ApplyPreviewLayer()
         {
-            if (previewLayerChanges == null)
-            {
-                return;
-            }
-
-            Layer[] layers = new Layer[previewLayerChanges.Count];
-
-            for (int i = 0; i < layers.Length; i++)
-            {
-                layers[i] = Manager.ActiveDocument.Layers.First(x => x.LayerGuid == previewLayerChanges[i].LayerGuid);
-            }
-
-            if (layers.Length > 0)
-            {
-                IEnumerable<LayerChange> oldValues =
-                    ApplyToLayers(layers, previewLayerChanges.ToArray());
-
-                foreach (var oldValue in oldValues)
-                {
-                    var previewChanges = previewLayerChanges.First(x => x.LayerGuid == oldValue.LayerGuid);
-
-                    BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
-                        previewChanges.PixelChanges,
-                        oldValue.PixelChanges,
-                        previewChanges.LayerGuid));
-                }
-
-                Manager.ActiveDocument.GeneratePreviewLayer();
-            }
-
-            previewLayerChanges = null;
+            // Don't forget about firing BitmapChanged
+            Manager.ActiveDocument.GeneratePreviewLayer();
         }
 
         private void UseTool(List<Coordinates> mouseMoveCords, BitmapOperationTool tool, SKColor color)
@@ -125,8 +92,8 @@ namespace PixiEditor.Models.Controllers
             int thickness = sizeSetting != null ? sizeSetting.Value : 1;
 
             bool shiftDown = Keyboard.IsKeyDown(Key.LeftShift);
-
-            if (shiftDown)
+           
+            if (shiftDown && tool.UsesShift)
             {
                 bool mouseInLine = MouseCordsNotInLine(mouseMoveCords, thickness);
 
@@ -142,54 +109,14 @@ namespace PixiEditor.Models.Controllers
 
             if (!tool.RequiresPreviewLayer)
             {
-                LayerChange[] modifiedLayers = tool.Use(Manager.ActiveLayer, mouseMoveCords, color);
-                LayerChange[] oldPixelsValues = new LayerChange[modifiedLayers.Length];
-                for (int i = 0; i < modifiedLayers.Length; i++)
-                {
-                    Layer layer = Manager.ActiveDocument.Layers.First(x => x.LayerGuid == modifiedLayers[i].LayerGuid);
-                    oldPixelsValues[i] = ApplyToLayer(layer, modifiedLayers[i]);
-
-                    BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
-                        modifiedLayers[i].PixelChanges,
-                        oldPixelsValues[i].PixelChanges,
-                        modifiedLayers[i].LayerGuid));
-                }
+                tool.Use(Manager.ActiveLayer, mouseMoveCords, color);
+                // BitmapChanged was here
             }
             else
             {
                 UseToolOnPreviewLayer(mouseMoveCords, tool.ClearPreviewLayerOnEachIteration);
             }
         }
-
-        private LayerChange ApplyToLayer(Layer layer, LayerChange change)
-        {
-            return ApplyToLayers(new Layer[] { layer }, new LayerChange[] { change })[0];
-        }
-
-        private LayerChange[] ApplyToLayers(Layer[] layers, LayerChange[] changes)
-        {
-            LayerChange[] oldPixelValues = new LayerChange[changes.Length];
-            for (int i = 0; i < layers.Length; i++)
-            {
-                Layer layer = layers[i];
-                LayerChange change = changes.First(x => x.LayerGuid == layer.LayerGuid);
-                layer.DynamicResize(change.PixelChanges);
-
-                oldPixelValues[i] = new LayerChange(
-                GetOldPixelsValues(change.PixelChanges.ChangedPixels.Keys.ToArray()),
-                change.LayerGuid);
-            }
-
-            for (int i = 0; i < layers.Length; i++)
-            {
-                Layer layer = layers[i];
-                LayerChange change = changes.First(x => x.LayerGuid == layer.LayerGuid);
-                layer.SetPixels(change.PixelChanges, false);
-            }
-
-            return oldPixelValues;
-        }
-
         private bool MouseCordsNotInLine(List<Coordinates> cords, int thickness)
         {
             return (cords[0].X > cords[^1].X - thickness && cords[0].X < cords[^1].X + thickness)
@@ -256,45 +183,17 @@ namespace PixiEditor.Models.Controllers
 
         private void UseToolOnPreviewLayer(List<Coordinates> mouseMove, bool clearPreviewLayer = true)
         {
-            LayerChange[] modifiedLayers;
             if (mouseMove.Count > 0 && mouseMove[0] != lastMousePos)
             {
                 if (clearPreviewLayer || Manager.ActiveDocument.PreviewLayer == null)
                 {
                     Manager.ActiveDocument.GeneratePreviewLayer();
                 }
-
-                modifiedLayers = ((BitmapOperationTool)Manager.SelectedTool).Use(
+                // TODO: Use on preview layer
+                ((BitmapOperationTool)Manager.SelectedTool).Use(
                     Manager.ActiveDocument.ActiveLayer,
                     mouseMove,
                     Manager.PrimaryColor);
-
-                BitmapPixelChanges[] changes = modifiedLayers.Select(x => x.PixelChanges).ToArray();
-                if (changes.Length == 0)
-                {
-                    return;
-                }
-
-                Manager.ActiveDocument.PreviewLayer.SetPixels(BitmapPixelChanges.CombineOverride(changes));
-
-                if (clearPreviewLayer || previewLayerChanges == null)
-                {
-                    previewLayerChanges = new List<LayerChange>(modifiedLayers);
-                }
-                else
-                {
-                    InjectPreviewLayerChanges(modifiedLayers);
-                }
-            }
-        }
-
-        private void InjectPreviewLayerChanges(LayerChange[] modifiedLayers)
-        {
-            for (int i = 0; i < modifiedLayers.Length; i++)
-            {
-                var layer = previewLayerChanges.First(x => x.LayerGuid == modifiedLayers[i].LayerGuid);
-                layer.PixelChanges.ChangedPixels.AddRangeOverride(modifiedLayers[i].PixelChanges.ChangedPixels);
-                layer.PixelChanges = layer.PixelChanges.WithoutTransparentPixels();
             }
         }
     }

+ 0 - 106
PixiEditor/Models/Controllers/PixelChangesController.cs

@@ -1,106 +0,0 @@
-using PixiEditor.Models.DataHolders;
-using SkiaSharp;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace PixiEditor.Models.Controllers
-{
-    public class PixelChangesController
-    {
-        private Dictionary<Guid, LayerChange> LastChanges { get; set; }
-
-        private Dictionary<Guid, LayerChange> LastOldValues { get; set; }
-
-        /// <summary>
-        ///     Adds layer changes to controller.
-        /// </summary>
-        /// <param name="changes">New changes.</param>
-        /// <param name="oldValues">Old values of changes.</param>
-        public void AddChanges(LayerChange changes, LayerChange oldValues)
-        {
-            if (changes.PixelChanges.ChangedPixels.Count > 0)
-            {
-                if (LastChanges == null)
-                {
-                    LastChanges = new Dictionary<Guid, LayerChange> { { changes.LayerGuid, changes } };
-                    LastOldValues = new Dictionary<Guid, LayerChange> { { oldValues.LayerGuid, oldValues } };
-                }
-                else if (LastChanges.ContainsKey(changes.LayerGuid))
-                {
-                    AddToExistingLayerChange(changes, oldValues);
-                }
-                else
-                {
-                    AddNewLayerChange(changes, oldValues);
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Returns all changes and deletes them from controller.
-        /// </summary>
-        /// <returns>Tuple array with new changes and old values.</returns>
-        public Tuple<LayerChange, LayerChange>[] PopChanges()
-        {
-            // Maybe replace Tuple with custom data type
-            if (LastChanges == null)
-            {
-                return null;
-            }
-
-            Tuple<LayerChange, LayerChange>[] result = new Tuple<LayerChange, LayerChange>[LastChanges.Count];
-            int i = 0;
-            foreach (KeyValuePair<Guid, LayerChange> change in LastChanges)
-            {
-                Dictionary<Position.Coordinates, SKColor> pixelChanges =
-                    change.Value.PixelChanges.ChangedPixels.ToDictionary(entry => entry.Key, entry => entry.Value);
-                Dictionary<Position.Coordinates, SKColor> oldValues = LastOldValues[change.Key].PixelChanges.ChangedPixels
-                    .ToDictionary(entry => entry.Key, entry => entry.Value);
-
-                LayerChange tmp = new LayerChange(new BitmapPixelChanges(pixelChanges), change.Key);
-                LayerChange oldValuesTmp = new LayerChange(new BitmapPixelChanges(oldValues), change.Key);
-
-                result[i] = new Tuple<LayerChange, LayerChange>(tmp, oldValuesTmp);
-                i++;
-            }
-
-            LastChanges = null;
-            LastOldValues = null;
-            return result;
-        }
-
-        private void AddNewLayerChange(LayerChange changes, LayerChange oldValues)
-        {
-            LastChanges[changes.LayerGuid] = changes;
-            LastOldValues[changes.LayerGuid] = oldValues;
-        }
-
-        private void AddToExistingLayerChange(LayerChange layerChange, LayerChange oldValues)
-        {
-            foreach (KeyValuePair<Position.Coordinates, SKColor> change in layerChange.PixelChanges.ChangedPixels)
-            {
-                if (LastChanges[layerChange.LayerGuid].PixelChanges.ChangedPixels.ContainsKey(change.Key))
-                {
-                    continue;
-                }
-                else
-                {
-                    LastChanges[layerChange.LayerGuid].PixelChanges.ChangedPixels.Add(change.Key, change.Value);
-                }
-            }
-
-            foreach (KeyValuePair<Position.Coordinates, SKColor> change in oldValues.PixelChanges.ChangedPixels)
-            {
-                if (LastOldValues[layerChange.LayerGuid].PixelChanges.ChangedPixels.ContainsKey(change.Key))
-                {
-                    continue;
-                }
-                else
-                {
-                    LastOldValues[layerChange.LayerGuid].PixelChanges.ChangedPixels.Add(change.Key, change.Value);
-                }
-            }
-        }
-    }
-}

+ 0 - 24
PixiEditor/Models/DataHolders/LayerChange.cs

@@ -1,24 +0,0 @@
-using System;
-using PixiEditor.Models.Layers;
-
-namespace PixiEditor.Models.DataHolders
-{
-    public class LayerChange
-    {
-        public LayerChange(BitmapPixelChanges pixelChanges, Guid layerGuid)
-        {
-            PixelChanges = pixelChanges;
-            LayerGuid = layerGuid;
-        }
-
-        public LayerChange(BitmapPixelChanges pixelChanges, Layer layer)
-        {
-            PixelChanges = pixelChanges;
-            LayerGuid = layer.LayerGuid;
-        }
-
-        public BitmapPixelChanges PixelChanges { get; set; }
-
-        public Guid LayerGuid { get; set; }
-    }
-}

+ 31 - 10
PixiEditor/Models/Layers/Layer.cs

@@ -30,6 +30,8 @@ namespace PixiEditor.Models.Layers
 
         private string layerHighlightColor = "#666666";
 
+        private BitmapPixelChanges singleCache = BitmapPixelChanges.Empty;
+
         public Layer(string name)
         {
             Name = name;
@@ -300,6 +302,16 @@ namespace PixiEditor.Models.Layers
             return LayerBitmap.GetSRGBPixel(x, y);
         }
 
+        public void SetPixelWithOffset(Coordinates coordinates, Color color)
+        {
+            LayerBitmap.SetPixel(coordinates.X - OffsetX, coordinates.Y - OffsetY, color);
+        }
+
+        public void SetPixelWithOffset(int x, int y, Color color)
+        {
+            LayerBitmap.SetPixel(x - OffsetX, y - OffsetY, color);
+        }
+
         /// <summary>
         ///     Applies pixel to layer.
         /// </summary>
@@ -309,7 +321,8 @@ namespace PixiEditor.Models.Layers
         /// <param name="applyOffset">Converts pixels coordinates to relative to bitmap.</param>
         public void SetPixel(Coordinates coordinates, SKColor color, bool dynamicResize = true, bool applyOffset = true)
         {
-            SetPixels(BitmapPixelChanges.FromSingleColoredArray(new[] { coordinates }, color), dynamicResize, applyOffset);
+            singleCache.ChangedPixels[coordinates] = color;
+            SetPixels(singleCache, dynamicResize, applyOffset);
         }
 
         /// <summary>
@@ -396,15 +409,7 @@ namespace PixiEditor.Models.Layers
 
             if (!(pixels.WasBuiltAsSingleColored && pixels.ChangedPixels.First().Value.Alpha == 0))
             {
-                if ((newMaxX + 1 > Width && Width < MaxWidth) || (newMaxY + 1 > Height && Height < MaxHeight))
-                {
-                    IncreaseSizeToBottomAndRight(newMaxX, newMaxY);
-                }
-
-                if ((newMinX < 0 && Width < MaxWidth) || (newMinY < 0 && Height < MaxHeight))
-                {
-                    IncreaseSizeToTopAndLeft(newMinX, newMinY);
-                }
+                DynamicResize(newMaxX, newMaxY, newMinX, newMinY);
             }
 
             // if clip is requested
@@ -414,6 +419,22 @@ namespace PixiEditor.Models.Layers
             }
         }
 
+        /// <summary>
+        ///     Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth.
+        /// </summary>
+        public void DynamicResize(int newMaxX, int newMaxY, int newMinX, int newMinY)
+        {
+            if ((newMaxX + 1 > Width && Width < MaxWidth) || (newMaxY + 1 > Height && Height < MaxHeight))
+            {
+                IncreaseSizeToBottomAndRight(newMaxX, newMaxY);
+            }
+
+            if ((newMinX < 0 && Width < MaxWidth) || (newMinY < 0 && Height < MaxHeight))
+            {
+                IncreaseSizeToTopAndLeft(newMinX, newMinY);
+            }
+        }
+
         /// <summary>
         ///     Changes size of bitmap to fit content.
         /// </summary>

+ 14 - 0
PixiEditor/Models/Position/CoordinatesCalculator.cs

@@ -62,6 +62,20 @@ namespace PixiEditor.Models.Position
             return coordinates;
         }
 
+        public static void DrawRectangle(Layer layer, Color color, int x1, int y1, int x2, int y2)
+        {
+            using var ctx = layer.LayerBitmap.GetBitmapContext();
+            x2++;
+            y2++;
+            for (int y = y1; y < y1 + (y2 - y1); y++)
+            {
+                for (int x = x1; x < x1 + (x2 - x1); x++)
+                {
+                    layer.SetPixelWithOffset(x, y, color);
+                }
+            }
+        }
+
         public static IEnumerable<Coordinates> RectangleToCoordinates(DoubleCords coordinates)
         {
             return RectangleToCoordinates(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X, coordinates.Coords2.Y);

+ 18 - 30
PixiEditor/Models/Tools/BitmapOperationTool.cs

@@ -1,34 +1,22 @@
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using SkiaSharp;
-using System;
+using System;
 using System.Collections.Generic;
+using System.Windows.Documents;
+using System.Windows.Media;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 
-namespace PixiEditor.Models.Tools
-{
-    public abstract class BitmapOperationTool : Tool
-    {
-        public bool RequiresPreviewLayer { get; set; }
-
-        public bool ClearPreviewLayerOnEachIteration { get; set; } = true;
+namespace PixiEditor.Models.Tools
+{
+    public abstract class BitmapOperationTool : Tool
+    {
+        public bool RequiresPreviewLayer { get; set; }
 
-        public bool UseDefaultUndoMethod { get; set; } = true;
-
-        private readonly LayerChange[] onlyLayerArr = new LayerChange[] { new LayerChange(BitmapPixelChanges.Empty, Guid.Empty) };
+        public bool ClearPreviewLayerOnEachIteration { get; set; } = true;
 
-        public abstract LayerChange[] Use(Layer layer, List<Coordinates> mouseMove, SKColor color);
-
-        protected LayerChange[] Only(BitmapPixelChanges changes, Layer layer)
-        {
-            onlyLayerArr[0] = new LayerChange(changes, layer);
-            return onlyLayerArr;
-        }
-
-        protected LayerChange[] Only(BitmapPixelChanges changes, Guid layerGuid)
-        {
-            onlyLayerArr[0] = new LayerChange(changes, layerGuid);
-            return onlyLayerArr;
-        }
-    }
-}
+        public bool UseDefaultUndoMethod { get; set; } = true;
+        public virtual bool UsesShift => true;
+
+        public abstract void Use(Layer layer, List<Coordinates> mouseMove, Color color);
+    }
+}

+ 7 - 6
PixiEditor/Models/Tools/ShapeTool.cs

@@ -58,17 +58,18 @@ namespace PixiEditor.Models.Tools
         // TODO: Add cache for lines 31, 32 (hopefully it would speed up calculation)
         public abstract override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color);
 
-        protected static IEnumerable<Coordinates> GetThickShape(IEnumerable<Coordinates> shape, int thickness)
+        protected static void ThickenShape(Layer layer, Color color, IEnumerable<Coordinates> shape, int thickness)
         {
-            List<Coordinates> output = new List<Coordinates>();
             foreach (Coordinates item in shape)
             {
-                output.AddRange(
-                    CoordinatesCalculator.RectangleToCoordinates(
-                        CoordinatesCalculator.CalculateThicknessCenter(item, thickness)));
+                ThickenShape(layer, color, item, thickness);
             }
+        }
 
-            return output.Distinct();
+        protected static void ThickenShape(Layer layer, Color color, Coordinates coords, int thickness)
+        {
+            var dcords = CoordinatesCalculator.CalculateThicknessCenter(coords, thickness);
+            CoordinatesCalculator.DrawRectangle(layer, color, dcords.Coords1.X, dcords.Coords1.Y, dcords.Coords2.X, dcords.Coords2.Y);
         }
     }
 }

+ 31 - 33
PixiEditor/Models/Tools/Tool.cs

@@ -1,11 +1,17 @@
-using PixiEditor.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Input;
+using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using System.Windows.Input;
-
+using PixiEditor.Models.Undo;
+
 namespace PixiEditor.Models.Tools
 {
     public abstract class Tool : NotifyableObject
@@ -15,21 +21,13 @@ namespace PixiEditor.Models.Tools
 
         public virtual string ToolName => GetType().Name.Replace("Tool", string.Empty);
 
-        public virtual string DisplayName => ToolName.AddSpacesBeforeUppercaseLetters();
-
-        public virtual string ImagePath => $"/Images/Tools/{ToolName}Image.png";
-
-        public virtual bool HideHighlight { get; }
-
-        public abstract string Tooltip { get; }
-
-        public string ActionDisplay
+        public bool IsActive
         {
-            get => actionDisplay;
+            get => isActive;
             set
             {
                 actionDisplay = value;
-                RaisePropertyChanged("ActionDisplay");
+                RaisePropertyChanged(nameof(ActionDisplay));
             }
         }
 
@@ -39,7 +37,7 @@ namespace PixiEditor.Models.Tools
             set
             {
                 isActive = value;
-                RaisePropertyChanged("IsActive");
+                RaisePropertyChanged(nameof(IsActive));
             }
         }
 
@@ -53,24 +51,12 @@ namespace PixiEditor.Models.Tools
         {
         }
 
-        public virtual void OnMouseUp(MouseEventArgs e)
-        {
+        public virtual void OnKeyUp(KeyEventArgs e)

+        {

         }
-
-        public virtual void OnKeyDown(KeyEventArgs e)
-        {
-        }
-
-        public virtual void OnKeyUp(KeyEventArgs e)
-        {
-        }
-
-        public virtual void OnStart(Coordinates clickPosition)
-        {
-        }
-
-        public virtual void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
+
+        public virtual void OnStart(Coordinates clickPosition)

+        {

         }
 
         public virtual void OnStoppedRecordingMouseUp(MouseEventArgs e)
@@ -80,9 +66,21 @@ namespace PixiEditor.Models.Tools
         public virtual void OnMouseMove(MouseEventArgs e)
         {
         }
+
+        public virtual void AddUndoProcess(Document document)
+        {
+            //StorageBasedChange change = new StorageBasedChange(document, affectedLayers, false);
+
+            //manager.AddUndoChange(change.ToChange(), )
+        }
+
+        public virtual void RedoProcess(UndoManager manager)
+        {
+
+        }
 
         public virtual void AfterAddedUndo(UndoManager undoManager)
         {
         }
     }
-}
+}

+ 14 - 12
PixiEditor/Models/Tools/Tools/BrightnessTool.cs

@@ -1,6 +1,10 @@
-using PixiEditor.Helpers.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Colors;
-using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
@@ -24,6 +28,8 @@ namespace PixiEditor.Models.Tools.Tools
             Toolbar = new BrightnessToolToolbar(CorrectionFactor);
         }
 
+        public override bool UsesShift => false;
+
         public override string Tooltip => "Makes pixel brighter or darker pixel (U). Hold Ctrl to make pixel darker.";
 
         public BrightnessMode Mode { get; set; } = BrightnessMode.Default;
@@ -55,20 +61,17 @@ namespace PixiEditor.Models.Tools.Tools
             float correctionFactor = Toolbar.GetSetting<FloatSetting>("CorrectionFactor").Value;
             Mode = Toolbar.GetEnumSetting<BrightnessMode>("BrightnessMode").Value;
 
-            LayerChange[] layersChanges = new LayerChange[1];
             if (Keyboard.IsKeyDown(Key.LeftCtrl))
             {
-                layersChanges[0] = new LayerChange(ChangeBrightness(layer, coordinates[0], toolSize, -correctionFactor), layer);
+                ChangeBrightness(layer, coordinates[0], toolSize, -correctionFactor);
             }
             else
             {
-                layersChanges[0] = new LayerChange(ChangeBrightness(layer, coordinates[0], toolSize, correctionFactor), layer);
+                ChangeBrightness(layer, coordinates[0], toolSize, correctionFactor);
             }
-
-            return layersChanges;
         }
 
-        public BitmapPixelChanges ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
+        public void ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
         {
             DoubleCords centeredCoords = CoordinatesCalculator.CalculateThicknessCenter(coordinates, toolSize);
             IEnumerable<Coordinates> rectangleCoordinates = CoordinatesCalculator.RectangleToCoordinates(
@@ -78,6 +81,8 @@ namespace PixiEditor.Models.Tools.Tools
                 centeredCoords.Coords2.Y);
             BitmapPixelChanges changes = new BitmapPixelChanges(new Dictionary<Coordinates, SKColor>());
 
+            using var ctx = layer.LayerBitmap.GetBitmapContext();
+
             foreach (Coordinates coordinate in rectangleCoordinates)
             {
                 if (Mode == BrightnessMode.Default)
@@ -94,12 +99,9 @@ namespace PixiEditor.Models.Tools.Tools
                 SKColor newColor = ExColor.ChangeColorBrightness(
                     pixel,
                     correctionFactor);
-                changes.ChangedPixels.Add(
-                    new Coordinates(coordinate.X, coordinate.Y),
-                    newColor);
+                layer.SetPixel(new Coordinates(coordinate.X, coordinate.Y), newColor);
             }
 
-            return changes;
         }
     }
 }

+ 53 - 45
PixiEditor/Models/Tools/Tools/CircleTool.cs

@@ -9,6 +9,12 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Windows.Input;
 using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.Models.Tools.Tools
 {
@@ -41,40 +47,30 @@ namespace PixiEditor.Models.Tools.Tools
         {
             int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
             DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
-            IEnumerable<Coordinates> outline = CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2, thickness);
-            BitmapPixelChanges pixels = BitmapPixelChanges.FromSingleColoredArray(outline, color);
+            var outline = CreateEllipse(layer, color, fixedCoordinates.Coords1, fixedCoordinates.Coords2, thickness);
+
             if (Toolbar.GetSetting<BoolSetting>("Fill").Value)
             {
                 Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 SKColor fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
-                pixels.ChangedPixels.AddRangeNewOnly(
-                    BitmapPixelChanges.FromSingleColoredArray(CalculateFillForEllipse(outline), fillColor)
-                        .ChangedPixels);
+                DrawEllipseFill(layer, fillColor, outline);
             }
-
-            return new[] { new LayerChange(pixels, layer) };
         }
 
         /// <summary>
-        ///     Calculates ellipse points for specified coordinates and thickness.
+        ///     Draws ellipse for specified coordinates and thickness.
         /// </summary>
         /// <param name="startCoordinates">Top left coordinate of ellipse.</param>
         /// <param name="endCoordinates">Bottom right coordinate of ellipse.</param>
         /// <param name="thickness">Thickness of ellipse.</param>
         /// <param name="filled">Should ellipse be filled.</param>
-        /// <returns>Coordinates for ellipse.</returns>
-        public IEnumerable<Coordinates> CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, int thickness, bool filled)
+        public void CreateEllipse(Layer layer, Color color, Coordinates startCoordinates, Coordinates endCoordinates, int thickness, bool filled)
         {
-            List<Coordinates> output = new List<Coordinates>();
-            IEnumerable<Coordinates> outline = CreateEllipse(startCoordinates, endCoordinates, thickness);
-            output.AddRange(outline);
+            IEnumerable<Coordinates> outline = CreateEllipse(layer, color, startCoordinates, endCoordinates, thickness);
             if (filled)
             {
-                output.AddRange(CalculateFillForEllipse(outline));
-                return output.Distinct();
+                DrawEllipseFill(layer, color, outline);
             }
-
-            return output;
         }
 
         /// <summary>
@@ -83,33 +79,29 @@ namespace PixiEditor.Models.Tools.Tools
         /// <param name="startCoordinates">Top left coordinate of ellipse.</param>
         /// <param name="endCoordinates">Bottom right coordinate of ellipse.</param>
         /// <param name="thickness">Thickness of ellipse.</param>
-        /// <returns>Coordinates for ellipse.</returns>
-        public IEnumerable<Coordinates> CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, int thickness)
+        public IEnumerable<Coordinates> CreateEllipse(Layer layer, Color color, Coordinates startCoordinates, Coordinates endCoordinates, int thickness)
         {
             double radiusX = (endCoordinates.X - startCoordinates.X) / 2.0;
             double radiusY = (endCoordinates.Y - startCoordinates.Y) / 2.0;
             double centerX = (startCoordinates.X + endCoordinates.X + 1) / 2.0;
             double centerY = (startCoordinates.Y + endCoordinates.Y + 1) / 2.0;
 
-            List<Coordinates> output = new List<Coordinates>();
-            IEnumerable<Coordinates> ellipse = MidpointEllipse(radiusX, radiusY, centerX, centerY);
-            if (thickness == 1)
+            IEnumerable<Coordinates> ellipse = GenerateMidpointEllipse(layer, color, radiusX, radiusY, centerX, centerY);
+            if (thickness > 1)
             {
-                output.AddRange(ellipse);
-            }
-            else
-            {
-                output.AddRange(GetThickShape(ellipse, thickness));
+                ThickenShape(layer, color, ellipse, thickness);
             }
 
-            return output.Distinct();
+            return ellipse;
         }
 
-        public IEnumerable<Coordinates> MidpointEllipse(double halfWidth, double halfHeight, double centerX, double centerY)
+        public List<Coordinates> GenerateMidpointEllipse(Layer layer, Color color, double halfWidth, double halfHeight, double centerX, double centerY)
         {
+            using var ctx = layer.LayerBitmap.GetBitmapContext();
+
             if (halfWidth < 1 || halfHeight < 1)
             {
-                return FallbackRectangle(halfWidth, halfHeight, centerX, centerY);
+                return DrawFallbackRectangle(layer, color, halfWidth, halfHeight, centerX, centerY);
             }
 
             // ellipse formula: halfHeight^2 * x^2 + halfWidth^2 * y^2 - halfHeight^2 * halfWidth^2 = 0
@@ -125,7 +117,7 @@ namespace PixiEditor.Models.Tools.Tools
             // from PI/2 to middle
             do
             {
-                outputCoordinates.AddRange(GetRegionPoints(currentX, centerX, currentY, centerY));
+                outputCoordinates.AddRange(DrawRegionPoints(layer, color, currentX, centerX, currentY, centerY));
 
                 // calculate next pixel coords
                 currentX++;
@@ -147,7 +139,7 @@ namespace PixiEditor.Models.Tools.Tools
             // from middle to 0
             while (currentY - centerY >= 0)
             {
-                outputCoordinates.AddRange(GetRegionPoints(currentX, centerX, currentY, centerY));
+                outputCoordinates.AddRange(DrawRegionPoints(layer, color, currentX, centerX, currentY, centerY));
 
                 currentY--;
                 if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX + 0.5, 2)) +
@@ -161,13 +153,13 @@ namespace PixiEditor.Models.Tools.Tools
             return outputCoordinates;
         }
 
-        public IEnumerable<Coordinates> CalculateFillForEllipse(IEnumerable<Coordinates> outlineCoordinates)
+        public void DrawEllipseFill(Layer layer, Color color, IEnumerable<Coordinates> outlineCoordinates)
         {
-            List<Coordinates> finalCoordinates = new List<Coordinates>();
+            using var ctx = layer.LayerBitmap.GetBitmapContext();
 
             if (!outlineCoordinates.Any())
             {
-                return finalCoordinates;
+                return;
             }
 
             int bottom = outlineCoordinates.Max(x => x.Y);
@@ -179,38 +171,54 @@ namespace PixiEditor.Models.Tools.Tools
                 int left = rowCords.Min(x => x.X);
                 for (int j = left + 1; j < right; j++)
                 {
-                    finalCoordinates.Add(new Coordinates(j, i));
+                    layer.SetPixel(new Coordinates(j, i), color);
                 }
             }
-
-            return finalCoordinates;
         }
 
-        private Coordinates[] FallbackRectangle(double halfWidth, double halfHeight, double centerX, double centerY)
+        private List<Coordinates> DrawFallbackRectangle(Layer layer, Color color, double halfWidth, double halfHeight, double centerX, double centerY)
         {
+            using var ctx = layer.LayerBitmap.GetBitmapContext();
+
             List<Coordinates> coordinates = new List<Coordinates>();
+
             for (double x = centerX - halfWidth; x <= centerX + halfWidth; x++)
             {
-                coordinates.Add(new Coordinates((int)x, (int)(centerY - halfHeight)));
-                coordinates.Add(new Coordinates((int)x, (int)(centerY + halfHeight)));
+                var cords = new Coordinates((int)x, (int)(centerY - halfHeight));
+                coordinates.Add(cords);
+                layer.SetPixel(cords, color);
+
+                cords = new Coordinates((int)x, (int)(centerY + halfHeight));
+                coordinates.Add(cords);
+                layer.SetPixel(cords, color);
             }
 
             for (double y = centerY - halfHeight + 1; y <= centerY + halfHeight - 1; y++)
             {
-                coordinates.Add(new Coordinates((int)(centerX - halfWidth), (int)y));
-                coordinates.Add(new Coordinates((int)(centerX + halfWidth), (int)y));
+                var cords = new Coordinates((int)(centerX - halfWidth), (int)y);
+                coordinates.Add(cords);
+                layer.SetPixel(cords, color);
+
+                cords = new Coordinates((int)(centerX + halfWidth), (int)y);
+                coordinates.Add(cords);
+                layer.SetPixel(cords, color);
             }
 
-            return coordinates.ToArray();
+            return coordinates;
         }
 
-        private Coordinates[] GetRegionPoints(double x, double xc, double y, double yc)
+        private Coordinates[] DrawRegionPoints(Layer layer, Color color, double x, double xc, double y, double yc)
         {
             Coordinates[] outputCoordinates = new Coordinates[4];
             outputCoordinates[0] = new Coordinates((int)Math.Floor(x), (int)Math.Floor(y));
+            layer.SetPixel(outputCoordinates[0], color);
             outputCoordinates[1] = new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(y));
+            layer.SetPixel(outputCoordinates[1], color);
             outputCoordinates[2] = new Coordinates((int)Math.Floor(x), (int)Math.Floor(-(y - yc) + yc));
+            layer.SetPixel(outputCoordinates[2], color);
             outputCoordinates[3] = new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(-(y - yc) + yc));
+            layer.SetPixel(outputCoordinates[3], color);
+
             return outputCoordinates;
         }
     }

+ 38 - 37
PixiEditor/Models/Tools/Tools/EraserTool.cs

@@ -1,37 +1,38 @@
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
-using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using SkiaSharp;
-using System.Collections.Generic;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class EraserTool : BitmapOperationTool
-    {
-        private readonly PenTool pen;
-
-        public EraserTool(BitmapManager bitmapManager)
-        {
-            ActionDisplay = "Draw to remove color from a pixel.";
-            Toolbar = new BasicToolbar();
-            pen = new PenTool(bitmapManager);
-        }
-
-        public override string Tooltip => "Erasers color from pixel. (E)";
-
-        public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color)
-        {
-            return Erase(layer, coordinates, Toolbar.GetSetting<SizeSetting>("ToolSize").Value);
-        }
-
-        public LayerChange[] Erase(Layer layer, List<Coordinates> coordinates, int toolSize)
-        {
-            Coordinates startingCords = coordinates.Count > 1 ? coordinates[1] : coordinates[0];
-            BitmapPixelChanges pixels = pen.Draw(startingCords, coordinates[0], SKColors.Transparent, toolSize);
-            return Only(pixels, layer);
-        }
-    }
-}
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
+using PixiEditor.Models.Tools.ToolSettings.Toolbars;
+using SkiaSharp;
+using System.Collections.Generic;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class EraserTool : BitmapOperationTool
+    {
+        private readonly PenTool pen;
+
+        public EraserTool(BitmapManager bitmapManager)
+        {
+            ActionDisplay = "Draw to remove color from a pixel.";
+            Toolbar = new BasicToolbar();
+            pen = new PenTool(bitmapManager);
+        }

+

+        public override bool UsesShift => false;

+

+        public override string Tooltip => "Erasers color from pixel. (E)";
+
+        public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        {
+            Erase(layer, coordinates, Toolbar.GetSetting<SizeSetting>("ToolSize").Value);
+        }
+
+        public void Erase(Layer layer, List<Coordinates> coordinates, int toolSize)
+        {
+            Coordinates startingCords = coordinates.Count > 1 ? coordinates[1] : coordinates[0];
+            pen.Draw(layer, startingCords, coordinates[0], SKColors.Transparent, toolSize);
+        }
+    }
+}

+ 1 - 1
PixiEditor/Models/Tools/Tools/FloodFill.cs

@@ -55,7 +55,7 @@ namespace PixiEditor.Models.Tools.Tools
         private void PerformLinearFill(
             Layer layer,
             List<Coordinates> changedCoords, Queue<FloodFillRange> floodFillQueue,
-            Coordinates coords, int width, SKColor colorToReplace, bool[] visited)
+        public override LayderChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color)
         {
             // Find the Left Edge of the Color Area
             int fillXLeft = coords.X;

+ 60 - 44
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -1,4 +1,9 @@
-using PixiEditor.Models.DataHolders;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
@@ -14,6 +19,7 @@ namespace PixiEditor.Models.Tools.Tools
     public class LineTool : ShapeTool
     {
         private readonly CircleTool circleTool;
+        private List<Coordinates> linePoints = new List<Coordinates>();
 
         public LineTool()
         {
@@ -42,81 +48,81 @@ namespace PixiEditor.Models.Tools.Tools
 
         public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color)
         {
-            BitmapPixelChanges pixels =
-                BitmapPixelChanges.FromSingleColoredArray(
-                    CreateLine(
-                        coordinates,
-                        Toolbar.GetSetting<SizeSetting>("ToolSize").Value,
-                        CapType.Square,
-                        CapType.Square), color);
-            return Only(pixels, layer);
+            layer.DynamicResize(coordinates.Max(x => x.X), coordinates.Max(x => x.Y), coordinates.Min(x => x.X), coordinates.Min(x => x.Y));
+
+            CreateLine(
+                layer, color,
+                coordinates,
+                Toolbar.GetSetting<SizeSetting>("ToolSize").Value,
+                CapType.Square,
+                CapType.Square);
         }
 
-        public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness)
+        public List<Coordinates> CreateLine(Layer layer, Color color, Coordinates start, Coordinates end, int thickness)
         {
-            return CreateLineFastest(start, end, thickness);
+            return CreateLineFastest(layer, color, start, end, thickness);
         }
 
-        public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
+        private List<Coordinates> CreateLine(Layer layer, Color color, IEnumerable<Coordinates> coordinates, int thickness, CapType startCap, CapType endCap)
         {
-            return CreateLine(new List<Coordinates>() { end, start }, thickness, startCap, endCap);
+            Coordinates startingCoordinates = coordinates.Last();
+            Coordinates latestCoordinates = coordinates.First();
+
+            return CreateLine(layer, color, startingCoordinates, latestCoordinates, thickness, startCap, endCap);
         }
 
-        private IEnumerable<Coordinates> CreateLine(IEnumerable<Coordinates> coordinates, int thickness, CapType startCap, CapType endCap)
+        private List<Coordinates> CreateLine(Layer layer, Color color, Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
         {
-            Coordinates startingCoordinates = coordinates.Last();
-            Coordinates latestCoordinates = coordinates.First();
             if (thickness == 1)
             {
-                return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X, latestCoordinates.Y);
+                return BresenhamLine(layer, color, start.X, start.Y, end.X, end.Y);
             }
 
-            return GetLinePoints(startingCoordinates, latestCoordinates, thickness, startCap, endCap);
+            return GenerateLine(layer, color, start, end, thickness, startCap, endCap);
         }
 
-        private IEnumerable<Coordinates> CreateLineFastest(Coordinates start, Coordinates end, int thickness)
+        private List<Coordinates> CreateLineFastest(Layer layer, Color color, Coordinates start, Coordinates end, int thickness)
         {
-            IEnumerable<Coordinates> line = BresenhamLine(start.X, start.Y, end.X, end.Y);
+            var line = BresenhamLine(layer, color, start.X, start.Y, end.X, end.Y);
             if (thickness == 1)
             {
                 return line;
             }
 
-            return GetThickShape(line, thickness);
+            ThickenShape(layer, color, line, thickness);
+            return line;
         }
 
-        private IEnumerable<Coordinates> GetLinePoints(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
+        private List<Coordinates> GenerateLine(Layer layer, Color color, Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
         {
-            IEnumerable<Coordinates> startingCap = GetCapCoordinates(startCap, start, thickness);
+            ApplyCap(layer, color, startCap, start, thickness);
             if (start == end)
             {
-                return startingCap;
+                return new List<Coordinates>() { start };
             }
 
-            IEnumerable<Coordinates> line = BresenhamLine(start.X, start.Y, end.X, end.Y);
-
-            List<Coordinates> output = new List<Coordinates>(startingCap);
+            var line = BresenhamLine(layer, color, start.X, start.Y, end.X, end.Y);
 
-            output.AddRange(GetCapCoordinates(endCap, end, thickness));
+            ApplyCap(layer, color, endCap, end, thickness);
             if (line.Count() > 2)
             {
-                output.AddRange(GetThickShape(line.Except(new[] { start, end }), thickness));
+                ThickenShape(layer, color, line.Except(new[] { start, end }), thickness);
             }
 
-            return output.Distinct();
+            return line;
         }
 
-        private IEnumerable<Coordinates> GetCapCoordinates(CapType cap, Coordinates position, int thickness)
+        private void ApplyCap(Layer layer, Color color, CapType cap, Coordinates position, int thickness)
         {
             switch (cap)
             {
                 case CapType.Round:
-                    {
-                        return GetRoundCap(position, thickness); // Round cap is not working very well, circle tool must be improved
-                    }
+                    ApplyRoundCap(layer, color, position, thickness); // Round cap is not working very well, circle tool must be improved
+                    break;
 
                 default:
-                    return GetThickShape(new[] { position }, thickness);
+                    ThickenShape(layer, color, position, thickness);
+                    break;
             }
         }
 
@@ -125,19 +131,23 @@ namespace PixiEditor.Models.Tools.Tools
         /// </summary>
         /// <param name="position">Starting position of cap.</param>
         /// <param name="thickness">Thickness of cap.</param>
-        private IEnumerable<Coordinates> GetRoundCap(Coordinates position, int thickness)
+        private void ApplyRoundCap(Layer layer, Color color, Coordinates position, int thickness)
         {
             IEnumerable<Coordinates> rectangleCords = CoordinatesCalculator.RectangleToCoordinates(
                 CoordinatesCalculator.CalculateThicknessCenter(position, thickness));
-            return circleTool.CreateEllipse(rectangleCords.First(), rectangleCords.Last(), 1, true);
+            circleTool.CreateEllipse(layer, color, rectangleCords.First(), rectangleCords.Last(), 1, true);
         }
 
-        private IEnumerable<Coordinates> BresenhamLine(int x1, int y1, int x2, int y2)
+        private List<Coordinates> BresenhamLine(Layer layer, Color color, int x1, int y1, int x2, int y2)
         {
-            List<Coordinates> coordinates = new List<Coordinates>();
+            using BitmapContext context = layer.LayerBitmap.GetBitmapContext();
+            linePoints.Clear();
+            Coordinates cords;
             if (x1 == x2 && y1 == y2)
             {
-                return new[] { new Coordinates(x1, y1) };
+                cords = new Coordinates(x1, y1);
+                layer.SetPixelWithOffset(cords, color);
+                linePoints.Add(cords);
             }
 
             int d, dx, dy, ai, bi, xi, yi;
@@ -165,7 +175,9 @@ namespace PixiEditor.Models.Tools.Tools
                 dy = y1 - y2;
             }
 
-            coordinates.Add(new Coordinates(x, y));
+            cords = new Coordinates(x, y);
+            layer.SetPixelWithOffset(cords, color);
+            linePoints.Add(cords);
 
             if (dx > dy)
             {
@@ -187,7 +199,9 @@ namespace PixiEditor.Models.Tools.Tools
                         x += xi;
                     }
 
-                    coordinates.Add(new Coordinates(x, y));
+                    cords = new Coordinates(x, y);
+                    layer.SetPixelWithOffset(cords, color);
+                    linePoints.Add(cords);
                 }
             }
             else
@@ -210,11 +224,13 @@ namespace PixiEditor.Models.Tools.Tools
                         y += yi;
                     }
 
-                    coordinates.Add(new Coordinates(x, y));
+                    cords = new Coordinates(x, y);
+                    layer.SetPixelWithOffset(cords, color);
+                    linePoints.Add(cords);
                 }
             }
 
-            return coordinates;
+            return linePoints;
         }
     }
 }

+ 225 - 225
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -1,250 +1,250 @@
-using PixiEditor.Helpers.Extensions;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Enums;
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
-using SkiaSharp;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows;
-using System.Windows.Input;
-using Transform = PixiEditor.Models.ImageManipulation.Transform;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class MoveTool : BitmapOperationTool
-    {
-        private Layer[] affectedLayers;
-        private Dictionary<Guid, bool> clearedPixels = new Dictionary<Guid, bool>();
-        private Coordinates[] currentSelection;
-        private Coordinates lastMouseMove;
-        private Dictionary<Guid, SKColor[]> startPixelColors;
-        private Dictionary<Guid, SKColor[]> endPixelColors;
-        private Dictionary<Guid, Thickness> startingOffsets;
-        private Coordinates[] startSelection;
-        private bool updateViewModelSelection = true;
-
-        public MoveTool(BitmapManager bitmapManager)
-        {
-            ActionDisplay = "Hold mouse to move selected pixels. Hold Ctrl to move all layers.";
-            Cursor = Cursors.Arrow;
-            RequiresPreviewLayer = true;
-            UseDefaultUndoMethod = true;
-
-            BitmapManager = bitmapManager;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Undo;

+using PixiEditor.ViewModels;
+using SkiaSharp;

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using Transform = PixiEditor.Models.ImageManipulation.Transform;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class MoveTool : BitmapOperationTool
+    {
+        private Layer[] affectedLayers;
+        private Dictionary<Guid, bool> clearedPixels = new Dictionary<Guid, bool>();
+        private Coordinates[] currentSelection;
+        private Coordinates lastMouseMove;
+        private Dictionary<Guid, SKColor[]> startPixelColors;
+        private Dictionary<Guid, SKColor[]> endPixelColors;
+        private Dictionary<Guid, Thickness> startingOffsets;
+        private Coordinates[] startSelection;
+        private bool updateViewModelSelection = true;

+

+        public MoveTool(BitmapManager bitmapManager)
+        {
+            ActionDisplay = "Hold mouse to move selected pixels. Hold Ctrl to move all layers.";
+            Cursor = Cursors.Arrow;
+            RequiresPreviewLayer = true;
+            UseDefaultUndoMethod = true;
+
+            BitmapManager = bitmapManager;
+        }

+

+        public override string Tooltip => "Moves selected pixels (V). Hold Ctrl to move all layers.";

+

+        public override bool HideHighlight => true;

+

+        public bool MoveAll { get; set; }
+
+        private BitmapManager BitmapManager { get; }
+
+        public override void OnKeyDown(KeyEventArgs e)

+        {

+            if (e.Key == Key.LeftCtrl)

+            {

+                ActionDisplay = "Hold mouse to move all selected layers.";

+            }

         }
 
-        public override string Tooltip => "Moves selected pixels (V). Hold Ctrl to move all layers.";
+        public override void OnKeyUp(KeyEventArgs e)

+        {

+            if (e.Key == Key.LeftCtrl)

+            {

+                ActionDisplay = "Hold mouse to move selected pixels. Hold Ctrl to move all layers.";

+            }

+        }
 
-        public override bool HideHighlight => true;
+        public override void AfterAddedUndo(UndoManager undoManager)
+        {

+            //if (currentSelection == null || currentSelection.Length == 0)

+            //{

+            //    return;

+            //}

+

+            //Change changes = undoManager.UndoStack.Peek();

+

+            //// Inject to default undo system change custom changes made by this tool

+            //foreach (var item in startPixelColors)

+            //{

+            //    BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(startSelection, item.Value);

+            //    BitmapPixelChanges afterMovePixels = BitmapPixelChanges.FromArrays(currentSelection, endPixelColors[item.Key]);

+            //    Guid layerGuid = item.Key;

+            //    var oldValue = (LayerChange[])changes.OldValue;

+

+            //    if (oldValue.Any(x => x.LayerGuid == layerGuid))

+            //    {

+            //        var layer = oldValue.First(x => x.LayerGuid == layerGuid);

+            //        layer.PixelChanges.ChangedPixels.AddRangeOverride(afterMovePixels.ChangedPixels);

+            //        layer.PixelChanges.ChangedPixels

+            //            .AddRangeOverride(beforeMovePixels.ChangedPixels);

+

+            //        ((LayerChange[])changes.NewValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels

+            //            .AddRangeNewOnly(BitmapPixelChanges

+            //                .FromSingleColoredArray(startSelection, SKColors.Transparent)
+            //                .ChangedPixels);

+            //    }

+            //}
+        }
 
-        public bool MoveAll { get; set; }
-
-        private BitmapManager BitmapManager { get; }
-
-        public override void OnKeyDown(KeyEventArgs e)
-        {
-            if (e.Key == Key.LeftCtrl)
-            {
-                ActionDisplay = "Hold mouse to move all selected layers.";
-            }
-        }
-
-        public override void OnKeyUp(KeyEventArgs e)
+        // This adds undo if there is no selection, reason why this isn't in AfterUndoAdded,
+        // is because it doesn't fire if no pixel changes were made.
+        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
         {
-            if (e.Key == Key.LeftCtrl)
+            if (currentSelection != null && currentSelection.Length == 0)
             {
-                ActionDisplay = "Hold mouse to move selected pixels. Hold Ctrl to move all layers.";
-            }
-        }
-
-        public override void AfterAddedUndo(UndoManager undoManager)
-        {
-            if (currentSelection == null || currentSelection.Length == 0)
-            {
-                return;
+                BitmapManager.ActiveDocument.UndoManager.AddUndoChange(new Change(

+                    ApplyOffsets,

+                    new object[] { startingOffsets },

+                    ApplyOffsets,

+                    new object[] { GetOffsets(affectedLayers) },

+                    "Move layers"));
             }
+        }
 
-            Change changes = undoManager.UndoStack.Peek();
+        public override void OnStart(Coordinates startPos)

+        {

+            ResetSelectionValues(startPos);

+

+            // Move offset if no selection

+            Document doc = BitmapManager.ActiveDocument;

+            Selection selection = doc.ActiveSelection;

+            if (selection != null && selection.SelectedPoints.Count > 0)

+            {

+                currentSelection = selection.SelectedPoints.ToArray();

+            }

+            else

+            {

+                currentSelection = Array.Empty<Coordinates>();

+            }

+

+            if (Keyboard.IsKeyDown(Key.LeftCtrl) || MoveAll)

+            {

+                affectedLayers = doc.Layers.Where(x => x.IsVisible)

+                    .ToArray();

+            }

+            else

+            {

+                affectedLayers = doc.Layers.Where(x => x.IsActive && doc.GetFinalLayerIsVisible(x)).ToArray();

+            }

+

+            startSelection = currentSelection;

+            startPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, startSelection);

+            startingOffsets = GetOffsets(affectedLayers);

+        }
 
-            // Inject to default undo system change custom changes made by this tool
-            foreach (var item in startPixelColors)
-            {
-                BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(startSelection, item.Value);
-                BitmapPixelChanges afterMovePixels = BitmapPixelChanges.FromArrays(currentSelection, endPixelColors[item.Key]);
-                Guid layerGuid = item.Key;
-                var oldValue = (LayerChange[])changes.OldValue;
-
-                if (oldValue.Any(x => x.LayerGuid == layerGuid))
-                {
-                    var layer = oldValue.First(x => x.LayerGuid == layerGuid);
-                    layer.PixelChanges.ChangedPixels.AddRangeOverride(afterMovePixels.ChangedPixels);
-                    layer.PixelChanges.ChangedPixels
-                        .AddRangeOverride(beforeMovePixels.ChangedPixels);
-
-                    ((LayerChange[])changes.NewValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels
-                        .AddRangeNewOnly(BitmapPixelChanges
-                            .FromSingleColoredArray(startSelection, SKColors.Transparent)
-                            .ChangedPixels);
-                }
-            }
-        }
-
-        // This adds undo if there is no selection, reason why this isn't in AfterUndoAdded,
-        // is because it doesn't fire if no pixel changes were made.
-        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
-        {
-            if (currentSelection != null && currentSelection.Length == 0)
-            {
-                BitmapManager.ActiveDocument.UndoManager.AddUndoChange(new Change(
-                    ApplyOffsets,
-                    new object[] { startingOffsets },
-                    ApplyOffsets,
-                    new object[] { GetOffsets(affectedLayers) },
-                    "Move layers"));
-            }
-        }
-
-        public override void OnStart(Coordinates startPos)
+        public override void Use(Layer layer, List<Coordinates> mouseMove, SKColor color)
         {
-            ResetSelectionValues(startPos);
+            //LayerChange[] result = new LayerChange[affectedLayers.Length];
+            //var end = mouseMove[0];

+            //var lastSelection = currentSelection.ToArray();
+            //for (int i = 0; i < affectedLayers.Length; i++)
+            //{
+            //    if (currentSelection.Length > 0)
+            //    {
+            //        endPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, currentSelection);
+            //        var changes = MoveSelection(affectedLayers[i], mouseMove);

+            //        ClearSelectedPixels(affectedLayers[i], lastSelection);
+
+            //        changes = RemoveTransparentPixels(changes);
+
+            //        result[i] = new LayerChange(changes, affectedLayers[i]);
+            //    }
+            //    else
+            //    {
+            //        var vector = Transform.GetTranslation(lastMouseMove, end);
+            //        affectedLayers[i].Offset = new Thickness(affectedLayers[i].OffsetX + vector.X, affectedLayers[i].OffsetY + vector.Y, 0, 0);
+            //        result[i] = new LayerChange(BitmapPixelChanges.Empty, affectedLayers[i]);
+            //    }
+            //}
+

+            //lastMouseMove = end;
+
+            // return result;
+        }

+

+        public BitmapPixelChanges MoveSelection(Layer layer, IEnumerable<Coordinates> mouseMove)
+        {
+            Coordinates end = mouseMove.First();
 
-            // Move offset if no selection
-            Document doc = BitmapManager.ActiveDocument;
-            Selection selection = doc.ActiveSelection;
-            if (selection != null && selection.SelectedPoints.Count > 0)
-            {
-                currentSelection = selection.SelectedPoints.ToArray();
+            currentSelection = TranslateSelection(end);
+            if (updateViewModelSelection)

+            {

+                ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(currentSelection, SelectionType.New);

             }
-            else
+

+            lastMouseMove = end;
+            return BitmapPixelChanges.FromArrays(currentSelection, startPixelColors[layer.LayerGuid]);
+        }

+

+        private void ApplyOffsets(object[] parameters)
+        {
+            Dictionary<Guid, Thickness> offsets = (Dictionary<Guid, Thickness>)parameters[0];
+            foreach (var offset in offsets)
             {
-                currentSelection = Array.Empty<Coordinates>();
+                Layer layer = ViewModelMain.Current?.BitmapManager?.
+                    ActiveDocument?.Layers?.First(x => x.LayerGuid == offset.Key);
+                layer.Offset = offset.Value;
             }
+        }
 
-            if (Keyboard.IsKeyDown(Key.LeftCtrl) || MoveAll)
-            {
-                affectedLayers = doc.Layers.Where(x => x.IsVisible)
-                    .ToArray();
-            }
-            else
+        private Dictionary<Guid, Thickness> GetOffsets(Layer[] layers)
+        {
+            Dictionary<Guid, Thickness> dict = new Dictionary<Guid, Thickness>();
+            for (int i = 0; i < layers.Length; i++)
             {
-                affectedLayers = doc.Layers.Where(x => x.IsActive && doc.GetFinalLayerIsVisible(x)).ToArray();
+                dict.Add(layers[i].LayerGuid, layers[i].Offset);
             }
 
-            startSelection = currentSelection;
-            startPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, startSelection);
-            startingOffsets = GetOffsets(affectedLayers);
-        }
-
-        public override LayerChange[] Use(Layer layer, List<Coordinates> mouseMove, SKColor color)
-        {
-            LayerChange[] result = new LayerChange[affectedLayers.Length];
-            var end = mouseMove[0];
-            var lastSelection = currentSelection.ToArray();
-            for (int i = 0; i < affectedLayers.Length; i++)
-            {
-                if (currentSelection.Length > 0)
-                {
-                    endPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, currentSelection);
-                    var changes = MoveSelection(affectedLayers[i], mouseMove);
-                    ClearSelectedPixels(affectedLayers[i], lastSelection);
-
-                    changes = RemoveTransparentPixels(changes);
-
-                    result[i] = new LayerChange(changes, affectedLayers[i]);
-                }
-                else
-                {
-                    var vector = Transform.GetTranslation(lastMouseMove, end);
-                    affectedLayers[i].Offset = new Thickness(affectedLayers[i].OffsetX + vector.X, affectedLayers[i].OffsetY + vector.Y, 0, 0);
-                    result[i] = new LayerChange(BitmapPixelChanges.Empty, affectedLayers[i]);
-                }
-            }
-
-            lastMouseMove = end;
-
-            return result;
+            return dict;
         }
 
-        public BitmapPixelChanges MoveSelection(Layer layer, IEnumerable<Coordinates> mouseMove)
-        {
-            Coordinates end = mouseMove.First();
-
-            currentSelection = TranslateSelection(end);
-            if (updateViewModelSelection)
-            {
-                ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
-            }
+        private BitmapPixelChanges RemoveTransparentPixels(BitmapPixelChanges pixels)
+        {
+            foreach (var item in pixels.ChangedPixels.Where(x => x.Value.Alpha == 0).ToList())

+            {

+                pixels.ChangedPixels.Remove(item.Key);

+            }

+

+            return pixels;
+        }
 
-            lastMouseMove = end;
-            return BitmapPixelChanges.FromArrays(currentSelection, startPixelColors[layer.LayerGuid]);
+        private void ResetSelectionValues(Coordinates start)
+        {
+            lastMouseMove = start;
+            clearedPixels = new Dictionary<Guid, bool>();
+            endPixelColors = new Dictionary<Guid, SKColor[]>();
+            currentSelection = null;
+            affectedLayers = null;
+            updateViewModelSelection = true;
+            startPixelColors = null;
+            startSelection = null;
         }
 
-        private void ApplyOffsets(object[] parameters)
-        {
-            Dictionary<Guid, Thickness> offsets = (Dictionary<Guid, Thickness>)parameters[0];
-            foreach (var offset in offsets)
-            {
-                Layer layer = ViewModelMain.Current?.BitmapManager?.
-                    ActiveDocument?.Layers?.First(x => x.LayerGuid == offset.Key);
-                layer.Offset = offset.Value;
-            }
-        }
-
-        private Dictionary<Guid, Thickness> GetOffsets(Layer[] layers)
-        {
-            Dictionary<Guid, Thickness> dict = new Dictionary<Guid, Thickness>();
-            for (int i = 0; i < layers.Length; i++)
-            {
-                dict.Add(layers[i].LayerGuid, layers[i].Offset);
-            }
-
-            return dict;
-        }
-
-        private BitmapPixelChanges RemoveTransparentPixels(BitmapPixelChanges pixels)
-        {
-            foreach (var item in pixels.ChangedPixels.Where(x => x.Value.Alpha == 0).ToList())
+        private Coordinates[] TranslateSelection(Coordinates end)
+        {
+            Coordinates translation = Transform.GetTranslation(lastMouseMove, end);
+            return Transform.Translate(currentSelection, translation);
+        }
+
+        private void ClearSelectedPixels(Layer layer, Coordinates[] selection)
+        {
+            Guid layerGuid = layer.LayerGuid;
+            if (!clearedPixels.ContainsKey(layerGuid) || clearedPixels[layerGuid] == false)
             {
-                pixels.ChangedPixels.Remove(item.Key);
-            }
+                BitmapManager.ActiveDocument.Layers.First(x => x == layer)
+                    .SetPixels(BitmapPixelChanges.FromSingleColoredArray(selection, SKColors.Transparent));
 
-            return pixels;
-        }
-
-        private void ResetSelectionValues(Coordinates start)
-        {
-            lastMouseMove = start;
-            clearedPixels = new Dictionary<Guid, bool>();
-            endPixelColors = new Dictionary<Guid, SKColor[]>();
-            currentSelection = null;
-            affectedLayers = null;
-            updateViewModelSelection = true;
-            startPixelColors = null;
-            startSelection = null;
-        }
-
-        private Coordinates[] TranslateSelection(Coordinates end)
-        {
-            Coordinates translation = Transform.GetTranslation(lastMouseMove, end);
-            return Transform.Translate(currentSelection, translation);
-        }
-
-        private void ClearSelectedPixels(Layer layer, Coordinates[] selection)
-        {
-            Guid layerGuid = layer.LayerGuid;
-            if (!clearedPixels.ContainsKey(layerGuid) || clearedPixels[layerGuid] == false)
-            {
-                BitmapManager.ActiveDocument.Layers.First(x => x == layer)
-                    .SetPixels(BitmapPixelChanges.FromSingleColoredArray(selection, SKColors.Transparent));
-
-                clearedPixels[layerGuid] = true;
-            }
-        }
-    }
-}
+                clearedPixels[layerGuid] = true;
+            }
+        }
+    }
+}

+ 148 - 151
PixiEditor/Models/Tools/Tools/PenTool.cs

@@ -1,151 +1,148 @@
-using PixiEditor.Helpers.Extensions;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.ToolSettings;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
-using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using SkiaSharp;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows.Input;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class PenTool : ShapeTool
-    {
-        private readonly SizeSetting toolSizeSetting;
-        private readonly BoolSetting pixelPerfectSetting;
-        private readonly List<Coordinates> confirmedPixels = new List<Coordinates>();
-        private readonly LineTool lineTool;
-        private Coordinates[] lastChangedPixels = new Coordinates[3];
-        private byte changedPixelsindex;
-
-        private BitmapManager BitmapManager { get; }
-
-        public PenTool(BitmapManager bitmapManager)
-        {
-            Cursor = Cursors.Pen;
-            ActionDisplay = "Click and move to draw.";
-            Toolbar = new PenToolbar();
-            toolSizeSetting = Toolbar.GetSetting<SizeSetting>("ToolSize");
-            pixelPerfectSetting = Toolbar.GetSetting<BoolSetting>("PixelPerfectEnabled");
-            pixelPerfectSetting.ValueChanged += PixelPerfectSettingValueChanged;
-            ClearPreviewLayerOnEachIteration = false;
-            BitmapManager = bitmapManager;
-            lineTool = new LineTool();
-        }
-
-        public override string Tooltip => "Standard brush. (B)";
-
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
-            base.OnRecordingLeftMouseDown(e);
-            changedPixelsindex = 0;
-            lastChangedPixels = new Coordinates[3];
-            confirmedPixels.Clear();
-        }
-
-        public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color)
-        {
-            Coordinates startingCords = coordinates.Count > 1 ? coordinates[1] : coordinates[0];
-            BitmapPixelChanges pixels = Draw(
-                startingCords,
-                coordinates[0],
-                color,
-                toolSizeSetting.Value,
-                pixelPerfectSetting.Value,
-                BitmapManager.ActiveDocument.PreviewLayer);
-            return Only(pixels, layer);
-        }
-
-        public BitmapPixelChanges Draw(Coordinates startingCoords, Coordinates latestCords, SKColor color, int toolSize, bool pixelPerfect = false, Layer previewLayer = null)
-        {
-            if (!pixelPerfect)
-            {
-                return BitmapPixelChanges.FromSingleColoredArray(
-                    lineTool.CreateLine(startingCoords, latestCords, toolSize), color);
-            }
-
-            if (previewLayer != null && previewLayer.GetPixelWithOffset(latestCords.X, latestCords.Y).Alpha > 0)
-            {
-                confirmedPixels.Add(latestCords);
-            }
-
-            var latestPixels = lineTool.CreateLine(startingCoords, latestCords, 1);
-            SetPixelToCheck(latestPixels);
-
-            if (changedPixelsindex == 2)
-            {
-                var changes = ApplyPixelPerfectToPixels(
-                    lastChangedPixels[0],
-                    lastChangedPixels[1],
-                    lastChangedPixels[2],
-                    color,
-                    toolSize);
-
-                MovePixelsToCheck(changes);
-
-                changes.ChangedPixels.AddRangeNewOnly(
-                    BitmapPixelChanges.FromSingleColoredArray(GetThickShape(latestPixels, toolSize), color).ChangedPixels);
-
-                return changes;
-            }
-
-            changedPixelsindex += changedPixelsindex >= 2 ? (byte)0 : (byte)1;
-
-            var result = BitmapPixelChanges.FromSingleColoredArray(GetThickShape(latestPixels, toolSize), color);
-
-            return result;
-        }
-
-        private void MovePixelsToCheck(BitmapPixelChanges changes)
-        {
-            if (changes.ChangedPixels[lastChangedPixels[1]].Alpha != 0)
-            {
-                lastChangedPixels[0] = lastChangedPixels[1];
-                lastChangedPixels[1] = lastChangedPixels[2];
-                changedPixelsindex = 2;
-            }
-            else
-            {
-                lastChangedPixels[0] = lastChangedPixels[2];
-                changedPixelsindex = 1;
-            }
-        }
-
-        private void SetPixelToCheck(IEnumerable<Coordinates> latestPixels)
-        {
-            if (latestPixels.Count() == 1)
-            {
-                lastChangedPixels[changedPixelsindex] = latestPixels.First();
-            }
-            else
-            {
-                lastChangedPixels[changedPixelsindex] = latestPixels.ElementAt(1);
-            }
-        }
-
-        private BitmapPixelChanges ApplyPixelPerfectToPixels(Coordinates p1, Coordinates p2, Coordinates p3, SKColor color, int toolSize)
-        {
-            if (Math.Abs(p3.X - p1.X) == 1 && Math.Abs(p3.Y - p1.Y) == 1 && !confirmedPixels.Contains(p2))
-            {
-                var changes = BitmapPixelChanges.FromSingleColoredArray(GetThickShape(new Coordinates[] { p1, p3 }, toolSize), color);
-                changes.ChangedPixels.AddRangeNewOnly(
-                    BitmapPixelChanges.FromSingleColoredArray(
-                        GetThickShape(new[] { p2 }, toolSize),
-                        SKColors.Transparent).ChangedPixels);
-                return changes;
-            }
-
-            return BitmapPixelChanges.FromSingleColoredArray(GetThickShape(new Coordinates[] { p2, p3 }.Distinct(), toolSize), color);
-        }
-
-        private void PixelPerfectSettingValueChanged(object sender, SettingValueChangedEventArgs<bool> e)
-        {
-            RequiresPreviewLayer = e.NewValue;
-        }
-    }
-}
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.ToolSettings;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
+using PixiEditor.Models.Tools.ToolSettings.Toolbars;
+using SkiaSharp;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Input;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class PenTool : ShapeTool
+    {
+        private readonly SizeSetting toolSizeSetting;
+        private readonly BoolSetting pixelPerfectSetting;
+        private readonly List<Coordinates> confirmedPixels = new List<Coordinates>();
+        private readonly LineTool lineTool;
+        private Coordinates[] lastChangedPixels = new Coordinates[3];
+        private byte changedPixelsindex;
+
+        private BitmapManager BitmapManager { get; }
+
+        public PenTool(BitmapManager bitmapManager)
+        {
+            Cursor = Cursors.Pen;
+            ActionDisplay = "Click and move to draw.";
+            Toolbar = new PenToolbar();
+            toolSizeSetting = Toolbar.GetSetting<SizeSetting>("ToolSize");
+            pixelPerfectSetting = Toolbar.GetSetting<BoolSetting>("PixelPerfectEnabled");
+            pixelPerfectSetting.ValueChanged += PixelPerfectSettingValueChanged;
+            ClearPreviewLayerOnEachIteration = false;
+            BitmapManager = bitmapManager;
+            lineTool = new LineTool();
+        }
+
+        public override string Tooltip => "Standard brush. (B)";
+        public override bool UsesShift => false;

+
+
+        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
+        {
+            base.OnRecordingLeftMouseDown(e);
+            changedPixelsindex = 0;
+            lastChangedPixels = new Coordinates[3];
+            confirmedPixels.Clear();
+        }
+
+        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        {
+            Coordinates startingCords = coordinates.Count > 1 ? coordinates[1] : coordinates[0];
+            layer.DynamicResize(coordinates.Max(x => x.X), coordinates.Max(x => x.Y), coordinates.Min(x => x.X), coordinates.Min(x => x.Y));
+            Draw(
+                layer,
+                startingCords,
+                coordinates[0],
+                color,
+                toolSizeSetting.Value,
+                pixelPerfectSetting.Value,
+                BitmapManager.ActiveDocument.PreviewLayer);
+        }
+
+        public void Draw(Layer layer, Coordinates startingCoords, Coordinates latestCords, SKColor color, int toolSize, bool pixelPerfect = false, Layer previewLayer = null)
+        {
+            if (!pixelPerfect)
+            {
+                lineTool.CreateLine(layer, color, startingCoords, latestCords, toolSize);
+                return;
+            }
+
+            if (previewLayer != null && previewLayer.GetPixelWithOffset(latestCords.X, latestCords.Y).Alpha > 0)
+            {
+                confirmedPixels.Add(latestCords);
+            }
+
+            var latestPixels = lineTool.CreateLine(layer, color, startingCoords, latestCords, 1);
+            SetPixelToCheck(latestPixels);
+
+            if (changedPixelsindex == 2)
+            {
+                var changes = ApplyPixelPerfectToPixels(
+                    layer,
+                    lastChangedPixels[0],
+                    lastChangedPixels[1],
+                    lastChangedPixels[2],
+                    color,
+                    toolSize);
+
+                MovePixelsToCheck(changes);
+
+                ThickenShape(layer, color, latestPixels, toolSize);
+                return;
+            }
+
+            changedPixelsindex += changedPixelsindex >= 2 ? (byte)0 : (byte)1;
+
+            ThickenShape(layer, color, latestPixels, toolSize);
+        }

+

+        private void MovePixelsToCheck(BitmapPixelChanges changes)
+        {
+            if (changes.ChangedPixels[lastChangedPixels[1]].Alpha != 0)
+            {
+                lastChangedPixels[0] = lastChangedPixels[1];
+                lastChangedPixels[1] = lastChangedPixels[2];
+                changedPixelsindex = 2;
+            }
+            else
+            {
+                lastChangedPixels[0] = lastChangedPixels[2];
+                changedPixelsindex = 1;
+            }
+        }
+
+        private void SetPixelToCheck(IEnumerable<Coordinates> latestPixels)
+        {
+            if (latestPixels.Count() == 1)
+            {
+                lastChangedPixels[changedPixelsindex] = latestPixels.First();
+            }
+            else
+            {
+                lastChangedPixels[changedPixelsindex] = latestPixels.ElementAt(1);
+            }
+        }
+
+        private BitmapPixelChanges ApplyPixelPerfectToPixels(Coordinates p1, Coordinates p2, Coordinates p3, SKColor color, int toolSize)
+        {
+            if (Math.Abs(p3.X - p1.X) == 1 && Math.Abs(p3.Y - p1.Y) == 1 && !confirmedPixels.Contains(p2))
+            {
+                ThickenShape(layer, color, new Coordinates[] { p1, p3 }, toolSize);

+                ThickenShape(layer, color, new[] { p2 }, toolSize);
+            }
+
+            ThickenShape(layer, color, new Coordinates[] { p2, p3 }.Distinct(), toolSize);
+            return BitmapPixelChanges.Empty;
+        }
+
+        private void PixelPerfectSettingValueChanged(object sender, SettingValueChangedEventArgs<bool> e)
+        {
+            RequiresPreviewLayer = e.NewValue;
+        }
+    }
+}

+ 30 - 39
PixiEditor/Models/Tools/Tools/RectangleTool.cs

@@ -1,4 +1,10 @@
-using PixiEditor.Helpers.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
@@ -38,54 +44,46 @@ namespace PixiEditor.Models.Tools.Tools
             }
         }
 
-        public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
         {
             int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
-            BitmapPixelChanges pixels =
-                BitmapPixelChanges.FromSingleColoredArray(CreateRectangle(coordinates, thickness), color);
+            CreateRectangle(layer, color, coordinates, thickness);
             if (Toolbar.GetSetting<BoolSetting>("Fill").Value)
             {
-                var tempColor = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
-                SKColor fillColor = new SKColor(tempColor.R, tempColor.G, tempColor.B, tempColor.A);
-                pixels.ChangedPixels.AddRangeOverride(
-                    BitmapPixelChanges.FromSingleColoredArray(
-                            CalculateFillForRectangle(coordinates[^1], coordinates[0], thickness), fillColor)
-                        .ChangedPixels);
+                Color fillColor = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
+                DrawRectangleFill(layer, color, coordinates[^1], coordinates[0], thickness);
             }
-
-            return new[] { new LayerChange(pixels, layer) };
         }
 
-        public IEnumerable<Coordinates> CreateRectangle(List<Coordinates> coordinates, int thickness)
+        public void CreateRectangle(Layer layer, Color color, List<Coordinates> coordinates, int thickness)
         {
             DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
-            List<Coordinates> output = new List<Coordinates>();
-            IEnumerable<Coordinates> rectangle = CalculateRectanglePoints(fixedCoordinates);
-            output.AddRange(rectangle);
+
+            using var ctx = layer.LayerBitmap.GetBitmapContext();
+
+            DrawRectangle(layer, color, fixedCoordinates);
 
             for (int i = 1; i < (int)Math.Floor(thickness / 2f) + 1; i++)
             {
-                output.AddRange(CalculateRectanglePoints(new DoubleCords(
+                DrawRectangle(layer, color, new DoubleCords(
                     new Coordinates(fixedCoordinates.Coords1.X - i, fixedCoordinates.Coords1.Y - i),
-                    new Coordinates(fixedCoordinates.Coords2.X + i, fixedCoordinates.Coords2.Y + i))));
+                    new Coordinates(fixedCoordinates.Coords2.X + i, fixedCoordinates.Coords2.Y + i)));
             }
 
             for (int i = 1; i < (int)Math.Ceiling(thickness / 2f); i++)
             {
-                output.AddRange(CalculateRectanglePoints(new DoubleCords(
+                DrawRectangle(layer, color, new DoubleCords(
                     new Coordinates(fixedCoordinates.Coords1.X + i, fixedCoordinates.Coords1.Y + i),
-                    new Coordinates(fixedCoordinates.Coords2.X - i, fixedCoordinates.Coords2.Y - i))));
+                    new Coordinates(fixedCoordinates.Coords2.X - i, fixedCoordinates.Coords2.Y - i)));
             }
-
-            return output.Distinct();
         }
 
-        public IEnumerable<Coordinates> CreateRectangle(Coordinates start, Coordinates end, int thickness)
+        public void CreateRectangle(Layer layer, Color color, Coordinates start, Coordinates end, int thickness)
         {
-            return CreateRectangle(new() { end, start }, thickness);
+            CreateRectangle(layer, color, new() { end, start }, thickness);
         }
 
-        public IEnumerable<Coordinates> CalculateFillForRectangle(Coordinates start, Coordinates end, int thickness)
+        public void DrawRectangleFill(Layer layer, Color color, Coordinates start, Coordinates end, int thickness)
         {
             int offset = (int)Math.Ceiling(thickness / 2f);
             DoubleCords fixedCords = CalculateCoordinatesForShapeRotation(start, end);
@@ -101,40 +99,33 @@ namespace PixiEditor.Models.Tools.Tools
 
             if (height < 1 || width < 1)
             {
-                return Array.Empty<Coordinates>();
+                return;
             }
 
-            Coordinates[] filledCoordinates = new Coordinates[width * height];
             int i = 0;
             for (int y = 0; y < height; y++)
             {
                 for (int x = 0; x < width; x++)
                 {
-                    filledCoordinates[i] = new Coordinates(innerCords.Coords1.X + x, innerCords.Coords1.Y + y);
+                    layer.SetPixel(new Coordinates(innerCords.Coords1.X + x, innerCords.Coords1.Y + y), color);
                     i++;
                 }
             }
-
-            return filledCoordinates.Distinct();
         }
 
-        private IEnumerable<Coordinates> CalculateRectanglePoints(DoubleCords coordinates)
+        private void DrawRectangle(Layer layer, Color color, DoubleCords coordinates)
         {
-            List<Coordinates> finalCoordinates = new List<Coordinates>();
-
             for (int i = coordinates.Coords1.X; i < coordinates.Coords2.X + 1; i++)
             {
-                finalCoordinates.Add(new Coordinates(i, coordinates.Coords1.Y));
-                finalCoordinates.Add(new Coordinates(i, coordinates.Coords2.Y));
+                layer.SetPixel(new Coordinates(i, coordinates.Coords1.Y), color);
+                layer.SetPixel(new Coordinates(i, coordinates.Coords2.Y), color);
             }
 
             for (int i = coordinates.Coords1.Y + 1; i <= coordinates.Coords2.Y - 1; i++)
             {
-                finalCoordinates.Add(new Coordinates(coordinates.Coords1.X, i));
-                finalCoordinates.Add(new Coordinates(coordinates.Coords2.X, i));
+                layer.SetPixel(new Coordinates(coordinates.Coords1.X, i), color);
+                layer.SetPixel(new Coordinates(coordinates.Coords2.X, i), color);
             }
-
-            return finalCoordinates;
         }
     }
 }

+ 7 - 7
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -66,17 +66,17 @@ namespace PixiEditor.Models.Tools.Tools
 
         public IEnumerable<Coordinates> GetRectangleSelectionForPoints(Coordinates start, Coordinates end)
         {
-            List<Coordinates> selection = rectangleTool.CreateRectangle(start, end, 1).ToList();
-            selection.AddRange(rectangleTool.CalculateFillForRectangle(start, end, 1));
-            return selection;
+            //List<Coordinates> selection = rectangleTool.CreateRectangle(start, end, 1).ToList();
+            //selection.AddRange(rectangleTool.CalculateFillForRectangle(start, end, 1));
+            return Array.Empty<Coordinates>(); //selection;
         }
 
         public IEnumerable<Coordinates> GetCircleSelectionForPoints(Coordinates start, Coordinates end)
         {
-            DoubleCords fixedCoordinates = ShapeTool.CalculateCoordinatesForShapeRotation(start, end);
-            List<Coordinates> selection = circleTool.CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2, 1).ToList();
-            selection.AddRange(circleTool.CalculateFillForEllipse(selection));
-            return selection;
+            //DoubleCords fixedCoordinates = ShapeTool.CalculateCoordinatesForShapeRotation(start, end);
+            //List<Coordinates> selection = circleTool.CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2, 1).ToList();
+            //selection.AddRange(circleTool.CalculateFillForEllipse(selection));
+            return Array.Empty<Coordinates>(); //selection;
         }
 
         /// <summary>

+ 5 - 29
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -15,21 +15,6 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
         public RelayCommand RedoCommand { get; set; }
 
-        private LayerChange[] undoChanges;
-
-        public LayerChange[] UndoChanges // This acts like UndoManager process, but it was implemented before process system, so it can be transformed into it
-        {
-            get => undoChanges;
-            set
-            {
-                undoChanges = value;
-                for (int i = 0; i < value.Length; i++)
-                {
-                    Owner.BitmapManager.ActiveDocument.Layers.First(x => x.LayerGuid == value[i].LayerGuid).SetPixels(value[i].PixelChanges);
-                }
-            }
-        }
-
         public UndoViewModel(ViewModelMain owner)
             : base(owner)
         {
@@ -43,21 +28,12 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             ClearUndoTempDirectory();
         }
 
-        public void TriggerNewUndoChange(Tool toolUsed)
+        public void TriggerNewUndoChange(Tool selectedTool)
         {
-            if (BitmapManager.IsOperationTool(toolUsed)
-                && ((BitmapOperationTool)toolUsed).UseDefaultUndoMethod)
-            {
-                Tuple<LayerChange, LayerChange>[] changes = Owner.ChangesController.PopChanges();
-                if (changes != null && changes.Length > 0)
-                {
-                    LayerChange[] newValues = changes.Select(x => x.Item1).ToArray();
-                    LayerChange[] oldValues = changes.Select(x => x.Item2).ToArray();
-                    Owner.BitmapManager.ActiveDocument.UndoManager.AddUndoChange(
-                        new Change("UndoChanges", oldValues, newValues, root: this));
-                    toolUsed.AfterAddedUndo(Owner.BitmapManager.ActiveDocument.UndoManager);
-                }
-            }
+            var activeDoc = Owner.BitmapManager.ActiveDocument;
+            if (activeDoc is null) return;
+
+            selectedTool.AddUndoProcess(activeDoc);
         }
 
         /// <summary>

+ 0 - 6
PixiEditor/ViewModels/ViewModelMain.cs

@@ -70,8 +70,6 @@ namespace PixiEditor.ViewModels
 
         public BitmapManager BitmapManager { get; set; }
 
-        public PixelChangesController ChangesController { get; set; }
-
         public ShortcutController ShortcutController { get; set; }
 
         public StylusViewModel StylusSubViewModel { get; set; }
@@ -148,7 +146,6 @@ namespace PixiEditor.ViewModels
 
             SelectionSubViewModel = new SelectionViewModel(this);
 
-            ChangesController = new PixelChangesController();
             OnStartupCommand = new RelayCommand(OnStartup);
             CloseWindowCommand = new RelayCommand(CloseWindow);
 
@@ -379,9 +376,6 @@ namespace PixiEditor.ViewModels
 
         private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
         {
-            ChangesController.AddChanges(
-                new LayerChange(e.PixelsChanged, e.ChangedLayerGuid),
-                new LayerChange(e.OldPixelsValues, e.ChangedLayerGuid));
             BitmapManager.ActiveDocument.ChangesSaved = false;
             if (BitmapManager.IsOperationTool())
             {

+ 2 - 5
PixiEditor/Views/Dialogs/HelloTherePopup.xaml

@@ -11,9 +11,6 @@
         WindowStyle="None" WindowStartupLocation="CenterScreen">
 
     <Window.Resources>
-        <converters:EqualityBoolToVisibilityConverter x:Key="EqualityBoolToVisibilityConverter"/>
-        <converters:FileExtensionToColorConverter x:Key="FileExtensionToColorConverter"/>
-
         <Style TargetType="TextBlock">
             <Setter Property="Foreground" Value="White"/>
             <Setter Property="FontSize" Value="16"/>
@@ -76,7 +73,7 @@
                     <TextBlock Margin="0,12.5,0,0" Foreground="LightGray" HorizontalAlignment="Center">
                         <TextBlock.Visibility>
                             <Binding Path="RecentlyOpened.Count"
-                                     Converter="{StaticResource EqualityBoolToVisibilityConverter}">
+                                     Converter="{converters:EqualityBoolToVisibilityConverter}">
                                 <Binding.ConverterParameter>
                                     <sys:Int32/>
                                 </Binding.ConverterParameter>
@@ -98,7 +95,7 @@
                                             <Grid Width="100" Height="100">
                                                 <Image Source="{Binding PreviewBitmap}" Margin="20"/>
                                                 <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder" Margin="5"
-                                                        Background="{Binding FileExtension, Converter={StaticResource FileExtensionToColorConverter}}" 
+                                                        Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}" 
                                                         VerticalAlignment="Bottom" HorizontalAlignment="Right">
                                                     <Border.Style>
                                                         <Style TargetType="Border">

+ 0 - 4
PixiEditor/Views/Dialogs/ImportFilePopup.xaml

@@ -4,16 +4,12 @@
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor.Views"
-        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
-        xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
-        xmlns:helpers="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d" BorderBrush="Black" BorderThickness="1"
         Title="ImportFilePopup" Topmost="True" ShowInTaskbar="False" Height="350" Width="300" WindowStyle="None" WindowStartupLocation="CenterScreen" Name="importFilePopup" MinHeight="350" MinWidth="300"
         DataContext="{DynamicResource ImportFilePopupViewModel}">
     <Window.Resources>
         <vm:ImportFilePopupViewModel x:Key="ImportFilePopupViewModel" />
-        <helpers:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     <WindowChrome.WindowChrome>
         <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"

+ 0 - 4
PixiEditor/Views/Dialogs/NewFilePopup.xaml

@@ -4,16 +4,12 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:PixiEditor.Views"
-        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
-        xmlns:helpers="clr-namespace:PixiEditor.Helpers.Behaviours"
-        xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d"
         d:DesignHeight="600" Topmost="True" ShowInTaskbar="False" d:DesignWidth="450"
         DataContext="{DynamicResource NewFileMenuViewModel}" WindowStyle="None" WindowStartupLocation="CenterScreen" MinHeight="300" MinWidth="400" Height="600" Width="450" Name="newFilePopup" BorderBrush="Black" BorderThickness="1">
     <Window.Resources>
         <vm:NewFileMenuViewModel x:Key="NewFileMenuViewModel" />
-        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     <WindowChrome.WindowChrome>
         <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"

+ 0 - 5
PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml

@@ -6,15 +6,10 @@
         xmlns:local="clr-namespace:PixiEditor.Views"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
-        xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d" Name="window"
         Title="ResizeCanvasPopup" Topmost="True" ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
         Height="390" Width="400" WindowStyle="None">
 
-    <Window.Resources>
-        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
-    </Window.Resources>
-
     <WindowChrome.WindowChrome>
         <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />

+ 0 - 5
PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml

@@ -5,15 +5,10 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:local="clr-namespace:PixiEditor.Views" xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
-        xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d" Name="window"
         Title="ResizeDocumentPopup" Topmost="True" ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
         Height="300" Width="400" WindowStyle="None">
 
-    <Window.Resources>
-        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
-    </Window.Resources>
-
     <WindowChrome.WindowChrome>
         <WindowChrome CaptionHeight="32" GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />

+ 2 - 6
PixiEditor/Views/Dialogs/SaveFilePopup.xaml

@@ -5,14 +5,10 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor.Views"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
-        xmlns:vm="clr-namespace:PixiEditor.ViewModels"
-        xmlns:helpers="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:helpers1="clr-namespace:PixiEditor.Helpers"
+        xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
         mc:Ignorable="d" BorderBrush="Black" BorderThickness="1"
         Title="SaveFilePopup" Height="300" Width="400" WindowStyle="None" MinHeight="300" MinWidth="400"
         WindowStartupLocation="CenterScreen" Name="saveFilePopup">
-    <Window.Resources>
-        <helpers1:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
-    </Window.Resources>
     <WindowChrome.WindowChrome>
         <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
@@ -29,7 +25,7 @@
             <RowDefinition />
         </Grid.RowDefinitions>
         <i:Interaction.Behaviors>
-            <helpers:ClearFocusOnClickBehavior/>
+            <behaviours:ClearFocusOnClickBehavior/>
         </i:Interaction.Behaviors>
 
         <DockPanel Grid.Row="0" Background="{StaticResource MainColor}">

+ 3 - 4
PixiEditor/Views/Dialogs/SettingsWindow.xaml

@@ -12,7 +12,6 @@
         BorderBrush="Black" BorderThickness="1">
     <Window.Resources>
         <viewmodels:SettingsWindowViewModel x:Key="SettingsWindowViewModel"/>
-        <converters:EqualityBoolToVisibilityConverter x:Key="EqualityBoolToVisibilityConverter"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
     </Window.Resources>
     <WindowChrome.WindowChrome>
@@ -54,7 +53,7 @@
                     Command="{Binding SelectCategoryCommand}" CommandParameter="Discord">Discord</Button>
         </StackPanel>
         <Grid Grid.Row="1" Grid.Column="1" Background="{StaticResource AccentColor}">
-            <Grid Visibility="{Binding SelectedCategory, Converter={StaticResource EqualityBoolToVisibilityConverter},
+            <Grid Visibility="{Binding SelectedCategory, Converter={converters:EqualityBoolToVisibilityConverter},
             ConverterParameter='General'}">
                 <StackPanel Orientation="Vertical">
                     <CheckBox Content="Show Document Preview in Taskbar" Margin="25,30,0,0"
@@ -77,7 +76,7 @@
                     </StackPanel>
                 </StackPanel>
             </Grid>
-            <Grid Visibility="{Binding SelectedCategory, Converter={StaticResource EqualityBoolToVisibilityConverter},
+            <Grid Visibility="{Binding SelectedCategory, Converter={converters:EqualityBoolToVisibilityConverter},
             ConverterParameter='Updates'}">
                 <StackPanel Orientation="Vertical">
                     <Label Style="{StaticResource Header1}" Content="Auto-updates"/>
@@ -86,7 +85,7 @@
                     </StackPanel>
                 </StackPanel>
             </Grid>
-            <Grid Visibility="{Binding SelectedCategory, Converter={StaticResource EqualityBoolToVisibilityConverter},
+            <Grid Visibility="{Binding SelectedCategory, Converter={converters:EqualityBoolToVisibilityConverter},
             ConverterParameter='Discord'}">
                 <StackPanel Orientation="Vertical">
                     <Label Style="{StaticResource Header1}" Content="Rich Presence"/>

+ 2 - 3
PixiEditor/Views/Dialogs/ShortcutPopup.xaml

@@ -11,7 +11,6 @@
         Title="ShortcutPopup" Height="815" Width="620" WindowStyle="None"
         MinHeight="400" MinWidth="350" Topmost="{Binding IsTopmost}">
     <Window.Resources>
-        <converters:KeyToStringConverter x:Key="KeyToStringConverter"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibility"/>
         
         <Style TargetType="Border" x:Key="KeyBorder">
@@ -89,7 +88,7 @@
                                                     <ItemsControl.ItemTemplate>
                                                         <DataTemplate DataType="{x:Type ModifierKeys}">
                                                             <Border Style="{StaticResource KeyBorder}">
-                                                                <TextBlock Text="{Binding BindsDirectlyToSource=True, Converter={StaticResource KeyToStringConverter}}" Style="{StaticResource KeyBorderText}"/>
+                                                                <TextBlock Text="{Binding BindsDirectlyToSource=True, Converter={converters:KeyToStringConverter}}" Style="{StaticResource KeyBorderText}"/>
                                                             </Border>
                                                         </DataTemplate>
                                                     </ItemsControl.ItemTemplate>
@@ -100,7 +99,7 @@
                                                     </ItemsControl.ItemsPanel>
                                                 </ItemsControl>
                                                 <Border Style="{StaticResource KeyBorderLast}">
-                                                    <TextBlock Text="{Binding ShortcutKey, Converter={StaticResource KeyToStringConverter}}" Style="{StaticResource KeyBorderText}"/>
+                                                    <TextBlock Text="{Binding ShortcutKey, Converter={converters:KeyToStringConverter}}" Style="{StaticResource KeyBorderText}"/>
                                                 </Border>
 
                                                 <TextBlock Text="{Binding Description}" Foreground="#FFEEEEEE"  FontSize="14" Margin="8,0,0,0"/>

+ 7 - 7
PixiEditor/Views/MainWindow.xaml

@@ -271,8 +271,8 @@
                                         MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
                                         MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}"
                                         UseTouchGestures="{Binding XamlAccesibleViewModel.StylusSubViewModel.UseTouchGestures}"
-                                        IsUsingZoomTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={StaticResource IsZoomToolConverter}}"
-                                        IsUsingMoveViewportTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={StaticResource IsMoveViewportToolConverter}}"
+                                        IsUsingZoomTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={converters:IsSpecifiedTypeConverter SpecifiedType={x:Type tools:ZoomTool}}}"
+                                        IsUsingMoveViewportTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={converters:IsSpecifiedTypeConverter SpecifiedType={x:Type tools:MoveViewportTool}}}"
                                         Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False">
                                         <i:Interaction.Triggers>
                                             <i:EventTrigger EventName="PreviewMouseDown">
@@ -336,9 +336,9 @@
                                         <layerUserControls:LayersManager                                            
                                             LayerCommandsViewModel="{Binding LayersSubViewModel}"
                                             OpacityInputEnabled="{Binding BitmapManager.ActiveDocument, 
-                    Converter={StaticResource NotNullToBoolConverter}}">
+                    Converter={converters:NotNullToBoolConverter}}">
                                             <layerUserControls:LayersManager.LayerTreeRoot>
-                                                <MultiBinding Converter="{StaticResource LayersToStructuredLayersConverter}">
+                                                <MultiBinding Converter="{converters:LayersToStructuredLayersConverter}">
                                                     <Binding Path="BitmapManager.ActiveDocument.Layers" />
                                                     <Binding Path="BitmapManager.ActiveDocument.LayerStructure"/>
                                                 </MultiBinding>
@@ -377,7 +377,7 @@
                                 BorderThickness="{Binding IsActive, Converter={StaticResource BoolToIntConverter}}"
                                 Style="{StaticResource ToolButtonStyle}"
                                 Command="{Binding Path=DataContext.ToolsSubViewModel.SelectToolCommand,
-                            RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
+                                                  RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                 CommandParameter="{Binding}" ToolTip="{Binding Tooltip}">
                                 <Button.Background>
                                     <ImageBrush ImageSource="{Binding ImagePath}" Stretch="Uniform" />
@@ -403,9 +403,9 @@
                 <TextBlock Text="{Binding ActionDisplay}" Foreground="White" FontSize="15" Margin="10,0,0,0" VerticalAlignment="Center"/>
                 <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
                     <TextBlock Text="X:" Foreground="White" FontSize="16"/>
-                    <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseXOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
+                    <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseXOnCanvas, Converter={converters:DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
                     <TextBlock Text="Y:" Foreground="White" FontSize="16"/>
-                    <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseYOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
+                    <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseYOnCanvas, Converter={converters:DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
                 </StackPanel>
             </DockPanel>
             <StackPanel Margin="10,0,0,0" VerticalAlignment="Center" Grid.Row="3"

+ 8 - 9
PixiEditor/Views/UserControls/DiscordRPPreview.xaml

@@ -8,9 +8,7 @@
              d:DesignHeight="280" d:DesignWidth="300"
              x:Name="uc">
     <UserControl.Resources>
-        <converters:EmptyStringToVisibilityConverter x:Key="EmptyStringToVisibility"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibility"/>
-        <converters:BoolToBrushConverter x:Key="BoolToBrush"/>
         <converters:BrushTuple FirstBrush="#7289da" SecondBrush="#202225" x:Key="BackgroundBrushTuple"/>
         <converters:BrushTuple FirstBrush="White" SecondBrush="#7289da" x:Key="BotLabelTuple"/>
         <converters:BrushTuple FirstBrush="#7289da" SecondBrush="White" x:Key="BotTextTuple"/>
@@ -20,7 +18,7 @@
         <Grid.OpacityMask>
             <VisualBrush Visual="{Binding ElementName=OutsideBorder}"/>
         </Grid.OpacityMask>
-        <Border CornerRadius="5" Background="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BackgroundBrushTuple}}" x:Name="OutsideBorder"/>
+        <Border CornerRadius="5" Background="{Binding ElementName=uc, Path=IsPlaying, Converter={converters:BoolToBrushConverter}, ConverterParameter={StaticResource BackgroundBrushTuple}}" x:Name="OutsideBorder"/>
         <Grid x:Name="background">
             <Grid.RowDefinitions>
                     <RowDefinition Height="Auto"/>
@@ -31,7 +29,8 @@
                     <StackPanel>
                         <Grid  Width="80" Height="80" Margin="20">
                             <Image Source="{Binding ElementName=uc, Path=UserSource}"/>
-                        <Border Height="30" Width="30" Background="#FF43B581" CornerRadius="90" BorderThickness="5" BorderBrush="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BackgroundBrushTuple}}">
+                        <Border Height="30" Width="30" Background="#FF43B581" CornerRadius="90" BorderThickness="5" 
+                                BorderBrush="{Binding ElementName=uc, Path=IsPlaying, Converter={converters:BoolToBrushConverter}, ConverterParameter={StaticResource BackgroundBrushTuple}}">
                                 <Border.RenderTransform>
                                     <TransformGroup>
                                         <TranslateTransform X="27" Y="27"></TranslateTransform>
@@ -42,9 +41,9 @@
 
                         <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,15">
                             <TextBlock Foreground="White" FontSize="16" FontWeight="SemiBold">PixiBot</TextBlock>
-                        <TextBlock Foreground="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource DiscriminatorTuple}}" FontSize="16">#8523</TextBlock>
-                            <Border CornerRadius="3" BorderThickness="1" Background="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BotLabelTuple}}" Margin="5,0,0,0" VerticalAlignment="Center">
-                            <TextBlock Foreground="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BotTextTuple}}" FontSize="12" Margin="4,2,4,2" FontWeight="Medium">BOT</TextBlock>
+                        <TextBlock Foreground="{Binding ElementName=uc, Path=IsPlaying, Converter={converters:BoolToBrushConverter}, ConverterParameter={StaticResource DiscriminatorTuple}}" FontSize="16">#8523</TextBlock>
+                        <Border CornerRadius="3" BorderThickness="1" Background="{Binding ElementName=uc, Path=IsPlaying, Converter={converters:BoolToBrushConverter}, ConverterParameter={StaticResource BotLabelTuple}}" Margin="5,0,0,0" VerticalAlignment="Center">
+                            <TextBlock Foreground="{Binding ElementName=uc, Path=IsPlaying, Converter={converters:BoolToBrushConverter}, ConverterParameter={StaticResource BotTextTuple}}" FontSize="12" Margin="4,2,4,2" FontWeight="Medium">BOT</TextBlock>
                             </Border>
                         </StackPanel>
                     </StackPanel>
@@ -56,8 +55,8 @@
                             <Image Source="../../Images/PixiEditorLogo.png" Height="70"/>
                             <StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
                                 <TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold">PixiEditor</TextBlock>
-                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, Path=Detail, Converter={StaticResource EmptyStringToVisibility}}"/>
-                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=State}" Visibility="{Binding ElementName=uc, Path=State, Converter={StaticResource EmptyStringToVisibility}}"/>
+                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, Path=Detail, Converter={converters:EmptyStringToVisibilityConverter}}"/>
+                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=State}" Visibility="{Binding ElementName=uc, Path=State, Converter={converters:EmptyStringToVisibilityConverter}}"/>
                             <TextBlock Foreground="White" FontSize="12">00:00 elapsed</TextBlock>
                             </StackPanel>
                         </StackPanel>

+ 1 - 4
PixiEditor/Views/UserControls/EditableTextBlock.xaml

@@ -6,9 +6,6 @@
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              mc:Ignorable="d"
              d:DesignHeight="60" d:DesignWidth="100">
-    <UserControl.Resources>
-        <converters:OppositeVisibilityConverter x:Key="OppositeVisibilityConverter" />
-    </UserControl.Resources>
     <Grid>
         <TextBlock Foreground="Snow" MouseLeftButtonDown="TextBlock_MouseDown"
                    TextTrimming="CharacterEllipsis" Name="textBlock"
@@ -20,7 +17,7 @@
                  KeyDown="TextBox_KeyDown"
                  LostKeyboardFocus="TextBox_LostKeyboardFocus"
                  Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
-            Converter={StaticResource OppositeVisibilityConverter}}"
+                                      Converter={converters:OppositeVisibilityConverter}}"
                  Name="textBox">
             <i:Interaction.Behaviors>
                 <behaviours:GlobalShortcutFocusBehavior/>

+ 6 - 2
PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml

@@ -7,7 +7,6 @@
              mc:Ignorable="d" Focusable="True"
              d:DesignHeight="60" d:DesignWidth="250" Name="groupControl" DragEnter="GroupControl_DragEnter" DragLeave="GroupControl_DragLeave" DragDrop.Drop="GroupControl_DragLeave">
     <UserControl.Resources>
-        <converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
         <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
     </UserControl.Resources>
     <Border BorderThickness="0 0 0 0.5" BorderBrush="Gray" MinWidth="60" Focusable="True" Tag="{Binding ElementName=groupControl}" MouseDown="Border_MouseDown">
@@ -49,7 +48,12 @@
                     <Image Source="/Images/Folder.png" Height="20" Margin="0,0,10,0" HorizontalAlignment="Right"/>
                 </StackPanel>
             </Grid>
-            <Grid DragEnter="Grid_DragEnter" Drop="Grid_Drop_Bottom"  DragLeave="Grid_DragLeave" Grid.Row="2" AllowDrop="{Binding  GroupData.IsExpanded, ElementName=groupControl, Converter={StaticResource InverseBooleanConverter}}" Grid.ColumnSpan="2" Background="Transparent"/>
+            <Grid DragEnter="Grid_DragEnter" Drop="Grid_Drop_Bottom" 
+                  DragLeave="Grid_DragLeave" Grid.Row="2"
+                  AllowDrop="{Binding GroupData.IsExpanded,
+                                      ElementName=groupControl,
+                                      Converter={converters:InverseBooleanConverter}}"
+                  Grid.ColumnSpan="2" Background="Transparent"/>
         </Grid>
         <Border.ContextMenu>
             <ContextMenu>

+ 1 - 4
PixiEditor/Views/UserControls/Layers/LayersManager.xaml

@@ -9,9 +9,6 @@
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:layerUserControls="clr-namespace:PixiEditor.Views.UserControls.Layers"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="250" x:Name="layersManager">
-    <UserControl.Resources>
-        <converters:IndexOfConverter x:Key="IndexOfConverter"/>
-    </UserControl.Resources>
     <Grid>
         <Grid.RowDefinitions>
             <RowDefinition Height="37.5"/>
@@ -82,7 +79,7 @@
                         <layerUserControls:LayerStructureItemContainer    
                             MouseDown="SelectActiveItem"
                             MouseMove="LayerStructureItemContainer_MouseMove" 
-                            ContainerIndex="{Binding Converter={StaticResource IndexOfConverter}}"
+                            ContainerIndex="{Binding Converter={converters:IndexOfConverter}}"
                             Layer="{Binding}" LayerCommandsViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}"/>
                     </DataTemplate>
                 </TreeView.Resources>

+ 1 - 4
PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml

@@ -6,9 +6,6 @@
              xmlns:local="clr-namespace:PixiEditor.Views.UserControls" xmlns:ui="clr-namespace:PixiEditor.Helpers.UI" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              mc:Ignorable="d" 
              d:DesignHeight="450" d:DesignWidth="250" Name="rawLayersControl">
-    <UserControl.Resources>
-        <converters:LayerStructureToGroupsConverter x:Key="LayerStructureToGroupsConverter"/>
-    </UserControl.Resources>
     <StackPanel DataContext="{Binding ElementName=rawLayersControl}">
         <ItemsControl ItemsSource="{Binding Layers}">
             <ItemsControl.ItemsPanel>
@@ -28,7 +25,7 @@
         <Separator/>
         <ItemsControl>
             <ItemsControl.ItemsSource>
-                <MultiBinding Converter="{StaticResource LayerStructureToGroupsConverter}">
+                <MultiBinding Converter="{converters:LayerStructureToGroupsConverter}">
                     <Binding Path="Structure"/>
                     <Binding Path="Structure.Groups.Count"/>
                     <Binding Path="Layers.Count"/>

+ 16 - 10
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml

@@ -6,10 +6,6 @@
              xmlns:local="clr-namespace:PixiEditor.Views.UserControls.Layers" xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:local1="clr-namespace:PixiEditor.Views.UserControls" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              mc:Ignorable="d" 
              d:DesignHeight="60" d:DesignWidth="350" VerticalAlignment="Center" Name="uc">
-    <UserControl.Resources>
-        <converters:NotNullToVisibilityConverter x:Key="NotNullToVisibilityConverter"/>
-        <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter"/>
-    </UserControl.Resources>
     <Border BorderBrush="{StaticResource DarkerAccentColor}" BorderThickness="0 2 0 0" MinWidth="60" Focusable="True">
         <i:Interaction.Behaviors>
             <behaviors:ClearFocusOnClickBehavior/>
@@ -25,19 +21,29 @@
                     <Grid MouseDown="Grid_MouseDown" Cursor="Hand" Visibility="{Binding ElementName=visibilityCheckbox, Path=IsChecked, Converter={InverseBoolToVisibilityConverter}}"  Background="Transparent"/>
                 </Grid>
                 <Grid Grid.Column="0" Height="16" Name="layerVisibilityCheckboxGrid">
-                    <CheckBox Visibility="{Binding Layer, ElementName=uc, Converter={StaticResource NotNullToVisibilityConverter}}" Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
-                      IsThreeState="False" HorizontalAlignment="Center" 
-                      IsChecked="{Binding Path=Layer.IsVisible, Mode=TwoWay, ElementName=uc}"/>
+                    <CheckBox 
+                        Visibility="{Binding Layer, 
+                                             ElementName=uc, 
+                                             Converter={converters:NotNullToVisibilityConverter}}"
+                        Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
+                        IsThreeState="False" HorizontalAlignment="Center" 
+                        IsChecked="{Binding Path=Layer.IsVisible, Mode=TwoWay, ElementName=uc}"/>
                 </Grid>
                 <StackPanel Name="middleStackPanel" Height="40" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Center">
-                    <Border HorizontalAlignment="Left" Visibility="{Binding Layer, ElementName=uc, Converter={StaticResource NotNullToVisibilityConverter}}" Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
-                           Margin="5, 0, 10, 0">
+                    <Border HorizontalAlignment="Left" 
+                            Visibility="{Binding Layer, ElementName=uc, Converter={converters:NotNullToVisibilityConverter}}" 
+                            Width="30" Height="30"
+                            BorderThickness="1" BorderBrush="Black"
+                            Background="{StaticResource MainColor}"
+                            Margin="5, 0, 10, 0">
                         <Image Source="{Binding Layer.LayerBitmap, ElementName=uc}" Stretch="Uniform" Width="25" Height="25" 
                        RenderOptions.BitmapScalingMode="NearestNeighbor"/>
                     </Border>
                     <Image Margin="0 0 5 0" Width="20" Source="/Images/Layer-add.png"  Visibility="{Binding Layer, ElementName=uc, Converter={converters:NullToVisibilityConverter}}"/>
 
-                    <local1:PrependTextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}" Margin="0 0 5 0" Prepend="Add " Foreground="White" HidePrepend="{Binding Layer, ElementName=uc, Converter={StaticResource NotNullToBoolConverter}}"
+                    <local1:PrependTextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}" 
+                                             Margin="0 0 5 0" Prepend="Add " Foreground="White" 
+                                             HidePrepend="{Binding Layer, ElementName=uc, Converter={converters:NotNullToBoolConverter}}"
                                              FontSize="15" VerticalAlignment="Center" Text="Reference Layer" />
                     <Button Click="TrashButton_Click" Cursor="Hand" Grid.Column="1" Visibility="{Binding Layer, ElementName=uc, Converter={BoolToVisibilityConverter}}" Style="{StaticResource ImageButtonStyle}" Width="20" Height="20" HorizontalAlignment="Right">
                         <Button.Background>

+ 1 - 1
PixiEditor/Views/UserControls/PreviewWindow.xaml

@@ -24,7 +24,7 @@
 
         <Viewbox Margin="30" VerticalAlignment="Center" x:Name="previewWindowViewbox">
             <Grid x:Name="imageGrid" RenderOptions.BitmapScalingMode="NearestNeighbor"
-              Visibility="{Binding Document, Converter={StaticResource NullToVisibilityConverter}, ElementName=uc}"
+              Visibility="{Binding Document, Converter={converters:NullToVisibilityConverter}, ElementName=uc}"
               Height="{Binding Document.Height, ElementName=uc}" Width="{Binding Document.Width, ElementName=uc}"
               d:Width="8" d:Height="8">
                 <Grid Background="{Binding ActiveItem.Value, ElementName=backgroundButton}">

+ 2 - 5
PixiEditor/Views/UserControls/SizeInput.xaml

@@ -5,19 +5,16 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
-             xmlns:converters="clr-namespace:PixiEditor.Helpers"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              xmlns:validators="clr-namespace:PixiEditor.Helpers.Validators"
              mc:Ignorable="d"
              d:DesignHeight="30" d:DesignWidth="160" Name="uc" LayoutUpdated="UserControlLayoutUpdated">
-    <UserControl.Resources>
-        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
-    </UserControl.Resources>
     <TextBox IsEnabled="{Binding IsEnabled, ElementName=uc}" HorizontalContentAlignment="Center"
              Style="{StaticResource DarkTextBoxStyle}" MaxLength="4" InputScope="Number">
         <TextBox.Text>
             <Binding ElementName="uc"
                      Path="Size" Mode="TwoWay"
-                     Converter="{StaticResource ToolSizeToIntConverter}">
+                     Converter="{converters:ToolSizeToIntConverter}">
                 <Binding.ValidationRules>
                     <validators:SizeValidationRule ValidatesOnTargetUpdated="True" />
                 </Binding.ValidationRules>

+ 4 - 8
PixiEditor/Views/UserControls/SmallColorPicker.xaml

@@ -19,18 +19,14 @@
             <ResourceDictionary.MergedDictionaries>
                 <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
             </ResourceDictionary.MergedDictionaries>
-            <conv:IntToPickerTypeConverter x:Key="IntToPickerTypeConverter"/>
-            <conv:FloorConverter x:Key="FloorConverter"/>
-            <conv:ThresholdVisibilityConverter x:Key="ShowWhenBig" CheckIfLess="False" Threshold="380"/>
-            <conv:ThresholdVisibilityConverter x:Key="ShowWhenSmall" CheckIfLess="True" Threshold="380"/>
         </ResourceDictionary>
     </UserControl.Resources>
     <Grid>
         <colorpicker:StandardColorPicker ColorState="{Binding ElementName=uc, Path=ColorState, Mode=TwoWay, Delay=10}"
                                          SecondColorState="{Binding ElementName=uc, Path=SecondColorState, Mode=TwoWay, Delay=10}"
-                                         Visibility="{Binding ElementName=uc, Path=ActualHeight, Converter={StaticResource ShowWhenBig}}"
+                                         Visibility="{Binding ElementName=uc, Path=ActualHeight, Converter={conv:ThresholdVisibilityConverter CheckIfLess=False, Threshold=380}}"
                                          Style="{StaticResource DefaultColorPickerStyle}" x:Name="mainColorPicker" />
-        <Grid Visibility="{Binding ElementName=uc, Path=ActualHeight, Converter={StaticResource ShowWhenSmall}}">
+        <Grid Visibility="{Binding ElementName=uc, Path=ActualHeight, Converter={conv:ThresholdVisibilityConverter CheckIfLess=True, Threshold=380}}">
             <Grid.ColumnDefinitions>
                 <ColumnDefinition MaxWidth="80" Width="1*"/>
                 <ColumnDefinition Width="3*"/>
@@ -43,7 +39,7 @@
             </Grid.RowDefinitions>
             <colorpicker:SquarePicker Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="3"
                                 ColorState="{Binding ColorState, Mode=TwoWay, ElementName=uc}"
-                                PickerType="{Binding ElementName=colorSpaceComboBox, Path=SelectedIndex, Converter={StaticResource IntToPickerTypeConverter}}"/>
+                                PickerType="{Binding ElementName=colorSpaceComboBox, Path=SelectedIndex, Converter={conv:IntToPickerTypeConverter}}"/>
             <colorpicker:ColorDisplay Grid.Row="1" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="3,0,0,0"
                                 ColorState="{Binding ColorState, Mode=TwoWay, ElementName=uc}" 
                                 SecondColorState="{Binding SecondColorState, Mode=TwoWay, ElementName=uc}"/>
@@ -68,7 +64,7 @@
                     Min="0" Max="255"
                     Width="40" Height="20"
                     VerticalAlignment="Center"
-                    Value="{Binding ElementName=uc, Path=Color.A, Mode=TwoWay, Delay=10, Converter={StaticResource FloorConverter}}" />
+                    Value="{Binding ElementName=uc, Path=Color.A, Mode=TwoWay, Delay=10, Converter={conv:FloorConverter}}" />
             </Grid>
         </Grid>
     </Grid>

+ 1 - 1
PixiEditor/Views/UserControls/SwatchesView.xaml

@@ -34,7 +34,7 @@
             <ItemsControl.ItemTemplate>
                 <DataTemplate>
                     <Grid Width="45" Height="45" Margin="0 5 5 5">
-                        <Border CornerRadius="5.5" Width="44" Height="44">
+                        <Border CornerRadius="5.5" Width="44" Height="44" RenderOptions.BitmapScalingMode="NearestNeighbor">
                             <Border.Background>
                                 <VisualBrush>
                                     <VisualBrush.Visual>