Browse Source

Fix preview layer and swatches background

Equbuxu 4 years ago
parent
commit
a27cf06ec2

+ 4 - 2
PixiEditor/Models/Controllers/DocumentRenderer.cs → PixiEditor/Models/Controllers/LayerStackRenderer.cs

@@ -11,7 +11,7 @@ using System.Windows.Media.Imaging;
 
 namespace PixiEditor.Models.Controllers
 {
-    public class DocumentRenderer : INotifyPropertyChanged, IDisposable
+    public class LayerStackRenderer : INotifyPropertyChanged, IDisposable
     {
         private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
 
@@ -32,7 +32,7 @@ namespace PixiEditor.Models.Controllers
         }
 
         public event PropertyChangedEventHandler PropertyChanged;
-        public DocumentRenderer(ObservableCollection<Layer> layers, LayerStructure structure, int width, int height)
+        public LayerStackRenderer(ObservableCollection<Layer> layers, LayerStructure structure, int width, int height)
         {
             this.layers = layers;
             this.structure = structure;
@@ -72,6 +72,8 @@ namespace PixiEditor.Models.Controllers
             finalSurface.SkiaSurface.Canvas.Clear();
             foreach (var layer in layers)
             {
+                if (!layer.IsVisible)
+                    continue;
                 BlendingPaint.Color = new SKColor(255, 255, 255, (byte)(LayerStructureUtils.GetFinalLayerOpacity(layer, structure) * 255));
                 layer.LayerBitmap.SkiaSurface.Draw(
                     finalSurface.SkiaSurface.Canvas,

+ 81 - 0
PixiEditor/Models/Controllers/SingleLayerRenderer.cs

@@ -0,0 +1,81 @@
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using SkiaSharp;
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class SingleLayerRenderer : INotifyPropertyChanged, IDisposable
+    {
+        private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
+        private Layer layer;
+
+        private Surface finalSurface;
+        private SKSurface backingSurface;
+        private WriteableBitmap finalBitmap;
+        public WriteableBitmap FinalBitmap
+        {
+            get => finalBitmap;
+            set
+            {
+                finalBitmap = value;
+                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FinalBitmap)));
+            }
+        }
+
+        public event PropertyChangedEventHandler PropertyChanged;
+        public SingleLayerRenderer(Layer layer, int width, int height)
+        {
+            this.layer = layer;
+            layer.LayerBitmapChanged += OnLayerBitmapChanged;
+            Resize(width, height);
+        }
+
+        public void Resize(int newWidth, int newHeight)
+        {
+            finalSurface?.Dispose();
+            backingSurface?.Dispose();
+
+            finalSurface = new Surface(newWidth, newHeight);
+            finalBitmap = new WriteableBitmap(newWidth, newHeight, 96, 96, PixelFormats.Pbgra32, null);
+            var imageInfo = new SKImageInfo(newWidth, newHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb());
+            backingSurface = SKSurface.Create(imageInfo, finalBitmap.BackBuffer, finalBitmap.BackBufferStride);
+            Update(new Int32Rect(0, 0, newWidth, newHeight));
+        }
+
+        public void Dispose()
+        {
+            finalSurface.Dispose();
+            backingSurface.Dispose();
+            BlendingPaint.Dispose();
+            layer.LayerBitmapChanged -= OnLayerBitmapChanged;
+        }
+
+        private void Update(Int32Rect dirtyRectangle)
+        {
+            finalSurface.SkiaSurface.Canvas.Clear();
+            if (layer.IsVisible)
+            {
+                BlendingPaint.Color = new SKColor(255, 255, 255, (byte)(layer.Opacity * 255));
+                layer.LayerBitmap.SkiaSurface.Draw(
+                    finalSurface.SkiaSurface.Canvas,
+                    layer.OffsetX,
+                    layer.OffsetY,
+                    BlendingPaint);
+            }
+            finalBitmap.Lock();
+            finalSurface.SkiaSurface.Draw(backingSurface.Canvas, 0, 0, Surface.ReplacingPaint);
+            finalBitmap.AddDirtyRect(dirtyRectangle);
+            finalBitmap.Unlock();
+        }
+
+        private void OnLayerBitmapChanged(object sender, Int32Rect e)
+        {
+            Update(e);
+        }
+    }
+}

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

@@ -12,7 +12,7 @@ namespace PixiEditor.Models.DataHolders
         {
             Width = width;
             Height = height;
-            Renderer = new DocumentRenderer(layers, layerStructure, Width, Height);
+            Renderer = new LayerStackRenderer(layers, layerStructure, Width, Height);
             DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(1, 1, width, height));
         }
 

+ 17 - 10
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -47,8 +47,8 @@ namespace PixiEditor.Models.DataHolders
             }
         }
 
-        private DocumentRenderer renderer;
-        public DocumentRenderer Renderer
+        private LayerStackRenderer renderer;
+        public LayerStackRenderer Renderer
         {
             get => renderer;
             private set
@@ -203,8 +203,7 @@ namespace PixiEditor.Models.DataHolders
 
         public void AddNewLayer(string name, Surface bitmap, bool setAsActive = true)
         {
-            AddNewLayer(name, bitmap.Width, bitmap.Height, setAsActive);
-            Layers.Last().LayerBitmap = bitmap;
+            AddNewLayer(name, bitmap.Width, bitmap.Height, setAsActive, bitmap);
         }
 
         public void AddNewLayer(string name, bool setAsActive = true)
@@ -212,15 +211,23 @@ namespace PixiEditor.Models.DataHolders
             AddNewLayer(name, 1, 1, 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, Surface bitmap = null)
         {
             Layer layer;
 
-            Layers.Add(layer = new Layer(name, width, height)
+            if (bitmap != null)
             {
-                MaxHeight = Height,
-                MaxWidth = Width
-            });
+                if (width != bitmap.Width || height != bitmap.Height)
+                    throw new ArgumentException("Inconsistent width and height");
+            }
+            if (width <= 0 || height <= 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;
+
+            Layers.Add(layer);
 
             layer.Name = GetLayerSuffix(layer);
 
@@ -533,7 +540,7 @@ namespace PixiEditor.Models.DataHolders
 
             if (Layers.Count == 0)
             {
-                Layer layer = new("Base Layer", 0, 0) { MaxHeight = Height, MaxWidth = Width };
+                Layer layer = new("Base Layer", 1, 1) { MaxHeight = Height, MaxWidth = Width };
                 Layers.Add(layer);
                 undoAction = (Layer[] layers, UndoLayer[] undoData) =>
                 {

+ 12 - 2
PixiEditor/Models/DataHolders/Document/Document.Preview.cs

@@ -1,6 +1,7 @@
-using System.Windows.Media.Imaging;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.Layers;
+using System.Windows.Media.Imaging;
 
 namespace PixiEditor.Models.DataHolders
 {
@@ -14,6 +15,7 @@ namespace PixiEditor.Models.DataHolders
         }
 
         private Layer previewLayer;
+        private SingleLayerRenderer previewLayerRenderer;
 
         public Layer PreviewLayer
         {
@@ -21,10 +23,18 @@ namespace PixiEditor.Models.DataHolders
             set
             {
                 previewLayer = value;
+                previewLayerRenderer?.Dispose();
+                previewLayerRenderer = previewLayer == null ? null : new SingleLayerRenderer(previewLayer, Width, Height);
                 RaisePropertyChanged(nameof(PreviewLayer));
+                RaisePropertyChanged(nameof(PreviewLayerRenderer));
             }
         }
 
+        public SingleLayerRenderer PreviewLayerRenderer
+        {
+            get => previewLayerRenderer;
+        }
+
         public void UpdatePreviewImage()
         {
             previewImage = BitmapUtils.GeneratePreviewBitmap(this, 30, 20);
@@ -40,4 +50,4 @@ namespace PixiEditor.Models.DataHolders
             };
         }
     }
-}
+}

+ 20 - 1
PixiEditor/Models/Layers/Layer.cs

@@ -98,6 +98,7 @@ namespace PixiEditor.Models.Layers
                 if (SetProperty(ref isVisible, value))
                 {
                     RaisePropertyChanged(nameof(IsVisibleUndoTriggerable));
+                    InvokeLayerBitmapChange();
                     ViewModelMain.Current.ToolsSubViewModel.TriggerCacheOutdated();
                 }
             }
@@ -137,10 +138,11 @@ namespace PixiEditor.Models.Layers
         public Surface LayerBitmap
         {
             get => layerBitmap;
-            set
+            private set
             {
                 layerBitmap = value;
                 RaisePropertyChanged(nameof(LayerBitmap));
+                InvokeLayerBitmapChange();
             }
         }
 
@@ -152,6 +154,7 @@ namespace PixiEditor.Models.Layers
                 if (SetProperty(ref opacity, value))
                 {
                     RaisePropertyChanged(nameof(OpacityUndoTriggerable));
+                    InvokeLayerBitmapChange();
                     ViewModelMain.Current.ToolsSubViewModel.TriggerCacheOutdated();
                 }
             }
@@ -198,6 +201,11 @@ namespace PixiEditor.Models.Layers
 
         public event EventHandler<Int32Rect> LayerBitmapChanged;
 
+        public void InvokeLayerBitmapChange()
+        {
+            LayerBitmapChanged?.Invoke(this, new Int32Rect(OffsetX, OffsetY, Width, Height));
+        }
+
         public void InvokeLayerBitmapChange(Int32Rect dirtyArea)
         {
             LayerBitmapChanged?.Invoke(this, dirtyArea);
@@ -331,6 +339,11 @@ namespace PixiEditor.Models.Layers
 
             LastRelativeCoordinates = pixels.ChangedPixels;
 
+            int minX = int.MaxValue;
+            int maxX = int.MinValue;
+            int minY = int.MaxValue;
+            int maxY = int.MinValue;
+
             foreach (KeyValuePair<Coordinates, SKColor> coords in pixels.ChangedPixels)
             {
                 if (OutOfBounds(coords.Key))
@@ -339,9 +352,15 @@ namespace PixiEditor.Models.Layers
                 }
 
                 LayerBitmap.SetSRGBPixel(coords.Key.X, coords.Key.Y, coords.Value);
+                minX = Math.Min(minX, coords.Key.X);
+                minY = Math.Min(minY, coords.Key.Y);
+                maxX = Math.Max(maxX, coords.Key.X);
+                maxY = Math.Max(maxY, coords.Key.Y);
             }
 
             ClipIfNecessary();
+            if (minX != int.MaxValue)
+                InvokeLayerBitmapChange(new Int32Rect(minX + OffsetX, minY + OffsetY, maxX - minX + 1, maxY - minY + 1));
         }
 
         /// <summary>

+ 0 - 41
PixiEditor/Views/UserControls/AvalonDockWindows/ReferenceLayerWindow.xaml

@@ -1,41 +0,0 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.AvalonDockWindows.ReferenceLayerWindow"
-             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.UserControls.AvalonDockWindows" xmlns:views="clr-namespace:PixiEditor.Views" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
-             mc:Ignorable="d" 
-             d:DesignHeight="200" d:DesignWidth="200"
-             Name="uc">
-    <UserControl.Resources>
-        <Style TargetType="TextBlock">
-            <Setter Property="Foreground" Value="White"/>
-        </Style>
-        <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter"/>
-    </UserControl.Resources>
-    
-    <StackPanel Margin="5">
-        <Grid>
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="Auto"/>
-                <ColumnDefinition/>
-                <ColumnDefinition Width="Auto"/>
-            </Grid.ColumnDefinitions>
-            <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Path:</TextBlock>
-            <TextBox Text="{Binding FilePath, ElementName=uc}" Grid.Column="1"
-                     Style="{StaticResource DarkTextBoxStyle}" FontSize="14"/>
-            <Button Grid.Column="2" Content="&#xE838;" VerticalAlignment="Center"
-                    Style="{StaticResource ToolSettingsGlyphButton}" Width="20"
-                    Command="{Binding OpenFilePickerCommand, ElementName=uc}"></Button>
-        </Grid>
-        <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
-            <TextBlock Text="Opacity: " Foreground="White" VerticalAlignment="Center"/>
-            <views:NumberInput Min="0" Max="100" Value="{Binding LayerOpacity, ElementName=uc, Mode=TwoWay}"
-                               Width="40" Height="20" VerticalAlignment="Center"/>
-            <TextBlock Text=" %" Foreground="White" VerticalAlignment="Center"/>
-        </StackPanel>
-        <Button Command="{Binding UpdateLayerCommand, ElementName=uc}"
-                Style="{StaticResource DarkRoundButton}" FontSize="14" 
-                Height="25" Margin="0,5">Update</Button>
-    </StackPanel>
-</UserControl>

+ 0 - 82
PixiEditor/Views/UserControls/AvalonDockWindows/ReferenceLayerWindow.xaml.cs

@@ -1,82 +0,0 @@
-using Microsoft.Win32;
-using PixiEditor.Helpers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.IO;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace PixiEditor.Views.UserControls.AvalonDockWindows
-{
-    /// <summary>
-    /// Interaction logic for ReferenceLayerWindow.xaml
-    /// </summary>
-    public partial class ReferenceLayerWindow : UserControl
-    {
-        public static readonly DependencyProperty DocumentProperty =
-            DependencyProperty.Register(nameof(Document), typeof(Document), typeof(ReferenceLayerWindow));
-
-        public Document Document
-        {
-            get => (Document)GetValue(DocumentProperty);
-            set => SetValue(DocumentProperty, value);
-        }
-
-        public static readonly DependencyProperty FilePathProperty =
-            DependencyProperty.Register(nameof(FilePath), typeof(string), typeof(ReferenceLayerWindow));
-
-        public string FilePath
-        {
-            get => (string)GetValue(FilePathProperty);
-            set => SetValue(FilePathProperty, value);
-        }
-
-        public static readonly DependencyProperty LayerOpacityProperty =
-            DependencyProperty.Register(nameof(LayerOpacity), typeof(float), typeof(ReferenceLayerWindow), new PropertyMetadata(100f));
-
-        public float LayerOpacity
-        {
-            get => (float)GetValue(LayerOpacityProperty);
-            set => SetValue(LayerOpacityProperty, value);
-        }
-
-        public static readonly DependencyProperty HasDocumentProperty =
-            DependencyProperty.Register(nameof(HasDocument), typeof(bool), typeof(ReferenceLayerWindow));
-
-        public bool HasDocument
-        {
-            get => (bool)GetValue(HasDocumentProperty);
-            set => SetValue(HasDocumentProperty, value);
-        }
-
-        public RelayCommand UpdateLayerCommand { get; set; }
-
-        public RelayCommand OpenFilePickerCommand { get; set; }
-
-        public ReferenceLayerWindow()
-        {
-            UpdateLayerCommand = new RelayCommand(UpdateLayer);
-            OpenFilePickerCommand = new RelayCommand(OpenFilePicker);
-            InitializeComponent();
-        }
-
-        private void UpdateLayer(object obj)
-        {
-            Document.ReferenceLayer.LayerBitmap = Importer.ImportSurface(FilePath);
-            Document.ReferenceLayer.Opacity = LayerOpacity;
-        }
-
-        private void OpenFilePicker(object obj)
-        {
-            OpenFileDialog dialog = new OpenFileDialog()
-            {
-                Filter = "PNG Files|*.png|JPEG Files|*.jpg;*.jpeg",
-                CheckFileExists = true
-            };
-
-            if ((bool)dialog.ShowDialog())
-            {
-                FilePath = dialog.FileName;
-            }
-        }
-    }
-}

+ 3 - 4
PixiEditor/Views/UserControls/DrawingViewPort.xaml

@@ -59,11 +59,10 @@
                        HorizontalAlignment="Center" Width="{Binding Width}"
                        Height="{Binding Height}"
                        RenderOptions.BitmapScalingMode="NearestNeighbor"/>
-                <Image Source="{Binding PreviewLayer.LayerBitmap}" Panel.ZIndex="2"
+                <Image Source="{Binding PreviewLayerRenderer.FinalBitmap}" Panel.ZIndex="2"
                                    RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
-                                   Width="{Binding PreviewLayer.Width}"
-                                   Height="{Binding PreviewLayer.Height}" 
-                                   Margin="{Binding PreviewLayer.Offset}"/>
+                                   Width="{Binding Width}"
+                                   Height="{Binding Height}"/>
 
                 <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Renderer.FinalBitmap}"
                                                RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform" />

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

@@ -36,8 +36,11 @@
                     <Grid Width="45" Height="45" Margin="0 5 5 5">
                         <Border CornerRadius="5.5" Width="44" Height="44">
                             <Border.Background>
-                                <ImageBrush ImageSource="../../Images/CheckerTile.png"
-                                                                        Stretch="UniformToFill" />
+                                <VisualBrush>
+                                    <VisualBrush.Visual>
+                                        <Image Source="../../Images/CheckerTile.png" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
+                                    </VisualBrush.Visual>
+                                </VisualBrush>
                             </Border.Background>
                         </Border>
                         <Border CornerRadius="5.5" BorderThickness="0 0 0 0.1" BorderBrush="White" Cursor="Hand">