Browse Source

Dynamic Layers WIP

flabbet 5 years ago
parent
commit
78ad993640

+ 15 - 2
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -95,9 +95,18 @@ namespace PixiEditor.Models.Controllers
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(index, LayerAction.SetActive));
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(index, LayerAction.SetActive));
         }
         }
 
 
+        public void AddNewLayer(string name, bool setAsActive = true)
+        {
+            AddNewLayer(name, 0, 0, setAsActive);
+        }
+
         public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
         public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
         {
         {
-            ActiveDocument.Layers.Add(new Layer(name, width, height));
+            ActiveDocument.Layers.Add(new Layer(name, width, height)
+            {
+                MaxHeight = ActiveDocument.Height,
+                MaxWidth = ActiveDocument.Width
+            });
             if (setAsActive) SetActiveLayer(ActiveDocument.Layers.Count - 1);
             if (setAsActive) SetActiveLayer(ActiveDocument.Layers.Count - 1);
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
         }
         }
@@ -147,7 +156,11 @@ namespace PixiEditor.Models.Controllers
         public void GeneratePreviewLayer()
         public void GeneratePreviewLayer()
         {
         {
             if (PreviewLayer == null && ActiveDocument != null || PreviewLayerSizeMismatch())
             if (PreviewLayer == null && ActiveDocument != null || PreviewLayerSizeMismatch())
-                PreviewLayer = new Layer("_previewLayer", ActiveDocument.Width, ActiveDocument.Height);
+                PreviewLayer = new Layer("_previewLayer", ActiveDocument.Width, ActiveDocument.Height)
+                {
+                    MaxWidth = ActiveDocument.Width,
+                    MaxHeight = ActiveDocument.Height
+                };
         }
         }
 
 
         private bool PreviewLayerSizeMismatch()
         private bool PreviewLayerSizeMismatch()

+ 1 - 1
PixiEditor/Models/Controllers/ClipboardController.cs

@@ -70,7 +70,7 @@ namespace PixiEditor.Models.Controllers
         {
         {
             Document doc = ViewModelMain.Current.BitmapManager.ActiveDocument;
             Document doc = ViewModelMain.Current.BitmapManager.ActiveDocument;
             Rect imgRect = new Rect(0, 0, image.PixelWidth, image.PixelHeight);
             Rect imgRect = new Rect(0, 0, image.PixelWidth, image.PixelHeight);
-            ViewModelMain.Current.BitmapManager.AddNewLayer("Image", doc.Width, doc.Height);
+            ViewModelMain.Current.BitmapManager.AddNewLayer("Image");
             ViewModelMain.Current.BitmapManager.ActiveLayer.LayerBitmap.Blit(imgRect, image, imgRect);
             ViewModelMain.Current.BitmapManager.ActiveLayer.LayerBitmap.Blit(imgRect, image, imgRect);
         }
         }
 
 

+ 5 - 31
PixiEditor/Models/DataHolders/Document.cs

@@ -36,14 +36,7 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
-        public ObservableCollection<Layer> Layers
-        {
-            get => _layers;
-            set
-            {
-                if (_layers != value) _layers = value;
-            }
-        }
+        public ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
 
 
         public Layer ActiveLayer => Layers.Count > 0 ? Layers[ActiveLayerIndex] : null;
         public Layer ActiveLayer => Layers.Count > 0 ? Layers[ActiveLayerIndex] : null;
 
 
@@ -61,9 +54,6 @@ namespace PixiEditor.Models.DataHolders
         public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
         public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
         private int _activeLayerIndex;
         private int _activeLayerIndex;
         private int _height;
         private int _height;
-
-        private ObservableCollection<Layer> _layers = new ObservableCollection<Layer>();
-
         private int _width;
         private int _width;
 
 
         public Document(int width, int height)
         public Document(int width, int height)
@@ -212,27 +202,11 @@ namespace PixiEditor.Models.DataHolders
         private void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int oldWidth, int oldHeight,
         private void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int oldWidth, int oldHeight,
             int newWidth, int newHeight)
             int newWidth, int newHeight)
         {
         {
-            int sizeOfArgb = 4;
-            int iteratorHeight = oldHeight > newHeight ? newHeight : oldHeight;
-            int count = oldWidth > newWidth ? newWidth : oldWidth;
             for (int i = 0; i < Layers.Count; i++)
             for (int i = 0; i < Layers.Count; i++)
-                using (var srcContext = Layers[i].LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
-                {
-                    var result = BitmapFactory.New(newWidth, newHeight);
-                    using (var destContext = result.GetBitmapContext())
-                    {
-                        for (int line = 0; line < iteratorHeight; line++)
-                        {
-                            var srcOff = ((offsetYSrc + line) * oldWidth + offsetXSrc) * sizeOfArgb;
-                            var dstOff = ((offsetY + line) * newWidth + offsetX) * sizeOfArgb;
-                            BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * sizeOfArgb);
-                        }
-
-                        Layers[i].LayerBitmap = result;
-                        Layers[i].Width = newWidth;
-                        Layers[i].Height = newHeight;
-                    }
-                }
+            {
+                Layers[i].ResizeCanvas(offsetX, offsetY, offsetXSrc, offsetYSrc, oldWidth, oldHeight, newWidth,
+                    newHeight);
+            }
 
 
             Width = newWidth;
             Width = newWidth;
             Height = newHeight;
             Height = newHeight;

+ 114 - 13
PixiEditor/Models/Layers/Layer.cs

@@ -1,5 +1,10 @@
 using System;
 using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
+using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 
 
 namespace PixiEditor.Models.Layers
 namespace PixiEditor.Models.Layers
@@ -57,6 +62,44 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
+        private int _offsetX;
+
+        public int OffsetX
+        {
+            get => _offsetX;
+            set
+            {
+                _offsetX = value;
+                Offset = new Thickness(value,Offset.Top, 0, 0);
+                RaisePropertyChanged("OffsetX");
+            }
+        }
+
+        private int _offsetY;
+
+        public int OffsetY
+        {
+            get => _offsetY;
+            set
+            {
+                _offsetY = value;
+                Offset = new Thickness(Offset.Left, value,0,0);
+                RaisePropertyChanged("OffsetY");
+            }
+        }
+
+        private Thickness _offset;
+
+        public Thickness Offset
+        {
+            get => _offset;
+            set
+            {
+                _offset = value;
+                RaisePropertyChanged("Offset");
+            }
+        }
+
         private bool _isActive;
         private bool _isActive;
 
 
         private bool _isRenaming;
         private bool _isRenaming;
@@ -65,6 +108,9 @@ namespace PixiEditor.Models.Layers
 
 
         private string _name;
         private string _name;
 
 
+        public int MaxWidth { get; set; } = int.MaxValue;
+        public int MaxHeight { get; set; } = int.MaxValue;
+
         public Layer(string name, int width, int height)
         public Layer(string name, int width, int height)
         {
         {
             Name = name;
             Name = name;
@@ -89,12 +135,13 @@ namespace PixiEditor.Models.Layers
         public void ApplyPixels(BitmapPixelChanges pixels)
         public void ApplyPixels(BitmapPixelChanges pixels)
         {
         {
             if (pixels.ChangedPixels == null) return;
             if (pixels.ChangedPixels == null) return;
+            DynamicResize(pixels);
+            pixels.ChangedPixels = ApplyOffset(pixels.ChangedPixels);
             using (var ctx = LayerBitmap.GetBitmapContext())
             using (var ctx = LayerBitmap.GetBitmapContext())
             {
             {
                 foreach (var coords in pixels.ChangedPixels)
                 foreach (var coords in pixels.ChangedPixels)
                 {
                 {
-                    if (coords.Key.X > Width - 1 || coords.Key.X < 0 || coords.Key.Y < 0 ||
-                        coords.Key.Y > Height - 1) continue;
+                    if (coords.Key.X < 0 || coords.Key.Y < 0) continue;
                     ctx.WriteableBitmap.SetPixel(Math.Clamp(coords.Key.X, 0, Width - 1),
                     ctx.WriteableBitmap.SetPixel(Math.Clamp(coords.Key.X, 0, Width - 1),
                         Math.Clamp(coords.Key.Y, 0, Height - 1),
                         Math.Clamp(coords.Key.Y, 0, Height - 1),
                         coords.Value);
                         coords.Value);
@@ -102,6 +149,49 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
+        private Dictionary<Coordinates, Color> ApplyOffset(Dictionary<Coordinates, Color> changedPixels)
+        {
+            return changedPixels.ToDictionary(d => new Coordinates(d.Key.X - OffsetX, d.Key.Y - OffsetY),
+                d => d.Value);
+        }
+
+        /// <summary>
+        /// Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
+        /// </summary>
+        /// <param name="pixels"></param>
+        private void DynamicResize(BitmapPixelChanges pixels)
+        {
+            RecalculateOffset(pixels);
+            int newMaxX = pixels.ChangedPixels.Max(x => x.Key.X) - OffsetX;
+            int newMaxY = pixels.ChangedPixels.Max(x => x.Key.Y) - OffsetY;
+            if (newMaxX + 1 > Width || newMaxY + 1 > Height)
+            {
+                newMaxX = Math.Clamp(Math.Max(newMaxX + 1, Width), 0, MaxWidth);
+                newMaxY = Math.Clamp(Math.Max(newMaxY + 1, Height), 0, MaxHeight);
+                ResizeCanvas(0, 0, 0, 0, Width, Height, newMaxX, newMaxY);
+                RecalculateOffset(pixels);
+                LayerBitmap.Clear(System.Windows.Media.Colors.Blue);
+            }
+        }
+
+        private void RecalculateOffset(BitmapPixelChanges pixels)
+        {
+            if (Width == 0 || Height == 0)
+            {
+                OffsetX = pixels.ChangedPixels.Min(x => x.Key.X);
+                OffsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
+            }
+        }
+
+        private Coordinates FindOffsetForNewSize(BitmapPixelChanges changes)
+        {
+            int newMaxX = changes.ChangedPixels.Max(x => x.Key.X);
+            int newMaxY = changes.ChangedPixels.Max(x => x.Key.Y);
+            int newMinX = changes.ChangedPixels.Min(x => x.Key.X);
+            int newMinY = changes.ChangedPixels.Min(x => x.Key.Y);
+            return new Coordinates(0,0);
+        }
+
         public void Clear()
         public void Clear()
         {
         {
             LayerBitmap.Clear();
             LayerBitmap.Clear();
@@ -115,19 +205,30 @@ namespace PixiEditor.Models.Layers
             return byteArray;
             return byteArray;
         }
         }
 
 
-        public byte[] ConvertBitmapToBytes(WriteableBitmap bitmap)
+        public void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int oldWidth, int oldHeight,
+            int newWidth, int newHeight)
         {
         {
-            bitmap.Lock();
-            byte[] byteArray = bitmap.ToByteArray();
-            bitmap.Unlock();
-            return byteArray;
-        }
+            int sizeOfArgb = 4;
+            int iteratorHeight = oldHeight > newHeight ? newHeight : oldHeight;
+            int count = oldWidth > newWidth ? newWidth : oldWidth;
 
 
-        public void Resize(int newWidth, int newHeight)
-        {
-            LayerBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
-            Height = newHeight;
-            Width = newWidth;
+            using (var srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
+            {
+                var result = BitmapFactory.New(newWidth, newHeight);
+                using (var destContext = result.GetBitmapContext())
+                {
+                    for (int line = 0; line < iteratorHeight; line++)
+                    {
+                        var srcOff = ((offsetYSrc + line) * oldWidth + offsetXSrc) * sizeOfArgb;
+                        var dstOff = ((offsetY + line) * newWidth + offsetX) * sizeOfArgb;
+                        BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * sizeOfArgb);
+                    }
+
+                    LayerBitmap = result;
+                    Width = newWidth;
+                    Height = newHeight;
+                }
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 4
PixiEditor/ViewModels/ViewModelMain.cs

@@ -278,8 +278,6 @@ namespace PixiEditor.ViewModels
 
 
         public ClipboardController ClipboardController { get; set; }
         public ClipboardController ClipboardController { get; set; }
 
 
-
-
         private void CenterContent(object property)
         private void CenterContent(object property)
         {
         {
             BitmapManager.ActiveDocument.CenterContent();
             BitmapManager.ActiveDocument.CenterContent();
@@ -655,8 +653,7 @@ namespace PixiEditor.ViewModels
 
 
         public void NewLayer(object parameter)
         public void NewLayer(object parameter)
         {
         {
-            BitmapManager.AddNewLayer($"New Layer {BitmapManager.ActiveDocument.Layers.Count}",
-                BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height);
+            BitmapManager.AddNewLayer($"New Layer {BitmapManager.ActiveDocument.Layers.Count}");
         }
         }
 
 
         public bool CanCreateNewLayer(object parameter)
         public bool CanCreateNewLayer(object parameter)

+ 4 - 2
PixiEditor/Views/MainWindow.xaml

@@ -182,10 +182,10 @@
                                 </ItemsControl.ItemsPanel>
                                 </ItemsControl.ItemsPanel>
                                 <ItemsControl.ItemTemplate>
                                 <ItemsControl.ItemTemplate>
                                     <DataTemplate>
                                     <DataTemplate>
-                                        <Image Source="{Binding LayerBitmap}"
+                                        <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding LayerBitmap}"
                                                Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}"
                                                Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}"
                                                RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
                                                RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
-                                               Width="{Binding Width}" Height="{Binding Height}" />
+                                               Width="{Binding Width}" Height="{Binding Height}" Margin="{Binding Offset}" />
                                     </DataTemplate>
                                     </DataTemplate>
                                 </ItemsControl.ItemTemplate>
                                 </ItemsControl.ItemTemplate>
                             </ItemsControl>
                             </ItemsControl>
@@ -226,6 +226,8 @@
                 <RowDefinition Height="250*" />
                 <RowDefinition Height="250*" />
                 <RowDefinition Height="209*" />
                 <RowDefinition Height="209*" />
             </Grid.RowDefinitions>
             </Grid.RowDefinitions>
+            <StackPanel Grid.Row="2" Orientation="Vertical" ZIndex="15">
+            </StackPanel>
             <vws:ColorPicker Grid.Row="0" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"
             <vws:ColorPicker Grid.Row="0" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"
                              SecondaryColor="{Binding SecondaryColor, Mode=TwoWay}" />
                              SecondaryColor="{Binding SecondaryColor, Mode=TwoWay}" />
             <avalondock:DockingManager Foreground="White" Background="{StaticResource AccentColor}" BorderThickness="0"
             <avalondock:DockingManager Foreground="White" Background="{StaticResource AccentColor}" BorderThickness="0"

+ 16 - 0
PixiEditor/Views/Rotatebox.xaml

@@ -0,0 +1,16 @@
+<UserControl x:Class="PixiEditor.Views.Rotatebox"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:PixiEditor.Views"
+             mc:Ignorable="d" 
+             d:DesignHeight="100" d:DesignWidth="160" x:Name="uc">
+    <StackPanel Orientation="Vertical" RenderTransformOrigin="0.5, 0.5">
+    <Image Name="knob" Source="../Images/AnchorDot.png" RenderTransformOrigin="0.5,0.5" Width="20" Height="20"/>
+        <Border Width="120" Height="60" BorderThickness="0.3" BorderBrush="DeepSkyBlue" CornerRadius="1"/>
+        <StackPanel.RenderTransform>
+            <RotateTransform Angle="{Binding Path=Angle, ElementName=uc}"/>
+        </StackPanel.RenderTransform>
+    </StackPanel>
+</UserControl>

+ 79 - 0
PixiEditor/Views/Rotatebox.xaml.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    /// Interaction logic for Rotatebox.xaml
+    /// </summary>
+    public partial class Rotatebox : UserControl
+    {
+        private double _height = 0, _width = 0;
+        private float _offset = 90;
+
+        public Rotatebox()
+        {
+            InitializeComponent();
+            MouseLeftButtonDown += OnMouseLeftButtonDown;
+            MouseUp += OnMouseUp;
+            MouseMove += OnMouseMove;
+        }
+
+        // Using a DependencyProperty backing store for Angle.
+        public static readonly DependencyProperty AngleProperty =
+            DependencyProperty.Register("Angle", typeof(double), typeof(Rotatebox), new UIPropertyMetadata(0.0));
+
+        public double Angle
+        {
+            get { return (double)GetValue(AngleProperty); }
+            set { SetValue(AngleProperty, value); }
+        }
+
+
+        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            Mouse.Capture(this);
+            _width = ActualWidth;
+            _height = ActualHeight;
+        }
+
+        private void OnMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            Mouse.Capture(null);
+        }
+
+        private void OnMouseMove(object sender, MouseEventArgs e)
+        {
+            if (Equals(Mouse.Captured, this))
+            {
+                // Get the current mouse position relative to the control
+                Point currentLocation = Mouse.GetPosition(this);
+
+                // We want to rotate around the center of the knob, not the top corner
+                Point knobCenter = new Point(_width / 2, _height/ 2);
+
+                // Calculate an angle
+                double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
+                                           (currentLocation.X - knobCenter.X));
+                Angle = radians * 180 / Math.PI + _offset;
+
+                // Apply a 180 degree shift when X is negative so that we can rotate
+                // all of the way around
+                if (currentLocation.X - knobCenter.X < 0)
+                {
+                    Angle += 180;
+                }
+            }
+        }
+    }
+}