瀏覽代碼

Dynamic Layers WIP

flabbet 5 年之前
父節點
當前提交
78ad993640

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

@@ -95,9 +95,18 @@ namespace PixiEditor.Models.Controllers
             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)
         {
-            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);
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
         }
@@ -147,7 +156,11 @@ namespace PixiEditor.Models.Controllers
         public void GeneratePreviewLayer()
         {
             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()

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

@@ -70,7 +70,7 @@ namespace PixiEditor.Models.Controllers
         {
             Document doc = ViewModelMain.Current.BitmapManager.ActiveDocument;
             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);
         }
 

+ 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;
 
@@ -61,9 +54,6 @@ namespace PixiEditor.Models.DataHolders
         public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
         private int _activeLayerIndex;
         private int _height;
-
-        private ObservableCollection<Layer> _layers = new ObservableCollection<Layer>();
-
         private int _width;
 
         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,
             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++)
-                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;
             Height = newHeight;

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

@@ -1,5 +1,10 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
 using System.Windows.Media.Imaging;
+using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 
 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 _isRenaming;
@@ -65,6 +108,9 @@ namespace PixiEditor.Models.Layers
 
         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)
         {
             Name = name;
@@ -89,12 +135,13 @@ namespace PixiEditor.Models.Layers
         public void ApplyPixels(BitmapPixelChanges pixels)
         {
             if (pixels.ChangedPixels == null) return;
+            DynamicResize(pixels);
+            pixels.ChangedPixels = ApplyOffset(pixels.ChangedPixels);
             using (var ctx = LayerBitmap.GetBitmapContext())
             {
                 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),
                         Math.Clamp(coords.Key.Y, 0, Height - 1),
                         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()
         {
             LayerBitmap.Clear();
@@ -115,19 +205,30 @@ namespace PixiEditor.Models.Layers
             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; }
 
-
-
         private void CenterContent(object property)
         {
             BitmapManager.ActiveDocument.CenterContent();
@@ -655,8 +653,7 @@ namespace PixiEditor.ViewModels
 
         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)

+ 4 - 2
PixiEditor/Views/MainWindow.xaml

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