Browse Source

Initial structure

flabbet 2 years ago
parent
commit
44b7e2a80b
51 changed files with 1101 additions and 131 deletions
  1. 9 4
      src/ChunkyImageLib/Chunk.cs
  2. 26 26
      src/ChunkyImageLib/ChunkyImage.cs
  3. 4 1
      src/ChunkyImageLib/ChunkyImageLib.csproj
  4. 1 1
      src/ChunkyImageLib/CommittedChunkStorage.cs
  5. 9 13
      src/ChunkyImageLib/DataHolders/ColorBounds.cs
  6. 1 1
      src/ChunkyImageLib/DataHolders/VecI.cs
  7. 1 1
      src/ChunkyImageLib/Operations/BresenhamLineOperation.cs
  8. 9 9
      src/ChunkyImageLib/Operations/ChunkyImageOperation.cs
  9. 4 4
      src/ChunkyImageLib/Operations/ClearPathOperation.cs
  10. 4 4
      src/ChunkyImageLib/Operations/ClearRegionOperation.cs
  11. 1 1
      src/ChunkyImageLib/Operations/EllipseOperation.cs
  12. 4 4
      src/ChunkyImageLib/Operations/ImageOperation.cs
  13. 1 1
      src/ChunkyImageLib/Operations/PathOperation.cs
  14. 1 1
      src/ChunkyImageLib/Operations/PixelOperation.cs
  15. 1 1
      src/ChunkyImageLib/Operations/PixelsOperation.cs
  16. 2 2
      src/ChunkyImageLib/Operations/RectangleOperation.cs
  17. 1 1
      src/ChunkyImageLib/Operations/ReplaceColorOperation.cs
  18. 1 1
      src/ChunkyImageLib/Operations/SkiaLineOperation.cs
  19. 19 16
      src/ChunkyImageLib/Surface.cs
  20. 1 1
      src/ChunkyImageLibTest/ChunkyImageTests.cs
  21. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs
  22. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs
  23. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelectedArea_UpdateableChange.cs
  24. 5 5
      src/PixiEditor.ChangeableDocument/Changes/Root/ResizeImage_Change.cs
  25. 13 13
      src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs
  26. 32 0
      src/PixiEditor.DrawingApi.Core/Bridge/DrawingBackendApi.cs
  27. 13 0
      src/PixiEditor.DrawingApi.Core/Bridge/IDrawingBackend.cs
  28. 11 0
      src/PixiEditor.DrawingApi.Core/Bridge/Operations/ICanvasOperations.cs
  29. 10 0
      src/PixiEditor.DrawingApi.Core/Bridge/Operations/IDrawingBackendColorOperations.cs
  30. 11 0
      src/PixiEditor.DrawingApi.Core/Bridge/Operations/IImageOperations.cs
  31. 9 0
      src/PixiEditor.DrawingApi.Core/Bridge/Operations/IPaintOperations.cs
  32. 212 0
      src/PixiEditor.DrawingApi.Core/ColorsImpl/Color.cs
  33. 400 0
      src/PixiEditor.DrawingApi.Core/ColorsImpl/ColorF.cs
  34. 15 0
      src/PixiEditor.DrawingApi.Core/Exceptions/InitializationDuplicateException.cs
  35. 13 0
      src/PixiEditor.DrawingApi.Core/PixiEditor.DrawingApi.Core.csproj
  36. 92 0
      src/PixiEditor.DrawingApi.Core/Surface/BlendMode.cs
  37. 14 0
      src/PixiEditor.DrawingApi.Core/Surface/Canvas.cs
  38. 18 0
      src/PixiEditor.DrawingApi.Core/Surface/DrawingSurface.cs
  39. 7 0
      src/PixiEditor.DrawingApi.Core/Surface/DrawingSurfaceProperties.cs
  40. 18 0
      src/PixiEditor.DrawingApi.Core/Surface/FilterQuality.cs
  41. 25 0
      src/PixiEditor.DrawingApi.Core/Surface/Image.cs
  42. 22 0
      src/PixiEditor.DrawingApi.Core/Surface/Paint.cs
  43. 9 0
      src/PixiEditor.DrawingApi.Core/Surface/PixelDataObject.cs
  44. 32 0
      src/PixiEditor.sln
  45. 1 1
      src/PixiEditor/Helpers/DocumentViewModelBuilder.cs
  46. 1 1
      src/PixiEditor/Helpers/SurfaceHelpers.cs
  47. 1 1
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  48. 1 1
      src/PixiEditor/Models/IO/Exporter.cs
  49. 1 1
      src/PixiEditor/Models/IO/Importer.cs
  50. 2 2
      src/PixiEditor/Models/Rendering/WriteableBitmapUpdater.cs
  51. 4 4
      src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

+ 9 - 4
src/ChunkyImageLib/Chunk.cs

@@ -1,11 +1,13 @@
 using ChunkyImageLib.DataHolders;
-using SkiaSharp;
+using PixiEditor.DrawingApi.Core.Surface;
+using PixiEditor.PixelE;
 
 namespace ChunkyImageLib;
 
 public class Chunk : IDisposable
 {
     private static volatile int chunkCounter = 0;
+    
     /// <summary>
     /// The number of chunks that haven't yet been returned (includes garbage collected chunks).
     /// Used in tests to make sure that all chunks are disposed.
@@ -13,14 +15,17 @@ public class Chunk : IDisposable
     public static int ChunkCounter => chunkCounter;
 
     private bool returned = false;
+    
     /// <summary>
     /// The surface of the chunk
     /// </summary>
     public Surface Surface { get; }
+    
     /// <summary>
     /// The size of the chunk
     /// </summary>
     public VecI PixelSize { get; }
+    
     /// <summary>
     /// The resolution of the chunk
     /// </summary>
@@ -50,9 +55,9 @@ public class Chunk : IDisposable
     /// </summary>
     /// <param name="pos">The destination for the <paramref name="surface"/></param>
     /// <param name="paint">The paint to use while drawing</param>
-    public void DrawOnSurface(SKSurface surface, VecI pos, SKPaint? paint = null)
+    public void DrawOnSurface(DrawingSurface surface, VecI pos, Paint? paint = null)
     {
-        surface.Canvas.DrawSurface(Surface.SkiaSurface, pos.X, pos.Y, paint);
+        surface.Canvas.DrawSurface(Surface.DrawingSurface, pos.X, pos.Y, paint);
     }
 
     /// <summary>
@@ -62,7 +67,7 @@ public class Chunk : IDisposable
     {
         if (returned)
             return;
-        Surface.SkiaSurface.Canvas.Flush();
+        Surface.DrawingSurface.Canvas.Flush();
         returned = true;
         Interlocked.Decrement(ref chunkCounter);
         ChunkPool.Instance.Push(this);

+ 26 - 26
src/ChunkyImageLib/ChunkyImage.cs

@@ -218,8 +218,8 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
                 using Chunk tempChunk = Chunk.Create(ChunkResolution.Eighth);
                 using SKPaint committedPaint = new SKPaint() { Color = committedColor, BlendMode = SKBlendMode.Src };
                 using SKPaint latestPaint = new SKPaint() { Color = latestColor, BlendMode = this.blendMode };
-                tempChunk.Surface.SkiaSurface.Canvas.DrawPoint(VecI.Zero, committedPaint);
-                tempChunk.Surface.SkiaSurface.Canvas.DrawPoint(VecI.Zero, latestPaint);
+                tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, committedPaint);
+                tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, latestPaint);
                 return tempChunk.Surface.GetSRGBPixel(VecI.Zero);
             }
         }
@@ -272,11 +272,11 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
 
             // combine with committed and then draw
             using var tempChunk = Chunk.Create(resolution);
-            tempChunk.Surface.SkiaSurface.Canvas.DrawSurface(committedChunk.Surface.SkiaSurface, 0, 0, ReplacingPaint);
+            tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(committedChunk.Surface.DrawingSurface, 0, 0, ReplacingPaint);
             blendModePaint.BlendMode = blendMode;
-            tempChunk.Surface.SkiaSurface.Canvas.DrawSurface(latestChunk.AsT2.Surface.SkiaSurface, 0, 0, blendModePaint);
+            tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(latestChunk.AsT2.Surface.DrawingSurface, 0, 0, blendModePaint);
             if (lockTransparency)
-                OperationHelper.ClampAlpha(tempChunk.Surface.SkiaSurface, committedChunk.Surface.SkiaSurface);
+                OperationHelper.ClampAlpha(tempChunk.Surface.DrawingSurface, committedChunk.Surface.DrawingSurface);
             tempChunk.DrawOnSurface(surface, pos, paint);
 
             return true;
@@ -786,13 +786,13 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
                     if (lockTransparency)
                     {
                         using Chunk tempChunk = Chunk.Create(resolution);
-                        tempChunk.Surface.SkiaSurface.Canvas.DrawSurface(maybeCommitted.Surface.SkiaSurface, 0, 0, ReplacingPaint);
-                        maybeCommitted.Surface.SkiaSurface.Canvas.DrawSurface(chunk.Surface.SkiaSurface, 0, 0, blendModePaint);
-                        OperationHelper.ClampAlpha(maybeCommitted.Surface.SkiaSurface, tempChunk.Surface.SkiaSurface);
+                        tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(maybeCommitted.Surface.DrawingSurface, 0, 0, ReplacingPaint);
+                        maybeCommitted.Surface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, 0, 0, blendModePaint);
+                        OperationHelper.ClampAlpha(maybeCommitted.Surface.DrawingSurface, tempChunk.Surface.DrawingSurface);
                     }
                     else
                     {
-                        maybeCommitted.Surface.SkiaSurface.Canvas.DrawSurface(chunk.Surface.SkiaSurface, 0, 0, blendModePaint);
+                        maybeCommitted.Surface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, 0, 0, blendModePaint);
                     }
 
                     chunk.Dispose();
@@ -912,7 +912,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             if (lockTransparency && !chunkData.IsDeleted && MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) is not null)
             {
                 var committed = GetCommittedChunk(chunkPos, resolution);
-                OperationHelper.ClampAlpha(targetChunk!.Surface.SkiaSurface, committed!.Surface.SkiaSurface);
+                OperationHelper.ClampAlpha(targetChunk!.Surface.DrawingSurface, committed!.Surface.DrawingSurface);
             }
 
             chunkData.QueueProgress = queuedOperations.Count;
@@ -936,13 +936,13 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
         }
 
         var intersection = Chunk.Create(resolution);
-        intersection.Surface.SkiaSurface.Canvas.Clear(SKColors.White);
+        intersection.Surface.DrawingSurface.Canvas.Clear(SKColors.White);
 
         foreach (var mask in activeClips)
         {
             if (mask.CommittedChunkExists(chunkPos))
             {
-                mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.SkiaSurface, VecI.Zero, ClippingPaint);
+                mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.DrawingSurface, VecI.Zero, ClippingPaint);
             }
             else
             {
@@ -974,7 +974,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
                 return chunkData.IsDeleted;
 
             if (chunkData.IsDeleted)
-                targetChunk.Surface.SkiaSurface.Canvas.Clear();
+                targetChunk.Surface.DrawingSurface.Canvas.Clear();
 
             // just regular drawing
             if (combinedRasterClips.IsT0) //Everything is visible as far as raster clips are concerned
@@ -987,14 +987,14 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             var clip = combinedRasterClips.AsT2;
 
             using var tempChunk = Chunk.Create(targetChunk.Resolution);
-            targetChunk.DrawOnSurface(tempChunk.Surface.SkiaSurface, VecI.Zero, ReplacingPaint);
+            targetChunk.DrawOnSurface(tempChunk.Surface.DrawingSurface, VecI.Zero, ReplacingPaint);
 
             CallDrawWithClip(chunkOperation, tempChunk, resolution, chunkPos);
 
-            clip.DrawOnSurface(tempChunk.Surface.SkiaSurface, VecI.Zero, ClippingPaint);
-            clip.DrawOnSurface(targetChunk.Surface.SkiaSurface, VecI.Zero, InverseClippingPaint);
+            clip.DrawOnSurface(tempChunk.Surface.DrawingSurface, VecI.Zero, ClippingPaint);
+            clip.DrawOnSurface(targetChunk.Surface.DrawingSurface, VecI.Zero, InverseClippingPaint);
 
-            tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, VecI.Zero, AddingPaint);
+            tempChunk.DrawOnSurface(targetChunk.Surface.DrawingSurface, VecI.Zero, AddingPaint);
             return false;
         }
 
@@ -1010,15 +1010,15 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
     {
         if (clippingPath is not null && !clippingPath.IsEmpty)
         {
-            int count = targetChunk.Surface.SkiaSurface.Canvas.Save();
+            int count = targetChunk.Surface.DrawingSurface.Canvas.Save();
 
             using SKPath transformedPath = new(clippingPath);
             float scale = (float)resolution.Multiplier();
             VecD trans = -chunkPos * FullChunkSize * scale;
             transformedPath.Transform(SKMatrix.CreateScaleTranslation(scale, scale, (float)trans.X, (float)trans.Y));
-            targetChunk.Surface.SkiaSurface.Canvas.ClipPath(transformedPath);
+            targetChunk.Surface.DrawingSurface.Canvas.ClipPath(transformedPath);
             operation.DrawOnChunk(targetChunk, chunkPos);
-            targetChunk.Surface.SkiaSurface.Canvas.RestoreToCount(count);
+            targetChunk.Surface.DrawingSurface.Canvas.RestoreToCount(count);
         }
         else
         {
@@ -1101,11 +1101,11 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
         if (existingFullResChunk is not null)
         {
             var newChunk = Chunk.Create(resolution);
-            newChunk.Surface.SkiaSurface.Canvas.Save();
-            newChunk.Surface.SkiaSurface.Canvas.Scale((float)resolution.Multiplier());
+            newChunk.Surface.DrawingSurface.Canvas.Save();
+            newChunk.Surface.DrawingSurface.Canvas.Scale((float)resolution.Multiplier());
 
-            newChunk.Surface.SkiaSurface.Canvas.DrawSurface(existingFullResChunk.Surface.SkiaSurface, 0, 0, SmoothReplacingPaint);
-            newChunk.Surface.SkiaSurface.Canvas.Restore();
+            newChunk.Surface.DrawingSurface.Canvas.DrawSurface(existingFullResChunk.Surface.DrawingSurface, 0, 0, SmoothReplacingPaint);
+            newChunk.Surface.DrawingSurface.Canvas.Restore();
             committedChunks[resolution][chunkPos] = newChunk;
             return newChunk;
         }
@@ -1137,7 +1137,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             if (blendMode == SKBlendMode.Src)
                 maybeCommittedAnyRes.Surface.CopyTo(newChunk.Surface);
             else
-                newChunk.Surface.SkiaSurface.Canvas.Clear();
+                newChunk.Surface.DrawingSurface.Canvas.Clear();
             latestChunks[resolution][chunkPos] = newChunk;
             return newChunk;
         }
@@ -1157,7 +1157,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
 
         // no previous chunks exist
         var newLatestChunk = Chunk.Create(resolution);
-        newLatestChunk.Surface.SkiaSurface.Canvas.Clear();
+        newLatestChunk.Surface.DrawingSurface.Canvas.Clear();
         latestChunks[resolution][chunkPos] = newLatestChunk;
         return newLatestChunk;
     }

+ 4 - 1
src/ChunkyImageLib/ChunkyImageLib.csproj

@@ -12,7 +12,10 @@
     <PackageReference Include="ComputeSharp.Core" Version="2.0.0-alpha.27" />
     <PackageReference Include="ComputeSharp.Dynamic" Version="2.0.0-alpha.27" />
     <PackageReference Include="OneOf" Version="3.0.216" />
-    <PackageReference Include="SkiaSharp" Version="2.80.3" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/ChunkyImageLib/CommittedChunkStorage.cs

@@ -14,7 +14,7 @@ public class CommittedChunkStorage : IDisposable
         foreach (var chunkPos in committedChunksToSave)
         {
             Chunk copy = Chunk.Create();
-            if (!image.DrawCommittedChunkOn(chunkPos, ChunkResolution.Full, copy.Surface.SkiaSurface, VecI.Zero, ReplacingPaint))
+            if (!image.DrawCommittedChunkOn(chunkPos, ChunkResolution.Full, copy.Surface.DrawingSurface, VecI.Zero, ReplacingPaint))
             {
                 copy.Dispose();
                 savedChunks.Add((chunkPos, null));

+ 9 - 13
src/ChunkyImageLib/DataHolders/ColorBounds.cs

@@ -1,11 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
 using ComputeSharp;
-using SkiaSharp;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.PixelE;
 
 namespace ChunkyImageLib.DataHolders;
 
@@ -20,7 +16,7 @@ public struct ColorBounds
     public float UpperB { get; set; }
     public float UpperA { get; set; }
 
-    public ColorBounds(SKColor color)
+    public ColorBounds(Color color)
     {
         static (float lower, float upper) FindInclusiveBoundaryPremul(byte channel, float alpha)
         {
@@ -36,12 +32,12 @@ public struct ColorBounds
             return (subHalf / 255f, addHalf / 255f);
         }
 
-        float a = color.Alpha / 255f;
+        float a = color.A / 255f;
 
-        (LowerR, UpperR) = FindInclusiveBoundaryPremul(color.Red, a);
-        (LowerG, UpperG) = FindInclusiveBoundaryPremul(color.Green, a);
-        (LowerB, UpperB) = FindInclusiveBoundaryPremul(color.Blue, a);
-        (LowerA, UpperA) = FindInclusiveBoundary(color.Alpha);
+        (LowerR, UpperR) = FindInclusiveBoundaryPremul(color.R, a);
+        (LowerG, UpperG) = FindInclusiveBoundaryPremul(color.G, a);
+        (LowerB, UpperB) = FindInclusiveBoundaryPremul(color.B, a);
+        (LowerA, UpperA) = FindInclusiveBoundary(color.A);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 1 - 1
src/ChunkyImageLib/DataHolders/VecI.cs

@@ -1,4 +1,4 @@
-using SkiaSharp;
+using PixiEditor.PixelE;
 
 namespace ChunkyImageLib.DataHolders;
 

+ 1 - 1
src/ChunkyImageLib/Operations/BresenhamLineOperation.cs

@@ -27,7 +27,7 @@ internal class BresenhamLineOperation : IDrawOperation
         // a hacky way to make the lines look slightly better on non full res chunks
         paint.Color = new SKColor(color.Red, color.Green, color.Blue, (byte)(color.Alpha * chunk.Resolution.Multiplier()));
 
-        var surf = chunk.Surface.SkiaSurface;
+        var surf = chunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

+ 9 - 9
src/ChunkyImageLib/Operations/ChunkyImageOperation.cs

@@ -21,7 +21,7 @@ internal class ChunkyImageOperation : IDrawOperation
 
     public void DrawOnChunk(Chunk chunk, VecI chunkPos)
     {
-        chunk.Surface.SkiaSurface.Canvas.Save();
+        chunk.Surface.DrawingSurface.Canvas.Save();
 
         {
             VecI pixelPos = chunkPos * ChunkyImage.FullChunkSize;
@@ -29,7 +29,7 @@ internal class ChunkyImageOperation : IDrawOperation
             SKRect clippingRect = SKRect.Create(
                 OperationHelper.ConvertForResolution(topLeftImageCorner - pixelPos, chunk.Resolution),
                 OperationHelper.ConvertForResolution(imageToDraw.CommittedSize, chunk.Resolution));
-            chunk.Surface.SkiaSurface.Canvas.ClipRect(clippingRect);
+            chunk.Surface.DrawingSurface.Canvas.ClipRect(clippingRect);
         }
 
         VecI chunkPixelCenter = chunkPos * ChunkyImage.FullChunkSize;
@@ -40,12 +40,12 @@ internal class ChunkyImageOperation : IDrawOperation
         VecI chunkSize = chunk.PixelSize;
         if (mirrorHorizontal)
         {
-            chunk.Surface.SkiaSurface.Canvas.Scale(-1, 1, chunkSize.X / 2f, chunkSize.Y / 2f);
+            chunk.Surface.DrawingSurface.Canvas.Scale(-1, 1, chunkSize.X / 2f, chunkSize.Y / 2f);
             chunkCenterOnImage.X = -chunkCenterOnImage.X;
         }
         if (mirrorVertical)
         {
-            chunk.Surface.SkiaSurface.Canvas.Scale(1, -1, chunkSize.X / 2f, chunkSize.Y / 2f);
+            chunk.Surface.DrawingSurface.Canvas.Scale(1, -1, chunkSize.X / 2f, chunkSize.Y / 2f);
             chunkCenterOnImage.Y = -chunkCenterOnImage.Y;
         }
 
@@ -61,7 +61,7 @@ internal class ChunkyImageOperation : IDrawOperation
         imageToDraw.DrawCommittedChunkOn(
             topLeft,
             chunk.Resolution,
-            chunk.Surface.SkiaSurface,
+            chunk.Surface.DrawingSurface,
             (VecI)((topLeft * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * chunk.Resolution.Multiplier()));
 
         VecI gridShift = pos % ChunkyImage.FullChunkSize;
@@ -70,7 +70,7 @@ internal class ChunkyImageOperation : IDrawOperation
             imageToDraw.DrawCommittedChunkOn(
             topRight,
             chunk.Resolution,
-            chunk.Surface.SkiaSurface,
+            chunk.Surface.DrawingSurface,
             (VecI)((topRight * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * chunk.Resolution.Multiplier()));
         }
         if (gridShift.Y != 0)
@@ -78,7 +78,7 @@ internal class ChunkyImageOperation : IDrawOperation
             imageToDraw.DrawCommittedChunkOn(
             bottomLeft,
             chunk.Resolution,
-            chunk.Surface.SkiaSurface,
+            chunk.Surface.DrawingSurface,
             (VecI)((bottomLeft * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * chunk.Resolution.Multiplier()));
         }
         if (gridShift.X != 0 && gridShift.Y != 0)
@@ -86,11 +86,11 @@ internal class ChunkyImageOperation : IDrawOperation
             imageToDraw.DrawCommittedChunkOn(
             bottomRight,
             chunk.Resolution,
-            chunk.Surface.SkiaSurface,
+            chunk.Surface.DrawingSurface,
             (VecI)((bottomRight * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * chunk.Resolution.Multiplier()));
         }
 
-        chunk.Surface.SkiaSurface.Canvas.Restore();
+        chunk.Surface.DrawingSurface.Canvas.Restore();
     }
 
     public HashSet<VecI> FindAffectedChunks(VecI imageSize)

+ 4 - 4
src/ChunkyImageLib/Operations/ClearPathOperation.cs

@@ -17,15 +17,15 @@ internal class ClearPathOperation : IDrawOperation
 
     public void DrawOnChunk(Chunk chunk, VecI chunkPos)
     {
-        chunk.Surface.SkiaSurface.Canvas.Save();
+        chunk.Surface.DrawingSurface.Canvas.Save();
 
         using SKPath transformedPath = new(path);
         float scale = (float)chunk.Resolution.Multiplier();
         VecD trans = -chunkPos * ChunkyImage.FullChunkSize * scale;
         transformedPath.Transform(SKMatrix.CreateScaleTranslation(scale, scale, (float)trans.X, (float)trans.Y));
-        chunk.Surface.SkiaSurface.Canvas.ClipPath(transformedPath);
-        chunk.Surface.SkiaSurface.Canvas.Clear();
-        chunk.Surface.SkiaSurface.Canvas.Restore();
+        chunk.Surface.DrawingSurface.Canvas.ClipPath(transformedPath);
+        chunk.Surface.DrawingSurface.Canvas.Clear();
+        chunk.Surface.DrawingSurface.Canvas.Restore();
     }
 
     public HashSet<VecI> FindAffectedChunks(VecI imageSize)

+ 4 - 4
src/ChunkyImageLib/Operations/ClearRegionOperation.cs

@@ -19,10 +19,10 @@ internal class ClearRegionOperation : IDrawOperation
         VecI convPos = OperationHelper.ConvertForResolution(rect.Pos, chunk.Resolution);
         VecI convSize = OperationHelper.ConvertForResolution(rect.Size, chunk.Resolution);
 
-        chunk.Surface.SkiaSurface.Canvas.Save();
-        chunk.Surface.SkiaSurface.Canvas.ClipRect(SKRect.Create(convPos - chunkPos.Multiply(chunk.PixelSize), convSize));
-        chunk.Surface.SkiaSurface.Canvas.Clear();
-        chunk.Surface.SkiaSurface.Canvas.Restore();
+        chunk.Surface.DrawingSurface.Canvas.Save();
+        chunk.Surface.DrawingSurface.Canvas.ClipRect(SKRect.Create(convPos - chunkPos.Multiply(chunk.PixelSize), convSize));
+        chunk.Surface.DrawingSurface.Canvas.Clear();
+        chunk.Surface.DrawingSurface.Canvas.Restore();
     }
 
     public HashSet<VecI> FindAffectedChunks(VecI imageSize)

+ 1 - 1
src/ChunkyImageLib/Operations/EllipseOperation.cs

@@ -53,7 +53,7 @@ internal class EllipseOperation : IDrawOperation
     {
         if (!init)
             Init();
-        var surf = chunk.Surface.SkiaSurface;
+        var surf = chunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

+ 4 - 4
src/ChunkyImageLib/Operations/ImageOperation.cs

@@ -86,10 +86,10 @@ internal class ImageOperation : IDrawOperation
         var scaleTrans = SKMatrix.CreateScaleTranslation(scaleMult, scaleMult, (float)trans.X * scaleMult, (float)trans.Y * scaleMult);
         var finalMatrix = SKMatrix.Concat(scaleTrans, transformMatrix);
 
-        chunk.Surface.SkiaSurface.Canvas.Save();
-        chunk.Surface.SkiaSurface.Canvas.SetMatrix(finalMatrix);
-        chunk.Surface.SkiaSurface.Canvas.DrawSurface(toPaint.SkiaSurface, 0, 0, customPaint);
-        chunk.Surface.SkiaSurface.Canvas.Restore();
+        chunk.Surface.DrawingSurface.Canvas.Save();
+        chunk.Surface.DrawingSurface.Canvas.SetMatrix(finalMatrix);
+        chunk.Surface.DrawingSurface.Canvas.DrawSurface(toPaint.DrawingSurface, 0, 0, customPaint);
+        chunk.Surface.DrawingSurface.Canvas.Restore();
     }
 
     public HashSet<VecI> FindAffectedChunks(VecI imageSize)

+ 1 - 1
src/ChunkyImageLib/Operations/PathOperation.cs

@@ -23,7 +23,7 @@ internal class PathOperation : IDrawOperation
     public void DrawOnChunk(Chunk chunk, VecI chunkPos)
     {
         paint.IsAntialias = chunk.Resolution != ChunkResolution.Full;
-        var surf = chunk.Surface.SkiaSurface;
+        var surf = chunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

+ 1 - 1
src/ChunkyImageLib/Operations/PixelOperation.cs

@@ -24,7 +24,7 @@ internal class PixelOperation : IDrawOperation
         // a hacky way to make the lines look slightly better on non full res chunks
         paint.Color = new SKColor(color.Red, color.Green, color.Blue, (byte)(color.Alpha * chunk.Resolution.Multiplier()));
 
-        SKSurface surf = chunk.Surface.SkiaSurface;
+        SKSurface surf = chunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

+ 1 - 1
src/ChunkyImageLib/Operations/PixelsOperation.cs

@@ -24,7 +24,7 @@ internal class PixelsOperation : IDrawOperation
         // a hacky way to make the lines look slightly better on non full res chunks
         paint.Color = new SKColor(color.Red, color.Green, color.Blue, (byte)(color.Alpha * chunk.Resolution.Multiplier()));
 
-        SKSurface surf = chunk.Surface.SkiaSurface;
+        SKSurface surf = chunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

+ 2 - 2
src/ChunkyImageLib/Operations/RectangleOperation.cs

@@ -16,9 +16,9 @@ internal class RectangleOperation : IDrawOperation
 
     public void DrawOnChunk(Chunk chunk, VecI chunkPos)
     {
-        var skiaSurf = chunk.Surface.SkiaSurface;
+        var skiaSurf = chunk.Surface.DrawingSurface;
 
-        var surf = chunk.Surface.SkiaSurface;
+        var surf = chunk.Surface.DrawingSurface;
 
         var rect = RectD.FromCenterAndSize(Data.Center, Data.Size.Abs());
         var innerRect = rect.Inflate(-Data.StrokeWidth);

+ 1 - 1
src/ChunkyImageLib/Operations/ReplaceColorOperation.cs

@@ -38,7 +38,7 @@ internal class ReplaceColorOperation : IDrawOperation
 
     private static void ReplaceColor(HlslColorBounds oldColorBounds, SKColor newColor, Chunk chunk)
     {
-        Span<UInt2> span = chunk.Surface.SkiaSurface.PeekPixels().GetPixelSpan<uint2>();
+        Span<UInt2> span = chunk.Surface.DrawingSurface.PeekPixels().GetPixelSpan<uint2>();
         using var texture = GraphicsDevice.GetDefault()
             .AllocateReadWriteTexture2D<uint2>(chunk.PixelSize.X, chunk.PixelSize.Y);
 

+ 1 - 1
src/ChunkyImageLib/Operations/SkiaLineOperation.cs

@@ -27,7 +27,7 @@ internal class SkiaLineOperation : IDrawOperation
     public void DrawOnChunk(Chunk chunk, VecI chunkPos)
     {
         paint.IsAntialias = chunk.Resolution != ChunkResolution.Full;
-        var surf = chunk.Surface.SkiaSurface;
+        var surf = chunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

+ 19 - 16
src/ChunkyImageLib/Surface.cs

@@ -1,6 +1,9 @@
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surface;
+using PixiEditor.PixelE;
 using SkiaSharp;
 
 namespace ChunkyImageLib;
@@ -9,12 +12,12 @@ public class Surface : IDisposable
 {
     private bool disposed;
     public IntPtr PixelBuffer { get; }
-    public SKSurface SkiaSurface { get; }
+    public DrawingSurface DrawingSurface { get; }
     public int BytesPerPixel { get; }
     public VecI Size { get; }
 
-    private SKPaint drawingPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
-    private SKPaint nearestNeighborReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src, FilterQuality = SKFilterQuality.None };
+    private Paint drawingPaint = new Paint() { BlendMode = BlendMode.Src };
+    private Paint nearestNeighborReplacingPaint = new() { BlendMode = BlendMode.Src, FilterQuality = FilterQuality.None };
 
     public Surface(VecI size)
     {
@@ -27,24 +30,24 @@ public class Surface : IDisposable
 
         BytesPerPixel = 8;
         PixelBuffer = CreateBuffer(size.X, size.Y, BytesPerPixel);
-        SkiaSurface = CreateSKSurface();
+        DrawingSurface = CreateDrawingSurface();
     }
 
     public Surface(Surface original) : this(original.Size)
     {
-        SkiaSurface.Canvas.DrawSurface(original.SkiaSurface, 0, 0);
+        DrawingSurface.Canvas.DrawSurface(original.DrawingSurface, 0, 0);
     }
 
     public static Surface Load(string path)
     {
         if (!File.Exists(path))
             throw new FileNotFoundException(null, path);
-        using var image = SKImage.FromEncodedData(path);
+        using var image = Image.FromEncodedData(path);
         if (image is null)
             throw new ArgumentException($"The image with path {path} couldn't be loaded");
 
         var surface = new Surface(new VecI(image.Width, image.Height));
-        surface.SkiaSurface.Canvas.DrawImage(image, 0, 0);
+        surface.DrawingSurface.Canvas.DrawImage(image, 0, 0);
 
         return surface;
     }
@@ -57,15 +60,15 @@ public class Surface : IDisposable
         {
             using SKPixmap map = new(info, new IntPtr(pointer));
             using SKSurface surface = SKSurface.Create(map);
-            surface.Draw(SkiaSurface.Canvas, 0, 0, drawingPaint);
+            surface.Draw(DrawingSurface.Canvas, 0, 0, drawingPaint);
         }
     }
 
     public Surface ResizeNearestNeighbor(VecI newSize)
     {
-        using SKImage image = SkiaSurface.Snapshot();
+        using Image image = DrawingSurface.Snapshot();
         Surface newSurface = new(newSize);
-        newSurface.SkiaSurface.Canvas.DrawImage(image, new SKRect(0, 0, newSize.X, newSize.Y), nearestNeighborReplacingPaint);
+        newSurface.DrawingSurface.Canvas.DrawImage(image, new SKRect(0, 0, newSize.X, newSize.Y), nearestNeighborReplacingPaint);
         return newSurface;
     }
 
@@ -74,24 +77,24 @@ public class Surface : IDisposable
         if (other.Size != Size)
             throw new ArgumentException("Target Surface must have the same dimensions");
         int bytesC = Size.X * Size.Y * BytesPerPixel;
-        using var pixmap = other.SkiaSurface.PeekPixels();
+        using var pixmap = other.DrawingSurface.PeekPixels();
         Buffer.MemoryCopy((void*)PixelBuffer, (void*)pixmap.GetPixels(), bytesC, bytesC);
     }
 
     /// <summary>
     /// Consider getting a pixmap from SkiaSurface.PeekPixels().GetPixels() and writing into it's buffer for bulk pixel get/set. Don't forget to dispose the pixmap afterwards.
     /// </summary>
-    public unsafe SKColor GetSRGBPixel(VecI pos)
+    public unsafe Color GetSRGBPixel(VecI pos)
     {
         Half* ptr = (Half*)(PixelBuffer + (pos.X + pos.Y * Size.X) * BytesPerPixel);
         float a = (float)ptr[3];
         return (SKColor)new SKColorF((float)ptr[0] / a, (float)ptr[1] / a, (float)ptr[2] / a, (float)ptr[3]);
     }
 
-    public void SetSRGBPixel(VecI pos, SKColor color)
+    public void SetSRGBPixel(VecI pos, Color color)
     {
         drawingPaint.Color = color;
-        SkiaSurface.Canvas.DrawPoint(pos.X, pos.Y, drawingPaint);
+        DrawingSurface.Canvas.DrawPixel(pos.X, pos.Y, drawingPaint);
     }
 
     public unsafe bool IsFullyTransparent()
@@ -110,7 +113,7 @@ public class Surface : IDisposable
     public void SaveToDesktop(string filename = "savedSurface.png")
     {
         using var final = SKSurface.Create(new SKImageInfo(Size.X, Size.Y, SKColorType.Rgba8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()));
-        final.Canvas.DrawSurface(SkiaSurface, 0, 0);
+        final.Canvas.DrawSurface(DrawingSurface, 0, 0);
         using (var snapshot = final.Snapshot())
         {
             using var stream = File.Create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), filename));
@@ -119,7 +122,7 @@ public class Surface : IDisposable
         }
     }
 
-    private SKSurface CreateSKSurface()
+    private DrawingSurface CreateDrawingSurface()
     {
         var surface = SKSurface.Create(new SKImageInfo(Size.X, Size.Y, SKColorType.RgbaF16, SKAlphaType.Premul, SKColorSpace.CreateSrgb()), PixelBuffer);
         if (surface is null)

+ 1 - 1
src/ChunkyImageLibTest/ChunkyImageTests.cs

@@ -34,7 +34,7 @@ public class ChunkyImageTests
         image.EnqueueDrawRectangle(new(new(5, 5), new(80, 80), 0, 2, SKColors.AliceBlue, SKColors.Snow));
         using (Chunk target = Chunk.Create())
         {
-            image.DrawMostUpToDateChunkOn(new(0, 0), ChunkResolution.Full, target.Surface.SkiaSurface, VecI.Zero);
+            image.DrawMostUpToDateChunkOn(new(0, 0), ChunkResolution.Full, target.Surface.DrawingSurface, VecI.Zero);
             image.CancelChanges();
             image.EnqueueResize(new(ChunkyImage.FullChunkSize * 4, ChunkyImage.FullChunkSize * 4));
             image.EnqueueDrawRectangle(new(VecD.Zero, image.CommittedSize, 0, 2, SKColors.AliceBlue, SKColors.Snow, SKBlendMode.Multiply));

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs

@@ -53,7 +53,7 @@ internal class FloodFillChunkCache : IDisposable
             return new EmptyChunk();
         Chunk chunkOnImage = Chunk.Create(ChunkResolution.Full);
 
-        if (!image.DrawMostUpToDateChunkOn(pos, ChunkResolution.Full, chunkOnImage.Surface.SkiaSurface, VecI.Zero, ReplacingPaint))
+        if (!image.DrawMostUpToDateChunkOn(pos, ChunkResolution.Full, chunkOnImage.Surface.DrawingSurface, VecI.Zero, ReplacingPaint))
         {
             chunkOnImage.Dispose();
             acquiredChunks[pos] = new EmptyChunk();

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs

@@ -66,7 +66,7 @@ internal static class FloodFillHelper
             if (!drawingChunks.ContainsKey(chunkPos))
             {
                 var chunk = Chunk.Create();
-                chunk.Surface.SkiaSurface.Canvas.Clear(SKColors.Transparent);
+                chunk.Surface.DrawingSurface.Canvas.Clear(SKColors.Transparent);
                 drawingChunks[chunkPos] = chunk;
             }
             var drawingChunk = drawingChunks[chunkPos];
@@ -77,7 +77,7 @@ internal static class FloodFillHelper
             {
                 if (colorToReplace.Alpha == 0 && !processedEmptyChunks.Contains(chunkPos))
                 {
-                    drawingChunk.Surface.SkiaSurface.Canvas.Clear(drawingColor);
+                    drawingChunk.Surface.DrawingSurface.Canvas.Clear(drawingColor);
                     for (int i = 0; i < chunkSize; i++)
                     {
                         if (chunkPos.Y > 0)
@@ -143,10 +143,10 @@ internal static class FloodFillHelper
         byte[] pixelStates = new byte[chunkSize * chunkSize];
         DrawSelection(pixelStates, selection, globalSelectionBounds, chunkPos, chunkSize);
 
-        using var refPixmap = referenceChunk.Surface.SkiaSurface.PeekPixels();
+        using var refPixmap = referenceChunk.Surface.DrawingSurface.PeekPixels();
         Half* refArray = (Half*)refPixmap.GetPixels();
 
-        using var drawPixmap = drawingChunk.Surface.SkiaSurface.PeekPixels();
+        using var drawPixmap = drawingChunk.Surface.DrawingSurface.PeekPixels();
         Half* drawArray = (Half*)drawPixmap.GetPixels();
 
         Stack<VecI> toVisit = new();

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelectedArea_UpdateableChange.cs

@@ -81,10 +81,10 @@ internal class TransformSelectedArea_UpdateableChange : UpdateableChange
 
         // draw
         Surface output = new(pathBounds.Size);
-        output.SkiaSurface.Canvas.Save();
-        output.SkiaSurface.Canvas.ClipPath(clipPath);
-        image.DrawMostUpToDateRegionOn(pathBounds, ChunkResolution.Full, output.SkiaSurface, VecI.Zero);
-        output.SkiaSurface.Canvas.Restore();
+        output.DrawingSurface.Canvas.Save();
+        output.DrawingSurface.Canvas.ClipPath(clipPath);
+        image.DrawMostUpToDateRegionOn(pathBounds, ChunkResolution.Full, output.DrawingSurface, VecI.Zero);
+        output.DrawingSurface.Canvas.Restore();
 
         return (output, pathBounds);
     }

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changes/Root/ResizeImage_Change.cs

@@ -49,7 +49,7 @@ internal class ResizeImage_Change : Change
         image.DrawMostUpToDateRegionOn(
             new(VecI.Zero, originalSize), 
             ChunkResolution.Full,
-            originalSurface.SkiaSurface,
+            originalSurface.DrawingSurface,
             VecI.Zero);
         
         bool downscaling = newSize.LengthSquared < originalSize.LengthSquared;
@@ -61,10 +61,10 @@ internal class ResizeImage_Change : Change
         };
 
         using Surface newSurface = new(newSize);
-        newSurface.SkiaSurface.Canvas.Save();
-        newSurface.SkiaSurface.Canvas.Scale(newSize.X / (float)originalSize.X, newSize.Y / (float)originalSize.Y);
-        newSurface.SkiaSurface.Canvas.DrawSurface(originalSurface.SkiaSurface, 0, 0, paint);
-        newSurface.SkiaSurface.Canvas.Restore();
+        newSurface.DrawingSurface.Canvas.Save();
+        newSurface.DrawingSurface.Canvas.Scale(newSize.X / (float)originalSize.X, newSize.Y / (float)originalSize.Y);
+        newSurface.DrawingSurface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
+        newSurface.DrawingSurface.Canvas.Restore();
         
         image.EnqueueResize(newSize);
         image.EnqueueClear();

+ 13 - 13
src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs

@@ -48,13 +48,13 @@ public static class ChunkRenderer
         context.UpdateFromMember(layer);
 
         Chunk renderingResult = Chunk.Create(resolution);
-        if (!layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, VecI.Zero, context.ReplacingPaintWithOpacity))
+        if (!layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.DrawingSurface, VecI.Zero, context.ReplacingPaintWithOpacity))
         {
             renderingResult.Dispose();
             return new EmptyChunk();
         }
 
-        if (!layer.Mask!.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, VecI.Zero, ClippingPaint))
+        if (!layer.Mask!.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.DrawingSurface, VecI.Zero, ClippingPaint))
         {
             // should pretty much never happen due to the check above, but you can never be sure with many threads
             renderingResult.Dispose();
@@ -62,9 +62,9 @@ public static class ChunkRenderer
         }
 
         if (clippingChunk.IsT2)
-            OperationHelper.ClampAlpha(renderingResult.Surface.SkiaSurface, clippingChunk.AsT2.Surface.SkiaSurface);
+            OperationHelper.ClampAlpha(renderingResult.Surface.DrawingSurface, clippingChunk.AsT2.Surface.DrawingSurface);
 
-        targetChunk.Surface.SkiaSurface.Canvas.DrawSurface(renderingResult.Surface.SkiaSurface, 0, 0, context.BlendModePaint);
+        targetChunk.Surface.DrawingSurface.Canvas.DrawSurface(renderingResult.Surface.DrawingSurface, 0, 0, context.BlendModePaint);
         return renderingResult;
     }
 
@@ -79,15 +79,15 @@ public static class ChunkRenderer
 
         context.UpdateFromMember(layer);
         Chunk renderingResult = Chunk.Create(resolution);
-        if (!layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, VecI.Zero, context.ReplacingPaintWithOpacity))
+        if (!layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.DrawingSurface, VecI.Zero, context.ReplacingPaintWithOpacity))
         {
             renderingResult.Dispose();
             return new EmptyChunk();
         }
 
         if (clippingChunk.IsT2)
-            OperationHelper.ClampAlpha(renderingResult.Surface.SkiaSurface, clippingChunk.AsT2.Surface.SkiaSurface);
-        targetChunk.Surface.SkiaSurface.Canvas.DrawSurface(renderingResult.Surface.SkiaSurface, 0, 0, context.BlendModePaint);
+            OperationHelper.ClampAlpha(renderingResult.Surface.DrawingSurface, clippingChunk.AsT2.Surface.DrawingSurface);
+        targetChunk.Surface.DrawingSurface.Canvas.DrawSurface(renderingResult.Surface.DrawingSurface, 0, 0, context.BlendModePaint);
         return renderingResult;
     }
 
@@ -112,7 +112,7 @@ public static class ChunkRenderer
             return;
         }
         context.UpdateFromMember(layer);
-        layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, targetChunk.Surface.SkiaSurface, VecI.Zero, context.BlendModeOpacityPaint);
+        layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, targetChunk.Surface.DrawingSurface, VecI.Zero, context.BlendModeOpacityPaint);
     }
 
     private static OneOf<EmptyChunk, Chunk> RenderFolder(
@@ -140,7 +140,7 @@ public static class ChunkRenderer
 
         if (folder.Mask is not null && folder.MaskIsVisible)
         {
-            if (!folder.Mask.DrawMostUpToDateChunkOn(chunkPos, resolution, contents.Surface.SkiaSurface, VecI.Zero, ClippingPaint))
+            if (!folder.Mask.DrawMostUpToDateChunkOn(chunkPos, resolution, contents.Surface.DrawingSurface, VecI.Zero, ClippingPaint))
             {
                 // this shouldn't really happen due to the check above, but another thread could edit the mask in the meantime
                 contents.Dispose();
@@ -149,10 +149,10 @@ public static class ChunkRenderer
         }
 
         if (clippingChunk.IsT2)
-            OperationHelper.ClampAlpha(contents.Surface.SkiaSurface, clippingChunk.AsT2.Surface.SkiaSurface);
+            OperationHelper.ClampAlpha(contents.Surface.DrawingSurface, clippingChunk.AsT2.Surface.DrawingSurface);
         context.UpdateFromMember(folder);
-        contents.Surface.SkiaSurface.Canvas.DrawSurface(contents.Surface.SkiaSurface, 0, 0, context.ReplacingPaintWithOpacity);
-        targetChunk.Surface.SkiaSurface.Canvas.DrawSurface(contents.Surface.SkiaSurface, 0, 0, context.BlendModePaint);
+        contents.Surface.DrawingSurface.Canvas.DrawSurface(contents.Surface.DrawingSurface, 0, 0, context.ReplacingPaintWithOpacity);
+        targetChunk.Surface.DrawingSurface.Canvas.DrawSurface(contents.Surface.DrawingSurface, 0, 0, context.BlendModePaint);
 
         return contents;
     }
@@ -168,7 +168,7 @@ public static class ChunkRenderer
             return new EmptyChunk();
 
         Chunk targetChunk = Chunk.Create(resolution);
-        targetChunk.Surface.SkiaSurface.Canvas.Clear();
+        targetChunk.Surface.DrawingSurface.Canvas.Clear();
 
         OneOf<FilledChunk, EmptyChunk, Chunk> clippingChunk = new FilledChunk();
         for (int i = 0; i < folder.Children.Count; i++)

+ 32 - 0
src/PixiEditor.DrawingApi.Core/Bridge/DrawingBackendApi.cs

@@ -0,0 +1,32 @@
+using System;
+using PixiEditor.DrawingApi.Core.Exceptions;
+
+namespace PixiEditor.DrawingApi.Core.Bridge
+{
+    public class DrawingBackendApi
+    {
+        private static IDrawingBackend _current;
+
+        public static IDrawingBackend Current
+        {
+            get
+            {
+                if (_current == null)
+                    throw new NullReferenceException("Either drawing backend was not yet initialized or reference was somehow lost.");
+
+                return _current;
+            }
+        }
+        
+        public void SetupBackend(IDrawingBackend backend)
+        {
+            if (Current != null)
+            {
+                throw new InitializationDuplicateException("Drawing backend was already initialized.");
+            }
+            
+            _current = backend;
+            backend.Setup();
+        }
+    }
+}

+ 13 - 0
src/PixiEditor.DrawingApi.Core/Bridge/IDrawingBackend.cs

@@ -0,0 +1,13 @@
+using PixiEditor.DrawingApi.Core.Bridge.Operations;
+
+namespace PixiEditor.DrawingApi.Core.Bridge
+{
+    public interface IDrawingBackend
+    {
+        public void Setup();
+        public IDrawingBackendColorOperations ColorOperations { get; }
+        public IImageOperations ImageOperations { get; }
+        public ICanvasOperations CanvasOperations { get; }
+        public IPaintOperations PaintOperations { get; set; }
+    }
+}

+ 11 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/ICanvasOperations.cs

@@ -0,0 +1,11 @@
+using PixiEditor.DrawingApi.Core.Surface;
+
+namespace PixiEditor.DrawingApi.Core.Bridge.Operations
+{
+    public interface ICanvasOperations
+    {
+        public void DrawPixel(int posX, int posY, Paint drawingPaint);
+        public void DrawSurface(DrawingSurface drawingSurface, int x, int y);
+        public void DrawImage(Image image, int x, int y);
+    }
+}

+ 10 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/IDrawingBackendColorOperations.cs

@@ -0,0 +1,10 @@
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+
+namespace PixiEditor.DrawingApi.Core.Bridge.Operations
+{
+    public interface IDrawingBackendColorOperations
+    {
+        public ColorF ColorToColorF(uint colorValue);
+        public Color ColorFToColor(ColorF color);
+    }
+}

+ 11 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/IImageOperations.cs

@@ -0,0 +1,11 @@
+using PixiEditor.DrawingApi.Core.Surface;
+
+namespace PixiEditor.DrawingApi.Core.Bridge.Operations
+{
+    public interface IImageOperations
+    {
+        public Image Snapshot(DrawingSurface drawingSurface);
+        public void DisposeImage(Image image);
+        public Image FromEncodedData(string path);
+    }
+}

+ 9 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/IPaintOperations.cs

@@ -0,0 +1,9 @@
+using PixiEditor.DrawingApi.Core.Surface;
+
+namespace PixiEditor.DrawingApi.Core.Bridge.Operations
+{
+    public interface IPaintOperations
+    {
+        public void Dispose(Paint paint);
+    }
+}

+ 212 - 0
src/PixiEditor.DrawingApi.Core/ColorsImpl/Color.cs

@@ -0,0 +1,212 @@
+using System;
+using System.Globalization;
+
+namespace PixiEditor.DrawingApi.Core.ColorsImpl
+{
+   /// <summary>32-bit ARGB unpremultiplied color value.</summary>
+  /// <remarks>The color components are always in a known order.</remarks>
+  public readonly struct Color : IEquatable<Color>
+  {
+    /// <summary>Gets an "empty" color, with zero for all the components.</summary>
+    public static readonly Color Empty = default;
+    private readonly uint _colorValue;
+
+    public Color(uint value) => this._colorValue = value;
+    public Color(byte red, byte green, byte blue, byte alpha) => this._colorValue = (uint) ((int) alpha << 24 | (int) red << 16 | (int) green << 8) | (uint) blue;
+
+    public Color(byte red, byte green, byte blue) => this._colorValue = (uint) (-16777216 | (int) red << 16 | (int) green << 8) | (uint) blue;
+
+    /// <param name="red">The new red component.</param>
+    /// <summary>Returns a new color based on this current instance, but with the new red channel value.</summary>
+    public Color WithRed(byte red) => new Color(red, this.G, this.B, this.A);
+
+    /// <param name="green">The new green component.</param>
+    /// <summary>Returns a new color based on this current instance, but with the new green channel value.</summary>
+    public Color WithGreen(byte green) => new Color(this.R, green, this.B, this.A);
+
+    /// <param name="blue">The new blue component.</param>
+    /// <summary>Returns a new color based on this current instance, but with the new blue channel value.</summary>
+    public Color WithBlue(byte blue) => new Color(this.R, this.G, blue, this.A);
+
+    /// <param name="alpha">The new alpha component.</param>
+    /// <summary>Returns a new color based on this current instance, but with the new alpha channel value.</summary>
+    public Color WithAlpha(byte alpha) => new Color(this.R, this.G, this.B, alpha);
+
+    /// <summary>Gets the alpha component of the color.</summary>
+    /// <value />
+    public byte A => (byte)(this._colorValue >> 24 & (uint) byte.MaxValue);
+
+    /// <summary>Gets the red component of the color.</summary>
+    /// <value />
+    public byte R => (byte)(this._colorValue >> 16 & (uint) byte.MaxValue);
+
+    /// <summary>Gets the green component of the color.</summary>
+    /// <value />
+    public byte G => (byte)(this._colorValue >> 8 & (uint) byte.MaxValue);
+
+    /// <summary>Gets the blue component of the color.</summary>
+    /// <value />
+    public byte B => (byte)(this._colorValue & (uint) byte.MaxValue);
+
+    /// <summary>Gets the hue value.</summary>
+    /// <value />
+    public float Hue
+    {
+      get
+      {
+        float h;
+        this.ToHsv(out h, out float _, out float _);
+        return h;
+      }
+    }
+
+    /// <param name="h">The hue value.</param>
+    /// <param name="s">The saturation value.</param>
+    /// <param name="l">The lightness/luminosity value.</param>
+    /// <param name="a">The alpha value.</param>
+    /// <summary>Creates a color from the specified hue, saturation, lightness/luminosity and alpha values.</summary>
+    /// <returns>The new <see cref="T:SkiaSharp.Color" /> instance.</returns>
+    public static Color FromHsl(float h, float s, float l, byte a = 255)
+    {
+      ColorF ColorF = ColorF.FromHsl(h, s, l);
+      return new Color((byte) (ColorF.R * (float) byte.MaxValue), (byte) (ColorF.G * (float) byte.MaxValue), (byte) (ColorF.B * (float) byte.MaxValue), a);
+    }
+
+    /// <param name="h">The hue value.</param>
+    /// <param name="s">The saturation value.</param>
+    /// <param name="v">The value/brightness value.</param>
+    /// <param name="a">The alpha value.</param>
+    /// <summary>Creates a color from the specified hue, saturation, value/brightness and alpha values.</summary>
+    /// <returns>The new <see cref="T:SkiaSharp.Color" /> instance.</returns>
+    public static Color FromHsv(float h, float s, float v, byte a = 255)
+    {
+      ColorF ColorF = ColorF.FromHsv(h, s, v);
+      return new Color((byte) (ColorF.R * (float) byte.MaxValue), (byte) (ColorF.G * (float) byte.MaxValue), (byte) (ColorF.B * (float) byte.MaxValue), a);
+    }
+
+    /// <param name="h">The hue value.</param>
+    /// <param name="s">The saturation value.</param>
+    /// <param name="l">The lightness/luminosity value.</param>
+    /// <summary>Converts the current color into it's hue, saturation and lightness/luminosity values.</summary>
+    /// <remarks>The alpha value is separate from the HSL calculation and will always be the same as <see cref="P:SkiaSharp.Color.Alpha" />.</remarks>
+    public void ToHsl(out float h, out float s, out float l) => new ColorF((float) this.R / (float) byte.MaxValue, (float) this.G / (float) byte.MaxValue, (float) this.B / (float) byte.MaxValue).ToHsl(out h, out s, out l);
+
+    /// <param name="h">The hue value.</param>
+    /// <param name="s">The saturation value.</param>
+    /// <param name="v">The value/brightness value.</param>
+    /// <summary>Converts the current color into it's hue, saturation and value/brightness values.</summary>
+    /// <remarks>The alpha value is separate from the HSV/HSB calculation and will always be the same as <see cref="P:SkiaSharp.Color.Alpha" />.</remarks>
+    public void ToHsv(out float h, out float s, out float v) => new ColorF((float) this.R / (float) byte.MaxValue, (float) this.G / (float) byte.MaxValue, (float) this.B / (float) byte.MaxValue).ToHsv(out h, out s, out v);
+
+    /// <summary>Returns the color as a string in the format: #AARRGGBB.</summary>
+    /// <returns />
+    /// <remarks />
+    public override string ToString() =>
+        $"#{(object)this.A:x2}{(object)this.R:x2}{(object)this.G:x2}{(object)this.B:x2}";
+
+    /// <param name="obj">The color to compare with the current color.</param>
+    /// <summary>Determines whether the specified object is equal to the current object.</summary>
+    /// <returns>Returns <see langword="true" /> if the specified object is equal to the current object; otherwise, <see langword="false" />.</returns>
+    public bool Equals(Color obj) => (int) obj._colorValue == (int) this._colorValue;
+
+    /// <param name="other">The object to compare with the current object.</param>
+    /// <summary>Determines whether the specified object is equal to the current object.</summary>
+    /// <returns>Returns <see langword="true" /> if the specified object is equal to the current object; otherwise, <see langword="false" />.</returns>
+    public override bool Equals(object other) => other is Color Color && this.Equals(Color);
+
+    /// <param name="left">The first color to compare.</param>
+    /// <param name="right">The second color to compare.</param>
+    /// <summary>Indicates whether two <see cref="T:SkiaSharp.Color" /> objects are equal.</summary>
+    /// <returns>Returns <see langword="true" /> if <paramref name="left" /> is equal to <paramref name="right" />, otherwise <see langword="false" />.</returns>
+    public static bool operator ==(Color left, Color right) => left.Equals(right);
+
+    /// <param name="left">The first color to compare.</param>
+    /// <param name="right">The second color to compare.</param>
+    /// <summary>Indicates whether two <see cref="T:SkiaSharp.Color" /> objects are different.</summary>
+    /// <returns>Returns <see langword="true" /> if <paramref name="left" /> is not equal to <paramref name="right" />, otherwise <see langword="false" />.</returns>
+    public static bool operator !=(Color left, Color right) => !left.Equals(right);
+
+    /// <summary>Serves as the default hash function.</summary>
+    /// <returns>Returns a hash code for the current object.</returns>
+    public override int GetHashCode() => this._colorValue.GetHashCode();
+
+    /// <param name="color">The UInt32 representation of a color.</param>
+    /// <summary>Converts a UInt32 to a <see cref="T:SkiaSharp.Color" />.</summary>
+    /// <returns>The new <see cref="T:SkiaSharp.Color" /> instance.</returns>
+    public static implicit operator Color(uint color) => new Color(color);
+
+    /// <param name="color">The color to convert.</param>
+    /// <summary>Converts a <see cref="T:SkiaSharp.Color" /> to a UInt32.</summary>
+    /// <returns>The UInt32 value for the color.</returns>
+    public static explicit operator uint(Color color) => color._colorValue;
+
+    /// <param name="hexString">The hexadecimal string representation of a color.</param>
+    /// <summary>Converts the hexadecimal string representation of a color to its <see cref="T:SkiaSharp.Color" /> equivalent.</summary>
+    /// <returns>The new <see cref="T:SkiaSharp.Color" /> instance.</returns>
+    /// <remarks>This method can parse a string in the forms with or without a preceding '#' character: AARRGGB, RRGGBB, ARGB, RGB.</remarks>
+    public static Color Parse(string hexString)
+    {
+      Color color;
+      if (!Color.TryParse(hexString, out color))
+        throw new ArgumentException("Invalid hexadecimal color string.", nameof (hexString));
+      return color;
+    }
+
+    /// <param name="hexString">The hexadecimal string representation of a color.</param>
+    /// <param name="color">The new <see cref="T:SkiaSharp.Color" /> instance.</param>
+    /// <summary>Converts the hexadecimal string representation of a color to its <see cref="T:SkiaSharp.Color" /> equivalent.</summary>
+    /// <returns>Returns true if the conversion was successful, otherwise false.</returns>
+    /// <remarks>This method can parse a string in the forms with or without a preceding '#' character: AARRGGB, RRGGBB, ARGB, RGB.</remarks>
+    public static bool TryParse(string hexString, out Color color)
+    {
+      if (string.IsNullOrWhiteSpace(hexString))
+      {
+        color = Color.Empty;
+        return false;
+      }
+      hexString = hexString.Trim().ToUpperInvariant();
+      if (hexString[0] == '#')
+        hexString = hexString.Substring(1);
+      int length = hexString.Length;
+      switch (length)
+      {
+        case 3:
+        case 4:
+          byte result1;
+          if (length == 4)
+          {
+            if (!byte.TryParse(hexString[length - 4].ToString() + (object) hexString[length - 4], NumberStyles.HexNumber, (IFormatProvider)CultureInfo.InvariantCulture, out result1))
+            {
+              color = Color.Empty;
+              return false;
+            }
+          }
+          else
+          {
+              result1 = byte.MaxValue;
+          }
+
+          if (!byte.TryParse(hexString[length - 3].ToString() + (object)hexString[length - 3], NumberStyles.HexNumber, (IFormatProvider)CultureInfo.InvariantCulture, out var result2) || !byte.TryParse(hexString[length - 2].ToString() + (object) hexString[length - 2], NumberStyles.HexNumber, (IFormatProvider) CultureInfo.InvariantCulture, out var result3) || !byte.TryParse(hexString[length - 1].ToString() + (object) hexString[length - 1], NumberStyles.HexNumber, (IFormatProvider) CultureInfo.InvariantCulture, out var result4))
+          {
+            color = Color.Empty;
+            return false;
+          }
+          color = new Color(result2, result3, result4, result1);
+          return true;
+        case 6:
+        case 8: if (!uint.TryParse(hexString, NumberStyles.HexNumber, (IFormatProvider)CultureInfo.InvariantCulture, out var result5))
+          {
+            color = Color.Empty;
+            return false;
+          }
+          color = (Color)result5;
+          if (length == 6)
+            color = color.WithAlpha(byte.MaxValue);
+          return true;
+        default:
+          color = Empty;
+          return false;
+      }
+    }
+  }
+}

+ 400 - 0
src/PixiEditor.DrawingApi.Core/ColorsImpl/ColorF.cs

@@ -0,0 +1,400 @@
+using System;
+using PixiEditor.DrawingApi.Core.Bridge;
+
+namespace PixiEditor.DrawingApi.Core.ColorsImpl
+{
+    public readonly struct ColorF : IEquatable<ColorF>
+    {
+        private const float Epsilon = 0.001f;
+
+        /// <summary>Gets an "empty" color, with zero for all the components.</summary>
+        public static readonly ColorF Empty;
+        
+        private readonly float _fR;
+        private readonly float _fG;
+        private readonly float _fB;
+        private readonly float _fA;
+
+        public ColorF(float r, float g, float b)
+        {
+            _fR = r;
+            _fG = g;
+            _fB = b;
+            _fA = 1f;
+        }
+
+        public ColorF(float r, float g, float b, float a)
+        {
+            _fR = r;
+            _fG = g;
+            _fB = b;
+            _fA = a;
+        }
+
+        /// <summary>Gets the hue value.</summary>
+        /// <value />
+        public float Hue
+        {
+            get
+            {
+                float h;
+                ToHsv(out h, out var _, out var _);
+                return h;
+            }
+        }
+
+        /// <summary>Gets the red component of the color.</summary>
+        /// <value />
+        public float R => _fR;
+
+        /// <summary>Gets the green component of the color.</summary>
+        /// <value />
+
+        public float G => _fG;
+
+        /// <summary>Gets the blue component of the color.</summary>
+        /// <value />
+
+        public float B => _fB;
+
+        /// <summary>Gets the alpha component of the color.</summary>
+        /// <value />
+
+        public float A => _fA;
+
+        /// <param name="obj">The color to compare with the current color.</param>
+        /// <summary>Determines whether the specified object is equal to the current object.</summary>
+        /// <returns>
+        ///     Returns <see langword="true" /> if the specified object is equal to the current object; otherwise,
+        ///     <see langword="false" />.
+        /// </returns>
+        /// 
+        public bool Equals(ColorF obj)
+        {
+            return R == (double)obj._fR && G == (double)obj._fG && B == (double)obj._fB &&
+                   A == (double)obj._fA;
+        }
+
+        /// <param name="red">The new red component.</param>
+        /// <summary>Returns a new color based on this current instance, but with the new red channel value.</summary>
+        /// <returns />
+        public ColorF WithRed(float red)
+        {
+            return new ColorF(red, G, B, A);
+        }
+
+        /// <param name="green">The new green component.</param>
+        /// <summary>Returns a new color based on this current instance, but with the new green channel value.</summary>
+        /// <returns />
+        public ColorF WithGreen(float green)
+        {
+            return new ColorF(R, green, B, A);
+        }
+
+        /// <param name="blue">The new blue component.</param>
+        /// <summary>Returns a new color based on this current instance, but with the new blue channel value.</summary>
+        public ColorF WithBlue(float blue)
+        {
+            return new ColorF(R, G, blue, A);
+        }
+
+        /// <param name="alpha">The new alpha component.</param>
+        /// <summary>Returns a new color based on this current instance, but with the new alpha channel value.</summary>
+        public ColorF WithAlpha(float alpha)
+        {
+            return new ColorF(R, G, B, alpha);
+        }
+
+        /// <summary>Clamp the color components in the range [0..1].</summary>
+        /// <returns>Returns the clamped color.</returns>
+        public ColorF Clamp()
+        {
+            return new ColorF(Clamp(R), Clamp(G), Clamp(B), Clamp(A));
+
+            static float Clamp(float v)
+            {
+                if (v > 1.0)
+                {
+                    return 1f;
+                }
+
+                return v < 0.0 ? 0.0f : v;
+            }
+        }
+
+        /// <param name="h">The hue value.</param>
+        /// <param name="s">The saturation value.</param>
+        /// <param name="l">The lightness/luminosity value.</param>
+        /// <param name="a">The alpha value.</param>
+        /// <summary>Creates a color from the specified hue, saturation, lightness/luminosity and alpha values.</summary>
+        /// <returns>The new <see cref="T:DrawingApiCore.ColorF" /> instance.</returns>
+        public static ColorF FromHsl(float h, float s, float l, float a = 1f)
+        {
+            h /= 360f;
+            s /= 100f;
+            l /= 100f;
+            var red = l;
+            var green = l;
+            var blue = l;
+            if (Math.Abs(s) > 1.0 / 1000.0)
+            {
+                var v2 = l >= 0.5 ? (float)(l + (double)s - (s * (double)l)) : l * (1f + s);
+                var v1 = (2f * l) - v2;
+                red = HueToRgb(v1, v2, h + 0.33333334f);
+                green = HueToRgb(v1, v2, h);
+                blue = HueToRgb(v1, v2, h - 0.33333334f);
+            }
+
+            return new ColorF(red, green, blue, a);
+        }
+
+        private static float HueToRgb(float v1, float v2, float vH)
+        {
+            if (vH < 0.0)
+            {
+                ++vH;
+            }
+
+            if (vH > 1.0)
+            {
+                --vH;
+            }
+
+            if (6.0 * vH < 1.0)
+            {
+                return v1 + ((float)((v2 - (double)v1) * 6.0) * vH);
+            }
+
+            if (2.0 * vH < 1.0)
+            {
+                return v2;
+            }
+
+            return 3.0 * vH < 2.0 ? v1 + (float)((v2 - (double)v1) * (0.6666666865348816 - vH) * 6.0) : v1;
+        }
+
+        /// <param name="h">The hue value.</param>
+        /// <param name="s">The saturation value.</param>
+        /// <param name="v">The value/brightness value.</param>
+        /// <param name="a">The alpha value.</param>
+        /// <summary>Creates a color from the specified hue, saturation, value/brightness and alpha values.</summary>
+        /// <returns>The new <see cref="T:DrawingApiCore.ColorF" /> instance.</returns>
+        public static ColorF FromHsv(float h, float s, float v, float a = 1f)
+        {
+            h /= 360f;
+            s /= 100f;
+            v /= 100f;
+            var red = v;
+            var green = v;
+            var blue = v;
+            if (Math.Abs(s) > 1.0 / 1000.0)
+            {
+                h *= 6f;
+                if (Math.Abs(h - 6f) < 1.0 / 1000.0)
+                {
+                    h = 0.0f;
+                }
+
+                var num1 = (int)h;
+                var num2 = v * (1f - s);
+                var num3 = v * (float)(1.0 - (s * (h - (double)num1)));
+                var num4 = v * (float)(1.0 - (s * (1.0 - (h - (double)num1))));
+                switch (num1)
+                {
+                    case 0:
+                        red = v;
+                        green = num4;
+                        blue = num2;
+                        break;
+                    case 1:
+                        red = num3;
+                        green = v;
+                        blue = num2;
+                        break;
+                    case 2:
+                        red = num2;
+                        green = v;
+                        blue = num4;
+                        break;
+                    case 3:
+                        red = num2;
+                        green = num3;
+                        blue = v;
+                        break;
+                    case 4:
+                        red = num4;
+                        green = num2;
+                        blue = v;
+                        break;
+                    default:
+                        red = v;
+                        green = num2;
+                        blue = num3;
+                        break;
+                }
+            }
+
+            return new ColorF(red, green, blue, a);
+        }
+
+        /// <param name="h">The hue value.</param>
+        /// <param name="s">The saturation value.</param>
+        /// <param name="l">The lightness/luminosity value.</param>
+        /// <summary>Converts the current color into it's hue, saturation and lightness/luminosity values.</summary>
+        /// <remarks>
+        ///     The alpha value is separate from the HSL calculation and will always be the same as
+        ///     <see cref="P:DrawingApiCore.ColorF.Alpha" />.
+        /// </remarks>
+        public void ToHsl(out float h, out float s, out float l)
+        {
+            var fR = R;
+            var fG = G;
+            var fB = B;
+            var num1 = Math.Min(Math.Min(fR, fG), fB);
+            var num2 = Math.Max(Math.Max(fR, fG), fB);
+            var num3 = num2 - num1;
+            h = 0.0f;
+            s = 0.0f;
+            l = (float)((num2 + (double)num1) / 2.0);
+            if (Math.Abs(num3) > 1.0 / 1000.0)
+            {
+                s = l >= 0.5 ? num3 / (2f - num2 - num1) : num3 / (num2 + num1);
+                var num4 = (float)(((num2 - (double)fR) / 6.0) + (num3 / 2.0)) / num3;
+                var num5 = (float)(((num2 - (double)fG) / 6.0) + (num3 / 2.0)) / num3;
+                var num6 = (float)(((num2 - (double)fB) / 6.0) + (num3 / 2.0)) / num3;
+                h = Math.Abs(fR - num2) >= 1.0 / 1000.0
+                    ? Math.Abs(fG - num2) >= 1.0 / 1000.0 ? 0.6666667f + num5 - num4 : 0.33333334f + num4 - num6
+                    : num6 - num5;
+                if (h < 0.0)
+                {
+                    ++h;
+                }
+
+                if (h > 1.0)
+                {
+                    --h;
+                }
+            }
+
+            h *= 360f;
+            s *= 100f;
+            l *= 100f;
+        }
+
+        /// <param name="h">The hue value.</param>
+        /// <param name="s">The saturation value.</param>
+        /// <param name="v">The value/brightness value.</param>
+        /// <summary>Converts the current color into it's hue, saturation and value/brightness values.</summary>
+        /// <remarks>
+        ///     The alpha value is separate from the HSV/HSB calculation and will always be the same as
+        ///     <see cref="P:DrawingApiCore.ColorF.Alpha" />.
+        /// </remarks>
+        public void ToHsv(out float h, out float s, out float v)
+        {
+            var fR = R;
+            var fG = G;
+            var fB = B;
+            var num1 = Math.Min(Math.Min(fR, fG), fB);
+            var num2 = Math.Max(Math.Max(fR, fG), fB);
+            var num3 = num2 - num1;
+            h = 0.0f;
+            s = 0.0f;
+            v = num2;
+            if (Math.Abs(num3) > 1.0 / 1000.0)
+            {
+                s = num3 / num2;
+                var num4 = (float)(((num2 - (double)fR) / 6.0) + (num3 / 2.0)) / num3;
+                var num5 = (float)(((num2 - (double)fG) / 6.0) + (num3 / 2.0)) / num3;
+                var num6 = (float)(((num2 - (double)fB) / 6.0) + (num3 / 2.0)) / num3;
+                h = Math.Abs(fR - num2) >= 1.0 / 1000.0
+                    ? Math.Abs(fG - num2) >= 1.0 / 1000.0 ? 0.6666667f + num5 - num4 : 0.33333334f + num4 - num6
+                    : num6 - num5;
+                if (h < 0.0)
+                {
+                    ++h;
+                }
+
+                if (h > 1.0)
+                {
+                    --h;
+                }
+            }
+
+            h *= 360f;
+            s *= 100f;
+            v *= 100f;
+        }
+
+        /// <summary>Returns the color as a string in the format: #AARRGGBB.</summary>
+        /// <remarks>As a result of converting a floating-point color to an integer color, some data loss will occur.</remarks>
+        public override string ToString()
+        {
+            return ((Color)this).ToString();
+        }
+
+        /// <param name="color">The <see cref="T:DrawingApiCore.SKColor" />.</param>
+        /// <summary>Converts a <see cref="T:DrawingApiCore.SKColor" /> to a <see cref="T:DrawingApiCore.ColorF" />.</summary>
+        /// <returns>The new <see cref="T:DrawingApiCore.ColorF" /> instance.</returns>
+        public static implicit operator ColorF(Color color)
+        {
+            return DrawingBackendApi.Current.ColorOperations.ColorToColorF((uint)color);
+        }
+
+        /// <param name="color">The color to convert.</param>
+        /// <summary>Converts a <see cref="T:DrawingApiCore.ColorF" /> to a <see cref="T:DrawingApiCore.SKColor" />.</summary>
+        /// <returns>The <see cref="T:DrawingApiCore.SKColor" />.</returns>
+        /// <remarks>As a result of converting a floating-point color to an integer color, some data loss will occur.</remarks>
+        public static explicit operator Color(ColorF color)
+        {
+            return DrawingBackendApi.Current.ColorOperations.ColorFToColor(color);
+        }
+
+        /// <param name="obj">The object to compare with the current object.</param>
+        /// <summary>Determines whether the specified object is equal to the current object.</summary>
+        /// <returns>
+        ///     Returns <see langword="true" /> if the specified object is equal to the current object; otherwise,
+        ///     <see langword="false" />.
+        /// </returns>
+        public override bool Equals(object obj)
+        {
+            return obj is ColorF colorF && Equals(colorF);
+        }
+
+        /// <param name="left">The first color to compare.</param>
+        /// <param name="right">The second color to compare.</param>
+        /// <summary>Indicates whether two <see cref="T:DrawingApiCore.ColorF" /> objects are equal.</summary>
+        /// <returns>
+        ///     Returns <see langword="true" /> if <paramref name="left" /> is equal to <paramref name="right" />, otherwise
+        ///     <see langword="false" />.
+        /// </returns>
+        public static bool operator ==(ColorF left, ColorF right)
+        {
+            return left.Equals(right);
+        }
+
+        /// <param name="left">The first color to compare.</param>
+        /// <param name="right">The second color to compare.</param>
+        /// <summary>Indicates whether two <see cref="T:DrawingApiCore.ColorF" /> objects are different.</summary>
+        /// <returns>
+        ///     Returns <see langword="true" /> if <paramref name="left" /> is not equal to <paramref name="right" />,
+        ///     otherwise <see langword="false" />.
+        /// </returns>
+        public static bool operator !=(ColorF left, ColorF right)
+        {
+            return !left.Equals(right);
+        }
+
+        /// <summary>Serves as the default hash function.</summary>
+        /// <returns>Returns a hash code for the current object.</returns>
+        public override int GetHashCode()
+        {
+            var hashCode = new HashCode();
+            hashCode.Add(R);
+            hashCode.Add(G);
+            hashCode.Add(B);
+            hashCode.Add(A);
+            return hashCode.ToHashCode();
+        }
+    }
+}
+

+ 15 - 0
src/PixiEditor.DrawingApi.Core/Exceptions/InitializationDuplicateException.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Core.Exceptions
+{
+    public class InitializationDuplicateException : Exception
+    {
+        public InitializationDuplicateException()
+        {
+        }
+
+        public InitializationDuplicateException(string message) : base(message)
+        {
+        }
+    }
+}

+ 13 - 0
src/PixiEditor.DrawingApi.Core/PixiEditor.DrawingApi.Core.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>netstandard2.1</TargetFramework>
+        <Nullable>enable</Nullable>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+        <LangVersion>10</LangVersion>
+    </PropertyGroup>
+  
+  <ItemGroup>
+    <PackageReference Include="SkiaSharp" Version="2.80.3" />
+  </ItemGroup>
+</Project>

+ 92 - 0
src/PixiEditor.DrawingApi.Core/Surface/BlendMode.cs

@@ -0,0 +1,92 @@
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    public enum BlendMode
+  {
+    /// <summary>No regions are enabled. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_clr.svg)</summary>
+    Clear,
+    
+    /// <summary>Only the source will be present. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_src.svg)</summary>
+    Src,
+    
+    /// <summary>Only the destination will be present. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_dst.svg)</summary>
+    Dst,
+    
+    /// <summary>Source is placed over the destination. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_src-over.svg)</summary>
+    SrcOver,
+    
+    /// <summary>Destination is placed over the source. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_dst-over.svg)</summary>
+    DstOver,
+    
+    /// <summary>The source that overlaps the destination, replaces the destination. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_src-in.svg)</summary>
+    SrcIn,
+    
+    /// <summary>Destination which overlaps the source, replaces the source. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_dst-in.svg)</summary>
+    DstIn,
+    
+    /// <summary>Source is placed, where it falls outside of the destination. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_src-out.svg)</summary>
+    SrcOut,
+    
+    /// <summary>Destination is placed, where it falls outside of the source. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_dst-out.svg)</summary>
+    DstOut,
+    
+    /// <summary>Source which overlaps the destination, replaces the destination. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_src-atop.svg)</summary>
+    SrcATop,
+    
+    /// <summary>Destination which overlaps the source replaces the source. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_dst-atop.svg)</summary>
+    DstATop,
+    
+    /// <summary>The non-overlapping regions of source and destination are combined. [Porter Duff Compositing Operators] (https://drafts.fxtf.org/compositing-1/examples/PD_xor.svg)</summary>
+    Xor,
+    
+    /// <summary>Display the sum of the source image and destination image. [Porter Duff Compositing Operators]</summary>
+    Plus,
+    
+    /// <summary>Multiplies all components (= alpha and color). [Separable Blend Modes]</summary>
+    Modulate,
+    
+    /// <summary>Multiplies the complements of the backdrop and source color values, then complements the result. [Separable Blend Modes]</summary>
+    Screen,
+    
+    /// <summary>Multiplies or screens the colors, depending on the backdrop color value. [Separable Blend Modes]</summary>
+    Overlay,
+    
+    /// <summary>Selects the darker of the backdrop and source colors. [Separable Blend Modes]</summary>
+    Darken,
+    
+    /// <summary>Selects the lighter of the backdrop and source colors. [Separable Blend Modes]</summary>
+    Lighten,
+    
+    /// <summary>Brightens the backdrop color to reflect the source color. [Separable Blend Modes]</summary>
+    ColorDodge,
+    
+    /// <summary>Darkens the backdrop color to reflect the source color. [Separable Blend Modes]</summary>
+    ColorBurn,
+    
+    /// <summary>Multiplies or screens the colors, depending on the source color value. [Separable Blend Modes]</summary>
+    HardLight,
+    
+    /// <summary>Darkens or lightens the colors, depending on the source color value. [Separable Blend Modes]</summary>
+    SoftLight,
+    
+    /// <summary>Subtracts the darker of the two constituent colors from the lighter color. [Separable Blend Modes]</summary>
+    Difference,
+    
+    /// <summary>Produces an effect similar to that of the Difference mode but lower in contrast. [Separable Blend Modes]</summary>
+    Exclusion,
+    
+    /// <summary>The source color is multiplied by the destination color and replaces the destination [Separable Blend Modes]</summary>
+    Multiply,
+    
+    /// <summary>Creates a color with the hue of the source color and the saturation and luminosity of the backdrop color. [Non-Separable Blend Modes]</summary>
+    Hue,
+    
+    /// <summary>Creates a color with the saturation of the source color and the hue and luminosity of the backdrop color. [Non-Separable Blend Modes]</summary>
+    Saturation,
+    
+    /// <summary>Creates a color with the hue and saturation of the source color and the luminosity of the backdrop color. [Non-Separable Blend Modes]</summary>
+    Color,
+    
+    /// <summary>Creates a color with the luminosity of the source color and the hue and saturation of the backdrop color. [Non-Separable Blend Modes]</summary>
+    Luminosity,
+  }
+}

+ 14 - 0
src/PixiEditor.DrawingApi.Core/Surface/Canvas.cs

@@ -0,0 +1,14 @@
+using PixiEditor.DrawingApi.Core.Bridge;
+
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    public class Canvas
+    {
+        public void DrawPixel(int posX, int posY, Paint drawingPaint) => DrawingBackendApi.Current.CanvasOperations.DrawPixel(posX, posY, drawingPaint);
+
+        public void DrawSurface(DrawingSurface original, int x, int y) 
+            => DrawingBackendApi.Current.CanvasOperations.DrawSurface(original, x, y);
+
+        public void DrawImage(Image image, int x, int y) => DrawingBackendApi.Current.CanvasOperations.DrawImage(image, x, y);
+    }
+}

+ 18 - 0
src/PixiEditor.DrawingApi.Core/Surface/DrawingSurface.cs

@@ -0,0 +1,18 @@
+using PixiEditor.DrawingApi.Core.Bridge;
+
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    public class DrawingSurface
+    {
+        public float Width { get; set; }
+        public float Height { get; set; }
+        
+        public DrawingSurfaceProperties Properties { get; private set; }
+        public Canvas Canvas { get; private set; }
+
+        public Image Snapshot()
+        {
+            return DrawingBackendApi.Current.ImageOperations.Snapshot(this);
+        }
+    }
+}

+ 7 - 0
src/PixiEditor.DrawingApi.Core/Surface/DrawingSurfaceProperties.cs

@@ -0,0 +1,7 @@
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    public struct DrawingSurfaceProperties
+    {
+        
+    }
+}

+ 18 - 0
src/PixiEditor.DrawingApi.Core/Surface/FilterQuality.cs

@@ -0,0 +1,18 @@
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    /// <summary>Filter quality settings.</summary>
+    public enum FilterQuality
+    {
+        /// <summary>Unspecified.</summary>
+        None,
+        
+        /// <summary>Low quality.</summary>
+        Low,
+        
+        /// <summary>Medium quality.</summary>
+        Medium,
+        
+        /// <summary>High quality.</summary>
+        High,
+    }
+}

+ 25 - 0
src/PixiEditor.DrawingApi.Core/Surface/Image.cs

@@ -0,0 +1,25 @@
+using PixiEditor.DrawingApi.Core.Bridge;
+
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    /// <summary>An abstraction for drawing a rectangle of pixels.</summary>
+    /// <remarks>
+    ///     <para>An image is an abstraction of pixels, though the particular type of image could be actually storing its data on the GPU, or as drawing commands (picture or PDF or otherwise), ready to be played back into another canvas.</para>
+    ///     <para />
+    ///     <para>The content of an image is always immutable, though the actual storage may change, if for example that image can be recreated via encoded data or other means.</para>
+    ///     <para />
+    ///     <para>An image always has a non-zero dimensions. If there is a request to create a new image, either directly or via a surface, and either of the requested dimensions are zero, then <see langword="null" /> will be returned.</para>
+    /// </remarks>
+    public class Image : PixelDataObject
+    {
+        public override void Dispose()
+        {
+            DrawingBackendApi.Current.ImageOperations.DisposeImage(this);
+        }
+
+        public static Image FromEncodedData(string path)
+        {
+            return DrawingBackendApi.Current.ImageOperations.FromEncodedData(path);
+        }
+    }
+}

+ 22 - 0
src/PixiEditor.DrawingApi.Core/Surface/Paint.cs

@@ -0,0 +1,22 @@
+using System;
+using PixiEditor.DrawingApi.Core.Bridge;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    /// <summary>
+    ///     Class used to define surface paint, which is a collection of paint operations.
+    /// </summary>
+    public class Paint : IDisposable
+    {
+        public Color Color { get; set; }
+        public BlendMode BlendMode { get; set; } = BlendMode.Src;
+        public FilterQuality FilterQuality { get; set; } = FilterQuality.None;
+        public bool IsAntiAliased { get; set; } = false;
+
+        public void Dispose()
+        {
+            DrawingBackendApi.Current.PaintOperations.Dispose(this);
+        }
+    }
+}

+ 9 - 0
src/PixiEditor.DrawingApi.Core/Surface/PixelDataObject.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Core.Surface
+{
+    public abstract class PixelDataObject : IDisposable
+    {
+        public abstract void Dispose();
+    }
+}

+ 32 - 0
src/PixiEditor.sln

@@ -32,6 +32,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.ChangeableDocume
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Zoombox", "PixiEditor.Zoombox\PixiEditor.Zoombox.csproj", "{69DD5830-C682-49FB-B1A5-D2A506EEA06B}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.DrawingApi.Core", "PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj", "{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -392,6 +394,36 @@ Global
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Release|x64.Build.0 = Release|Any CPU
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Release|x86.ActiveCfg = Release|Any CPU
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Release|x86.Build.0 = Release|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Debug|x64.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Debug|x86.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Dev Release|Any CPU.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Dev Release|Any CPU.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Dev Release|x64.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Dev Release|x64.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Dev Release|x86.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Dev Release|x86.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX Debug|x64.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX Debug|x86.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX|Any CPU.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX|x64.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX|x64.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX|x86.ActiveCfg = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.MSIX|x86.Build.0 = Debug|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Release|x64.ActiveCfg = Release|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Release|x64.Build.0 = Release|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Release|x86.ActiveCfg = Release|Any CPU
+		{5FC5E9C5-F439-43AA-92AF-9B7554D6FA13}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 1
src/PixiEditor/Helpers/DocumentViewModelBuilder.cs

@@ -189,7 +189,7 @@ internal class DocumentViewModelBuilder : ChildrenBuilder
         
         public SurfaceBuilder WithImage(ReadOnlySpan<byte> buffer, int x, int y)
         {
-            Surface.SkiaSurface.Canvas.DrawBitmap(SKBitmap.Decode(buffer), 0, 0);
+            Surface.DrawingSurface.Canvas.DrawBitmap(SKBitmap.Decode(buffer), 0, 0);
             return this;
         }
     }

+ 1 - 1
src/PixiEditor/Helpers/SurfaceHelpers.cs

@@ -46,7 +46,7 @@ public static class SurfaceHelpers
         byte[] buffer = new byte[width * height * imageInfo.BytesPerPixel];
         fixed (void* pointer = buffer)
         {
-            if (!surface.SkiaSurface.ReadPixels(imageInfo, new IntPtr(pointer), imageInfo.RowBytes, 0, 0))
+            if (!surface.DrawingSurface.ReadPixels(imageInfo, new IntPtr(pointer), imageInfo.RowBytes, 0, 0))
             {
                 throw new InvalidOperationException("Could not read surface into buffer");
             }

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

@@ -41,7 +41,7 @@ internal static class ClipboardController
         var (actuallySurface, _) = surface.AsT2;
         DataObject data = new DataObject();
 
-        using (SKData pngData = actuallySurface.SkiaSurface.Snapshot().Encode())
+        using (SKData pngData = actuallySurface.DrawingSurface.Snapshot().Encode())
         {
             // Stream should not be disposed
             MemoryStream pngStream = new MemoryStream();

+ 1 - 1
src/PixiEditor/Models/IO/Exporter.cs

@@ -138,7 +138,7 @@ internal class Exporter
         var bytes = new byte[rectToSave.Width * rectToSave.Height * 8 + 8];
         try
         {
-            surface.SkiaSurface.ReadPixels(imageInfo, unmanagedBuffer, rectToSave.Width * 8, rectToSave.Left, rectToSave.Top);
+            surface.DrawingSurface.ReadPixels(imageInfo, unmanagedBuffer, rectToSave.Width * 8, rectToSave.Left, rectToSave.Top);
             Marshal.Copy(unmanagedBuffer, bytes, 8, rectToSave.Width * rectToSave.Height * 8);
         }
         finally

+ 1 - 1
src/PixiEditor/Models/IO/Importer.cs

@@ -95,7 +95,7 @@ internal class Importer : NotifyableObject
             SKSurface surface = SKSurface.Create(map);
             Surface finalSurface = new Surface(new VecI(width, height));
             using SKPaint paint = new() { BlendMode = SKBlendMode.Src };
-            surface.Draw(finalSurface.SkiaSurface.Canvas, 0, 0, paint);
+            surface.Draw(finalSurface.DrawingSurface.Canvas, 0, 0, paint);
             return finalSurface;
         }
         finally

+ 2 - 2
src/PixiEditor/Models/Rendering/WriteableBitmapUpdater.cs

@@ -227,7 +227,7 @@ internal class WriteableBitmapUpdater
                     OneOf<Chunk, EmptyChunk> rendered = ChunkRenderer.MergeWholeStructure(chunk, ChunkResolution.Full, folder);
                     if (rendered.IsT0)
                     {
-                        memberVM.PreviewSurface.Canvas.DrawSurface(rendered.AsT0.Surface.SkiaSurface, pos, SmoothReplacingPaint);
+                        memberVM.PreviewSurface.Canvas.DrawSurface(rendered.AsT0.Surface.DrawingSurface, pos, SmoothReplacingPaint);
                         rendered.AsT0.Dispose();
                     }
                     else
@@ -288,7 +288,7 @@ internal class WriteableBitmapUpdater
         ChunkRenderer.MergeWholeStructure(chunkPos, resolution, internals.Tracker.Document.StructureRoot).Switch(
             (Chunk chunk) =>
             {
-                screenSurface.Canvas.DrawSurface(chunk.Surface.SkiaSurface, chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
+                screenSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
                 chunk.Dispose();
             },
             (EmptyChunk _) =>

+ 4 - 4
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -290,18 +290,18 @@ internal class DocumentViewModel : NotifyableObject
 
         SKPath clipPath = new SKPath(SelectionPathBindable) { FillType = SKPathFillType.EvenOdd };
         clipPath.Transform(SKMatrix.CreateTranslation(-bounds.X, -bounds.Y));
-        output.SkiaSurface.Canvas.Save();
-        output.SkiaSurface.Canvas.ClipPath(clipPath);
+        output.DrawingSurface.Canvas.Save();
+        output.DrawingSurface.Canvas.ClipPath(clipPath);
         try
         {
-            layer.LayerImage.DrawMostUpToDateRegionOn(bounds, ChunkResolution.Full, output.SkiaSurface, VecI.Zero);
+            layer.LayerImage.DrawMostUpToDateRegionOn(bounds, ChunkResolution.Full, output.DrawingSurface, VecI.Zero);
         }
         catch (ObjectDisposedException)
         {
             output.Dispose();
             return new Error();
         }
-        output.SkiaSurface.Canvas.Restore();
+        output.DrawingSurface.Canvas.Restore();
 
         return (output, bounds);
     }