Browse Source

Convert Layer to Skia

Equbuxu 4 years ago
parent
commit
50772ea332

+ 72 - 0
PixiEditor/Models/DataHolders/Surface.cs

@@ -0,0 +1,72 @@
+using SkiaSharp;
+using System;
+
+namespace PixiEditor.Models.DataHolders
+{
+    public class Surface
+    {
+        public static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
+        private static readonly SKPaint nearestNeighborReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src, FilterQuality = SKFilterQuality.Low };
+
+        public SKSurface SKSurface { get; }
+        public int Width { get; }
+        public int Height { get; }
+
+        private SKPaint drawingPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
+
+        public Surface(int w, int h)
+        {
+            SKSurface = CreateSurface(w, h);
+            Width = w;
+            Height = h;
+        }
+
+        public Surface(Surface original)
+        {
+            Width = original.Width;
+            Height = original.Height;
+            var newSurface = CreateSurface(Width, Height);
+            original.SKSurface.Draw(newSurface.Canvas, 0, 0, ReplacingPaint);
+            SKSurface = newSurface;
+        }
+
+        public Surface ResizeNearestNeighbor(int newW, int newH)
+        {
+            SKImage image = SKSurface.Snapshot();
+            Surface newSurface = new(newW, newH);
+            newSurface.SKSurface.Canvas.DrawImage(image, new SKRect(0, 0, newW, newH), nearestNeighborReplacingPaint);
+            return newSurface;
+        }
+
+        /// <summary>
+        /// probably doesn't work correctly
+        /// </summary>
+        public SKColor GetSRGBPixel(int x, int y)
+        {
+            var imageInfo = new SKImageInfo(1, 1, SKColorType.Bgra8888, SKAlphaType.Premul);
+            using SKBitmap bitmap = new SKBitmap(imageInfo);
+            IntPtr dstpixels = bitmap.GetPixels();
+            SKSurface.ReadPixels(imageInfo, dstpixels, imageInfo.RowBytes, x, y);
+            return bitmap.GetPixel(0, 0);
+        }
+
+        public void SetSRGBPixel(int x, int y, SKColor color)
+        {
+            drawingPaint.Color = color;
+            SKSurface.Canvas.DrawPoint(x, y, drawingPaint);
+        }
+
+        /// <summary>
+        /// probably doesn't work correctly
+        /// </summary>
+        public byte[] ToSRGBByteArray()
+        {
+            return SKSurface.Snapshot().Encode(SKEncodedImageFormat.Bmp, 100).ToArray();
+        }
+
+        private static SKSurface CreateSurface(int w, int h)
+        {
+            return SKSurface.Create(new SKImageInfo(0, 0, SKColorType.RgbaF16, SKAlphaType.Premul, SKColorSpace.CreateSrgb()));
+        }
+    }
+}

+ 35 - 45
PixiEditor/Models/Layers/Layer.cs

@@ -2,13 +2,13 @@
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Undo;
 using PixiEditor.ViewModels;
+using SkiaSharp;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
 using System.Windows;
 using System.Windows.Media;
-using System.Windows.Media.Imaging;
 
 namespace PixiEditor.Models.Layers
 {
@@ -23,7 +23,7 @@ namespace PixiEditor.Models.Layers
 
         private bool isRenaming;
         private bool isVisible = true;
-        private WriteableBitmap layerBitmap;
+        private Surface layerBitmap;
 
         private string name;
 
@@ -36,27 +36,27 @@ namespace PixiEditor.Models.Layers
         public Layer(string name)
         {
             Name = name;
-            LayerBitmap = BitmapFactory.New(0, 0);
-            Width = 0;
-            Height = 0;
+            LayerBitmap = new Surface(1, 1);
+            Width = 1;
+            Height = 1;
             LayerGuid = Guid.NewGuid();
         }
 
         public Layer(string name, int width, int height)
         {
             Name = name;
-            LayerBitmap = BitmapFactory.New(width, height);
+            LayerBitmap = new Surface(width, height);
             Width = width;
             Height = height;
             LayerGuid = Guid.NewGuid();
         }
 
-        public Layer(string name, WriteableBitmap layerBitmap)
+        public Layer(string name, Surface layerBitmap)
         {
             Name = name;
             LayerBitmap = layerBitmap;
-            Width = layerBitmap.PixelWidth;
-            Height = layerBitmap.PixelHeight;
+            Width = layerBitmap.Width;
+            Height = layerBitmap.Height;
             LayerGuid = Guid.NewGuid();
         }
 
@@ -136,7 +136,7 @@ namespace PixiEditor.Models.Layers
             }
         }
 
-        private WriteableBitmap LayerBitmap
+        public Surface LayerBitmap
         {
             get => layerBitmap;
             set
@@ -218,7 +218,7 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         public Layer Clone(bool generateNewGuid = false)
         {
-            return new Layer(Name, LayerBitmap.Clone())
+            return new Layer(Name, new Surface(LayerBitmap))
             {
                 IsVisible = IsVisible,
                 Offset = Offset,
@@ -245,7 +245,7 @@ namespace PixiEditor.Models.Layers
         /// <param name="newMaxHeight">New layer maximum height, this should be document height.</param>
         public void Resize(int width, int height, int newMaxWidth, int newMaxHeight)
         {
-            LayerBitmap = LayerBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+            LayerBitmap = LayerBitmap.ResizeNearestNeighbor(width, height);
             Width = width;
             Height = height;
             MaxWidth = newMaxWidth;
@@ -266,7 +266,7 @@ namespace PixiEditor.Models.Layers
         /// <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)
+        public SKColor GetPixelWithOffset(int x, int y)
         {
             //This does not use GetRelativePosition for better performance
             return GetPixel(x - OffsetX, y - OffsetY);
@@ -278,14 +278,14 @@ namespace PixiEditor.Models.Layers
         /// <param name="x">X coordinate.</param>
         /// <param name="y">Y Coordinate.</param>
         /// <returns>Color of pixel, if out of bounds, returns transparent pixel.</returns>
-        public Color GetPixel(int x, int y)
+        public SKColor GetPixel(int x, int y)
         {
             if (x > Width - 1 || x < 0 || y > Height - 1 || y < 0)
             {
-                return transparent;
+                return SKColors.Empty;
             }
 
-            return LayerBitmap.GetPixel(x, y);
+            return LayerBitmap.GetSRGBPixel(x, y);
         }
 
         /// <summary>
@@ -325,17 +325,14 @@ namespace PixiEditor.Models.Layers
 
             LastRelativeCoordinates = pixels.ChangedPixels;
 
-            using (BitmapContext ctx = LayerBitmap.GetBitmapContext())
+            foreach (KeyValuePair<Coordinates, Color> coords in pixels.ChangedPixels)
             {
-                foreach (KeyValuePair<Coordinates, Color> coords in pixels.ChangedPixels)
+                if (OutOfBounds(coords.Key))
                 {
-                    if (OutOfBounds(coords.Key))
-                    {
-                        continue;
-                    }
-
-                    ctx.WriteableBitmap.SetPixel(coords.Key.X, coords.Key.Y, coords.Value);
+                    continue;
                 }
+
+                LayerBitmap.SetSRGBPixel(coords.Key.X, coords.Key.Y, new SKColor(coords.Value.R, coords.Value.G, coords.Value.B, coords.Value.A));
             }
 
             ClipIfNecessary();
@@ -421,7 +418,7 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         public void Clear()
         {
-            LayerBitmap.Clear();
+            LayerBitmap.SKSurface.Canvas.Clear();
             ClipCanvas();
         }
 
@@ -430,10 +427,7 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         public byte[] ConvertBitmapToBytes()
         {
-            LayerBitmap.Lock();
-            byte[] byteArray = LayerBitmap.ToByteArray();
-            LayerBitmap.Unlock();
-            return byteArray;
+            return LayerBitmap.ToSRGBByteArray();
         }
 
         private Dictionary<Coordinates, Color> GetRelativePosition(Dictionary<Coordinates, Color> changedPixels)
@@ -561,23 +555,19 @@ namespace PixiEditor.Models.Layers
             int iteratorHeight = Height > newHeight ? newHeight : Height;
             int count = Width > newWidth ? newWidth : Width;
 
-            using (BitmapContext srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
+            Surface result = new Surface(newWidth, newHeight);
+
+            LayerBitmap.SKSurface.Draw(result.SKSurface.Canvas, offsetX - offsetXSrc, offsetY - offsetYSrc, Surface.ReplacingPaint);
+            /*for (int line = 0; line < iteratorHeight; line++)
             {
-                WriteableBitmap result = BitmapFactory.New(newWidth, newHeight);
-                using (BitmapContext destContext = result.GetBitmapContext())
-                {
-                    for (int line = 0; line < iteratorHeight; line++)
-                    {
-                        int srcOff = (((offsetYSrc + line) * Width) + offsetXSrc) * SizeOfArgb;
-                        int dstOff = (((offsetY + line) * newWidth) + offsetX) * SizeOfArgb;
-                        BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * SizeOfArgb);
-                    }
-
-                    LayerBitmap = result;
-                    Width = newWidth;
-                    Height = newHeight;
-                }
-            }
+                int srcOff = (((offsetYSrc + line) * Width) + offsetXSrc) * SizeOfArgb;
+                int dstOff = (((offsetY + line) * newWidth) + offsetX) * SizeOfArgb;
+                BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * SizeOfArgb);
+            }*/
+
+            LayerBitmap = result;
+            Width = newWidth;
+            Height = newHeight;
         }
     }
 }

+ 22 - 31
PixiEditor/Models/Position/CoordinatesCalculator.cs

@@ -1,7 +1,7 @@
-using System;
+using PixiEditor.Models.DataHolders;
+using SkiaSharp;
+using System;
 using System.Collections.Generic;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
 
 namespace PixiEditor.Models.Position
 {
@@ -71,7 +71,7 @@ namespace PixiEditor.Models.Position
         /// <summary>
         ///     Returns first pixel coordinates in bitmap that is most top left on canvas.
         /// </summary>
-        public static Coordinates FindMinEdgeNonTransparentPixel(WriteableBitmap bitmap)
+        public static Coordinates FindMinEdgeNonTransparentPixel(Surface bitmap)
         {
             return new Coordinates(FindMinXNonTransparent(bitmap), FindMinYNonTransparent(bitmap));
         }
@@ -79,20 +79,18 @@ namespace PixiEditor.Models.Position
         /// <summary>
         ///     Returns last pixel coordinates that is most bottom right.
         /// </summary>
-        public static Coordinates FindMostEdgeNonTransparentPixel(WriteableBitmap bitmap)
+        public static Coordinates FindMostEdgeNonTransparentPixel(Surface bitmap)
         {
             return new Coordinates(FindMaxXNonTransparent(bitmap), FindMaxYNonTransparent(bitmap));
         }
 
-        public static int FindMinYNonTransparent(WriteableBitmap bitmap)
+        public static int FindMinYNonTransparent(Surface bitmap)
         {
-            Color transparent = Color.FromArgb(0, 0, 0, 0);
-            using BitmapContext ctx = bitmap.GetBitmapContext(ReadWriteMode.ReadOnly);
-            for (int y = 0; y < ctx.Height; y++)
+            for (int y = 0; y < bitmap.Height; y++)
             {
-                for (int x = 0; x < ctx.Width; x++)
+                for (int x = 0; x < bitmap.Width; x++)
                 {
-                    if (ctx.WriteableBitmap.GetPixel(x, y) != transparent)
+                    if (bitmap.GetSRGBPixel(x, y) != SKColors.Transparent)
                     {
                         return y;
                     }
@@ -102,15 +100,13 @@ namespace PixiEditor.Models.Position
             return -1;
         }
 
-        public static int FindMinXNonTransparent(WriteableBitmap bitmap)
+        public static int FindMinXNonTransparent(Surface bitmap)
         {
-            Color transparent = Color.FromArgb(0, 0, 0, 0);
-            using BitmapContext ctx = bitmap.GetBitmapContext(ReadWriteMode.ReadOnly);
-            for (int x = 0; x < ctx.Width; x++)
+            for (int x = 0; x < bitmap.Width; x++)
             {
-                for (int y = 0; y < ctx.Height; y++)
+                for (int y = 0; y < bitmap.Height; y++)
                 {
-                    if (bitmap.GetPixel(x, y) != transparent)
+                    if (bitmap.GetSRGBPixel(x, y) != SKColors.Transparent)
                     {
                         return x;
                     }
@@ -120,15 +116,13 @@ namespace PixiEditor.Models.Position
             return -1;
         }
 
-        public static int FindMaxYNonTransparent(WriteableBitmap bitmap)
+        public static int FindMaxYNonTransparent(Surface bitmap)
         {
-            Color transparent = Color.FromArgb(0, 0, 0, 0);
-            using BitmapContext ctx = bitmap.GetBitmapContext(ReadWriteMode.ReadOnly);
-            for (int y = ctx.Height - 1; y >= 0; y--)
+            for (int y = bitmap.Height - 1; y >= 0; y--)
             {
-                for (int x = ctx.Width - 1; x >= 0; x--)
+                for (int x = bitmap.Width - 1; x >= 0; x--)
                 {
-                    if (bitmap.GetPixel(x, y) != transparent)
+                    if (bitmap.GetSRGBPixel(x, y) != SKColors.Transparent)
                     {
                         return y;
                     }
@@ -138,16 +132,13 @@ namespace PixiEditor.Models.Position
             return -1;
         }
 
-        public static int FindMaxXNonTransparent(WriteableBitmap bitmap)
+        public static int FindMaxXNonTransparent(Surface bitmap)
         {
-            Color transparent = Color.FromArgb(0, 0, 0, 0);
-
-            using BitmapContext ctx = bitmap.GetBitmapContext(ReadWriteMode.ReadOnly);
-            for (int x = ctx.Width - 1; x >= 0; x--)
+            for (int x = bitmap.Width - 1; x >= 0; x--)
             {
-                for (int y = ctx.Height - 1; y >= 0; y--)
+                for (int y = bitmap.Height - 1; y >= 0; y--)
                 {
-                    if (bitmap.GetPixel(x, y) != transparent)
+                    if (bitmap.GetSRGBPixel(x, y) != SKColors.Transparent)
                     {
                         return x;
                     }
@@ -157,4 +148,4 @@ namespace PixiEditor.Models.Position
             return -1;
         }
     }
-}
+}

+ 6 - 0
PixiEditorTests/ModelsTests/DataHoldersTests/SurfaceTests.cs

@@ -0,0 +1,6 @@
+namespace PixiEditorTests.ModelsTests.DataHoldersTests
+{
+    public class SurfaceTests
+    {
+    }
+}