Browse Source

Merge pull request #369 from PixiEditor/master

0.1.7.2-dev
Krzysztof Krysiński 3 years ago
parent
commit
0f8c160d5b
60 changed files with 552 additions and 300 deletions
  1. 23 4
      .github/ISSUE_TEMPLATE/report_a_bug.md
  2. 7 7
      CONTRIBUTING.md
  3. 1 1
      PixiEditor/Helpers/Behaviours/ClearFocusOnClickBehavior.cs
  4. 2 2
      PixiEditor/Helpers/Behaviours/GlobalShortcutFocusBehavior.cs
  5. 1 1
      PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs
  6. 27 0
      PixiEditor/Helpers/Converters/WidthToBitmapScalingModeConverter.cs
  7. 22 0
      PixiEditor/Helpers/Converters/ZoomLevelToBitmapScalingModeConverter.cs
  8. 4 4
      PixiEditor/Helpers/Extensions/ParserHelpers.cs
  9. 2 2
      PixiEditor/Models/Constants.cs
  10. 1 0
      PixiEditor/Models/Controllers/BitmapManager.cs
  11. 59 54
      PixiEditor/Models/Controllers/ClipboardController.cs
  12. 23 2
      PixiEditor/Models/Controllers/Shortcuts/ShortcutController.cs
  13. 9 2
      PixiEditor/Models/Controllers/SurfaceRenderer.cs
  14. 2 0
      PixiEditor/Models/DataHolders/Document/Document.Constructors.cs
  15. 30 13
      PixiEditor/Models/DataHolders/Document/Document.Layers.cs
  16. 26 12
      PixiEditor/Models/DataHolders/Document/Document.Operations.cs
  17. 1 5
      PixiEditor/Models/DataHolders/Document/Document.Preview.cs
  18. 80 24
      PixiEditor/Models/DataHolders/Document/Document.cs
  19. 20 0
      PixiEditor/Models/DataHolders/PixelSize.cs
  20. 14 14
      PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs
  21. 2 2
      PixiEditor/Models/DataHolders/Selection.cs
  22. 5 1
      PixiEditor/Models/IO/Exporter.cs
  23. 0 20
      PixiEditor/Models/IO/ImageFileMaxSizeChecker.cs
  24. 0 22
      PixiEditor/Models/IO/PixiFileMaxSizeChecker.cs
  25. 5 9
      PixiEditor/Models/ImageManipulation/BitmapUtils.cs
  26. 16 8
      PixiEditor/Models/Layers/Layer.cs
  27. 4 4
      PixiEditor/Models/Layers/LayerHelper.cs
  28. 16 0
      PixiEditor/Models/Tools/BitmapOperationTool.cs
  29. 4 2
      PixiEditor/Models/Tools/Tools/CircleTool.cs
  30. 4 2
      PixiEditor/Models/Tools/Tools/LineTool.cs
  31. 1 1
      PixiEditor/Models/Tools/Tools/MagicWandTool.cs
  32. 5 2
      PixiEditor/Models/Tools/Tools/RectangleTool.cs
  33. 1 3
      PixiEditor/Models/Undo/StorageBasedChange.cs
  34. 2 2
      PixiEditor/Properties/AssemblyInfo.cs
  35. 2 1
      PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs
  36. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  37. 4 2
      PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs
  38. 5 1
      PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  39. 3 0
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  40. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs
  41. 1 1
      PixiEditor/ViewModels/ViewModelMain.cs
  42. 8 1
      PixiEditor/Views/Dialogs/HelloTherePopup.xaml
  43. 2 13
      PixiEditor/Views/MainWindow.xaml.cs
  44. 5 2
      PixiEditor/Views/UserControls/DrawingViewPort.xaml
  45. 2 2
      PixiEditor/Views/UserControls/EditableTextBlock.xaml.cs
  46. 8 2
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml
  47. 1 1
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs
  48. 1 1
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml
  49. 4 0
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs
  50. 1 1
      PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs
  51. 9 1
      PixiEditor/Views/UserControls/PlainLayerView.xaml
  52. 25 3
      PixiEditor/Views/UserControls/PlainLayerView.xaml.cs
  53. 8 2
      PixiEditor/Views/UserControls/PreviewWindow.xaml
  54. 1 1
      PixiEditor/Views/UserControls/Zoombox.xaml.cs
  55. 2 2
      PixiEditorTests/ModelsTests/ControllersTests/ShortcutControllerTests.cs
  56. 23 23
      PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs
  57. 4 4
      PixiEditorTests/ModelsTests/DataHoldersTests/SelectionTests.cs
  58. 2 2
      PixiEditorTests/ModelsTests/UndoTests/StorageBasedChangeTests.cs
  59. 4 4
      README.md
  60. 6 5
      azure-pipelines.yml

+ 23 - 4
.github/ISSUE_TEMPLATE/report_a_bug.md

@@ -4,13 +4,32 @@ about: Report a bug, not working feature or anything related
 ---
 ---
 
 
 **Describe as detailed as possible when it happens**
 **Describe as detailed as possible when it happens**
+
 A clear and concise description of what the problem is. Ex. Holding CTRL+Z and P at the same time, causes program to crash  
 A clear and concise description of what the problem is. Ex. Holding CTRL+Z and P at the same time, causes program to crash  
 
 
-**Describe what you tried to do in order to fix it**
-A clear and concise description of what you tried to do to fix the problem (if possible).
+**Add reproduction steps**
+
+If you are able to, include steps to reproduce bug
+
+Example:
+1. Create new file with size 64x64
+2. Draw line anywhere
+3. Center content
+4. PixiEditor crashes
+
+**Expected behaviour**
+
+What should happen?
+
+**Include related files,**
+
+If bug makes PixiEditor crash, include crash report. If it is possible, include screenshots and videos. 
+
+**System information**
+
+Windows version: 11/10/8/7
 
 
-**Include screenshots of error**
-If it is possible, include screenshots, videos etc.
 
 
 **Additional context**
 **Additional context**
+
 Add any other context here.
 Add any other context here.

+ 7 - 7
CONTRIBUTING.md

@@ -1,21 +1,21 @@
 # Contributing
 # Contributing
 
 
-Hey! Thanks for being interested in project! It means a lot. But, before contributing please read this guide :) 
+Hey! Thanks for being interested in the project! It means a lot. But, before contributing please read this guide :) 
 
 
 When contributing to this repository, please first discuss the change you wish to make via issue,
 When contributing to this repository, please first discuss the change you wish to make via issue,
 email, or any other method with the owners of this repository before making a change. 
 email, or any other method with the owners of this repository before making a change. 
 
 
 ## Issues
 ## Issues
 
 
-If you want to report a bug, follow steps below, if you want to request a feature, check [this](https://github.com/flabbet/PixiEditor/blob/master/.github/ISSUE_TEMPLATE/feature_request.md)
+If you want to report a bug, follow the steps below, if you want to request a feature, check [this](https://github.com/flabbet/PixiEditor/blob/master/.github/ISSUE_TEMPLATE/feature_request.md)
 
 
 * First of all, check if the issue is on the [list](https://github.com/flabbet/PixiEditor/issues) and/or [board](https://github.com/flabbet/PixiEditor/projects), if yes, upvote it.
 * First of all, check if the issue is on the [list](https://github.com/flabbet/PixiEditor/issues) and/or [board](https://github.com/flabbet/PixiEditor/projects), if yes, upvote it.
 
 
-* If not, report an issue [here](https://github.com/flabbet/PixiEditor/issues) like that:
- 1. Clear as short as possible title
- 2. Describe issue as detailed as possible
- 3. Include screenshots if possible.
+* If not, report an issue [here](https://github.com/flabbet/PixiEditor/issues) while following these guidelines:
+ 1. Keep the title short and straightforward.
+ 2. Describe the issue as detailed as possible
+ 3. Include screenshots if you can.
 
 
  ## Pull Requests
  ## Pull Requests
 
 
- Before pull request, read [this](https://github.com/flabbet/PixiEditor/blob/master/PULL_REQUEST_TEMPLATE.md)
+ Before submitting a pull request, read [this](https://github.com/flabbet/PixiEditor/blob/master/PULL_REQUEST_TEMPLATE.md)

+ 1 - 1
PixiEditor/Helpers/Behaviours/ClearFocusOnClickBehavior.cs

@@ -20,7 +20,7 @@ namespace PixiEditor.Helpers.Behaviours
         private void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
         private void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
         {
         {
             AssociatedObject.Focus();
             AssociatedObject.Focus();
-            ShortcutController.BlockShortcutExecution = false;
+            ShortcutController.UnblockShortcutExecutionAll();
         }
         }
     }
     }
 }
 }

+ 2 - 2
PixiEditor/Helpers/Behaviours/GlobalShortcutFocusBehavior.cs

@@ -27,12 +27,12 @@ namespace PixiEditor.Helpers.Behaviours
 
 
         private void AssociatedObject_LostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
         private void AssociatedObject_LostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
         {
         {
-            ShortcutController.BlockShortcutExecution = false;
+            ShortcutController.UnblockShortcutExecution("GlobalShortcutFocusBehavior");
         }
         }
 
 
         private void AssociatedObject_GotKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
         private void AssociatedObject_GotKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
         {
         {
-            ShortcutController.BlockShortcutExecution = true;
+            ShortcutController.BlockShortcutExection("GlobalShortcutFocusBehavior");
         }
         }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs

@@ -85,7 +85,6 @@ namespace PixiEditor.Helpers.Behaviours
             }
             }
 
 
             FocusManager.SetFocusedElement(scope, parent);
             FocusManager.SetFocusedElement(scope, parent);
-            Keyboard.ClearFocus();
         }
         }
 
 
         private void AssociatedObjectGotKeyboardFocus(
         private void AssociatedObjectGotKeyboardFocus(
@@ -108,6 +107,7 @@ namespace PixiEditor.Helpers.Behaviours
         {
         {
             if (DeselectOnFocusLoss)
             if (DeselectOnFocusLoss)
                 AssociatedObject.Select(0, 0);
                 AssociatedObject.Select(0, 0);
+            RemoveFocus();
         }
         }
 
 
         private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
         private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

+ 27 - 0
PixiEditor/Helpers/Converters/WidthToBitmapScalingModeConverter.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Media;
+
+namespace PixiEditor.Helpers.Converters
+{
+    internal class WidthToBitmapScalingModeConverter : SingleInstanceMultiValueConverter<WidthToBitmapScalingModeConverter>
+    {
+        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        {
+            int? pixelWidth = values[0] as int?;
+            double? actualWidth = values[1] as double?;
+            if (pixelWidth == null || actualWidth == null)
+                return DependencyProperty.UnsetValue;
+            double zoomLevel = actualWidth.Value / pixelWidth.Value;
+            if (zoomLevel < 1)
+                return BitmapScalingMode.HighQuality;
+            return BitmapScalingMode.NearestNeighbor;
+        }
+
+        public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 22 - 0
PixiEditor/Helpers/Converters/ZoomLevelToBitmapScalingModeConverter.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Globalization;
+using System.Windows.Media;
+
+namespace PixiEditor.Helpers.Converters
+{
+    internal class ZoomLevelToBitmapScalingModeConverter : SingleInstanceConverter<ZoomLevelToBitmapScalingModeConverter>
+    {
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            double zoomLevel = (double)value;
+            if (zoomLevel < 1)
+                return BitmapScalingMode.HighQuality;
+            return BitmapScalingMode.NearestNeighbor;
+        }
+
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 4 - 4
PixiEditor/Helpers/Extensions/ParserHelpers.cs

@@ -35,19 +35,19 @@ namespace PixiEditor.Helpers.Extensions
             WpfObservableRangeCollection<Layer> layers = new();
             WpfObservableRangeCollection<Layer> layers = new();
             foreach (SerializableLayer slayer in document)
             foreach (SerializableLayer slayer in document)
             {
             {
-                layers.Add(slayer.ToLayer());
+                layers.Add(slayer.ToLayer(document.Width, document.Height));
             }
             }
 
 
             return layers;
             return layers;
         }
         }
 
 
-        public static Layer ToLayer(this SerializableLayer layer)
+        public static Layer ToLayer(this SerializableLayer layer, int maxWidth, int maxHeight)
         {
         {
-            return new Layer(layer.Name, new Surface(layer.ToSKImage()))
+            return new Layer(layer.Name, new Surface(layer.ToSKImage()), maxWidth, maxHeight)
             {
             {
                 Opacity = layer.Opacity,
                 Opacity = layer.Opacity,
                 IsVisible = layer.IsVisible,
                 IsVisible = layer.IsVisible,
-                Offset = new(layer.OffsetX, layer.OffsetY, 0, 0)
+                Offset = new(layer.OffsetX, layer.OffsetY, 0, 0),
             };
             };
         }
         }
 
 

+ 2 - 2
PixiEditor/Models/Constants.cs

@@ -3,8 +3,8 @@
     internal class Constants
     internal class Constants
     {
     {
         public const int DefaultCanvasSize = 64;
         public const int DefaultCanvasSize = 64;
-        public const int MaxPreviewWidth = 2048;
-        public const int MaxPreviewHeight = 2048;
+        public const int MaxPreviewWidth = 128;
+        public const int MaxPreviewHeight = 128;
 
 
         public const int MaxCanvasSize = 9999;
         public const int MaxCanvasSize = 9999;
 
 

+ 1 - 0
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -34,6 +34,7 @@ namespace PixiEditor.Models.Controllers
                 activeDocument?.UpdatePreviewImage();
                 activeDocument?.UpdatePreviewImage();
                 Document oldDoc = activeDocument;
                 Document oldDoc = activeDocument;
                 activeDocument = value;
                 activeDocument = value;
+                activeDocument?.UpdatePreviewImage();
                 RaisePropertyChanged(nameof(ActiveDocument));
                 RaisePropertyChanged(nameof(ActiveDocument));
                 ActiveWindow = value;
                 ActiveWindow = value;
                 DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
                 DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));

+ 59 - 54
PixiEditor/Models/Controllers/ClipboardController.cs

@@ -15,6 +15,7 @@ using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Collections.Specialized;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Windows;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
@@ -111,34 +112,66 @@ namespace PixiEditor.Models.Controllers
         /// <summary>
         /// <summary>
         ///     Pastes image from clipboard into new layer.
         ///     Pastes image from clipboard into new layer.
         /// </summary>
         /// </summary>
-        public static void PasteFromClipboard()
+        public static void PasteFromClipboard(Document document)
         {
         {
-            IEnumerable<Layer> layers;
+            Layer[] layers;
             try
             try
             {
             {
-                layers = GetLayersFromClipboard();
+                layers = GetLayersFromClipboard(document).ToArray();
             }
             }
             catch
             catch
             {
             {
                 return;
                 return;
             }
             }
 
 
-            Document activeDocument = ViewModelMain.Current.BitmapManager.ActiveDocument;
-            int startIndex = activeDocument.Layers.Count;
+            int resizedCount = 0;
+
+            Guid[] guids = layers.Select(x => x.GuidValue).ToArray();
+
+            var undoArgs = new object[] { guids, document, new PixelSize(document.Width, document.Height) };
 
 
             foreach (var layer in layers)
             foreach (var layer in layers)
             {
             {
-                activeDocument.Layers.Add(layer);
+                document.Layers.Add(layer);
+
+                if (layer.Width > document.Width || layer.Height > document.Height)
+                {
+                    ResizeToLayer(document, layer);
+                    resizedCount++;
+                }
+            }
+
+            StorageBasedChange change = new StorageBasedChange(document, layers, false);
+
+            document.UndoManager.AddUndoChange(change.ToChange(RemoveLayersProcess, undoArgs,
+                RestoreLayersProcess, new object[] { document }, "Paste from clipboard"));
+        }
+
+        private static void RemoveLayersProcess(object[] parameters)
+        {
+            if (parameters.Length > 2 && parameters[1] is Document document && parameters[2] is PixelSize size) 
+            {
+                document.RemoveLayersProcess(parameters);
+                document.ResizeCanvas(size.Width, size.Height, Enums.AnchorPoint.Left | Enums.AnchorPoint.Top, false);
             }
             }
+        }
 
 
-            activeDocument.UndoManager.AddUndoChange(
-                new Change(RemoveLayersProcess, new object[] { startIndex }, AddLayersProcess, new object[] { layers }) { DisposeProcess = DisposeProcess });
+        private static void RestoreLayersProcess(Layer[] layers, UndoLayer[] data, object[] parameters)
+        {
+            if (parameters.Length > 0 && parameters[0] is Document document)
+            {
+                document.RestoreLayersProcess(layers, data);
+                foreach (var layer in layers)
+                {
+                    ResizeToLayer(document, layer);
+                }
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
         ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
         ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
         /// </summary>
         /// </summary>
-        private static IEnumerable<Layer> GetLayersFromClipboard()
+        private static IEnumerable<Layer> GetLayersFromClipboard(Document document)
         {
         {
             DataObject data = ClipboardHelper.TryGetDataObject();
             DataObject data = ClipboardHelper.TryGetDataObject();
             if (data == null)
             if (data == null)
@@ -172,7 +205,7 @@ namespace PixiEditor.Models.Controllers
             else */
             else */
             if (TryFromSingleImage(data, out Surface singleImage))
             if (TryFromSingleImage(data, out Surface singleImage))
             {
             {
-                yield return new Layer("Image", singleImage);
+                yield return new Layer("Image", singleImage, document.Width, document.Height);
             }
             }
             else if (data.GetDataPresent(DataFormats.FileDrop))
             else if (data.GetDataPresent(DataFormats.FileDrop))
             {
             {
@@ -187,13 +220,13 @@ namespace PixiEditor.Models.Controllers
 
 
                     try
                     try
                     {
                     {
-                        layer = new(Path.GetFileName(path), Importer.ImportSurface(path));
+                        layer = new(Path.GetFileName(path), Importer.ImportSurface(path), document.Width, document.Height);
                     }
                     }
                     catch (CorruptedFileException)
                     catch (CorruptedFileException)
                     {
                     {
                     }
                     }
 
 
-                    yield return layer ?? new($"Corrupt {path}");
+                    yield return layer ?? new($"Corrupt {path}", document.Width, document.Height);
                 }
                 }
             }
             }
             else
             else
@@ -208,18 +241,24 @@ namespace PixiEditor.Models.Controllers
             if (dao == null)
             if (dao == null)
                 return false;
                 return false;
 
 
-            var files = dao.GetFileDropList();
-
-            if (files != null)
+            try
             {
             {
-                foreach (var file in files)
+                var files = dao.GetFileDropList();
+                if (files != null)
                 {
                 {
-                    if (Importer.IsSupportedFile(file))
+                    foreach (var file in files)
                     {
                     {
-                        return true;
+                        if (Importer.IsSupportedFile(file))
+                        {
+                            return true;
+                        }
                     }
                     }
                 }
                 }
             }
             }
+            catch(COMException)
+            {
+                return false;
+            }
 
 
             return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
             return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
                    dao.GetDataPresent(DataFormats.Bitmap) || dao.GetDataPresent(DataFormats.FileDrop) ||
                    dao.GetDataPresent(DataFormats.Bitmap) || dao.GetDataPresent(DataFormats.FileDrop) ||
@@ -303,43 +342,9 @@ namespace PixiEditor.Models.Controllers
             return false;
             return false;
         }
         }
 
 
-        private static void RemoveLayersProcess(object[] parameters)
+        private static void ResizeToLayer(Document document, Layer layer)
         {
         {
-            if (parameters.Length == 0 || parameters[0] is not int i)
-            {
-                return;
-            }
-
-            Document document = ViewModelMain.Current.BitmapManager.ActiveDocument;
-
-            while (i < document.Layers.Count)
-            {
-                document.RemoveLayer(i, false);
-            }
-        }
-
-        private static void AddLayersProcess(object[] parameters)
-        {
-            if (parameters.Length == 0 || parameters[0] is not IEnumerable<Layer> layers)
-            {
-                return;
-            }
-
-            foreach (var layer in layers)
-            {
-                ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Add(layer);
-            }
-        }
-
-        private static void DisposeProcess(object[] rev, object[] proc)
-        {
-            if (proc[0] is IEnumerable<Layer> layers)
-            {
-                foreach (var layer in layers)
-                {
-                    layer.LayerBitmap.Dispose();
-                }
-            }
+            document.ResizeCanvas(Math.Max(document.Width, layer.Width), Math.Max(document.Height, layer.Height), Enums.AnchorPoint.Left | Enums.AnchorPoint.Top, false);
         }
         }
     }
     }
 }
 }

+ 23 - 2
PixiEditor/Models/Controllers/Shortcuts/ShortcutController.cs

@@ -1,6 +1,8 @@
 using System;
 using System;
+using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
+using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Controllers.Shortcuts
 namespace PixiEditor.Models.Controllers.Shortcuts
@@ -12,7 +14,9 @@ namespace PixiEditor.Models.Controllers.Shortcuts
             ShortcutGroups = new ObservableCollection<ShortcutGroup>(shortcutGroups);
             ShortcutGroups = new ObservableCollection<ShortcutGroup>(shortcutGroups);
         }
         }
 
 
-        public static bool BlockShortcutExecution { get; set; }
+        public static bool ShortcutExecutionBlocked => _shortcutExecutionBlockers.Count > 0;
+
+        private static List<string> _shortcutExecutionBlockers = new List<string>();
 
 
         public ObservableCollection<ShortcutGroup> ShortcutGroups { get; init; }
         public ObservableCollection<ShortcutGroup> ShortcutGroups { get; init; }
 
 
@@ -20,6 +24,23 @@ namespace PixiEditor.Models.Controllers.Shortcuts
 
 
         public const Key MoveViewportToolTransientChangeKey = Key.Space;
         public const Key MoveViewportToolTransientChangeKey = Key.Space;
 
 
+        public static void BlockShortcutExection(string blocker)
+        {
+            if (_shortcutExecutionBlockers.Contains(blocker)) return;
+            _shortcutExecutionBlockers.Add(blocker);
+        }
+
+        public static void UnblockShortcutExecution(string blocker)
+        {
+            if (!_shortcutExecutionBlockers.Contains(blocker)) return;
+            _shortcutExecutionBlockers.Remove(blocker);
+        }
+
+        public static void UnblockShortcutExecutionAll()
+        {
+            _shortcutExecutionBlockers.Clear();
+        }
+
         public Shortcut GetToolShortcut<T>()
         public Shortcut GetToolShortcut<T>()
         {
         {
             return GetToolShortcut(typeof(T));
             return GetToolShortcut(typeof(T));
@@ -43,7 +64,7 @@ namespace PixiEditor.Models.Controllers.Shortcuts
 
 
         public void KeyPressed(Key key, ModifierKeys modifiers)
         public void KeyPressed(Key key, ModifierKeys modifiers)
         {
         {
-            if (!BlockShortcutExecution)
+            if (!ShortcutExecutionBlocked)
             {
             {
                 Shortcut[] shortcuts = ShortcutGroups.SelectMany(x => x.Shortcuts).ToList().FindAll(x => x.ShortcutKey == key).ToArray();
                 Shortcut[] shortcuts = ShortcutGroups.SelectMany(x => x.Shortcuts).ToList().FindAll(x => x.ShortcutKey == key).ToArray();
                 if (shortcuts.Length < 1)
                 if (shortcuts.Length < 1)

+ 9 - 2
PixiEditor/Models/Controllers/SurfaceRenderer.cs

@@ -12,6 +12,7 @@ namespace PixiEditor.Models.Controllers
         public SKSurface BackingSurface { get; private set; }
         public SKSurface BackingSurface { get; private set; }
         public WriteableBitmap FinalBitmap { get; private set; }
         public WriteableBitmap FinalBitmap { get; private set; }
         private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
         private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
+        private SKPaint HighQualityResizePaint { get; } = new SKPaint() { FilterQuality = SKFilterQuality.High };
         public SurfaceRenderer(int width, int height)
         public SurfaceRenderer(int width, int height)
         {
         {
             FinalBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null);
             FinalBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null);
@@ -23,15 +24,21 @@ namespace PixiEditor.Models.Controllers
         {
         {
             BackingSurface.Dispose();
             BackingSurface.Dispose();
             BlendingPaint.Dispose();
             BlendingPaint.Dispose();
+            HighQualityResizePaint.Dispose();
         }
         }
 
 
         public void Draw(Surface otherSurface, byte opacity)
         public void Draw(Surface otherSurface, byte opacity)
+        {
+            Draw(otherSurface, opacity, new SKRectI(0, 0, otherSurface.Width, otherSurface.Height));
+        }
+
+        public void Draw(Surface otherSurface, byte opacity, SKRectI drawRect)
         {
         {
             BackingSurface.Canvas.Clear();
             BackingSurface.Canvas.Clear();
             FinalBitmap.Lock();
             FinalBitmap.Lock();
             BlendingPaint.Color = new SKColor(255, 255, 255, opacity);
             BlendingPaint.Color = new SKColor(255, 255, 255, opacity);
-            using (var snapshot = otherSurface.SkiaSurface.Snapshot())
-                BackingSurface.Canvas.DrawImage(snapshot, new SKRect(0, 0, FinalBitmap.PixelWidth, FinalBitmap.PixelHeight));
+            using (var snapshot = otherSurface.SkiaSurface.Snapshot(drawRect))
+                BackingSurface.Canvas.DrawImage(snapshot, new SKRect(0, 0, FinalBitmap.PixelWidth, FinalBitmap.PixelHeight), HighQualityResizePaint);
             FinalBitmap.AddDirtyRect(new Int32Rect(0, 0, FinalBitmap.PixelWidth, FinalBitmap.PixelHeight));
             FinalBitmap.AddDirtyRect(new Int32Rect(0, 0, FinalBitmap.PixelWidth, FinalBitmap.PixelHeight));
             FinalBitmap.Unlock();
             FinalBitmap.Unlock();
         }
         }

+ 2 - 0
PixiEditor/Models/DataHolders/Document/Document.Constructors.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using System;
 using System;
 using System.Linq;
 using System.Linq;
@@ -31,6 +32,7 @@ namespace PixiEditor.Models.DataHolders
             LayerStructure.LayerStructureChanged += LayerStructure_LayerStructureChanged;
             LayerStructure.LayerStructureChanged += LayerStructure_LayerStructureChanged;
             DocumentSizeChanged += (sender, args) =>
             DocumentSizeChanged += (sender, args) =>
             {
             {
+                ActiveSelection = new Selection(Array.Empty<Coordinates>(), new PixelSize(args.NewWidth, args.NewHeight));
                 Renderer.Resize(args.NewWidth, args.NewHeight);
                 Renderer.Resize(args.NewWidth, args.NewHeight);
                 GeneratePreviewLayer();
                 GeneratePreviewLayer();
             };
             };

+ 30 - 13
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -12,6 +12,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Windows;
 using System.Windows;
+using Windows.Graphics;
 
 
 namespace PixiEditor.Models.DataHolders
 namespace PixiEditor.Models.DataHolders
 {
 {
@@ -221,9 +222,7 @@ namespace PixiEditor.Models.DataHolders
             if (width <= 0 || height <= 0)
             if (width <= 0 || height <= 0)
                 throw new ArgumentException("Dimensions must be greater than 0");
                 throw new ArgumentException("Dimensions must be greater than 0");
 
 
-            layer = bitmap == null ? new Layer(name, width, height) : new Layer(name, bitmap);
-            layer.MaxHeight = Height;
-            layer.MaxWidth = Width;
+            layer = bitmap == null ? new Layer(name, width, height, Width, Height) : new Layer(name, bitmap, Width, Height);
 
 
             Layers.Add(layer);
             Layers.Add(layer);
 
 
@@ -441,7 +440,7 @@ namespace PixiEditor.Models.DataHolders
 
 
             var groupParent = LayerStructure.GetGroupByLayer(layersToMerge[^1].GuidValue);
             var groupParent = LayerStructure.GetGroupByLayer(layersToMerge[^1].GuidValue);
 
 
-            Layer placeholderLayer = new("_placeholder");
+            Layer placeholderLayer = new("_placeholder", Width, Height);
             Layers.Insert(index, placeholderLayer);
             Layers.Insert(index, placeholderLayer);
             LayerStructure.AssignParent(placeholderLayer.GuidValue, groupParent?.GroupGuid);
             LayerStructure.AssignParent(placeholderLayer.GuidValue, groupParent?.GroupGuid);
 
 
@@ -449,6 +448,8 @@ namespace PixiEditor.Models.DataHolders
             {
             {
                 Layer firstLayer = mergedLayer;
                 Layer firstLayer = mergedLayer;
                 Layer secondLayer = layersToMerge[i + 1];
                 Layer secondLayer = layersToMerge[i + 1];
+                firstLayer.ClipCanvas();
+                secondLayer.ClipCanvas();
                 mergedLayer = firstLayer.MergeWith(secondLayer, name, Width, Height);
                 mergedLayer = firstLayer.MergeWith(secondLayer, name, Width, Height);
                 RemoveLayer(layersToMerge[i], false);
                 RemoveLayer(layersToMerge[i], false);
             }
             }
@@ -471,7 +472,7 @@ namespace PixiEditor.Models.DataHolders
                 throw new ArgumentException("Not enough layers were provided to merge. Minimum amount is 2");
                 throw new ArgumentException("Not enough layers were provided to merge. Minimum amount is 2");
             }
             }
 
 
-            IEnumerable<Layer> undoArgs = layersToMerge;
+            Layer[] undoArgs = layersToMerge;
 
 
             var oldLayerStructure = LayerStructure.CloneGroups();
             var oldLayerStructure = LayerStructure.CloneGroups();
 
 
@@ -602,7 +603,7 @@ namespace PixiEditor.Models.DataHolders
                 for (int i = 0; i < layers.Length; i++)
                 for (int i = 0; i < layers.Length; i++)
                 {
                 {
                     Layer layer = layers[i];
                     Layer layer = layers[i];
-                    layer.IsActive = true;
+                    layer.IsActive = data[i].IsActive;
                     Layers.Insert(data[i].LayerIndex, layer);
                     Layers.Insert(data[i].LayerIndex, layer);
                 }
                 }
 
 
@@ -614,20 +615,36 @@ namespace PixiEditor.Models.DataHolders
         /// <summary>
         /// <summary>
         ///     Moves offsets of layers by specified vector.
         ///     Moves offsets of layers by specified vector.
         /// </summary>
         /// </summary>
-        private void MoveOffsets(IEnumerable<Layer> layers, Coordinates moveVector)
+        private void MoveOffsets(IList<Layer> layers, IList<Int32Rect> bounds, Coordinates moveVector)
         {
         {
-            foreach (Layer layer in layers)
+            for (int i = 0; i < layers.Count; i++)
             {
             {
+                Layer layer = layers[i];
+                Int32Rect bound = bounds[i];
                 Thickness offset = layer.Offset;
                 Thickness offset = layer.Offset;
                 layer.Offset = new Thickness(offset.Left + moveVector.X, offset.Top + moveVector.Y, 0, 0);
                 layer.Offset = new Thickness(offset.Left + moveVector.X, offset.Top + moveVector.Y, 0, 0);
+                if (!bound.IsEmpty && layer.Bounds != bound)
+                {
+                    layer.DynamicResizeAbsolute(bound);
+                }
+                else
+                {
+                    layer.ClipCanvas();
+                }
             }
             }
         }
         }
 
 
         private void MoveOffsetsProcess(object[] arguments)
         private void MoveOffsetsProcess(object[] arguments)
         {
         {
-            if (arguments.Length > 0 && arguments[0] is IEnumerable<Layer> layers && arguments[1] is Coordinates vector)
+            if (arguments.Length > 0 && arguments[0] is List<Guid> guids && arguments[1] is List<Int32Rect> bounds && arguments[2] is Coordinates vector)
             {
             {
-                MoveOffsets(layers, vector);
+                List<Layer> layers = new List<Layer>(guids.Count);
+                foreach (Guid guid in guids)
+                {
+                    layers.Add(Layers.First(x => x.GuidValue == guid));
+                }
+
+                MoveOffsets(layers, bounds, vector);
             }
             }
             else
             else
             {
             {
@@ -712,7 +729,7 @@ namespace PixiEditor.Models.DataHolders
             Renderer.ForceRerender();
             Renderer.ForceRerender();
         }
         }
 
 
-        private void RestoreLayersProcess(Layer[] layers, UndoLayer[] layersData)
+        public void RestoreLayersProcess(Layer[] layers, UndoLayer[] layersData)
         {
         {
             for (int i = 0; i < layers.Length; i++)
             for (int i = 0; i < layers.Length; i++)
             {
             {
@@ -726,7 +743,7 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
-        private void RemoveLayerProcess(object[] parameters)
+        public void RemoveLayerProcess(object[] parameters)
         {
         {
             if (parameters is { Length: > 0 } && parameters[0] is Guid layerGuid)
             if (parameters is { Length: > 0 } && parameters[0] is Guid layerGuid)
             {
             {
@@ -837,7 +854,7 @@ namespace PixiEditor.Models.DataHolders
             return sucess;
             return sucess;
         }
         }
 
 
-        private void RemoveLayersProcess(object[] parameters)
+        public void RemoveLayersProcess(object[] parameters)
         {
         {
             if (parameters != null && parameters.Length > 0 && parameters[0] is IEnumerable<Guid> layerGuids)
             if (parameters != null && parameters.Length > 0 && parameters[0] is IEnumerable<Guid> layerGuids)
             {
             {

+ 26 - 12
PixiEditor/Models/DataHolders/Document/Document.Operations.cs

@@ -7,7 +7,6 @@ using SkiaSharp;
 using System;
 using System;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
-using Windows.Graphics;
 
 
 namespace PixiEditor.Models.DataHolders
 namespace PixiEditor.Models.DataHolders
 {
 {
@@ -24,7 +23,7 @@ namespace PixiEditor.Models.DataHolders
         ///     Point that will act as "starting position" of resizing. Use pipe to connect horizontal and
         ///     Point that will act as "starting position" of resizing. Use pipe to connect horizontal and
         ///     vertical.
         ///     vertical.
         /// </param>
         /// </param>
-        public void ResizeCanvas(int width, int height, AnchorPoint anchor)
+        public void ResizeCanvas(int width, int height, AnchorPoint anchor, bool addToUndo = true)
         {
         {
             int oldWidth = Width;
             int oldWidth = Width;
             int oldHeight = Height;
             int oldHeight = Height;
@@ -38,16 +37,23 @@ namespace PixiEditor.Models.DataHolders
             object[] processArgs = { newOffsets, width, height };
             object[] processArgs = { newOffsets, width, height };
             object[] reverseProcessArgs = { Width, Height };
             object[] reverseProcessArgs = { Width, Height };
 
 
-            StorageBasedChange change = new(this, Layers);
+            if (addToUndo) 
+            { 
+                StorageBasedChange change = new(this, Layers);
+                ResizeCanvas(newOffsets, width, height);
 
 
-            ResizeCanvas(newOffsets, width, height);
+                UndoManager.AddUndoChange(change.ToChange(
+                    RestoreDocumentLayersProcess,
+                    reverseProcessArgs,
+                    ResizeCanvasProcess,
+                    processArgs,
+                    "Resize canvas"));
+            }
+            else
+            {
+                ResizeCanvas(newOffsets, width, height);
+            }
 
 
-            UndoManager.AddUndoChange(change.ToChange(
-                RestoreDocumentLayersProcess,
-                reverseProcessArgs,
-                ResizeCanvasProcess,
-                processArgs,
-                "Resize canvas"));
             DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
             DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
         }
         }
 
 
@@ -231,9 +237,19 @@ namespace PixiEditor.Models.DataHolders
             for (int i = 0; i < Layers.Count; i++)
             for (int i = 0; i < Layers.Count; i++)
             {
             {
                 Layer layer = Layers[i];
                 Layer layer = Layers[i];
+                Layers[i].MaxWidth = newWidth;
+                Layers[i].MaxHeight = newHeight;
+                if (layer.IsReset)
+                    continue;
+
                 Thickness newOffset = offset[i];
                 Thickness newOffset = offset[i];
                 Int32Rect newRect = new((int)newOffset.Left, (int)newOffset.Top, layer.Width, layer.Height);
                 Int32Rect newRect = new((int)newOffset.Left, (int)newOffset.Top, layer.Width, layer.Height);
                 Int32Rect newLayerRect = newRect.Intersect(newCanvasRect);
                 Int32Rect newLayerRect = newRect.Intersect(newCanvasRect);
+                if (!newLayerRect.HasArea)
+                {
+                    layer.Reset();
+                    continue;
+                }
                 Surface newBitmap = new(newLayerRect.Width, newLayerRect.Height);
                 Surface newBitmap = new(newLayerRect.Width, newLayerRect.Height);
                 var oldBitmap = layer.LayerBitmap;
                 var oldBitmap = layer.LayerBitmap;
                 using var snapshot = oldBitmap.SkiaSurface.Snapshot();
                 using var snapshot = oldBitmap.SkiaSurface.Snapshot();
@@ -243,8 +259,6 @@ namespace PixiEditor.Models.DataHolders
                 oldBitmap.Dispose();
                 oldBitmap.Dispose();
 
 
                 Layers[i].Offset = new Thickness(newLayerRect.X, newLayerRect.Y, 0, 0);
                 Layers[i].Offset = new Thickness(newLayerRect.X, newLayerRect.Y, 0, 0);
-                Layers[i].MaxWidth = newWidth;
-                Layers[i].MaxHeight = newHeight;
             }
             }
 
 
             Width = newWidth;
             Width = newWidth;

+ 1 - 5
PixiEditor/Models/DataHolders/Document/Document.Preview.cs

@@ -43,11 +43,7 @@ namespace PixiEditor.Models.DataHolders
 
 
         public void GeneratePreviewLayer()
         public void GeneratePreviewLayer()
         {
         {
-            PreviewLayer = new Layer("_previewLayer")
-            {
-                MaxWidth = Width,
-                MaxHeight = Height
-            };
+            PreviewLayer = new Layer("_previewLayer", Width, Height);
         }
         }
     }
     }
 }
 }

+ 80 - 24
PixiEditor/Models/DataHolders/Document/Document.cs

@@ -61,7 +61,7 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
-        private Selection selection = new Selection(Array.Empty<Coordinates>());
+        private Selection selection;
 
 
         public Selection ActiveSelection
         public Selection ActiveSelection
         {
         {
@@ -114,16 +114,19 @@ namespace PixiEditor.Models.DataHolders
         /// </summary>
         /// </summary>
         public void ClipCanvas()
         public void ClipCanvas()
         {
         {
-            DoubleCoords points = GetEdgePoints(Layers);
-            int smallestX = points.Coords1.X;
-            int smallestY = points.Coords1.Y;
-            int biggestX = points.Coords2.X;
-            int biggestY = points.Coords2.Y;
+            DoubleCoords? maybePoints = GetEdgePoints(Layers);
 
 
-            if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
+            if (maybePoints == null)
             {
             {
+                //all layers are empty
                 return;
                 return;
             }
             }
+            DoubleCoords points = maybePoints.Value;
+
+            int smallestX = points.Coords1.X;
+            int smallestY = points.Coords1.Y;
+            int biggestX = points.Coords2.X;
+            int biggestY = points.Coords2.Y;
 
 
             int width = biggestX - smallestX;
             int width = biggestX - smallestX;
             int height = biggestY - smallestY;
             int height = biggestY - smallestY;
@@ -133,15 +136,15 @@ namespace PixiEditor.Models.DataHolders
             int oldWidth = Width;
             int oldWidth = Width;
             int oldHeight = Height;
             int oldHeight = Height;
 
 
-            MoveOffsets(Layers, moveVector);
+            StorageBasedChange change = new StorageBasedChange(this, Layers);
 
 
-            object[] reverseArguments = { oldOffsets, oldWidth, oldHeight };
-            object[] processArguments = { Layers.Select(x => x.Offset).ToArray(), width, height };
+            object[] reverseArguments = { oldWidth, oldHeight };
+            object[] processArguments = { Layers.Select(x => new Thickness(x.OffsetX - smallestX, x.OffsetY - smallestY, 0, 0)).ToArray(), width, height };
 
 
             ResizeCanvasProcess(processArguments);
             ResizeCanvasProcess(processArguments);
 
 
-            UndoManager.AddUndoChange(new Change(
-                ResizeCanvasProcess,
+            UndoManager.AddUndoChange(change.ToChange(
+                RestoreDocumentLayersProcess,
                 reverseArguments,
                 reverseArguments,
                 ResizeCanvasProcess,
                 ResizeCanvasProcess,
                 processArguments,
                 processArguments,
@@ -153,37 +156,41 @@ namespace PixiEditor.Models.DataHolders
         /// </summary>
         /// </summary>
         public void CenterContent()
         public void CenterContent()
         {
         {
-            var layersToCenter = Layers.Where(x => x.IsActive && LayerStructureUtils.GetFinalLayerIsVisible(x, LayerStructure));
-            if (!layersToCenter.Any())
+            var layersToCenter = Layers.Where(x => x.IsActive && LayerStructureUtils.GetFinalLayerIsVisible(x, LayerStructure)).ToList();
+            if (layersToCenter.Count == 0)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            DoubleCoords points = GetEdgePoints(layersToCenter);
+            List<Int32Rect> oldBounds = layersToCenter.Select(x => x.Bounds).ToList();
+
+            DoubleCoords? maybePoints = ClipLayersAndGetEdgePoints(layersToCenter);
+            if (maybePoints == null)
+                return;
+            DoubleCoords points = maybePoints.Value;
 
 
             int smallestX = points.Coords1.X;
             int smallestX = points.Coords1.X;
             int smallestY = points.Coords1.Y;
             int smallestY = points.Coords1.Y;
             int biggestX = points.Coords2.X;
             int biggestX = points.Coords2.X;
             int biggestY = points.Coords2.Y;
             int biggestY = points.Coords2.Y;
 
 
-            if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
-            {
-                return;
-            }
-
             Coordinates contentCenter = CoordinatesCalculator.GetCenterPoint(points.Coords1, points.Coords2);
             Coordinates contentCenter = CoordinatesCalculator.GetCenterPoint(points.Coords1, points.Coords2);
             Coordinates documentCenter = CoordinatesCalculator.GetCenterPoint(
             Coordinates documentCenter = CoordinatesCalculator.GetCenterPoint(
                 new Coordinates(0, 0),
                 new Coordinates(0, 0),
                 new Coordinates(Width, Height));
                 new Coordinates(Width, Height));
             Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
             Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
 
 
-            MoveOffsets(layersToCenter, moveVector);
+            List<Int32Rect> emptyBounds = Enumerable.Repeat(Int32Rect.Empty, layersToCenter.Count).ToList();
+
+            MoveOffsets(layersToCenter, emptyBounds, moveVector);
+
+            List <Guid> guids = layersToCenter.Select(x => x.GuidValue).ToList();
             UndoManager.AddUndoChange(
             UndoManager.AddUndoChange(
                 new Change(
                 new Change(
                     MoveOffsetsProcess,
                     MoveOffsetsProcess,
-                    new object[] { layersToCenter, new Coordinates(-moveVector.X, -moveVector.Y) },
+                    new object[] { guids, oldBounds, new Coordinates(-moveVector.X, -moveVector.Y) },
                     MoveOffsetsProcess,
                     MoveOffsetsProcess,
-                    new object[] { layersToCenter, moveVector },
+                    new object[] { guids, emptyBounds, moveVector },
                     "Center content"));
                     "Center content"));
         }
         }
 
 
@@ -241,7 +248,47 @@ namespace PixiEditor.Models.DataHolders
             return 0;
             return 0;
         }
         }
 
 
-        private DoubleCoords GetEdgePoints(IEnumerable<Layer> layers)
+        private DoubleCoords? GetEdgePoints(IEnumerable<Layer> layers)
+        {
+            if (Layers.Count == 0)
+                throw new ArgumentException("Not enough layers");
+
+            int smallestX = int.MaxValue;
+            int smallestY = int.MaxValue;
+            int biggestX = int.MinValue;
+            int biggestY = int.MinValue;
+
+            bool allLayersSkipped = true;
+
+            foreach (Layer layer in layers)
+            {
+                Int32Rect bounds = layer.TightBounds;
+                if (layer.IsReset || !bounds.HasArea)
+                    continue;
+                allLayersSkipped = false;
+
+                if (layer.OffsetX + bounds.X < smallestX)
+                    smallestX = layer.OffsetX + bounds.X;
+
+                if (layer.OffsetX + bounds.X + bounds.Width > biggestX)
+                    biggestX = layer.OffsetX + bounds.X + bounds.Width;
+
+                if (layer.OffsetY + bounds.Y < smallestY)
+                    smallestY = layer.OffsetY + bounds.Y;
+
+                if (layer.OffsetY + bounds.Y + bounds.Height > biggestY)
+                    biggestY = layer.OffsetY + bounds.Y + bounds.Height;
+            }
+
+            if (allLayersSkipped)
+                return null;
+
+            return new DoubleCoords(
+                new Coordinates(smallestX, smallestY),
+                new Coordinates(biggestX, biggestY));
+        }
+
+        private DoubleCoords? ClipLayersAndGetEdgePoints(IEnumerable<Layer> layers)
         {
         {
             if (Layers.Count == 0)
             if (Layers.Count == 0)
             {
             {
@@ -253,9 +300,15 @@ namespace PixiEditor.Models.DataHolders
             int biggestX = int.MinValue;
             int biggestX = int.MinValue;
             int biggestY = int.MinValue;
             int biggestY = int.MinValue;
 
 
+            bool allLayersSkipped = true;
+
             foreach (Layer layer in layers)
             foreach (Layer layer in layers)
             {
             {
                 layer.ClipCanvas();
                 layer.ClipCanvas();
+                if (layer.IsReset)
+                    continue;
+                allLayersSkipped = false;
+
                 if (layer.OffsetX < smallestX)
                 if (layer.OffsetX < smallestX)
                 {
                 {
                     smallestX = layer.OffsetX;
                     smallestX = layer.OffsetX;
@@ -277,6 +330,9 @@ namespace PixiEditor.Models.DataHolders
                 }
                 }
             }
             }
 
 
+            if (allLayersSkipped)
+                return null;
+
             return new DoubleCoords(
             return new DoubleCoords(
                 new Coordinates(smallestX, smallestY),
                 new Coordinates(smallestX, smallestY),
                 new Coordinates(biggestX, biggestY));
                 new Coordinates(biggestX, biggestY));

+ 20 - 0
PixiEditor/Models/DataHolders/PixelSize.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.DataHolders
+{
+    public struct PixelSize
+    {
+        public int Width { get; set; }
+        public int Height { get; set; }
+
+        public PixelSize(int width, int height)
+        {
+            Width = width;
+            Height = height;
+        }
+    }
+}

+ 14 - 14
PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs

@@ -3,6 +3,7 @@ using PixiEditor.Models.IO;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
 using PixiEditor.Parser.Skia;
 using PixiEditor.Parser.Skia;
+using System;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
@@ -88,7 +89,7 @@ namespace PixiEditor.Models.DataHolders
                               .Where(x => x.Opacity > 0.8)
                               .Where(x => x.Opacity > 0.8)
                               .Select(x => (x.ToSKImage(), new Coordinates(x.OffsetX, x.OffsetY))));
                               .Select(x => (x.ToSKImage(), new Coordinates(x.OffsetX, x.OffsetY))));
 
 
-                return surface.ToWriteableBitmap();
+                return DownscaleToMaxSize(surface.ToWriteableBitmap());
             }
             }
             else if (SupportedFilesHelper.IsExtensionSupported(FileExtension))
             else if (SupportedFilesHelper.IsExtensionSupported(FileExtension))
             {
             {
@@ -103,25 +104,24 @@ namespace PixiEditor.Models.DataHolders
                     corrupt = true;
                     corrupt = true;
                     return null;
                     return null;
                 }
                 }
-                
-                if (bitmap == null)//prevent crash
-                    return null;
-
-                ImageFileMaxSizeChecker imageFileMaxSizeChecker = new ImageFileMaxSizeChecker()
-                {
-                    MaxAllowedWidthInPixels = Constants.MaxPreviewWidth,
-                    MaxAllowedHeightInPixels = Constants.MaxPreviewHeight,
-                };
 
 
-                if (bitmap == null)
+                if (bitmap == null) //prevent crash
                     return null;
                     return null;
 
 
-                return imageFileMaxSizeChecker.IsFileUnderMaxSize(bitmap) ?
-                    bitmap
-                    : bitmap.Resize(width: Constants.MaxPreviewWidth, height: Constants.MaxPreviewHeight, WriteableBitmapExtensions.Interpolation.Bilinear);
+                return DownscaleToMaxSize(bitmap);
             }
             }
 
 
             return null;
             return null;
         }
         }
+
+        private WriteableBitmap DownscaleToMaxSize(WriteableBitmap bitmap)
+        {
+            if (bitmap.PixelWidth > Constants.MaxPreviewWidth || bitmap.PixelHeight > Constants.MaxPreviewHeight)
+            {
+                double factor = Math.Min(Constants.MaxPreviewWidth / (double)bitmap.PixelWidth, Constants.MaxPreviewHeight / (double)bitmap.PixelHeight);
+                return bitmap.Resize((int)(bitmap.PixelWidth * factor), (int)(bitmap.PixelHeight * factor), WriteableBitmapExtensions.Interpolation.Bilinear);
+            }
+            return bitmap;
+        }
     }
     }
 }
 }

+ 2 - 2
PixiEditor/Models/DataHolders/Selection.cs

@@ -17,10 +17,10 @@ namespace PixiEditor.Models.DataHolders
         private readonly SKColor selectionBlue;
         private readonly SKColor selectionBlue;
         private Layer selectionLayer;
         private Layer selectionLayer;
 
 
-        public Selection(Coordinates[] selectedPoints)
+        public Selection(Coordinates[] selectedPoints, PixelSize maxSize)
         {
         {
             SelectedPoints = new ObservableCollection<Coordinates>(selectedPoints);
             SelectedPoints = new ObservableCollection<Coordinates>(selectedPoints);
-            SelectionLayer = new Layer("_selectionLayer");
+            SelectionLayer = new Layer("_selectionLayer", maxSize.Width, maxSize.Height);
             selectionBlue = new SKColor(142, 202, 255, 255);
             selectionBlue = new SKColor(142, 202, 255, 255);
         }
         }
 
 

+ 5 - 1
PixiEditor/Models/IO/Exporter.cs

@@ -56,10 +56,14 @@ namespace PixiEditor.Models.IO
                 var bitmap = document.Renderer.FinalBitmap;
                 var bitmap = document.Renderer.FinalBitmap;
                 SaveAs(encodersFactory[chosenFormat](), path, bitmap.PixelWidth, bitmap.PixelHeight, bitmap);
                 SaveAs(encodersFactory[chosenFormat](), path, bitmap.PixelWidth, bitmap.PixelHeight, bitmap);
             }
             }
-            else
+            else if(Directory.Exists(Path.GetDirectoryName(path)))
             {
             {
                 Parser.PixiParser.Serialize(ParserHelpers.ToSerializable(document), path);
                 Parser.PixiParser.Serialize(ParserHelpers.ToSerializable(document), path);
             }
             }
+            else
+            {
+                SaveAsEditableFileWithDialog(document, out path);
+            }
 
 
             return path;
             return path;
         }
         }

+ 0 - 20
PixiEditor/Models/IO/ImageFileMaxSizeChecker.cs

@@ -1,20 +0,0 @@
-using System.Windows.Media.Imaging;
-
-namespace PixiEditor.Models.IO
-{
-    internal class ImageFileMaxSizeChecker
-    {
-        public int MaxAllowedWidthInPixels { get; init; } = Constants.MaxPreviewWidth;
-        public int MaxAllowedHeightInPixels { get; init; } = Constants.MaxPreviewHeight;
-
-        public ImageFileMaxSizeChecker()
-        {
-        }
-
-        public bool IsFileUnderMaxSize(WriteableBitmap fileToCheck)
-        {
-            return fileToCheck.PixelWidth <= MaxAllowedWidthInPixels
-                && fileToCheck.PixelHeight <= MaxAllowedHeightInPixels;
-        }
-    }
-}

+ 0 - 22
PixiEditor/Models/IO/PixiFileMaxSizeChecker.cs

@@ -1,22 +0,0 @@
-using PixiEditor.Parser;
-
-namespace PixiEditor.Models.IO
-{
-    internal class PixiFileMaxSizeChecker
-    {
-        public int MaxAllowedWidthInPixels { get; init; } = 1080;
-        public int MaxAllowedHeightInPixels { get; init; } = 1080;
-        public int MaxAllowedLayerCount { get; init; } = 5;
-
-        public PixiFileMaxSizeChecker()
-        {
-        }
-
-        public bool IsFileUnderMaxSize(SerializableDocument fileToCheck)
-        {
-            return fileToCheck.Width <= MaxAllowedWidthInPixels
-                && fileToCheck.Height <= MaxAllowedHeightInPixels
-                && fileToCheck.Layers.Count <= MaxAllowedLayerCount;
-        }
-    }
-}

+ 5 - 9
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -185,10 +185,7 @@ namespace PixiEditor.Models.ImageManipulation
                 throw new ArgumentException("There were not the same amount of bitmaps and offsets", nameof(layerBitmaps));
                 throw new ArgumentException("There were not the same amount of bitmaps and offsets", nameof(layerBitmaps));
             }
             }
 
 
-            using Surface previewSurface = new Surface(maxPreviewWidth, maxPreviewHeight);
-            return previewSurface.ToWriteableBitmap();
-            /*
-            WriteableBitmap previewBitmap = BitmapFactory.New(width, height);
+            using Surface previewSurface = new Surface(width, height);
 
 
             var layerBitmapsEnumerator = layerBitmaps.GetEnumerator();
             var layerBitmapsEnumerator = layerBitmaps.GetEnumerator();
             var offsetsXEnumerator = offsetsX.GetEnumerator();
             var offsetsXEnumerator = offsetsX.GetEnumerator();
@@ -199,19 +196,18 @@ namespace PixiEditor.Models.ImageManipulation
                 offsetsXEnumerator.MoveNext();
                 offsetsXEnumerator.MoveNext();
                 offsetsYEnumerator.MoveNext();
                 offsetsYEnumerator.MoveNext();
 
 
-                var bitmap = layerBitmapsEnumerator.Current;
+                var bitmap = layerBitmapsEnumerator.Current.SkiaSurface.Snapshot();
                 var offsetX = offsetsXEnumerator.Current;
                 var offsetX = offsetsXEnumerator.Current;
                 var offsetY = offsetsYEnumerator.Current;
                 var offsetY = offsetsYEnumerator.Current;
 
 
-                previewBitmap.Blit(
-                    new Rect(offsetX, offsetY, bitmap.Width, bitmap.Height),
+                previewSurface.SkiaSurface.Canvas.DrawImage(
                     bitmap,
                     bitmap,
-                    new Rect(0, 0, bitmap.Width, bitmap.Height));
+                    offsetX, offsetY, Surface.BlendingPaint);
             }
             }
 
 
             int newWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
             int newWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
             int newHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
             int newHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
-            return previewBitmap.Redesize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);*/
+            return previewSurface.ResizeNearestNeighbor(newWidth, newHeight).ToWriteableBitmap();
         }
         }
     }
     }
 }
 }

+ 16 - 8
PixiEditor/Models/Layers/Layer.cs

@@ -9,7 +9,6 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
-using Windows.Graphics;
 
 
 namespace PixiEditor.Models.Layers
 namespace PixiEditor.Models.Layers
 {
 {
@@ -32,32 +31,38 @@ namespace PixiEditor.Models.Layers
 
 
         private string layerHighlightColor = "#666666";
         private string layerHighlightColor = "#666666";
 
 
-        public Layer(string name)
+        public Layer(string name, int maxWidth, int maxHeight)
         {
         {
             Name = name;
             Name = name;
             LayerBitmap = new Surface(1, 1);
             LayerBitmap = new Surface(1, 1);
             IsReset = true;
             IsReset = true;
             Width = 1;
             Width = 1;
             Height = 1;
             Height = 1;
+            MaxWidth = maxWidth;
+            MaxHeight = maxHeight;
             GuidValue = Guid.NewGuid();
             GuidValue = Guid.NewGuid();
         }
         }
 
 
-        public Layer(string name, int width, int height)
+        public Layer(string name, int width, int height, int maxWidth, int maxHeight)
         {
         {
             Name = name;
             Name = name;
             LayerBitmap = new Surface(width, height);
             LayerBitmap = new Surface(width, height);
             IsReset = true;
             IsReset = true;
             Width = width;
             Width = width;
             Height = height;
             Height = height;
+            MaxWidth = maxWidth;
+            MaxHeight = maxHeight;
             GuidValue = Guid.NewGuid();
             GuidValue = Guid.NewGuid();
         }
         }
 
 
-        public Layer(string name, Surface layerBitmap)
+        public Layer(string name, Surface layerBitmap, int maxWidth, int maxHeight)
         {
         {
             Name = name;
             Name = name;
             LayerBitmap = layerBitmap;
             LayerBitmap = layerBitmap;
             Width = layerBitmap.Width;
             Width = layerBitmap.Width;
             Height = layerBitmap.Height;
             Height = layerBitmap.Height;
+            MaxWidth = maxWidth;
+            MaxHeight = maxHeight;
             GuidValue = Guid.NewGuid();
             GuidValue = Guid.NewGuid();
         }
         }
 
 
@@ -210,6 +215,7 @@ namespace PixiEditor.Models.Layers
         public bool IsReset { get; private set; }
         public bool IsReset { get; private set; }
 
 
         public Int32Rect TightBounds => GetContentDimensions();
         public Int32Rect TightBounds => GetContentDimensions();
+        public Int32Rect Bounds => new Int32Rect(OffsetX, OffsetY, Width, Height);
 
 
         public event EventHandler<Int32Rect> LayerBitmapChanged;
         public event EventHandler<Int32Rect> LayerBitmapChanged;
 
 
@@ -246,12 +252,10 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         /// </summary>
         public Layer Clone(bool generateNewGuid = false)
         public Layer Clone(bool generateNewGuid = false)
         {
         {
-            return new Layer(Name, new Surface(LayerBitmap))
+            return new Layer(Name, new Surface(LayerBitmap), MaxWidth, MaxHeight)
             {
             {
                 IsVisible = IsVisible,
                 IsVisible = IsVisible,
                 Offset = Offset,
                 Offset = Offset,
-                MaxHeight = MaxHeight,
-                MaxWidth = MaxWidth,
                 Opacity = Opacity,
                 Opacity = Opacity,
                 IsActive = IsActive,
                 IsActive = IsActive,
                 IsRenaming = IsRenaming,
                 IsRenaming = IsRenaming,
@@ -491,7 +495,11 @@ namespace PixiEditor.Models.Layers
         public void ClipCanvas()
         public void ClipCanvas()
         {
         {
             var dimensions = GetContentDimensions();
             var dimensions = GetContentDimensions();
-            if (dimensions == Int32Rect.Empty) return;
+            if (dimensions == Int32Rect.Empty)
+            {
+                Reset();
+                return;
+            }
 
 
             ResizeCanvas(0, 0, dimensions.X, dimensions.Y, dimensions.Width, dimensions.Height);
             ResizeCanvas(0, 0, dimensions.X, dimensions.Y, dimensions.Width, dimensions.Height);
             Offset = new Thickness(OffsetX + dimensions.X, OffsetY + dimensions.Y, 0, 0);
             Offset = new Thickness(OffsetX + dimensions.X, OffsetY + dimensions.Y, 0, 0);

+ 4 - 4
PixiEditor/Models/Layers/LayerHelper.cs

@@ -57,7 +57,7 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
-        public static Layer MergeWith(this Layer thisLayer, Layer otherLayer, string newName, Vector documentsSize)
+        public static Layer MergeWith(this Layer thisLayer, Layer otherLayer, string newName, PixelSize documentSize)
         {
         {
             Int32Rect thisRect = new(thisLayer.OffsetX, thisLayer.OffsetY, thisLayer.Width, thisLayer.Height);
             Int32Rect thisRect = new(thisLayer.OffsetX, thisLayer.OffsetY, thisLayer.Width, thisLayer.Height);
             Int32Rect otherRect = new(otherLayer.OffsetX, otherLayer.OffsetY, otherLayer.Width, otherLayer.Height);
             Int32Rect otherRect = new(otherLayer.OffsetX, otherLayer.OffsetY, otherLayer.Width, otherLayer.Height);
@@ -66,9 +66,9 @@ namespace PixiEditor.Models.Layers
 
 
             Surface mergedBitmap = BitmapUtils.CombineLayers(combined, new Layer[] { thisLayer, otherLayer });
             Surface mergedBitmap = BitmapUtils.CombineLayers(combined, new Layer[] { thisLayer, otherLayer });
 
 
-            Layer mergedLayer = new Layer(newName, mergedBitmap)
+            Layer mergedLayer = new Layer(newName, mergedBitmap, documentSize.Width, documentSize.Height)
             {
             {
-                Offset = new Thickness(combined.X, combined.Y, 0, 0)
+                Offset = new Thickness(combined.X, combined.Y, 0, 0),
             };
             };
 
 
             return mergedLayer;
             return mergedLayer;
@@ -76,7 +76,7 @@ namespace PixiEditor.Models.Layers
 
 
         public static Layer MergeWith(this Layer thisLayer, Layer otherLayer, string newName, int documentWidth, int documentHeight)
         public static Layer MergeWith(this Layer thisLayer, Layer otherLayer, string newName, int documentWidth, int documentHeight)
         {
         {
-            return MergeWith(thisLayer, otherLayer, newName, new Vector(documentWidth, documentHeight));
+            return MergeWith(thisLayer, otherLayer, newName, new PixelSize(documentWidth, documentHeight));
         }
         }
     }
     }
 }
 }

+ 16 - 0
PixiEditor/Models/Tools/BitmapOperationTool.cs

@@ -19,6 +19,9 @@ namespace PixiEditor.Models.Tools
 
 
         public bool UseDocumentRectForUndo { get; set; } = false;
         public bool UseDocumentRectForUndo { get; set; } = false;
 
 
+        private SKRectI _rectReportedByTool;
+        private bool _customRectReported = false;
+
         private StorageBasedChange _change;
         private StorageBasedChange _change;
 
 
         public abstract void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color);
         public abstract void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color);
@@ -51,6 +54,12 @@ namespace PixiEditor.Models.Tools
             _change = null;
             _change = null;
         }
         }
 
 
+        protected void ReportCustomSessionRect(SKRectI rect)
+        {
+            _rectReportedByTool = rect;
+            _customRectReported = true;
+        }
+
         private void InitializeStorageBasedChange(SKRectI toolSessionRect)
         private void InitializeStorageBasedChange(SKRectI toolSessionRect)
         {
         {
             Document doc = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
             Document doc = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
@@ -72,6 +81,13 @@ namespace PixiEditor.Models.Tools
                 finalRect = SKRectI.Create(0, 0, doc.Width, doc.Height);
                 finalRect = SKRectI.Create(0, 0, doc.Width, doc.Height);
             }
             }
 
 
+            if (_customRectReported)
+            {
+                _customRectReported = false;
+                finalRect = _rectReportedByTool;
+                _rectReportedByTool = SKRectI.Empty;
+            }
+
             _change = new StorageBasedChange(doc, new[] { new LayerChunk(doc.ActiveLayer, finalRect) });
             _change = new StorageBasedChange(doc, new[] { new LayerChunk(doc.ActiveLayer, finalRect) });
         }
         }
     }
     }

+ 4 - 2
PixiEditor/Models/Tools/Tools/CircleTool.cs

@@ -41,10 +41,11 @@ namespace PixiEditor.Models.Tools.Tools
                 CoordinatesHelper.GetSquareCoordiantes(recordedMouseMovement) :
                 CoordinatesHelper.GetSquareCoordiantes(recordedMouseMovement) :
                 (recordedMouseMovement[0], recordedMouseMovement[^1]);
                 (recordedMouseMovement[0], recordedMouseMovement[^1]);
 
 
-            DrawEllipseFromCoordinates(previewLayer, start, end, color, fill, thickness, hasFillColor);
+            var dirtyRect = DrawEllipseFromCoordinates(previewLayer, start, end, color, fill, thickness, hasFillColor);
+            ReportCustomSessionRect(SKRectI.Create(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height));
         }
         }
 
 
-        public static void DrawEllipseFromCoordinates(Layer layer, Coordinates first, Coordinates second,
+        public static Int32Rect DrawEllipseFromCoordinates(Layer layer, Coordinates first, Coordinates second,
             SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
             SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
         {
         {
             DoubleCoords corners = CalculateCoordinatesForShapeRotation(first, second);
             DoubleCoords corners = CalculateCoordinatesForShapeRotation(first, second);
@@ -71,6 +72,7 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
 
 
             layer.InvokeLayerBitmapChange(dirtyRect);
             layer.InvokeLayerBitmapChange(dirtyRect);
+            return dirtyRect;
         }
         }
 
 
         public static void DrawEllipseFill(Layer layer, SKColor color, List<Coordinates> outlineCoordinates)
         public static void DrawEllipseFill(Layer layer, SKColor color, List<Coordinates> outlineCoordinates)

+ 4 - 2
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -45,10 +45,11 @@ namespace PixiEditor.Models.Tools.Tools
             if (Session.IsShiftDown)
             if (Session.IsShiftDown)
                 (start, end) = CoordinatesHelper.GetSquareOrLineCoordinates(recordedMouseMovement);
                 (start, end) = CoordinatesHelper.GetSquareOrLineCoordinates(recordedMouseMovement);
 
 
-            DrawLine(previewLayer, start, end, color, thickness, SKBlendMode.Src);
+            var dirtyRect = DrawLine(previewLayer, start, end, color, thickness, SKBlendMode.Src);
+            ReportCustomSessionRect(SKRectI.Create(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height));
         }
         }
 
 
-        public void DrawLine(
+        public Int32Rect DrawLine(
             Layer layer, Coordinates start, Coordinates end, SKColor color, int thickness, SKBlendMode blendMode,
             Layer layer, Coordinates start, Coordinates end, SKColor color, int thickness, SKBlendMode blendMode,
             SKStrokeCap strokeCap = SKStrokeCap.Butt)
             SKStrokeCap strokeCap = SKStrokeCap.Butt)
         {
         {
@@ -90,6 +91,7 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
 
 
             layer.InvokeLayerBitmapChange(dirtyRect);
             layer.InvokeLayerBitmapChange(dirtyRect);
+            return dirtyRect;
         }
         }
 
 
         private void DrawBresenhamLine(Layer layer, int x1, int y1, int x2, int y2, SKPaint paint)
         private void DrawBresenhamLine(Layer layer, int x1, int y1, int x2, int y2, SKPaint paint)

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

@@ -88,7 +88,7 @@ namespace PixiEditor.Models.Tools.Tools
             cachedDocument ??= new Layer("_CombinedLayers", BitmapUtils.CombineLayers(
             cachedDocument ??= new Layer("_CombinedLayers", BitmapUtils.CombineLayers(
                 new Int32Rect(0, 0, document.Width, document.Height),
                 new Int32Rect(0, 0, document.Width, document.Height),
                 document.Layers,
                 document.Layers,
-                document.LayerStructure));
+                document.LayerStructure), document.Width, document.Height);
         }
         }
     }
     }
 }
 }

+ 5 - 2
PixiEditor/Models/Tools/Tools/RectangleTool.cs

@@ -38,10 +38,11 @@ namespace PixiEditor.Models.Tools.Tools
                 var temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 var temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
                 fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
             }
             }
-            CreateRectangle(previewLayer, color, fillColor, recordedMouseMovement, thickness);
+            var dirtyRect = CreateRectangle(previewLayer, color, fillColor, recordedMouseMovement, thickness);
+            ReportCustomSessionRect(SKRectI.Create(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height));
         }
         }
 
 
-        private void CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, IReadOnlyList<Coordinates> coordinates, int thickness)
+        private Int32Rect CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, IReadOnlyList<Coordinates> coordinates, int thickness)
         {
         {
             var (start, end) = Session.IsShiftDown ? CoordinatesHelper.GetSquareCoordiantes(coordinates) : (coordinates[0], coordinates[^1]);
             var (start, end) = Session.IsShiftDown ? CoordinatesHelper.GetSquareCoordiantes(coordinates) : (coordinates[0], coordinates[^1]);
 
 
@@ -75,7 +76,9 @@ namespace PixiEditor.Models.Tools.Tools
                 paint.Color = color;
                 paint.Color = color;
                 layer.LayerBitmap.SkiaSurface.Canvas.DrawRect(x, y, w, h, paint);
                 layer.LayerBitmap.SkiaSurface.Canvas.DrawRect(x, y, w, h, paint);
             }
             }
+
             layer.InvokeLayerBitmapChange(dirtyRect);
             layer.InvokeLayerBitmapChange(dirtyRect);
+            return dirtyRect;
         }
         }
     }
     }
 }
 }

+ 1 - 3
PixiEditor/Models/Undo/StorageBasedChange.cs

@@ -153,14 +153,12 @@ namespace PixiEditor.Models.Undo
             {
             {
                 UndoLayer storedLayer = StoredLayers[i];
                 UndoLayer storedLayer = StoredLayers[i];
                 var bitmap = Importer.LoadFromGZippedBytes(storedLayer.StoredPngLayerName);
                 var bitmap = Importer.LoadFromGZippedBytes(storedLayer.StoredPngLayerName);
-                layers[i] = new Layer(storedLayer.Name, bitmap)
+                layers[i] = new Layer(storedLayer.Name, bitmap, storedLayer.MaxWidth, storedLayer.MaxHeight)
                 {
                 {
                     Width = storedLayer.Width,
                     Width = storedLayer.Width,
                     Height = storedLayer.Height,
                     Height = storedLayer.Height,
                     Offset = new Thickness(storedLayer.OffsetX, storedLayer.OffsetY, 0, 0),
                     Offset = new Thickness(storedLayer.OffsetX, storedLayer.OffsetY, 0, 0),
                     Opacity = storedLayer.Opacity,
                     Opacity = storedLayer.Opacity,
-                    MaxWidth = storedLayer.MaxWidth,
-                    MaxHeight = storedLayer.MaxHeight,
                     IsVisible = storedLayer.IsVisible,
                     IsVisible = storedLayer.IsVisible,
                     IsActive = storedLayer.IsActive,
                     IsActive = storedLayer.IsActive,
                     LayerHighlightColor = storedLayer.LayerHighlightColor
                     LayerHighlightColor = storedLayer.LayerHighlightColor

+ 2 - 2
PixiEditor/Properties/AssemblyInfo.cs

@@ -50,5 +50,5 @@ using System.Windows;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.1.7.1")]
-[assembly: AssemblyFileVersion("0.1.7.1")]
+[assembly: AssemblyVersion("0.1.7.2")]
+[assembly: AssemblyFileVersion("0.1.7.2")]

+ 2 - 1
PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs

@@ -45,7 +45,8 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public void Paste(object parameter)
         public void Paste(object parameter)
         {
         {
-            ClipboardController.PasteFromClipboard();
+            if (Owner.BitmapManager.ActiveDocument == null) return;
+            ClipboardController.PasteFromClipboard(Owner.BitmapManager.ActiveDocument);
         }
         }
 
 
         private bool CanPaste(object property)
         private bool CanPaste(object property)

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -148,6 +148,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 Owner.BitmapManager.ActiveDocument.AddNewLayer(
                 Owner.BitmapManager.ActiveDocument.AddNewLayer(
                     "Image",
                     "Image",
                     Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight));
                     Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight));
+                Owner.BitmapManager.ActiveDocument.UpdatePreviewImage();
             }
             }
         }
         }
 
 

+ 4 - 2
PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -65,7 +65,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 Owner.ShortcutController.LastShortcut.Command == Owner.ToolsSubViewModel.SelectToolCommand)
                 Owner.ShortcutController.LastShortcut.Command == Owner.ToolsSubViewModel.SelectToolCommand)
             {
             {
                 restoreToolOnKeyUp = true;
                 restoreToolOnKeyUp = true;
-                ShortcutController.BlockShortcutExecution = true;
+                ShortcutController.BlockShortcutExection("ShortcutDown");
             }
             }
 
 
             Owner.ShortcutController.KeyPressed(key, Keyboard.Modifiers);
             Owner.ShortcutController.KeyPressed(key, Keyboard.Modifiers);
@@ -96,7 +96,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             {
             {
                 restoreToolOnKeyUp = false;
                 restoreToolOnKeyUp = false;
                 Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
                 Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
-                ShortcutController.BlockShortcutExecution = false;
+                ShortcutController.UnblockShortcutExecution("ShortcutDown");
             }
             }
         }
         }
 
 
@@ -132,6 +132,8 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             else if (Owner.ToolsSubViewModel.LastActionTool != null && Owner.ToolsSubViewModel.MoveToolIsTransient)
             else if (Owner.ToolsSubViewModel.LastActionTool != null && Owner.ToolsSubViewModel.MoveToolIsTransient)
             {
             {
                 Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
                 Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
+                restoreToolOnKeyUp = false;
+                ShortcutController.UnblockShortcutExecution("ShortcutDown");
             }
             }
         }
         }
 
 

+ 5 - 1
PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -217,7 +217,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             if (doc.Layers.Count > 1)
             if (doc.Layers.Count > 1)
             {
             {
                 doc.MoveLayerInStructure(doc.Layers[^1].GuidValue, lastActiveLayerGuid, true);
                 doc.MoveLayerInStructure(doc.Layers[^1].GuidValue, lastActiveLayerGuid, true);
-                Guid? parent = parameter is Layer or LayerStructureItemContainer ? activeLayerParent?.GroupGuid : activeLayerParent.Parent?.GroupGuid;
+                Guid? parent = null;
+                if (activeLayerParent != null)
+                {
+                    parent = parameter is Layer or LayerStructureItemContainer ? activeLayerParent?.GroupGuid : activeLayerParent.Parent?.GroupGuid;
+                }
                 doc.LayerStructure.AssignParent(doc.ActiveLayerGuid, parent);
                 doc.LayerStructure.AssignParent(doc.ActiveLayerGuid, parent);
                 doc.AddLayerStructureToUndo(oldGroups);
                 doc.AddLayerStructureToUndo(oldGroups);
                 doc.UndoManager.SquashUndoChanges(3, "Add New Layer");
                 doc.UndoManager.SquashUndoChanges(3, "Add New Layer");

+ 3 - 0
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -96,6 +96,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             if (ActiveTool != null)
             if (ActiveTool != null)
             {
             {
                 activeTool.IsActive = false;
                 activeTool.IsActive = false;
+                ActiveTool.Toolbar.SaveToolbarSettings();
             }
             }
 
 
             LastActionTool = ActiveTool;
             LastActionTool = ActiveTool;
@@ -103,6 +104,8 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
             ActiveTool = tool;
             ActiveTool = tool;
 
 
+            ActiveTool.Toolbar.LoadSharedSettings();
+
             if (LastActionTool != ActiveTool)
             if (LastActionTool != ActiveTool)
                 SelectedToolChanged?.Invoke(this, new SelectedToolEventArgs(LastActionTool, ActiveTool));
                 SelectedToolChanged?.Invoke(this, new SelectedToolEventArgs(LastActionTool, ActiveTool));
 
 

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs

@@ -27,6 +27,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         private void ShowAvalonDockWindow(string id)
         private void ShowAvalonDockWindow(string id)
         {
         {
+            if (MainWindow.Current?.LayoutRoot?.Manager?.Layout == null) return;
             var anchorables = new List<LayoutAnchorable>(MainWindow.Current.LayoutRoot.Manager.Layout
             var anchorables = new List<LayoutAnchorable>(MainWindow.Current.LayoutRoot.Manager.Layout
                     .Descendents()
                     .Descendents()
                     .OfType<LayoutAnchorable>());
                     .OfType<LayoutAnchorable>());

+ 1 - 1
PixiEditor/ViewModels/ViewModelMain.cs

@@ -362,7 +362,7 @@ namespace PixiEditor.ViewModels
 
 
         private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
         private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
         {
         {
-            BitmapManager.ActiveDocument.ActiveSelection = new Selection(Array.Empty<Coordinates>());
+            BitmapManager.ActiveDocument.ActiveSelection = new Selection(Array.Empty<Coordinates>(), new PixelSize(e.NewWidth, e.NewHeight));
             BitmapManager.ActiveDocument.ChangesSaved = false;
             BitmapManager.ActiveDocument.ChangesSaved = false;
             BitmapManager.ActiveDocument.CenterViewportTrigger.Execute(this, new Size(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
             BitmapManager.ActiveDocument.CenterViewportTrigger.Execute(this, new Size(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
         }
         }

+ 8 - 1
PixiEditor/Views/Dialogs/HelloTherePopup.xaml

@@ -119,7 +119,14 @@
                                                 Style="{StaticResource DarkRoundButton}"
                                                 Style="{StaticResource DarkRoundButton}"
                                                 x:Name="fileButton">
                                                 x:Name="fileButton">
                                             <Grid Width="100" Height="100">
                                             <Grid Width="100" Height="100">
-                                                <Image Source="{Binding PreviewBitmap}" RenderOptions.BitmapScalingMode="NearestNeighbor" Margin="20"/>
+                                                <Image Source="{Binding PreviewBitmap}" x:Name="image" Margin="20">
+                                                    <RenderOptions.BitmapScalingMode>
+                                                        <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
+                                                            <Binding Path="PreviewBitmap.PixelWidth"/>
+                                                            <Binding ElementName="image" Path="ActualWidth"/>
+                                                        </MultiBinding>
+                                                    </RenderOptions.BitmapScalingMode>
+                                                </Image>
                                                 <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder" Margin="5"
                                                 <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder" Margin="5"
                                                         Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}" 
                                                         Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}" 
                                                         VerticalAlignment="Bottom" HorizontalAlignment="Right">
                                                         VerticalAlignment="Bottom" HorizontalAlignment="Right">

+ 2 - 13
PixiEditor/Views/MainWindow.xaml.cs

@@ -4,7 +4,6 @@ using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
-using PixiEditor.Views.Dialogs;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.ComponentModel;
@@ -48,7 +47,6 @@ namespace PixiEditor
 
 
             UpdateWindowChromeBorderThickness();
             UpdateWindowChromeBorderThickness();
             StateChanged += MainWindow_StateChanged;
             StateChanged += MainWindow_StateChanged;
-            Activated += MainWindow_Activated;
 
 
             DataContext.CloseAction = Close;
             DataContext.CloseAction = Close;
             Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
             Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
@@ -66,6 +64,8 @@ namespace PixiEditor
                 }
                 }
             });
             });
 
 
+            Current = this;
+
             OnReleaseBuild();
             OnReleaseBuild();
         }
         }
 
 
@@ -97,12 +97,6 @@ namespace PixiEditor
             ((HwndSource)PresentationSource.FromVisual(this)).AddHook(Helpers.WindowSizeHelper.SetMaxSizeHook);
             ((HwndSource)PresentationSource.FromVisual(this)).AddHook(Helpers.WindowSizeHelper.SetMaxSizeHook);
         }
         }
 
 
-        [Conditional("RELEASE")]
-        private static void CloseHelloThereIfRelease()
-        {
-            Application.Current.Windows.OfType<HelloTherePopup>().ToList().ForEach(x => { if (!x.IsClosing) x.Close(); });
-        }
-
         [Conditional("RELEASE")]
         [Conditional("RELEASE")]
         private void OnReleaseBuild()
         private void OnReleaseBuild()
         {
         {
@@ -158,11 +152,6 @@ namespace PixiEditor
             SystemCommands.CloseWindow(this);
             SystemCommands.CloseWindow(this);
         }
         }
 
 
-        private void MainWindow_Activated(object sender, EventArgs e)
-        {
-            CloseHelloThereIfRelease();
-        }
-
         private void UpdateWindowChromeBorderThickness()
         private void UpdateWindowChromeBorderThickness()
         {
         {
             if (WindowState == WindowState.Maximized)
             if (WindowState == WindowState.Maximized)

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

@@ -82,11 +82,14 @@
                                           RenderOptions.BitmapScalingMode="NearestNeighbor"/>
                                           RenderOptions.BitmapScalingMode="NearestNeighbor"/>
 
 
                 <Image Source="{Binding PreviewLayerRenderer.FinalBitmap}" Panel.ZIndex="2"
                 <Image Source="{Binding PreviewLayerRenderer.FinalBitmap}" Panel.ZIndex="2"
-                       RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
+                       RenderOptions.BitmapScalingMode="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Zoombox}}, Path=Zoom, Converter={converters:ZoomLevelToBitmapScalingModeConverter}}" 
+                       Stretch="Uniform"
                        Width="{Binding Width}" Height="{Binding Height}"/>
                        Width="{Binding Width}" Height="{Binding Height}"/>
 
 
+
                 <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Renderer.FinalBitmap}"
                 <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Renderer.FinalBitmap}"
-                       RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform" 
+                       RenderOptions.BitmapScalingMode="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Zoombox}}, Path=Zoom, Converter={converters:ZoomLevelToBitmapScalingModeConverter}}" 
+                       Stretch="Uniform" 
                        Visibility="{Binding XamlAccesibleViewModel.BitmapManager.OnlyReferenceLayer, Converter={InverseBoolToVisibilityConverter}}"/>
                        Visibility="{Binding XamlAccesibleViewModel.BitmapManager.OnlyReferenceLayer, Converter={InverseBoolToVisibilityConverter}}"/>
 
 
                 <local:PlainLayerView TargetLayer="{Binding ActiveSelection.SelectionLayer}"
                 <local:PlainLayerView TargetLayer="{Binding ActiveSelection.SelectionLayer}"

+ 2 - 2
PixiEditor/Views/UserControls/EditableTextBlock.xaml.cs

@@ -62,7 +62,7 @@ namespace PixiEditor.Views
 
 
         public void EnableEditing()
         public void EnableEditing()
         {
         {
-            ShortcutController.BlockShortcutExecution = true;
+            ShortcutController.BlockShortcutExection("EditableTextBlock");
             TextBlockVisibility = Visibility.Hidden;
             TextBlockVisibility = Visibility.Hidden;
             IsEditing = true;
             IsEditing = true;
             Dispatcher.BeginInvoke(
             Dispatcher.BeginInvoke(
@@ -78,7 +78,7 @@ namespace PixiEditor.Views
         public void DisableEditing()
         public void DisableEditing()
         {
         {
             TextBlockVisibility = Visibility.Visible;
             TextBlockVisibility = Visibility.Visible;
-            ShortcutController.BlockShortcutExecution = false;
+            ShortcutController.UnblockShortcutExecution("EditableTextBlock");
             IsEditing = false;
             IsEditing = false;
         }
         }
 
 

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

@@ -36,8 +36,14 @@
                     <StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left">
                     <StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left">
                         <Border Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
                         <Border Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
                            Margin="5, 0, 10, 0">
                            Margin="5, 0, 10, 0">
-                            <Image Source="{Binding PreviewImage, ElementName=groupControl}" Stretch="Uniform" Width="20" Height="20" 
-                       RenderOptions.BitmapScalingMode="NearestNeighbor"/>
+                            <Image Source="{Binding PreviewImage, ElementName=groupControl}" Stretch="Uniform" Width="20" Height="20">
+                                <RenderOptions.BitmapScalingMode>
+                                    <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
+                                        <Binding Path="PreviewImage.PixelWidth" ElementName="groupControl"/>
+                                        <Binding RelativeSource="{RelativeSource Mode=Self}" Path="ActualWidth"/>
+                                    </MultiBinding>
+                                </RenderOptions.BitmapScalingMode>
+                            </Image>
                         </Border>
                         </Border>
                         <userControls:EditableTextBlock
                         <userControls:EditableTextBlock
                     FontSize="16"
                     FontSize="16"

+ 1 - 1
PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs

@@ -201,7 +201,7 @@ namespace PixiEditor.Views.UserControls.Layers
         private void MoveGroupWithTempLayer(bool above, Models.DataHolders.Document document, Guid group, int indexOfReferenceLayer, bool putItInside) // ¯\_(ツ)_/¯
         private void MoveGroupWithTempLayer(bool above, Models.DataHolders.Document document, Guid group, int indexOfReferenceLayer, bool putItInside) // ¯\_(ツ)_/¯
         {
         {
             // The trick here is to insert a temp layer, assign group to it, then delete it.
             // The trick here is to insert a temp layer, assign group to it, then delete it.
-            Layer tempLayer = new("_temp");
+            Layer tempLayer = new("_temp", document.Width, document.Height);
             document.Layers.Insert(indexOfReferenceLayer, tempLayer);
             document.Layers.Insert(indexOfReferenceLayer, tempLayer);
 
 
             Guid? refGuid = putItInside ? GroupData?.GroupGuid : GroupData?.Parent?.GroupGuid;
             Guid? refGuid = putItInside ? GroupData?.GroupGuid : GroupData?.Parent?.GroupGuid;

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

@@ -45,7 +45,7 @@
                     </Button.Background>
                     </Button.Background>
                 </Button>
                 </Button>
             </StackPanel>
             </StackPanel>
-            <StackPanel Orientation="Horizontal" DockPanel.Dock="Right" Margin="0,0,10,0" HorizontalAlignment="Right">
+            <StackPanel Orientation="Horizontal" DockPanel.Dock="Right" Margin="0,0,10,0" HorizontalAlignment="Right" Focusable="True">
                 <Label Content="Opacity" Foreground="White" VerticalAlignment="Center"/>
                 <Label Content="Opacity" Foreground="White" VerticalAlignment="Center"/>
                 <vws:NumberInput
                 <vws:NumberInput
                         Min="0" Max="100"
                         Min="0" Max="100"

+ 4 - 0
PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Controllers.Shortcuts;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
@@ -367,6 +368,9 @@ namespace PixiEditor.Views.UserControls.Layers
             {
             {
                 HandleGroupOpacityChange(groupControl.GroupData, val);
                 HandleGroupOpacityChange(groupControl.GroupData, val);
             }
             }
+
+            ShortcutController.UnblockShortcutExecutionAll();
+            MoveFocus(new System.Windows.Input.TraversalRequest(System.Windows.Input.FocusNavigationDirection.Next));
         }
         }
 
 
         private void HandleLayerOpacityChange(float val, Layer layer)
         private void HandleLayerOpacityChange(float val, Layer layer)

+ 1 - 1
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs

@@ -37,7 +37,7 @@ namespace PixiEditor.Views.UserControls.Layers
             if (path != null)
             if (path != null)
             {
             {
                 var bitmap = Importer.ImportSurface(path);
                 var bitmap = Importer.ImportSurface(path);
-                Layer = new Layer("_Reference Layer", bitmap);
+                Layer = new Layer("_Reference Layer", bitmap, bitmap.Width, bitmap.Height);
             }
             }
         }
         }
 
 

+ 9 - 1
PixiEditor/Views/UserControls/PlainLayerView.xaml

@@ -4,9 +4,17 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
              xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              mc:Ignorable="d" 
              mc:Ignorable="d" 
              x:Name="uc"
              x:Name="uc"
              d:DesignHeight="450" d:DesignWidth="800">
              d:DesignHeight="450" d:DesignWidth="800">
     <Image x:Name="image" Width="{Binding Width, ElementName=uc}" Height="{Binding Height, ElementName=uc}" 
     <Image x:Name="image" Width="{Binding Width, ElementName=uc}" Height="{Binding Height, ElementName=uc}" 
-           RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"/>
+           Stretch="Uniform">
+        <RenderOptions.BitmapScalingMode>
+            <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
+                <Binding RelativeSource="{RelativeSource Mode=Self}" Path="Source.PixelWidth"/>
+                <Binding RelativeSource="{RelativeSource Mode=Self}" Path="ActualWidth"/>
+            </MultiBinding>
+        </RenderOptions.BitmapScalingMode>
+    </Image>
 </UserControl>
 </UserControl>

+ 25 - 3
PixiEditor/Views/UserControls/PlainLayerView.xaml.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
+using SkiaSharp;
 using System;
 using System;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
@@ -21,6 +22,8 @@ namespace PixiEditor.Views.UserControls
         private int prevLayerWidth = -1;
         private int prevLayerWidth = -1;
         private int prevLayerHeight = -1;
         private int prevLayerHeight = -1;
 
 
+        private Int32Rect _cachedTightBounds;
+
         public PlainLayerView()
         public PlainLayerView()
         {
         {
             InitializeComponent();
             InitializeComponent();
@@ -49,14 +52,17 @@ namespace PixiEditor.Views.UserControls
                     view.TargetLayer = null;
                     view.TargetLayer = null;
                     return;
                     return;
                 }
                 }
+
                 layer.LayerBitmapChanged += view.OnLayerBitmapChanged;
                 layer.LayerBitmapChanged += view.OnLayerBitmapChanged;
-                view.Resize(layer.Width, layer.Height);
+                view._cachedTightBounds = GetTightBounds(layer);
+
+                view.Resize(view._cachedTightBounds.Width, view._cachedTightBounds.Height);
             }
             }
         }
         }
 
 
         private void Update()
         private void Update()
         {
         {
-            renderer.Draw(TargetLayer.LayerBitmap, (byte)(TargetLayer.Opacity * 255));
+            renderer.Draw(TargetLayer.LayerBitmap, (byte)(TargetLayer.Opacity * 255), SKRectI.Create(_cachedTightBounds.X, _cachedTightBounds.Y, _cachedTightBounds.Width, _cachedTightBounds.Height));
         }
         }
 
 
         private void OnControlSizeChanged(object sender, SizeChangedEventArgs e)
         private void OnControlSizeChanged(object sender, SizeChangedEventArgs e)
@@ -96,16 +102,32 @@ namespace PixiEditor.Views.UserControls
 
 
         private void OnLayerBitmapChanged(object sender, Int32Rect e)
         private void OnLayerBitmapChanged(object sender, Int32Rect e)
         {
         {
-            if (TargetLayer.Width != prevLayerWidth || TargetLayer.Height != prevLayerHeight)
+            if (TargetLayer.Width != prevLayerWidth || TargetLayer.Height != prevLayerHeight 
+                || TargetLayer.OffsetX != _cachedTightBounds.X
+                || TargetLayer.OffsetY != _cachedTightBounds.Y)
             {
             {
                 ResizeWithOptimized(RenderSize);
                 ResizeWithOptimized(RenderSize);
                 prevLayerWidth = TargetLayer.Width;
                 prevLayerWidth = TargetLayer.Width;
                 prevLayerHeight = TargetLayer.Height;
                 prevLayerHeight = TargetLayer.Height;
+                _cachedTightBounds = GetTightBounds(TargetLayer);
             }
             }
             else
             else
             {
             {
                 Update();
                 Update();
             }
             }
         }
         }
+
+        private static Int32Rect GetTightBounds(Layer targetLayer)
+        {
+            //var tightBounds = targetLayer.TightBounds;
+            //if (tightBounds.IsEmpty)
+            //{
+            //    tightBounds = new Int32Rect(0, 0, targetLayer.Width, targetLayer.Height);
+            //}
+
+            var tightBounds = new Int32Rect(0, 0, targetLayer.Width, targetLayer.Height);
+
+            return tightBounds;
+        }
     }
     }
 }
 }

+ 8 - 2
PixiEditor/Views/UserControls/PreviewWindow.xaml

@@ -34,8 +34,14 @@
                         </MultiBinding>
                         </MultiBinding>
                     </Grid.LayoutTransform>
                     </Grid.LayoutTransform>
                 </Grid>
                 </Grid>
-                <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Document.Renderer.FinalBitmap, ElementName=uc}"
-                                               RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform" />
+                <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Document.Renderer.FinalBitmap, ElementName=uc}" Stretch="Uniform">
+                    <RenderOptions.BitmapScalingMode>
+                        <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
+                            <Binding ElementName="uc" Path="Document.Renderer.FinalBitmap.PixelWidth"/>
+                            <Binding ElementName="previewWindowViewbox" Path="ActualWidth"/>
+                        </MultiBinding>
+                    </RenderOptions.BitmapScalingMode>
+                </Image>
                 <Border x:Name="colorCursor" Width="1" Height="1"
                 <Border x:Name="colorCursor" Width="1" Height="1"
                     Margin="{Binding ColorCursorPosition, ElementName=uc}"
                     Margin="{Binding ColorCursorPosition, ElementName=uc}"
                     HorizontalAlignment="Left" VerticalAlignment="Top"
                     HorizontalAlignment="Left" VerticalAlignment="Top"

+ 1 - 1
PixiEditor/Views/UserControls/Zoombox.xaml.cs

@@ -105,7 +105,7 @@ namespace PixiEditor.Views.UserControls
         public static readonly DependencyProperty UseTouchGesturesProperty =
         public static readonly DependencyProperty UseTouchGesturesProperty =
             DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(Zoombox));
             DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(Zoombox));
 
 
-        private const double zoomFactor = 1.1;
+        private const double zoomFactor = 1.09050773267; //2^(1/8)
         private const double maxZoom = 50;
         private const double maxZoom = 50;
         private double minZoom = -28;
         private double minZoom = -28;
         public object AdditionalContent
         public object AdditionalContent

+ 2 - 2
PixiEditorTests/ModelsTests/ControllersTests/ShortcutControllerTests.cs

@@ -42,7 +42,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
             RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
 
 
             ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, shortcutCommand);
             ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, shortcutCommand);
-            ShortcutController.BlockShortcutExecution = true;
+            ShortcutController.BlockShortcutExection("Test");
 
 
             controller.KeyPressed(Key.A, ModifierKeys.None);
             controller.KeyPressed(Key.A, ModifierKeys.None);
             Assert.Equal(-1, result);
             Assert.Equal(-1, result);
@@ -75,7 +75,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         {
         {
             ShortcutController controller = new ShortcutController();
             ShortcutController controller = new ShortcutController();
             controller.ShortcutGroups.Add(new ShortcutGroup(string.Empty, new Shortcut(shortcutKey, shortcutCommand, 0, modifiers)));
             controller.ShortcutGroups.Add(new ShortcutGroup(string.Empty, new Shortcut(shortcutKey, shortcutCommand, 0, modifiers)));
-            ShortcutController.BlockShortcutExecution = false;
+            ShortcutController.UnblockShortcutExecutionAll();
             return controller;
             return controller;
         }
         }
     }
     }

+ 23 - 23
PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs

@@ -11,7 +11,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatAddNewGroupAddsNewGroup()
         public void TestThatAddNewGroupAddsNewGroup()
         {
         {
             Document doc = new Document(1, 1);
             Document doc = new Document(1, 1);
-            doc.Layers.Add(new("_testLayer"));
+            doc.Layers.Add(new("_testLayer", 1, 1));
             var testLayer = doc.Layers[^1];
             var testLayer = doc.Layers[^1];
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
 
 
@@ -24,7 +24,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatAddNewGroupAddsNewGroupAsASubgroup()
         public void TestThatAddNewGroupAddsNewGroupAsASubgroup()
         {
         {
             Document doc = new Document(1, 1);
             Document doc = new Document(1, 1);
-            doc.Layers.Add(new("_testLayer"));
+            doc.Layers.Add(new("_testLayer", 1, 1));
             var testLayer = doc.Layers[^1];
             var testLayer = doc.Layers[^1];
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
             doc.LayerStructure.AddNewGroup("test1", testLayer.GuidValue);
             doc.LayerStructure.AddNewGroup("test1", testLayer.GuidValue);
@@ -41,8 +41,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatMoveGroupMovesSwapsLayerPlacesWithOtherGroup()
         public void TestThatMoveGroupMovesSwapsLayerPlacesWithOtherGroup()
         {
         {
             Document doc = new Document(1, 1);
             Document doc = new Document(1, 1);
-            doc.Layers.Add(new Layer("_testLayer"));
-            doc.Layers.Add(new Layer("_testLayer1"));
+            doc.Layers.Add(new Layer("_testLayer", 1, 1));
+            doc.Layers.Add(new Layer("_testLayer1", 1, 1));
             var testLayer = doc.Layers[0];
             var testLayer = doc.Layers[0];
             var testLayer1 = doc.Layers[^1];
             var testLayer1 = doc.Layers[^1];
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
@@ -61,7 +61,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatIsChildOfDetectsNestedGroupCorrectly()
         public void TestThatIsChildOfDetectsNestedGroupCorrectly()
         {
         {
             LayerStructure ls = new LayerStructure(new Document(1, 1));
             LayerStructure ls = new LayerStructure(new Document(1, 1));
-            Layer testLayer = new Layer("tst");
+            Layer testLayer = new Layer("tst", 1, 1);
             ls.Groups.Add(new GuidStructureItem("group 1", testLayer.GuidValue));
             ls.Groups.Add(new GuidStructureItem("group 1", testLayer.GuidValue));
             ls.Groups[0].Subgroups.Add(new GuidStructureItem("group 1 nested", testLayer.GuidValue));
             ls.Groups[0].Subgroups.Add(new GuidStructureItem("group 1 nested", testLayer.GuidValue));
 
 
@@ -73,7 +73,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatIsChildOfDetectsNestedLayersCorrectly()
         public void TestThatIsChildOfDetectsNestedLayersCorrectly()
         {
         {
             var doc = new Document(1, 1);
             var doc = new Document(1, 1);
-            doc.Layers.Add(new Layer("tst"));
+            doc.Layers.Add(new Layer("tst", 1, 1));
             Guid testLayerGuid = doc.Layers[0].GuidValue;
             Guid testLayerGuid = doc.Layers[0].GuidValue;
             LayerStructure ls = new LayerStructure(doc);
             LayerStructure ls = new LayerStructure(doc);
             ls.AddNewGroup("Test group", testLayerGuid);
             ls.AddNewGroup("Test group", testLayerGuid);
@@ -87,7 +87,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatGroupContainsOnlyLayerDetectsOnlySingleLayerCorrectly()
         public void TestThatGroupContainsOnlyLayerDetectsOnlySingleLayerCorrectly()
         {
         {
             var doc = new Document(1, 1);
             var doc = new Document(1, 1);
-            doc.Layers.Add(new Layer("layer"));
+            doc.Layers.Add(new Layer("layer", 1, 1));
             var guid = doc.Layers[0].GuidValue;
             var guid = doc.Layers[0].GuidValue;
             doc.LayerStructure.AddNewGroup("layer group", guid);
             doc.LayerStructure.AddNewGroup("layer group", guid);
             Assert.True(LayerStructure.GroupContainsOnlyLayer(guid, doc.LayerStructure.Groups[0]));
             Assert.True(LayerStructure.GroupContainsOnlyLayer(guid, doc.LayerStructure.Groups[0]));
@@ -97,7 +97,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatGroupContainsOnlyLayerDetectsOnlySingleLayerThatIsNested()
         public void TestThatGroupContainsOnlyLayerDetectsOnlySingleLayerThatIsNested()
         {
         {
             var doc = new Document(1, 1);
             var doc = new Document(1, 1);
-            doc.Layers.Add(new Layer("layer"));
+            doc.Layers.Add(new Layer("layer", 1, 1));
             var guid = doc.Layers[0].GuidValue;
             var guid = doc.Layers[0].GuidValue;
             doc.LayerStructure.AddNewGroup("layer group", guid);
             doc.LayerStructure.AddNewGroup("layer group", guid);
             doc.LayerStructure.AddNewGroup("layer group nested", guid);
             doc.LayerStructure.AddNewGroup("layer group nested", guid);
@@ -109,8 +109,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatCloneReturnsSameLayerStructure()
         public void TestThatCloneReturnsSameLayerStructure()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new("Test"));
-            doc.Layers.Add(new("Test2"));
+            doc.Layers.Add(new("Test", 1, 1));
+            doc.Layers.Add(new("Test2", 1, 1));
             LayerStructure structure = new(doc);
             LayerStructure structure = new(doc);
             structure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             structure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
@@ -125,7 +125,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatGetGroupByGuidReturnsNullForNonExistingGroup()
         public void TestThatGetGroupByGuidReturnsNullForNonExistingGroup()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new("Test"));
+            doc.Layers.Add(new("Test", 1, 1));
 
 
             Assert.Null(doc.LayerStructure.GetGroupByGuid(null));
             Assert.Null(doc.LayerStructure.GetGroupByGuid(null));
             Assert.Null(doc.LayerStructure.GetGroupByGuid(Guid.NewGuid()));
             Assert.Null(doc.LayerStructure.GetGroupByGuid(Guid.NewGuid()));
@@ -135,7 +135,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatGetGroupByGuidReturnsGroupCorrectly()
         public void TestThatGetGroupByGuidReturnsGroupCorrectly()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new("Test"));
+            doc.Layers.Add(new("Test", 1, 1));
             var group = doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             var group = doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
             Assert.Equal(group.GroupGuid, doc.LayerStructure.GetGroupByGuid(group.GroupGuid).GroupGuid);
             Assert.Equal(group.GroupGuid, doc.LayerStructure.GetGroupByGuid(group.GroupGuid).GroupGuid);
@@ -145,7 +145,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatPreMoveReassignBoundsMakesNestedGroupEmptyAndRemovesItAndParent()
         public void TestThatPreMoveReassignBoundsMakesNestedGroupEmptyAndRemovesItAndParent()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new("Test"));
+            doc.Layers.Add(new("Test", 1, 1));
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
 
 
@@ -158,11 +158,11 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatPostMoveReassignBoundsAssignsNewLayerToGroup()
         public void TestThatPostMoveReassignBoundsAssignsNewLayerToGroup()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new("Test"));
+            doc.Layers.Add(new("Test", 1, 1));
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
 
 
-            doc.Layers.Add(new("Test 1"));
+            doc.Layers.Add(new("Test 1", 1, 1));
 
 
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
@@ -181,13 +181,13 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatAssignParentAssignsParent()
         public void TestThatAssignParentAssignsParent()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new Layer("Test"));
+            doc.Layers.Add(new Layer("Test", 1, 1));
 
 
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
 
 
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
-            doc.Layers.Add(new Layer("Test 1"));
+            doc.Layers.Add(new Layer("Test 1", 1, 1));
 
 
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
 
 
@@ -201,13 +201,13 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatAssignParentDeAssignsParentOnNull()
         public void TestThatAssignParentDeAssignsParentOnNull()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new Layer("Test"));
+            doc.Layers.Add(new Layer("Test", 1, 1));
 
 
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
 
 
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
-            doc.Layers.Add(new Layer("Test 1"));
+            doc.Layers.Add(new Layer("Test 1", 1, 1));
 
 
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
 
 
@@ -222,10 +222,10 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         public void TestThatGetGroupLayersReturnsAllLayersInGroup()
         public void TestThatGetGroupLayersReturnsAllLayersInGroup()
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
-            doc.Layers.Add(new Layer("Test"));
-            doc.Layers.Add(new Layer("Test 1"));
-            doc.Layers.Add(new Layer("Test 2"));
-            doc.Layers.Add(new Layer("Test 3"));
+            doc.Layers.Add(new Layer("Test", 1, 1));
+            doc.Layers.Add(new Layer("Test 1", 1, 1));
+            doc.Layers.Add(new Layer("Test 2", 1, 1));
+            doc.Layers.Add(new Layer("Test 3", 1, 1));
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
             doc.LayerStructure.AssignParent(doc.Layers[1].GuidValue, doc.LayerStructure.Groups[0].GroupGuid);
             doc.LayerStructure.AssignParent(doc.Layers[1].GuidValue, doc.LayerStructure.Groups[0].GroupGuid);

+ 4 - 4
PixiEditorTests/ModelsTests/DataHoldersTests/SelectionTests.cs

@@ -13,7 +13,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         [Fact]
         public void TestThatSetSelectionNewSetsCorrectSelection()
         public void TestThatSetSelectionNewSetsCorrectSelection()
         {
         {
-            Selection selection = new Selection(Array.Empty<Coordinates>());
+            Selection selection = new Selection(Array.Empty<Coordinates>(), new (10, 10));
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
 
 
             selection.SetSelection(points, SelectionType.New);
             selection.SetSelection(points, SelectionType.New);
@@ -25,7 +25,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         [Fact]
         public void TestThatSetSelectionAddSetsCorrectSelection()
         public void TestThatSetSelectionAddSetsCorrectSelection()
         {
         {
-            Selection selection = new Selection(Array.Empty<Coordinates>());
+            Selection selection = new Selection(Array.Empty<Coordinates>(), new PixelSize(10, 10));
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
             Coordinates[] points2 = { new Coordinates(2, 4), new Coordinates(5, 7) };
             Coordinates[] points2 = { new Coordinates(2, 4), new Coordinates(5, 7) };
 
 
@@ -38,7 +38,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         [Fact]
         public void TestThatSetSelectionSubtractSetsCorrectSelection()
         public void TestThatSetSelectionSubtractSetsCorrectSelection()
         {
         {
-            Selection selection = new Selection(Array.Empty<Coordinates>());
+            Selection selection = new Selection(Array.Empty<Coordinates>(), new PixelSize(10, 10));
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
             Coordinates[] points2 = { new Coordinates(1, 1) };
             Coordinates[] points2 = { new Coordinates(1, 1) };
 
 
@@ -51,7 +51,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         [Fact]
         public void TestClearWorks()
         public void TestClearWorks()
         {
         {
-            Selection selection = new Selection(new[] { new Coordinates(0, 0), new Coordinates(5, 7) });
+            Selection selection = new Selection(new[] { new Coordinates(0, 0), new Coordinates(5, 7) }, new PixelSize(10, 10));
             selection.Clear();
             selection.Clear();
 
 
             Assert.Empty(selection.SelectedPoints);
             Assert.Empty(selection.SelectedPoints);

+ 2 - 2
PixiEditorTests/ModelsTests/UndoTests/StorageBasedChangeTests.cs

@@ -33,8 +33,8 @@ namespace PixiEditorTests.ModelsTests.UndoTests
             Random random = new Random();
             Random random = new Random();
             testDocument.Layers = new WpfObservableRangeCollection<Layer>()
             testDocument.Layers = new WpfObservableRangeCollection<Layer>()
             {
             {
-                new Layer("Test layer" + random.Next(int.MinValue, int.MaxValue), testBitmap),
-                new Layer("Test layer 2" + random.Next(int.MinValue, int.MaxValue), testBitmap2) { Offset = new System.Windows.Thickness(2, 3, 0, 0) }
+                new Layer("Test layer" + random.Next(int.MinValue, int.MaxValue), testBitmap, testDocument.Width, testDocument.Height),
+                new Layer("Test layer 2" + random.Next(int.MinValue, int.MaxValue), testBitmap2, testDocument.Width, testDocument.Height) { Offset = new System.Windows.Thickness(2, 3, 0, 0) }
             };
             };
             return testDocument;
             return testDocument;
         }
         }

+ 4 - 4
README.md

@@ -2,7 +2,7 @@
 
 
 ---
 ---
 
 
-**PixiEditor** is a Pixel art editing software. Create beautiful sprites for your games, animations (coming soon!) and edit images. All packed in eye-friendly dark theme.
+**PixiEditor** is a Pixel art editing software. Create beautiful sprites for your games, animations (coming soon!), and edit images. All packed in eye-friendly dark theme.
 
 
 [![Build Status](https://img.shields.io/azure-devops/build/flabbet/PixiEditor/6/master)](https://dev.azure.com/flabbet/PixiEditor/_build?definitionId=6) 
 [![Build Status](https://img.shields.io/azure-devops/build/flabbet/PixiEditor/6/master)](https://dev.azure.com/flabbet/PixiEditor/_build?definitionId=6) 
 [![codecov](https://codecov.io/gh/PixiEditor/PixiEditor/branch/master/graph/badge.svg)](https://codecov.io/gh/PixiEditor/PixiEditor)
 [![codecov](https://codecov.io/gh/PixiEditor/PixiEditor/branch/master/graph/badge.svg)](https://codecov.io/gh/PixiEditor/PixiEditor)
@@ -17,7 +17,7 @@
 
 
 ## About PixiEditor
 ## About PixiEditor
 
 
-Want to create beautiful pixel arts for your games? PixiEditor can help you! Our goal is to create fully open-source, fast and feature rich pixel art creator. 
+Want to create beautiful pixel art for your games? PixiEditor can help you! Our goal is to create a fully open-source, fast, and feature-rich pixel art creator. 
 
 
 ### Familiar interface
 ### Familiar interface
 
 
@@ -29,7 +29,7 @@ Have you ever used Photoshop or Gimp? Reinventing the wheel is unnecessary, we w
 
 
 ### Lightweight
 ### Lightweight
 
 
-Program weighs only 9 MB! Already have .NET 6 installed? Download installer and enjoy saved space.
+The program weighs only 9 MB! Already have .NET 6 installed? Download installer and enjoy saved space.
 
 
 ### Active development
 ### Active development
 
 
@@ -74,7 +74,7 @@ Struggling with something? You can find support in a few places:
 
 
 * Ask on [Discord](https://discord.gg/qSRMYmq)
 * Ask on [Discord](https://discord.gg/qSRMYmq)
 * Open new [Issue](https://github.com/flabbet/PixiEditor/issues)
 * Open new [Issue](https://github.com/flabbet/PixiEditor/issues)
-* Check out [FAQ](https://github.com/PixiEditor/PixiEditor/wiki/FAQ). 
+* Check out the [FAQ](https://github.com/PixiEditor/PixiEditor/wiki/FAQ). 
 
 
 
 
 
 

+ 6 - 5
azure-pipelines.yml

@@ -49,8 +49,9 @@ steps:
     workingDirectory: 'PixiEditorTests\'
     workingDirectory: 'PixiEditorTests\'
   displayName: Collect code coverage
   displayName: Collect code coverage
 
 
-- task: CmdLine@2
-  continueOnError: true
-  inputs:
-    script: codecov -f .\PixiEditorTests\PixiEditor_coverage.xml -t $(CODECOV_TOKEN)
-  displayName: Upload to Codecov.io
+  # Disiabled, because there is a problem with .NET 6 and OpenCover.Console.exe
+#- task: CmdLine@2
+#  continueOnError: true
+#  inputs:
+#    script: codecov -f .\PixiEditorTests\PixiEditor_coverage.xml -t $(CODECOV_TOKEN)
+#  displayName: Upload to Codecov.io