Browse Source

Dynamic Layers working

flabbet 5 years ago
parent
commit
3b6513b182

+ 16 - 12
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -82,11 +82,13 @@ namespace PixiEditor.Models.Controllers
                 LayerChange[] oldPixelsValues = new LayerChange[modifiedLayers.Length];
                 for (int i = 0; i < modifiedLayers.Length; i++)
                 {
+                    var layer = Manager.ActiveDocument.Layers[modifiedLayers[i].LayerIndex];
+                    layer.DynamicResize(modifiedLayers[i].PixelChanges);
                     oldPixelsValues[i] = new LayerChange(
                         GetOldPixelsValues(modifiedLayers[i].PixelChanges.ChangedPixels.Keys.ToArray()),
                         modifiedLayers[i].LayerIndex);
-                    Manager.ActiveDocument.Layers[modifiedLayers[i].LayerIndex]
-                        .ApplyPixels(modifiedLayers[i].PixelChanges);
+                    
+                        layer.ApplyPixels(modifiedLayers[i].PixelChanges);
                     BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(modifiedLayers[i].PixelChanges,
                         oldPixelsValues[i].PixelChanges, modifiedLayers[i].LayerIndex));
                 }
@@ -120,23 +122,25 @@ namespace PixiEditor.Models.Controllers
         private BitmapPixelChanges GetOldPixelsValues(Coordinates[] coordinates)
         {
             Dictionary<Coordinates, Color> values = new Dictionary<Coordinates, Color>();
-            Manager.ActiveLayer.LayerBitmap.Lock();
-            for (int i = 0; i < coordinates.Length; i++)
+            using (Manager.ActiveLayer.LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
             {
-                if (coordinates[i].X < 0 || coordinates[i].X > Manager.ActiveDocument.Width - 1 ||
-                    coordinates[i].Y < 0 || coordinates[i].Y > Manager.ActiveDocument.Height - 1)
-                    continue;
-                values.Add(coordinates[i],
-                    Manager.ActiveLayer.LayerBitmap.GetPixel(coordinates[i].X, coordinates[i].Y));
-            }
+                for (int i = 0; i < coordinates.Length; i++)
+                {
+                    coordinates = Manager.ActiveLayer.ConvertToRelativeCoordinates(coordinates);
+                    if (coordinates[i].X < 0 || coordinates[i].X > Manager.ActiveLayer.Width - 1 ||
+                        coordinates[i].Y < 0 || coordinates[i].Y > Manager.ActiveLayer.Height - 1)
+                        continue;
+                    values.Add(coordinates[i],
+                        Manager.ActiveLayer.LayerBitmap.GetPixel(coordinates[i].X, coordinates[i].Y));
+                }
 
-            Manager.ActiveLayer.LayerBitmap.Unlock();
+            }
             return new BitmapPixelChanges(values);
         }
 
         private void UseToolOnPreviewLayer(List<Coordinates> mouseMove)
         {
-            LayerChange[] modifiedLayers = null;
+            LayerChange[] modifiedLayers;
             if (mouseMove.Count > 0 && mouseMove[0] != _lastMousePos)
             {
                 Manager.GeneratePreviewLayer();

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

@@ -204,7 +204,7 @@ namespace PixiEditor.Models.DataHolders
         {
             for (int i = 0; i < Layers.Count; i++)
             {
-                Layers[i].ResizeCanvas(offsetX, offsetY, offsetXSrc, offsetYSrc, oldWidth, oldHeight, newWidth,
+                Layers[i].ResizeCanvas(offsetX, offsetY, offsetXSrc, offsetYSrc, newWidth,
                     newHeight);
             }
 

+ 125 - 53
PixiEditor/Models/Layers/Layer.cs

@@ -62,31 +62,9 @@ namespace PixiEditor.Models.Layers
             }
         }
 
-        private int _offsetX;
+        public int OffsetX => (int) Offset.Left;
 
-        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");
-            }
-        }
+        public int OffsetY => (int) Offset.Top;
 
         private Thickness _offset;
 
@@ -107,6 +85,7 @@ namespace PixiEditor.Models.Layers
         private WriteableBitmap _layerBitmap;
 
         private string _name;
+        private bool _clipRequested;
 
         public int MaxWidth { get; set; } = int.MaxValue;
         public int MaxHeight { get; set; } = int.MaxValue;
@@ -128,25 +107,35 @@ namespace PixiEditor.Models.Layers
             Height = (int) layerBitmap.Height;
         }
 
+        /// <summary>
+        ///     Returns pixel color by x and y coordinates relative to document using (x - OffsetX + 1) formula.
+        /// </summary>
+        /// <param name="x">Viewport relative X</param>
+        /// <param name="y">Viewport relative Y</param>
+        /// <returns>Color of a pixel</returns>
+        public Color GetPixelWithOffset(int x, int y)
+        {
+            return LayerBitmap.GetPixel(x - OffsetX + 1, y - OffsetY + 1);
+        }
+
         /// <summary>
         ///     Applies pixels to layer
         /// </summary>
         /// <param name="pixels"></param>
         public void ApplyPixels(BitmapPixelChanges pixels)
         {
-            if (pixels.ChangedPixels == null) return;
+            if (pixels.ChangedPixels == null || pixels.ChangedPixels.Count == 0) return;
             DynamicResize(pixels);
             pixels.ChangedPixels = ApplyOffset(pixels.ChangedPixels);
             using (var ctx = LayerBitmap.GetBitmapContext())
             {
                 foreach (var coords in pixels.ChangedPixels)
                 {
-                    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);
+                    if (OutOfBounds(coords.Key)) continue;
+                    ctx.WriteableBitmap.SetPixel(coords.Key.X, coords.Key.Y, coords.Value);
                 }
             }
+            ClipIfNecessary();
         }
 
         private Dictionary<Coordinates, Color> ApplyOffset(Dictionary<Coordinates, Color> changedPixels)
@@ -155,41 +144,125 @@ namespace PixiEditor.Models.Layers
                 d => d.Value);
         }
 
+        public Coordinates[] ConvertToRelativeCoordinates(Coordinates[] nonRelativeCords)
+        {
+            Coordinates[] result = new Coordinates[nonRelativeCords.Length];
+            for (int i = 0; i < result.Length; i++)
+            {
+                result[i] = new Coordinates(nonRelativeCords[i].X - OffsetX, nonRelativeCords[i].Y - OffsetY);
+            }
+
+            return result;
+        }
+
         /// <summary>
-        /// Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
+        ///     Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
         /// </summary>
         /// <param name="pixels"></param>
-        private void DynamicResize(BitmapPixelChanges pixels)
+        public void DynamicResize(BitmapPixelChanges pixels)
         {
-            RecalculateOffset(pixels);
+            ResetOffset(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)
+            int newMinX = pixels.ChangedPixels.Min(x => x.Key.X) - OffsetX;
+            int newMinY = pixels.ChangedPixels.Min(x => x.Key.Y) - OffsetY;
+
+            if (pixels.ChangedPixels.Any(x => x.Value.A != 0))
             {
-                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);
+                if (newMaxX + 1 > Width || newMaxY + 1 > Height)
+                {
+                    IncreaseSizeToBottom(newMaxX, newMaxY);
+                }
+                else if (newMinX < 0 || newMinY < 0)
+                {
+                    IncreaseSizeToTop(newMinX, newMinY);
+                }
+            } 
+            
+            if(pixels.ChangedPixels.Any(x=> IsBorderPixel(x.Key) && x.Value.A == 0))
+            {
+                _clipRequested = true;
             }
         }
 
-        private void RecalculateOffset(BitmapPixelChanges pixels)
+        private bool IsBorderPixel(Coordinates cords)
         {
-            if (Width == 0 || Height == 0)
+            return cords.X - OffsetX == 0 || cords.Y - OffsetY == 0 || cords.X - OffsetX == Width - 1 ||
+                   cords.Y - OffsetY == Height - 1;
+        }
+
+        private bool OutOfBounds(Coordinates cords)
+        {
+            return cords.X < 0 || cords.X > Width - 1 || cords.Y < 0 || cords.Y > Height - 1;
+        }
+
+        private void ClipIfNecessary()
+        {
+            if (_clipRequested)
             {
-                OffsetX = pixels.ChangedPixels.Min(x => x.Key.X);
-                OffsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
+                ClipCanvas();
+                _clipRequested = false;
             }
         }
 
-        private Coordinates FindOffsetForNewSize(BitmapPixelChanges changes)
+        public void ClipCanvas()
+        {
+            var points = GetEdgePoints();
+            int smallestX = points.Coords1.X;
+            int smallestY = points.Coords1.Y;
+            int biggestX = points.Coords2.X;
+            int biggestY = points.Coords2.Y;
+
+            if (smallestX < 0 && smallestY < 0 && biggestX < 0 && biggestY < 0)
+                return;
+
+            int width = biggestX - smallestX + 1;
+            int height = biggestY - smallestY + 1;
+            ResizeCanvas(0,0, smallestX, smallestY, width, height);
+            Offset = new Thickness(OffsetX + smallestX, OffsetY + smallestY, 0, 0);
+        }
+
+        private void IncreaseSizeToBottom(int newMaxX, int newMaxY)
+        {
+            newMaxX = Math.Clamp(Math.Max(newMaxX + 1, Width), 0, MaxWidth - OffsetX);
+            newMaxY = Math.Clamp(Math.Max(newMaxY + 1, Height), 0, MaxHeight - OffsetY);
+
+            ResizeCanvas(0, 0, 0, 0, newMaxX, newMaxY);
+        }
+
+        private void IncreaseSizeToTop(int newMinX, int newMinY)
         {
-            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);
+            newMinX = Math.Clamp(Math.Min(newMinX, Width), -OffsetX, 0);
+            newMinY = Math.Clamp(Math.Min(newMinY, Height), -OffsetY, 0);
+
+            Offset = new Thickness(Math.Clamp(OffsetX + newMinX, 0, MaxWidth),
+                Math.Clamp(OffsetY + newMinY, 0, MaxHeight), 0, 0);
+
+            int newWidth = Math.Clamp(Width - newMinX, 0, MaxWidth);
+            int newHeight = Math.Clamp(Height - newMinY, 0, MaxHeight);
+
+            int offsetX = Math.Abs(newWidth - Width);
+            int offsetY = Math.Abs(newHeight - Height);
+
+            ResizeCanvas(offsetX, offsetY, 0, 0, newWidth, newHeight);
+        }
+
+        private DoubleCords GetEdgePoints()
+        {
+            Coordinates smallestPixel = CoordinatesCalculator.FindMinEdgeNonTransparentPixel(LayerBitmap);
+            Coordinates biggestPixel = CoordinatesCalculator.FindMostEdgeNonTransparentPixel(LayerBitmap);
+
+            return new DoubleCords(smallestPixel, biggestPixel);
+        }
+
+        private void ResetOffset(BitmapPixelChanges pixels)
+        {
+            if (Width == 0 || Height == 0)
+            {
+                int offsetX = pixels.ChangedPixels.Min(x => x.Key.X);
+                int offsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
+                Offset = new Thickness(offsetX, offsetY, 0,0);
+            }
         }
 
         public void Clear()
@@ -205,12 +278,11 @@ namespace PixiEditor.Models.Layers
             return byteArray;
         }
 
-        public void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int oldWidth, int oldHeight,
-            int newWidth, int newHeight)
+        public void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int newWidth, int newHeight)
         {
             int sizeOfArgb = 4;
-            int iteratorHeight = oldHeight > newHeight ? newHeight : oldHeight;
-            int count = oldWidth > newWidth ? newWidth : oldWidth;
+            int iteratorHeight = Height > newHeight ? newHeight : Height;
+            int count = Width > newWidth ? newWidth : Width;
 
             using (var srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
             {
@@ -219,7 +291,7 @@ namespace PixiEditor.Models.Layers
                 {
                     for (int line = 0; line < iteratorHeight; line++)
                     {
-                        var srcOff = ((offsetYSrc + line) * oldWidth + offsetXSrc) * sizeOfArgb;
+                        var srcOff = ((offsetYSrc + line) * Width + offsetXSrc) * sizeOfArgb;
                         var dstOff = ((offsetY + line) * newWidth + offsetX) * sizeOfArgb;
                         BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * sizeOfArgb);
                     }

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

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
 
 namespace PixiEditor.Models.Position
 {

+ 1 - 1
PixiEditor/ViewModels/ViewModelMain.cs

@@ -641,7 +641,7 @@ namespace PixiEditor.ViewModels
         private void NewDocument(int width, int height)
         {
             BitmapManager.ActiveDocument = new Document(width, height);
-            BitmapManager.AddNewLayer("Base Layer", width, height);
+            BitmapManager.AddNewLayer("Base Layer");
             BitmapManager.PreviewLayer = null;
             UndoManager.UndoStack.Clear();
             UndoManager.RedoStack.Clear();

+ 3 - 1
PixiEditor/Views/MainWindow.xaml

@@ -182,10 +182,12 @@
                                 </ItemsControl.ItemsPanel>
                                 <ItemsControl.ItemTemplate>
                                     <DataTemplate>
+                                        <Border Width="{Binding Width}" Height="{Binding Height}" Margin="{Binding Offset}"  BorderThickness="0.1" BorderBrush="DodgerBlue">
                                         <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}" Margin="{Binding Offset}" />
+                                               Width="{Binding Width}" Height="{Binding Height}" />
+                                        </Border>
                                     </DataTemplate>
                                 </ItemsControl.ItemTemplate>
                             </ItemsControl>