瀏覽代碼

Drawing somewhat works

flabbet 11 月之前
父節點
當前提交
98e28b4aea
共有 83 個文件被更改,包括 569 次插入416 次删除
  1. 9 7
      src/ChunkyImageLib/Chunk.cs
  2. 38 38
      src/ChunkyImageLib/ChunkyImage.cs
  3. 1 1
      src/ChunkyImageLib/CommittedChunkStorage.cs
  4. 1 1
      src/ChunkyImageLib/Operations/ApplyMaskOperation.cs
  5. 1 1
      src/ChunkyImageLib/Operations/BresenhamLineOperation.cs
  6. 9 9
      src/ChunkyImageLib/Operations/ChunkyImageOperation.cs
  7. 4 4
      src/ChunkyImageLib/Operations/ClearPathOperation.cs
  8. 4 4
      src/ChunkyImageLib/Operations/ClearRegionOperation.cs
  9. 1 1
      src/ChunkyImageLib/Operations/DrawingSurfaceLineOperation.cs
  10. 1 1
      src/ChunkyImageLib/Operations/EllipseOperation.cs
  11. 11 12
      src/ChunkyImageLib/Operations/ImageOperation.cs
  12. 1 1
      src/ChunkyImageLib/Operations/PaintOperation.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/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IBackgroundInput.cs
  19. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyFolderNode.cs
  20. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs
  21. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs
  22. 5 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
  23. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/TimeNode.cs
  24. 16 16
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs
  25. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs
  26. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecD.cs
  27. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecI.cs
  28. 20 20
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs
  29. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs
  30. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs
  31. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs
  32. 10 10
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs
  33. 6 6
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EllipseNode.cs
  34. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs
  35. 7 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs
  36. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/FilterNode.cs
  37. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  38. 15 15
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  39. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs
  40. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LerpColorNode.cs
  41. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs
  42. 11 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  43. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  44. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  45. 7 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  46. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  47. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  48. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SampleImageNode.cs
  49. 44 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ShaderNode.cs
  50. 21 20
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  51. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs
  52. 11 11
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs
  53. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Drawing/PasteImage_UpdateableChange.cs
  54. 8 8
      src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelectedArea_UpdateableChange.cs
  55. 3 3
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs
  56. 5 5
      src/PixiEditor.ChangeableDocument/Changes/Root/FlipImage_Change.cs
  57. 5 5
      src/PixiEditor.ChangeableDocument/Changes/Root/ResizeImage_Change.cs
  58. 6 6
      src/PixiEditor.ChangeableDocument/Changes/Root/RotateImage_Change.cs
  59. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Selection/MagicWand/MagicWandHelper.cs
  60. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs
  61. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Structure/DuplicateLayer_Change.cs
  62. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  63. 16 16
      src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs
  64. 76 7
      src/PixiEditor.DrawingApi.Core/Texture.cs
  65. 1 1
      src/PixiEditor/Helpers/DocumentViewModelBuilder.cs
  66. 3 0
      src/PixiEditor/Helpers/Extensions/BitmapExtensions.cs
  67. 20 2
      src/PixiEditor/Helpers/SurfaceHelpers.cs
  68. 2 2
      src/PixiEditor/Models/Clipboard/DataImage.cs
  69. 9 9
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  70. 1 1
      src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs
  71. 4 4
      src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs
  72. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs
  73. 4 4
      src/PixiEditor/Models/IO/Importer.cs
  74. 41 43
      src/PixiEditor/Models/Rendering/CanvasUpdater.cs
  75. 2 2
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  76. 3 3
      src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs
  77. 6 6
      src/PixiEditor/Models/Serialization/Factories/SurfaceSerializationFactory.cs
  78. 5 5
      src/PixiEditor/Styles/Templates/NodeView.axaml
  79. 1 1
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  80. 3 2
      src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs
  81. 1 1
      src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs
  82. 0 1
      src/PixiEditor/Views/Rendering/Scene.cs
  83. 20 5
      src/PixiEditor/Views/Visuals/TextureControl.cs

+ 9 - 7
src/ChunkyImageLib/Chunk.cs

@@ -22,7 +22,7 @@ public class Chunk : IDisposable
     /// <summary>
     /// The surface of the chunk
     /// </summary>
-    public Surface Surface
+    public Texture Surface
     {
         get
         {
@@ -47,14 +47,14 @@ public class Chunk : IDisposable
     
     public bool Disposed => returned;
 
-    private Surface internalSurface;
+    private Texture internalSurface;
     private Chunk(ChunkResolution resolution)
     {
         int size = resolution.PixelSize();
 
         Resolution = resolution;
         PixelSize = new(size, size);
-        internalSurface = new Surface(PixelSize);
+        internalSurface = new Texture(PixelSize);
     }
 
     /// <summary>
@@ -75,7 +75,7 @@ public class Chunk : IDisposable
     /// <param name="paint">The paint to use while drawing</param>
     public void DrawChunkOn(DrawingSurface surface, VecI pos, Paint? paint = null)
     {
-        surface.Canvas.DrawSurface(Surface.DrawingSurface, pos.X, pos.Y, paint);
+        surface.Canvas.DrawSurface(Surface.Surface, pos.X, pos.Y, paint);
     }
     
     public unsafe RectI? FindPreciseBounds(RectI? passedSearchRegion = null)
@@ -88,8 +88,10 @@ public class Chunk : IDisposable
             throw new ArgumentException("Passed search region lies outside of the chunk's surface", nameof(passedSearchRegion));
 
         RectI searchRegion = passedSearchRegion ?? new RectI(VecI.Zero, Surface.Size);
-        
-        ulong* ptr = (ulong*)Surface.PixelBuffer;
+
+        Pixmap pixmap = Surface.PeekReadOnlyPixels();
+
+        ulong* ptr = (ulong*)pixmap.GetPixels();
         for (int y = searchRegion.Top; y < searchRegion.Bottom; y++)
         {
             for (int x = searchRegion.Left; x < searchRegion.Right; x++)
@@ -116,7 +118,7 @@ public class Chunk : IDisposable
         if (returned)
             return;
         Interlocked.Decrement(ref chunkCounter);
-        Surface.DrawingSurface.Canvas.Clear();
+        Surface.Surface.Canvas.Clear();
         ChunkPool.Instance.Push(this);
         returned = true;
     }

+ 38 - 38
src/ChunkyImageLib/ChunkyImage.cs

@@ -93,7 +93,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     private VectorPath? clippingPath;
     private double? horizontalSymmetryAxis = null;
     private double? verticalSymmetryAxis = null;
-    
+
     private int operationCounter = 0;
 
     private readonly Dictionary<ChunkResolution, Dictionary<VecI, Chunk>> committedChunks;
@@ -127,7 +127,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         };
     }
 
-    public ChunkyImage(Surface image) : this(image.Size)
+    public ChunkyImage(Texture image) : this(image.Size)
     {
         EnqueueDrawImage(VecI.Zero, image);
         CommitChanges();
@@ -315,8 +315,8 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 using Chunk tempChunk = Chunk.Create(ChunkResolution.Eighth);
                 using Paint committedPaint = new Paint() { Color = committedColor, BlendMode = BlendMode.Src };
                 using Paint latestPaint = new Paint() { Color = latestColor, BlendMode = this.blendMode };
-                tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, committedPaint);
-                tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, latestPaint);
+                tempChunk.Surface.Surface.Canvas.DrawPixel(VecI.Zero, committedPaint);
+                tempChunk.Surface.Surface.Canvas.DrawPixel(VecI.Zero, latestPaint);
                 return tempChunk.Surface.GetSRGBPixel(VecI.Zero);
             }
         }
@@ -346,7 +346,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             }
 
             var committedChunk = GetCommittedChunk(chunkPos, resolution);
-            
+
             // draw committed directly
             if (latestChunk.IsT0 || latestChunk.IsT1 && committedChunk is not null && blendMode != BlendMode.Src)
             {
@@ -370,13 +370,13 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
 
             // combine with committed and then draw
             using var tempChunk = Chunk.Create(resolution);
-            tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(committedChunk.Surface.DrawingSurface, 0, 0,
+            tempChunk.Surface.Surface.Canvas.DrawSurface(committedChunk.Surface.Surface, 0, 0,
                 ReplacingPaint);
             blendModePaint.BlendMode = blendMode;
-            tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(latestChunk.AsT2.Surface.DrawingSurface, 0, 0,
+            tempChunk.Surface.Surface.Canvas.DrawSurface(latestChunk.AsT2.Surface.Surface, 0, 0,
                 blendModePaint);
             if (lockTransparency)
-                OperationHelper.ClampAlpha(tempChunk.Surface.DrawingSurface, committedChunk.Surface.DrawingSurface);
+                OperationHelper.ClampAlpha(tempChunk.Surface.Surface, committedChunk.Surface.Surface);
             tempChunk.DrawChunkOn(surface, pos, paint);
 
             return true;
@@ -597,7 +597,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     /// Surface is NOT THREAD SAFE, so if you pass a Surface here with copyImage == false you must not do anything with that surface anywhere (not even read) until CommitChanges/CancelChanges is called.
     /// </summary>
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
-    public void EnqueueDrawImage(Matrix3X3 transformMatrix, Surface image, Paint? paint = null, bool copyImage = true)
+    public void EnqueueDrawImage(Matrix3X3 transformMatrix, Texture image, Paint? paint = null, bool copyImage = true)
     {
         lock (lockObject)
         {
@@ -611,7 +611,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     /// Be careful about the copyImage argument, see other overload for details
     /// </summary>
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
-    public void EnqueueDrawImage(ShapeCorners corners, Surface image, Paint? paint = null, bool copyImage = true)
+    public void EnqueueDrawImage(ShapeCorners corners, Texture image, Paint? paint = null, bool copyImage = true)
     {
         lock (lockObject)
         {
@@ -625,7 +625,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     /// Be careful about the copyImage argument, see other overload for details
     /// </summary>
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
-    public void EnqueueDrawImage(VecI pos, Surface image, Paint? paint = null, bool copyImage = true)
+    public void EnqueueDrawImage(VecI pos, Texture image, Paint? paint = null, bool copyImage = true)
     {
         lock (lockObject)
         {
@@ -956,16 +956,16 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                     if (lockTransparency)
                     {
                         using Chunk tempChunk = Chunk.Create(resolution);
-                        tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(maybeCommitted.Surface.DrawingSurface, 0, 0,
+                        tempChunk.Surface.Surface.Canvas.DrawSurface(maybeCommitted.Surface.Surface, 0, 0,
                             ReplacingPaint);
-                        maybeCommitted.Surface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, 0, 0,
+                        maybeCommitted.Surface.Surface.Canvas.DrawSurface(chunk.Surface.Surface, 0, 0,
                             blendModePaint);
-                        OperationHelper.ClampAlpha(maybeCommitted.Surface.DrawingSurface,
-                            tempChunk.Surface.DrawingSurface);
+                        OperationHelper.ClampAlpha(maybeCommitted.Surface.Surface,
+                            tempChunk.Surface.Surface);
                     }
                     else
                     {
-                        maybeCommitted.Surface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, 0, 0,
+                        maybeCommitted.Surface.Surface.Canvas.DrawSurface(chunk.Surface.Surface, 0, 0,
                             blendModePaint);
                     }
 
@@ -1107,7 +1107,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) is not null)
             {
                 var committed = GetCommittedChunk(chunkPos, resolution);
-                OperationHelper.ClampAlpha(targetChunk!.Surface.DrawingSurface, committed!.Surface.DrawingSurface);
+                OperationHelper.ClampAlpha(targetChunk!.Surface.Surface, committed!.Surface.Surface);
             }
 
             chunkData.QueueProgress = queuedOperations.Count;
@@ -1131,13 +1131,13 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         }
 
         var intersection = Chunk.Create(resolution);
-        intersection.Surface.DrawingSurface.Canvas.Clear(Colors.White);
+        intersection.Surface.Surface.Canvas.Clear(Colors.White);
 
         foreach (var mask in activeClips)
         {
             if (mask.CommittedChunkExists(chunkPos))
             {
-                mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.DrawingSurface, VecI.Zero,
+                mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.Surface, VecI.Zero,
                     ClippingPaint);
             }
             else
@@ -1171,7 +1171,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 return chunkData.IsDeleted;
 
             if (chunkData.IsDeleted)
-                targetChunk.Surface.DrawingSurface.Canvas.Clear();
+                targetChunk.Surface.Surface.Canvas.Clear();
 
             // just regular drawing
             if (combinedRasterClips.IsT0) // Everything is visible as far as the raster clips are concerned
@@ -1184,14 +1184,14 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             var clip = combinedRasterClips.AsT2;
 
             using var tempChunk = Chunk.Create(targetChunk.Resolution);
-            targetChunk.DrawChunkOn(tempChunk.Surface.DrawingSurface, VecI.Zero, ReplacingPaint);
+            targetChunk.DrawChunkOn(tempChunk.Surface.Surface, VecI.Zero, ReplacingPaint);
 
             CallDrawWithClip(chunkOperation, operationAffectedArea.GlobalArea, tempChunk, resolution, chunkPos);
 
-            clip.DrawChunkOn(tempChunk.Surface.DrawingSurface, VecI.Zero, ClippingPaint);
-            clip.DrawChunkOn(targetChunk.Surface.DrawingSurface, VecI.Zero, InverseClippingPaint);
+            clip.DrawChunkOn(tempChunk.Surface.Surface, VecI.Zero, ClippingPaint);
+            clip.DrawChunkOn(targetChunk.Surface.Surface, VecI.Zero, InverseClippingPaint);
 
-            tempChunk.DrawChunkOn(targetChunk.Surface.DrawingSurface, VecI.Zero, AddingPaint);
+            tempChunk.DrawChunkOn(targetChunk.Surface.Surface, VecI.Zero, AddingPaint);
             return false;
         }
 
@@ -1209,7 +1209,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         if (operationAffectedArea is null)
             return;
 
-        int count = targetChunk.Surface.DrawingSurface.Canvas.Save();
+        int count = targetChunk.Surface.Surface.Canvas.Save();
 
         float scale = (float)resolution.Multiplier();
         if (clippingPath is not null && !clippingPath.IsEmpty)
@@ -1218,17 +1218,17 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             VecD trans = -chunkPos * FullChunkSize * scale;
 
             transformedPath.Transform(Matrix3X3.CreateScaleTranslation(scale, scale, (float)trans.X, (float)trans.Y));
-            targetChunk.Surface.DrawingSurface.Canvas.ClipPath(transformedPath);
+            targetChunk.Surface.Surface.Canvas.ClipPath(transformedPath);
         }
 
         VecD affectedAreaPos = operationAffectedArea.Value.TopLeft;
         VecD affectedAreaSize = operationAffectedArea.Value.Size;
         affectedAreaPos = (affectedAreaPos - chunkPos * FullChunkSize) * scale;
         affectedAreaSize = affectedAreaSize * scale;
-        targetChunk.Surface.DrawingSurface.Canvas.ClipRect(new RectD(affectedAreaPos, affectedAreaSize));
+        targetChunk.Surface.Surface.Canvas.ClipRect(new RectD(affectedAreaPos, affectedAreaSize));
 
         operation.DrawOnChunk(targetChunk, chunkPos);
-        targetChunk.Surface.DrawingSurface.Canvas.RestoreToCount(count);
+        targetChunk.Surface.Surface.Canvas.RestoreToCount(count);
     }
 
     /// <summary>
@@ -1308,12 +1308,12 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         if (existingFullResChunk is not null)
         {
             var newChunk = Chunk.Create(resolution);
-            newChunk.Surface.DrawingSurface.Canvas.Save();
-            newChunk.Surface.DrawingSurface.Canvas.Scale((float)resolution.Multiplier());
+            newChunk.Surface.Surface.Canvas.Save();
+            newChunk.Surface.Surface.Canvas.Scale((float)resolution.Multiplier());
 
-            newChunk.Surface.DrawingSurface.Canvas.DrawSurface(existingFullResChunk.Surface.DrawingSurface, 0, 0,
+            newChunk.Surface.Surface.Canvas.DrawSurface(existingFullResChunk.Surface.Surface, 0, 0,
                 SmoothReplacingPaint);
-            newChunk.Surface.DrawingSurface.Canvas.Restore();
+            newChunk.Surface.Surface.Canvas.Restore();
             committedChunks[resolution][chunkPos] = newChunk;
             return newChunk;
         }
@@ -1345,7 +1345,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             if (blendMode == BlendMode.Src)
                 maybeCommittedAnyRes.Surface.CopyTo(newChunk.Surface);
             else
-                newChunk.Surface.DrawingSurface.Canvas.Clear();
+                newChunk.Surface.Surface.Canvas.Clear();
             latestChunks[resolution][chunkPos] = newChunk;
             return newChunk;
         }
@@ -1365,7 +1365,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
 
         // no previous chunks exist
         var newLatestChunk = Chunk.Create(resolution);
-        newLatestChunk.Surface.DrawingSurface.Canvas.Clear();
+        newLatestChunk.Surface.Surface.Canvas.Clear();
         latestChunks[resolution][chunkPos] = newLatestChunk;
         return newLatestChunk;
     }
@@ -1423,9 +1423,9 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     public int GetCacheHash()
     {
         return commitCounter + queuedOperations.Count + operationCounter + activeClips.Count
-            + (int)blendMode + (lockTransparency ? 1 : 0) 
-            + (horizontalSymmetryAxis is not null ? (int)(horizontalSymmetryAxis * 100) : 0) 
-            + (verticalSymmetryAxis is not null ? (int)(verticalSymmetryAxis * 100) : 0) 
-            + (clippingPath is not null ? 1 : 0);
+               + (int)blendMode + (lockTransparency ? 1 : 0)
+               + (horizontalSymmetryAxis is not null ? (int)(horizontalSymmetryAxis * 100) : 0)
+               + (verticalSymmetryAxis is not null ? (int)(verticalSymmetryAxis * 100) : 0)
+               + (clippingPath is not null ? 1 : 0);
     }
 }

+ 1 - 1
src/ChunkyImageLib/CommittedChunkStorage.cs

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

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

@@ -25,7 +25,7 @@ internal class ApplyMaskOperation : IDrawOperation
     
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
-        mask.DrawCommittedChunkOn(chunkPos, targetChunk.Resolution, targetChunk.Surface.DrawingSurface, VecI.Zero, clippingPaint);
+        mask.DrawCommittedChunkOn(chunkPos, targetChunk.Resolution, targetChunk.Surface.Surface, VecI.Zero, clippingPaint);
     }
 
     public void Dispose()

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

@@ -31,7 +31,7 @@ internal class BresenhamLineOperation : IMirroredDrawOperation
         // a hacky way to make the lines look slightly better on non full res chunks
         paint.Color = new Color(color.R, color.G, color.B, (byte)(color.A * targetChunk.Resolution.Multiplier()));
 
-        var surf = targetChunk.Surface.DrawingSurface;
+        var surf = targetChunk.Surface.Surface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

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

@@ -27,14 +27,14 @@ internal class ChunkyImageOperation : IMirroredDrawOperation
 
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
-        targetChunk.Surface.DrawingSurface.Canvas.Save();
+        targetChunk.Surface.Surface.Canvas.Save();
         {
             VecI pixelPos = chunkPos * ChunkyImage.FullChunkSize;
             VecI topLeftImageCorner = GetTopLeft();
             RectD clippingRect = RectD.Create(
                 OperationHelper.ConvertForResolution(topLeftImageCorner - pixelPos, targetChunk.Resolution),
                 OperationHelper.ConvertForResolution(drawUpToDate ? imageToDraw.LatestSize : imageToDraw.CommittedSize, targetChunk.Resolution));
-            targetChunk.Surface.DrawingSurface.Canvas.ClipRect(clippingRect);
+            targetChunk.Surface.Surface.Canvas.ClipRect(clippingRect);
         }
 
         VecI chunkPixelCenter = chunkPos * ChunkyImage.FullChunkSize;
@@ -45,12 +45,12 @@ internal class ChunkyImageOperation : IMirroredDrawOperation
         VecI chunkSize = targetChunk.PixelSize;
         if (mirrorHorizontal)
         {
-            targetChunk.Surface.DrawingSurface.Canvas.Scale(-1, 1, chunkSize.X / 2f, chunkSize.Y / 2f);
+            targetChunk.Surface.Surface.Canvas.Scale(-1, 1, chunkSize.X / 2f, chunkSize.Y / 2f);
             chunkCenterOnImage.X = -chunkCenterOnImage.X;
         }
         if (mirrorVertical)
         {
-            targetChunk.Surface.DrawingSurface.Canvas.Scale(1, -1, chunkSize.X / 2f, chunkSize.Y / 2f);
+            targetChunk.Surface.Surface.Canvas.Scale(1, -1, chunkSize.X / 2f, chunkSize.Y / 2f);
             chunkCenterOnImage.Y = -chunkCenterOnImage.Y;
         }
 
@@ -68,7 +68,7 @@ internal class ChunkyImageOperation : IMirroredDrawOperation
         drawMethod(
             topLeft,
             targetChunk.Resolution,
-            targetChunk.Surface.DrawingSurface,
+            targetChunk.Surface.Surface,
             (VecI)((topLeft * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * targetChunk.Resolution.Multiplier()), null);
 
         VecI gridShift = targetPos % ChunkyImage.FullChunkSize;
@@ -77,7 +77,7 @@ internal class ChunkyImageOperation : IMirroredDrawOperation
             drawMethod(
             topRight,
             targetChunk.Resolution,
-            targetChunk.Surface.DrawingSurface,
+            targetChunk.Surface.Surface,
             (VecI)((topRight * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * targetChunk.Resolution.Multiplier()),
             null);
         }
@@ -86,7 +86,7 @@ internal class ChunkyImageOperation : IMirroredDrawOperation
             drawMethod(
             bottomLeft,
             targetChunk.Resolution,
-            targetChunk.Surface.DrawingSurface,
+            targetChunk.Surface.Surface,
             (VecI)((bottomLeft * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * targetChunk.Resolution.Multiplier()),
             null);
         }
@@ -95,12 +95,12 @@ internal class ChunkyImageOperation : IMirroredDrawOperation
             drawMethod(
             bottomRight,
             targetChunk.Resolution,
-            targetChunk.Surface.DrawingSurface,
+            targetChunk.Surface.Surface,
             (VecI)((bottomRight * ChunkyImage.FullChunkSize - chunkCenterOnImage).Add(ChunkyImage.FullChunkSize / 2) * targetChunk.Resolution.Multiplier()),
             null);
         }
 
-        targetChunk.Surface.DrawingSurface.Canvas.Restore();
+        targetChunk.Surface.Surface.Canvas.Restore();
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)

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

@@ -19,15 +19,15 @@ internal class ClearPathOperation : IMirroredDrawOperation
 
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
-        targetChunk.Surface.DrawingSurface.Canvas.Save();
+        targetChunk.Surface.Surface.Canvas.Save();
 
         using VectorPath transformedPath = new(path);
         float scale = (float)targetChunk.Resolution.Multiplier();
         VecD trans = -chunkPos * ChunkyImage.FullChunkSize * scale;
         transformedPath.Transform(Matrix3X3.CreateScaleTranslation(scale, scale, (float)trans.X, (float)trans.Y));
-        targetChunk.Surface.DrawingSurface.Canvas.ClipPath(transformedPath);
-        targetChunk.Surface.DrawingSurface.Canvas.Clear();
-        targetChunk.Surface.DrawingSurface.Canvas.Restore();
+        targetChunk.Surface.Surface.Canvas.ClipPath(transformedPath);
+        targetChunk.Surface.Surface.Canvas.Clear();
+        targetChunk.Surface.Surface.Canvas.Restore();
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)

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

@@ -20,10 +20,10 @@ internal class ClearRegionOperation : IMirroredDrawOperation
         VecI convPos = OperationHelper.ConvertForResolution(rect.Pos, targetChunk.Resolution);
         VecI convSize = OperationHelper.ConvertForResolution(rect.Size, targetChunk.Resolution);
 
-        targetChunk.Surface.DrawingSurface.Canvas.Save();
-        targetChunk.Surface.DrawingSurface.Canvas.ClipRect(RectD.Create(convPos - chunkPos.Multiply(targetChunk.PixelSize), convSize));
-        targetChunk.Surface.DrawingSurface.Canvas.Clear();
-        targetChunk.Surface.DrawingSurface.Canvas.Restore();
+        targetChunk.Surface.Surface.Canvas.Save();
+        targetChunk.Surface.Surface.Canvas.ClipRect(RectD.Create(convPos - chunkPos.Multiply(targetChunk.PixelSize), convSize));
+        targetChunk.Surface.Surface.Canvas.Clear();
+        targetChunk.Surface.Surface.Canvas.Restore();
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)

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

@@ -31,7 +31,7 @@ internal class DrawingSurfaceLineOperation : IMirroredDrawOperation
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
         paint.IsAntiAliased = targetChunk.Resolution != ChunkResolution.Full;
-        var surf = targetChunk.Surface.DrawingSurface;
+        var surf = targetChunk.Surface.Surface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

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

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

+ 11 - 12
src/ChunkyImageLib/Operations/ImageOperation.cs

@@ -10,13 +10,13 @@ internal class ImageOperation : IMirroredDrawOperation
 {
     private Matrix3X3 transformMatrix;
     private ShapeCorners corners;
-    private Surface toPaint;
+    private Texture toPaint;
     private bool imageWasCopied = false;
     private readonly Paint? customPaint;
 
     public bool IgnoreEmptyChunks => false;
 
-    public ImageOperation(VecI pos, Surface image, Paint? paint = null, bool copyImage = true)
+    public ImageOperation(VecI pos, Texture image, Paint? paint = null, bool copyImage = true)
     {
         if (paint is not null)
             customPaint = paint.Clone();
@@ -34,13 +34,13 @@ internal class ImageOperation : IMirroredDrawOperation
 
         // copying is needed for thread safety
         if (copyImage)
-            toPaint = new Surface(image);
+            toPaint = new Texture(image);
         else
             toPaint = image;
         imageWasCopied = copyImage;
     }
 
-    public ImageOperation(ShapeCorners corners, Surface image, Paint? paint = null, bool copyImage = true)
+    public ImageOperation(ShapeCorners corners, Texture image, Paint? paint = null, bool copyImage = true)
     {
         if (paint is not null)
             customPaint = paint.Clone();
@@ -50,13 +50,13 @@ internal class ImageOperation : IMirroredDrawOperation
 
         // copying is needed for thread safety
         if (copyImage)
-            toPaint = new Surface(image);
+            toPaint = new Texture(image);
         else
             toPaint = image;
         imageWasCopied = copyImage;
     }
 
-    public ImageOperation(Matrix3X3 transformMatrix, Surface image, Paint? paint = null, bool copyImage = true)
+    public ImageOperation(Matrix3X3 transformMatrix, Texture image, Paint? paint = null, bool copyImage = true)
     {
         if (paint is not null)
             customPaint = paint.Clone();
@@ -72,14 +72,13 @@ internal class ImageOperation : IMirroredDrawOperation
 
         // copying is needed for thread safety
         if (copyImage)
-            toPaint = new Surface(image);
+            toPaint = new Texture(image);
         else
             toPaint = image;
         imageWasCopied = copyImage;
     }
 
 
-
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
         //customPaint.FilterQuality = chunk.Resolution != ChunkResolution.Full;
@@ -89,10 +88,10 @@ internal class ImageOperation : IMirroredDrawOperation
         var scaleTrans = Matrix3X3.CreateScaleTranslation(scaleMult, scaleMult, (float)trans.X * scaleMult, (float)trans.Y * scaleMult);
         var finalMatrix = Matrix3X3.Concat(scaleTrans, transformMatrix);
 
-        targetChunk.Surface.DrawingSurface.Canvas.Save();
-        targetChunk.Surface.DrawingSurface.Canvas.SetMatrix(finalMatrix);
-        targetChunk.Surface.DrawingSurface.Canvas.DrawSurface(toPaint.DrawingSurface, 0, 0, customPaint);
-        targetChunk.Surface.DrawingSurface.Canvas.Restore();
+        targetChunk.Surface.Surface.Canvas.Save();
+        targetChunk.Surface.Surface.Canvas.SetMatrix(finalMatrix);
+        targetChunk.Surface.Surface.Canvas.DrawSurface(toPaint.Surface, 0, 0, customPaint);
+        targetChunk.Surface.Surface.Canvas.Restore();
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)

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

@@ -21,7 +21,7 @@ public class PaintOperation : IDrawOperation
     public bool IgnoreEmptyChunks => false;
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
-        targetChunk.Surface.DrawingSurface.Canvas.DrawPaint(paint);
+        targetChunk.Surface.Surface.Canvas.DrawPaint(paint);
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)

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

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

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

@@ -41,7 +41,7 @@ internal class PixelOperation : IMirroredDrawOperation
         // a hacky way to make the lines look slightly better on non full res chunks
         paint.Color = GetColor(targetChunk, chunkPos);
 
-        DrawingSurface surf = targetChunk.Surface.DrawingSurface;
+        DrawingSurface surf = targetChunk.Surface.Surface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

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

@@ -30,7 +30,7 @@ internal class PixelsOperation : IMirroredDrawOperation
         // a hacky way to make the lines look slightly better on non full res chunks
         paint.Color = new Color(color.R, color.G, color.B, (byte)(color.A * targetChunk.Resolution.Multiplier()));
 
-        DrawingSurface surf = targetChunk.Surface.DrawingSurface;
+        DrawingSurface surf = targetChunk.Surface.Surface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);

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

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

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

@@ -34,7 +34,7 @@ internal class ReplaceColorOperation : IDrawOperation
         VecI imageSize = chunk.PixelSize;
         int rowsPerThread = imageSize.Y / maxThreads;
 
-        using Pixmap pixmap = chunk.Surface.DrawingSurface.PeekPixels();
+        using Pixmap pixmap = chunk.Surface.Surface.PeekPixels();
         IntPtr pixels = pixmap.GetPixels();
 
         Half* endOffset = (Half*)(pixels + pixmap.BytesSize);

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IBackgroundInput.cs

@@ -5,5 +5,5 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 public interface IBackgroundInput
 {
-    InputProperty<Surface?> Background { get; }
+    InputProperty<Texture?> Background { get; }
 }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyFolderNode.cs

@@ -5,5 +5,5 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 public interface IReadOnlyFolderNode : IReadOnlyStructureNode
 {
-    InputProperty<Surface?> Content { get; }
+    InputProperty<Texture?> Content { get; }
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs

@@ -13,10 +13,10 @@ public interface IReadOnlyNode
     public IReadOnlyList<IOutputProperty> OutputProperties { get; }
     public IReadOnlyList<IReadOnlyKeyFrameData> KeyFrames { get; }
     public VecD Position { get; }
-    public Surface? CachedResult { get; }
+    public Texture? CachedResult { get; }
     string DisplayName { get; }
 
-    public Surface? Execute(RenderingContext context);
+    public Texture? Execute(RenderingContext context);
     
     /// <summary>
     ///     Checks if the inputs are legal. If they are not, the node should not be executed.

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs

@@ -10,5 +10,5 @@ public interface IReadOnlyNodeGraph
     public void AddNode(IReadOnlyNode node);
     public void RemoveNode(IReadOnlyNode node);
     public bool TryTraverse(Action<IReadOnlyNode> action);
-    public Surface? Execute(RenderingContext context);
+    public Texture? Execute(RenderingContext context);
 }

+ 5 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs

@@ -118,11 +118,13 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         return true;
     }
 
-    public Surface? Execute(RenderingContext context)
+    public Texture? Execute(RenderingContext context)
     {
         if (OutputNode == null) return null;
 
         var queue = CalculateExecutionQueue(OutputNode);
+        
+        Stopwatch stopwatch = new();
 
         while (queue.Count > 0)
         {
@@ -130,7 +132,9 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
             
             if (node is Node typedNode)
             {
+                stopwatch.Restart();
                 typedNode.ExecuteInternal(context);
+                Debug.WriteLine($"{typedNode.DisplayName} took {stopwatch.ElapsedMilliseconds}ms");
             }
             else
             {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/TimeNode.cs

@@ -19,7 +19,7 @@ public class TimeNode : Node
         NormalizedTime = CreateOutput("NormalizedTime", "NORMALIZED_TIME", 0.0);
     }
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         ActiveFrame.Value = context.FrameTime.Frame;
         NormalizedTime.Value = context.FrameTime.NormalizedTime;

+ 16 - 16
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -16,62 +16,62 @@ public class CombineChannelsNode : Node
     private readonly ColorFilter _greenFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseGreen + ColorMatrix.OpaqueAlphaOffset);
     private readonly ColorFilter _blueFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.OpaqueAlphaOffset);
 
-    public InputProperty<Surface> Red { get; }
+    public InputProperty<Texture> Red { get; }
     
-    public InputProperty<Surface> Green { get; }
+    public InputProperty<Texture> Green { get; }
     
-    public InputProperty<Surface> Blue { get; }
+    public InputProperty<Texture> Blue { get; }
     
-    public InputProperty<Surface> Alpha { get; }
+    public InputProperty<Texture> Alpha { get; }
 
-    public OutputProperty<Surface> Image { get; }
+    public OutputProperty<Texture> Image { get; }
     
     // TODO: Either use a shader to combine each, or find a way to automatically "detect" if alpha channel is grayscale or not, oooor find an even better solution
     public InputProperty<bool> Grayscale { get; }
 
     public CombineChannelsNode()
     {
-        Red = CreateInput<Surface>(nameof(Red), "RED", null);
-        Green = CreateInput<Surface>(nameof(Green), "GREEN", null);
-        Blue = CreateInput<Surface>(nameof(Blue), "BLUE", null);
-        Alpha = CreateInput<Surface>(nameof(Alpha), "ALPHA", null);
+        Red = CreateInput<Texture>(nameof(Red), "RED", null);
+        Green = CreateInput<Texture>(nameof(Green), "GREEN", null);
+        Blue = CreateInput<Texture>(nameof(Blue), "BLUE", null);
+        Alpha = CreateInput<Texture>(nameof(Alpha), "ALPHA", null);
         
-        Image = CreateOutput<Surface>(nameof(Image), "IMAGE", null);
+        Image = CreateOutput<Texture>(nameof(Image), "IMAGE", null);
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         var size = GetSize();
 
         if (size == VecI.Zero)
             return null;
         
-        var workingSurface = new Surface(size);
+        var workingSurface = new Texture(size);
 
         if (Red.Value is { } red)
         {
             _screenPaint.ColorFilter = _redFilter;
-            workingSurface.DrawingSurface.Canvas.DrawSurface(red.DrawingSurface, 0, 0, _screenPaint);
+            workingSurface.Surface.Canvas.DrawSurface(red.Surface, 0, 0, _screenPaint);
         }
 
         if (Green.Value is { } green)
         {
             _screenPaint.ColorFilter = _greenFilter;
-            workingSurface.DrawingSurface.Canvas.DrawSurface(green.DrawingSurface, 0, 0, _screenPaint);
+            workingSurface.Surface.Canvas.DrawSurface(green.Surface, 0, 0, _screenPaint);
         }
 
         if (Blue.Value is { } blue)
         {
             _screenPaint.ColorFilter = _blueFilter;
-            workingSurface.DrawingSurface.Canvas.DrawSurface(blue.DrawingSurface, 0, 0, _screenPaint);
+            workingSurface.Surface.Canvas.DrawSurface(blue.Surface, 0, 0, _screenPaint);
         }
 
         if (Alpha.Value is { } alpha)
         {
             _clearPaint.ColorFilter = Grayscale.Value ? Filters.AlphaGrayscaleFilter : null;
 
-            workingSurface.DrawingSurface.Canvas.DrawSurface(alpha.DrawingSurface, 0, 0, _clearPaint);
+            workingSurface.Surface.Canvas.DrawSurface(alpha.Surface, 0, 0, _clearPaint);
         }
 
         Image.Value = workingSurface;

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs

@@ -41,7 +41,7 @@ public class CombineColorNode : Node
     }
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecD.cs

@@ -34,7 +34,7 @@ public class CombineVecD : Node
         return new VecD(r, g);
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecI.cs

@@ -33,7 +33,7 @@ public class CombineVecI : Node
     }
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 20 - 20
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs

@@ -20,33 +20,33 @@ public class SeparateChannelsNode : Node
     private readonly ColorFilter _blueGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.MapBlueToRedGreen + ColorMatrix.OpaqueAlphaOffset);
     private readonly ColorFilter _alphaGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
 
-    public OutputProperty<Surface?> Red { get; }
+    public OutputProperty<Texture?> Red { get; }
     
-    public OutputProperty<Surface?> Green { get; }
+    public OutputProperty<Texture?> Green { get; }
     
-    public OutputProperty<Surface?> Blue { get; }
+    public OutputProperty<Texture?> Blue { get; }
 
-    public OutputProperty<Surface?> Alpha { get; }
+    public OutputProperty<Texture?> Alpha { get; }
     
-    public InputProperty<Surface?> Image { get; }
+    public InputProperty<Texture?> Image { get; }
     
     public InputProperty<bool> Grayscale { get; }
 
     public SeparateChannelsNode()
     {
-        Red = CreateOutput<Surface>(nameof(Red), "RED", null);
-        Green = CreateOutput<Surface>(nameof(Green), "GREEN", null);
-        Blue = CreateOutput<Surface>(nameof(Blue), "BLUE", null);
-        Alpha = CreateOutput<Surface>(nameof(Alpha), "ALPHA", null);
+        Red = CreateOutput<Texture>(nameof(Red), "RED", null);
+        Green = CreateOutput<Texture>(nameof(Green), "GREEN", null);
+        Blue = CreateOutput<Texture>(nameof(Blue), "BLUE", null);
+        Alpha = CreateOutput<Texture>(nameof(Alpha), "ALPHA", null);
         
-        Image = CreateInput<Surface>(nameof(Image), "IMAGE", null);
+        Image = CreateInput<Texture>(nameof(Image), "IMAGE", null);
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
 
 
     public override string DisplayName { get; set; } = "SEPARATE_CHANNELS_NODE";
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         var image = Image.Value;
 
@@ -65,7 +65,7 @@ public class SeparateChannelsNode : Node
         Blue.Value = GetImage(image, blue);
         Alpha.Value = GetImage(image, alpha);
 
-        var previewSurface = new Surface(image.Size * 2);
+        var previewSurface = new Texture(image.Size * 2);
 
         var size = image.Size;
         
@@ -74,22 +74,22 @@ public class SeparateChannelsNode : Node
         var bluePos = new VecI(0, size.Y);
         var alphaPos = new VecI(size.X, size.Y);
         
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Red.Value.DrawingSurface, redPos, context.ReplacingPaintWithOpacity);
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Green.Value.DrawingSurface, greenPos, context.ReplacingPaintWithOpacity);
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Blue.Value.DrawingSurface, bluePos, context.ReplacingPaintWithOpacity);
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Alpha.Value.DrawingSurface, alphaPos, context.ReplacingPaintWithOpacity);
+        previewSurface.Surface.Canvas.DrawSurface(Red.Value.Surface, redPos, context.ReplacingPaintWithOpacity);
+        previewSurface.Surface.Canvas.DrawSurface(Green.Value.Surface, greenPos, context.ReplacingPaintWithOpacity);
+        previewSurface.Surface.Canvas.DrawSurface(Blue.Value.Surface, bluePos, context.ReplacingPaintWithOpacity);
+        previewSurface.Surface.Canvas.DrawSurface(Alpha.Value.Surface, alphaPos, context.ReplacingPaintWithOpacity);
         
         return previewSurface;
     }
 
-    private Surface GetImage(Surface image, ColorFilter filter)
+    private Texture GetImage(Texture image, ColorFilter filter)
     {
-        var imageSurface = new Surface(image.Size);
+        var imageTexture = new Texture(image.Size);
 
         _paint.ColorFilter = filter;
-        imageSurface.DrawingSurface.Canvas.DrawSurface(image.DrawingSurface, 0, 0, _paint);
+        imageTexture.Surface.Canvas.DrawSurface(image.Surface, 0, 0, _paint);
 
-        return imageSurface;
+        return imageTexture;
     }
 
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs

@@ -28,7 +28,7 @@ public class SeparateColorNode : Node
         A = CreateFuncOutput(nameof(A), "A", ctx => Color.Value(ctx).A / 255d);
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs

@@ -23,7 +23,7 @@ public class SeparateVecDNode : Node
     }
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs

@@ -22,7 +22,7 @@ public class SeparateVecINode : Node
         Vector = CreateFuncInput("Vector", "VECTOR", new VecI(0, 0));
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 10 - 10
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs

@@ -12,36 +12,36 @@ public class DebugBlendModeNode : Node
 {
     private Paint _paint = new();
     
-    public InputProperty<Surface?> Dst { get; }
+    public InputProperty<Texture?> Dst { get; }
 
-    public InputProperty<Surface?> Src { get; }
+    public InputProperty<Texture?> Src { get; }
 
     public InputProperty<DrawingApiBlendMode> BlendMode { get; }
 
-    public OutputProperty<Surface> Result { get; }
+    public OutputProperty<Texture> Result { get; }
 
     public override string DisplayName { get; set; } = "Debug Blend Mode";
     public DebugBlendModeNode()
     {
-        Dst = CreateInput<Surface?>(nameof(Dst), "Dst", null);
-        Src = CreateInput<Surface?>(nameof(Src), "Src", null);
+        Dst = CreateInput<Texture?>(nameof(Dst), "Dst", null);
+        Src = CreateInput<Texture?>(nameof(Src), "Src", null);
         BlendMode = CreateInput(nameof(BlendMode), "Blend Mode", DrawingApiBlendMode.SrcOver);
 
-        Result = CreateOutput<Surface>(nameof(Result), "Result", null);
+        Result = CreateOutput<Texture>(nameof(Result), "Result", null);
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         if (Dst.Value is not { } dst || Src.Value is not { } src)
             return null;
 
         var size = new VecI(Math.Max(src.Size.X, dst.Size.X), int.Max(src.Size.Y, dst.Size.Y));
-        var workingSurface = new Surface(size);
+        var workingSurface = new Texture(size);
 
-        workingSurface.DrawingSurface.Canvas.DrawSurface(dst.DrawingSurface, 0, 0, context.BlendModeOpacityPaint);
+        workingSurface.Surface.Canvas.DrawSurface(dst.Surface, 0, 0, context.BlendModeOpacityPaint);
 
         _paint.BlendMode = BlendMode.Value;
-        workingSurface.DrawingSurface.Canvas.DrawSurface(src.DrawingSurface, 0, 0, _paint);
+        workingSurface.Surface.Canvas.DrawSurface(src.Surface, 0, 0, _paint);
         
         Result.Value = workingSurface;
 

+ 6 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EllipseNode.cs

@@ -14,10 +14,10 @@ public class EllipseNode : Node
     public InputProperty<Color> StrokeColor { get; }
     public InputProperty<Color> FillColor { get; }
     public InputProperty<int> StrokeWidth { get; }
-    public OutputProperty<Surface> Output { get; }
+    public OutputProperty<Texture> Output { get; }
 
     private ChunkyImage? workingImage;
-    private Surface? targetSurface;
+    private Texture? targetSurface;
 
     private VecI _lastRadius = new VecI(-1, -1);
     private Color _lastStrokeColor = new Color(0, 0, 0, 0);
@@ -31,10 +31,10 @@ public class EllipseNode : Node
         StrokeColor = CreateInput<Color>("StrokeColor", "STROKE_COLOR", new Color(0, 0, 0, 255));
         FillColor = CreateInput<Color>("FillColor", "FILL_COLOR", new Color(0, 0, 0, 255));
         StrokeWidth = CreateInput<int>("StrokeWidth", "STROKE_WIDTH", 1);
-        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
+        Output = CreateOutput<Texture?>("Output", "OUTPUT", null);
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         var radius = Radius.Value;
         VecI targetDimensions = radius * 2;
@@ -52,7 +52,7 @@ public class EllipseNode : Node
             
             workingImage = new ChunkyImage(targetDimensions);
 
-            targetSurface = new Surface(targetDimensions);
+            targetSurface = new Texture(targetDimensions);
         }
 
         if (radius != _lastRadius || StrokeColor.Value != _lastStrokeColor || FillColor.Value != _lastFillColor ||
@@ -68,7 +68,7 @@ public class EllipseNode : Node
             workingImage.CommitChanges();
         }
 
-        workingImage.DrawMostUpToDateChunkOn(context.ChunkToUpdate, context.ChunkResolution, targetSurface.DrawingSurface, VecI.Zero,
+        workingImage.DrawMostUpToDateChunkOn(context.ChunkToUpdate, context.ChunkResolution, targetSurface.Surface, VecI.Zero,
             replacingPaint);
 
         Output.Value = targetSurface;

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs

@@ -12,7 +12,7 @@ public class CreateImageNode : Node
 {
     private Paint _paint = new();
     
-    public OutputProperty<Surface> Output { get; }
+    public OutputProperty<Texture> Output { get; }
 
     public InputProperty<VecI> Size { get; }
     
@@ -20,17 +20,17 @@ public class CreateImageNode : Node
 
     public CreateImageNode()
     {
-        Output = CreateOutput<Surface>(nameof(Output), "EMPTY_IMAGE", null);
+        Output = CreateOutput<Texture>(nameof(Output), "EMPTY_IMAGE", null);
         Size = CreateInput(nameof(Size), "SIZE", new VecI(32, 32));
         Fill = CreateInput(nameof(Fill), "FILL", new Color(0, 0, 0, 255));
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
-        var surface = new Surface(Size.Value);
+        var surface = new Texture(Size.Value);
 
         _paint.Color = Fill.Value;
-        surface.DrawingSurface.Canvas.DrawPaint(_paint);
+        surface.Surface.Canvas.DrawPaint(_paint);
 
         Output.Value = surface;
 

+ 7 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs

@@ -12,20 +12,20 @@ public class ApplyFilterNode : Node
     
     public override string DisplayName { get; set; } = "APPLY_FILTER_NODE";
     
-    public OutputProperty<Surface?> Output { get; }
+    public OutputProperty<Texture?> Output { get; }
 
-    public InputProperty<Surface?> Input { get; }
+    public InputProperty<Texture?> Input { get; }
     
     public InputProperty<Filter?> Filter { get; }
 
     public ApplyFilterNode()
     {
-        Output = CreateOutput<Surface>(nameof(Output), "IMAGE", null);
-        Input = CreateInput<Surface>(nameof(Input), "IMAGE", null);
+        Output = CreateOutput<Texture>(nameof(Output), "IMAGE", null);
+        Input = CreateInput<Texture>(nameof(Input), "IMAGE", null);
         Filter = CreateInput<Filter>(nameof(Filter), "FILTER", null);
     }
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         if (Input.Value is not { } input)
         {
@@ -34,9 +34,9 @@ public class ApplyFilterNode : Node
         
         _paint.SetFilters(Filter.Value);
 
-        var workingSurface = new Surface(input.Size);
+        var workingSurface = new Texture(input.Size);
         
-        workingSurface.DrawingSurface.Canvas.DrawSurface(input.DrawingSurface, 0, 0, _paint);
+        workingSurface.Surface.Canvas.DrawSurface(input.Surface, 0, 0, _paint);
 
         Output.Value = workingSurface;
         return workingSurface;

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/FilterNode.cs

@@ -16,7 +16,7 @@ public abstract class FilterNode : Node
         Input = CreateInput<Filter>(nameof(Input), "PREVIOUS", null);
     }
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         var colorFilter = GetColorFilter();
         var imageFilter = GetImageFilter();

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs

@@ -10,17 +10,17 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 [NodeInfo("Folder")]
 public class FolderNode : StructureNode, IReadOnlyFolderNode
 {
-    public InputProperty<Surface?> Content { get; }
+    public InputProperty<Texture?> Content { get; }
 
     public FolderNode()
     {
-        Content = CreateInput<Surface?>("Content", "CONTENT", null);
+        Content = CreateInput<Texture?>("Content", "CONTENT", null);
     }
 
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         if(Background.Value == null && Content.Value == null)
         {
@@ -87,12 +87,12 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
         
         if (Background.Value != null)
         {
-            Surface tempSurface = new Surface(outputWorkingSurface.Size);
+            Texture tempSurface = new Texture(outputWorkingSurface.Size);
             DrawBackground(tempSurface, context);
             
             blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
-            tempSurface.DrawingSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
+            tempSurface.Surface.Canvas.DrawSurface(outputWorkingSurface.Surface, 0, 0, blendPaint);
 
             Output.Value = tempSurface;
             return tempSurface;

+ 15 - 15
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -16,7 +16,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     public const string ImageFramesKey = "Frames";
     public const string ImageLayerKey = "LayerImage";
 
-    public OutputProperty<Surface> RawOutput { get; }
+    public OutputProperty<Texture> RawOutput { get; }
 
     public InputProperty<bool> LockTransparency { get; }
 
@@ -38,7 +38,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     public ImageLayerNode(VecI size)
     {
-        RawOutput = CreateOutput<Surface>(nameof(RawOutput), "RAW_LAYER_OUTPUT", null);
+        RawOutput = CreateOutput<Texture>(nameof(RawOutput), "RAW_LAYER_OUTPUT", null);
 
         LockTransparency = CreateInput<bool>("LockTransparency", "LOCK_TRANSPARENCY", false);
 
@@ -55,7 +55,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return GetLayerImageAtFrame(frameTime.Frame).FindTightCommittedBounds();
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
@@ -75,7 +75,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return Output.Value;
     }
 
-    private Surface RenderImage(ChunkyImage frameImage, RenderingContext context)
+    private Texture RenderImage(ChunkyImage frameImage, RenderingContext context)
     {
         var outputWorkingSurface = TryInitWorkingSurface(frameImage.LatestSize, context, 0);
         var filterlessWorkingSurface = TryInitWorkingSurface(frameImage.LatestSize, context, 1);
@@ -123,10 +123,10 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         if (Background.Value != null)
         {
-            Surface tempSurface = new Surface(outputWorkingSurface.Size);
+            Texture tempSurface = new Texture(outputWorkingSurface.Size);
             DrawBackground(tempSurface, context);
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
-            tempSurface.DrawingSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
+            tempSurface.Surface.Canvas.DrawSurface(outputWorkingSurface.Surface, 0, 0, blendPaint);
 
             Output.Value = tempSurface;
             return tempSurface;
@@ -137,7 +137,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return outputWorkingSurface;
     }
 
-    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface, bool shouldClear,
+    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Texture workingSurface, bool shouldClear,
         bool useFilters = true)
     {
         blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
@@ -153,18 +153,18 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             if (!frameImage.DrawMostUpToDateChunkOn(
                     context.ChunkToUpdate,
                     context.ChunkResolution,
-                    workingSurface.DrawingSurface,
+                    workingSurface.Surface,
                     context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
                     blendPaint) && shouldClear)
             {
-                workingSurface.DrawingSurface.Canvas.DrawRect(CalculateDestinationRect(context), clearPaint);
+                workingSurface.Surface.Canvas.DrawRect(CalculateDestinationRect(context), clearPaint);
             }
         }
     }
 
     // Draw with filters is a bit tricky since some filters sample data from chunks surrounding the chunk being drawn,
     // this is why we need to do intermediate drawing to a temporary surface and then apply filters to that surface
-    private void DrawWithFilters(ChunkyImage frameImage, RenderingContext context, Surface workingSurface,
+    private void DrawWithFilters(ChunkyImage frameImage, RenderingContext context, Texture workingSurface,
         bool shouldClear)
     {
         VecI imageChunksSize = frameImage.LatestSize / context.ChunkResolution.PixelSize();
@@ -200,14 +200,14 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         if (shouldClear)
         {
-            workingSurface.DrawingSurface.Canvas.DrawRect(
+            workingSurface.Surface.Canvas.DrawRect(
                 new RectI(
                     VecI.Zero,
                     tempSizeInChunks * context.ChunkResolution.PixelSize()),
                 clearPaint);
         }
         
-        using Surface tempSurface = new Surface(tempSizeInChunks * context.ChunkResolution.PixelSize());
+        using Texture tempSurface = new Texture(tempSizeInChunks * context.ChunkResolution.PixelSize());
 
         if (requiresTopLeft)
         {
@@ -253,10 +253,10 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         
         blendPaint.SetFilters(Filters.Value);
         var destinationRect = CalculateDestinationRect(context);
-        workingSurface.DrawingSurface.Canvas.DrawSurface(tempSurface.DrawingSurface, VecI.Zero, blendPaint);
+        workingSurface.Surface.Canvas.DrawSurface(tempSurface.Surface, VecI.Zero, blendPaint);
     }
 
-    private void DrawChunk(ChunkyImage frameImage, RenderingContext context, Surface tempSurface, VecI vecI)
+    private void DrawChunk(ChunkyImage frameImage, RenderingContext context, Texture tempSurface, VecI vecI)
     {
         VecI chunkPos = context.ChunkToUpdate + vecI;
         if (frameImage.LatestOrCommittedChunkExists(chunkPos))
@@ -264,7 +264,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             frameImage.DrawMostUpToDateChunkOn(
                 chunkPos,
                 context.ChunkResolution,
-                tempSurface.DrawingSurface,
+                tempSurface.Surface,
                 chunkPos * context.ChunkResolution.PixelSize(),
                 blendPaint);
         }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs

@@ -21,7 +21,7 @@ public class ImageSpaceNode : Node
     }
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LerpColorNode.cs

@@ -34,7 +34,7 @@ public class LerpColorNode : Node // TODO: ILerpable as inputs?
         return Color.Lerp(from, to, time); 
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs

@@ -58,7 +58,7 @@ public class MathNode : Node
     private (double x, double y) GetValues(FuncContext context) => (X.Value(context), Y.Value(context));
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return null;
     }

+ 11 - 11
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs

@@ -9,15 +9,15 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 [NodeInfo("Merge")]
 public class MergeNode : Node, IBackgroundInput
 {
-    public InputProperty<Surface?> Top { get; }
-    public InputProperty<Surface?> Bottom { get; }
-    public OutputProperty<Surface?> Output { get; }
+    public InputProperty<Texture?> Top { get; }
+    public InputProperty<Texture?> Bottom { get; }
+    public OutputProperty<Texture?> Output { get; }
     
     public MergeNode() 
     {
-        Top = CreateInput<Surface?>("Top", "TOP", null);
-        Bottom = CreateInput<Surface?>("Bottom", "BOTTOM", null);
-        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
+        Top = CreateInput<Texture?>("Top", "TOP", null);
+        Bottom = CreateInput<Texture?>("Bottom", "BOTTOM", null);
+        Output = CreateOutput<Texture?>("Output", "OUTPUT", null);
     }
 
     public override string DisplayName { get; set; } = "MERGE_NODE";
@@ -28,7 +28,7 @@ public class MergeNode : Node, IBackgroundInput
     }
 
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         if(Top.Value == null && Bottom.Value == null)
         {
@@ -39,16 +39,16 @@ public class MergeNode : Node, IBackgroundInput
         int width = Top.Value?.Size.X ?? Bottom.Value.Size.X;
         int height = Top.Value?.Size.Y ?? Bottom.Value.Size.Y;
         
-        Surface workingSurface = new Surface(new VecI(width, height));
+        Texture workingSurface = new Texture(new VecI(width, height));
         
         if(Bottom.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawSurface(Bottom.Value.DrawingSurface, 0, 0);
+            workingSurface.Surface.Canvas.DrawSurface(Bottom.Value.Surface, 0, 0);
         }
         
         if(Top.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawSurface(Top.Value.DrawingSurface, 0, 0);
+            workingSurface.Surface.Canvas.DrawSurface(Top.Value.Surface, 0, 0);
         }
 
         Output.Value = workingSurface;
@@ -56,5 +56,5 @@ public class MergeNode : Node, IBackgroundInput
         return Output.Value;
     }
 
-    InputProperty<Surface> IBackgroundInput.Background => Bottom;
+    InputProperty<Texture> IBackgroundInput.Background => Bottom;
 }

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs

@@ -15,7 +15,7 @@ public class ModifyImageLeftNode : Node
 {
     private Pixmap? pixmap;
 
-    public InputProperty<Surface?> Image { get; }
+    public InputProperty<Texture?> Image { get; }
     
     public FuncOutputProperty<VecD> Coordinate { get; }
     
@@ -25,7 +25,7 @@ public class ModifyImageLeftNode : Node
 
     public ModifyImageLeftNode()
     {
-        Image = CreateInput<Surface>(nameof(Surface), "IMAGE", null);
+        Image = CreateInput<Texture>(nameof(Surface), "IMAGE", null);
         Coordinate = CreateFuncOutput(nameof(Coordinate), "UV", ctx => ctx.Position);
         Color = CreateFuncOutput(nameof(Color), "COLOR", GetColor);
     }
@@ -45,10 +45,10 @@ public class ModifyImageLeftNode : Node
 
     internal void PreparePixmap()
     {
-        pixmap = Image.Value?.PeekPixels();
+        pixmap = Image.Value?.PeekReadOnlyPixels();
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return Image.Value;
     }

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -22,7 +22,7 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
     public FuncInputProperty<VecD> Coordinate { get; }
     public FuncInputProperty<Color> Color { get; }
 
-    public OutputProperty<Surface> Output { get; }
+    public OutputProperty<Texture> Output { get; }
 
     public override string DisplayName { get; set; } = "MODIFY_IMAGE_RIGHT_NODE";
 
@@ -30,10 +30,10 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
     {
         Coordinate = CreateFuncInput(nameof(Coordinate), "UV", new VecD());
         Color = CreateFuncInput(nameof(Color), "COLOR", new Color());
-        Output = CreateOutput<Surface>(nameof(Output), "OUTPUT", null);
+        Output = CreateOutput<Texture>(nameof(Output), "OUTPUT", null);
     }
 
-    protected override Surface? OnExecute(RenderingContext renderingContext)
+    protected override Texture? OnExecute(RenderingContext renderingContext)
     {
         if (StartNode == null)
         {
@@ -55,7 +55,7 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
         var width = size.X;
         var height = size.Y;
 
-        var surface = new Surface(size);
+        var surface = new Texture(size);
 
         var context = new FuncContext();
 
@@ -70,7 +70,7 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
                 
                 drawingPaint.Color = color;
 
-                surface.DrawingSurface.Canvas.DrawPixel(x, y, drawingPaint);
+                surface.Surface.Canvas.DrawPixel(x, y, drawingPaint);
             }
         }
 

+ 7 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs

@@ -23,7 +23,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
     public IReadOnlyList<OutputProperty> OutputProperties => outputs;
     public IReadOnlyList<KeyFrameData> KeyFrames => keyFrames;
 
-    public Surface? CachedResult
+    public Texture? CachedResult
     {
         get
         {
@@ -56,18 +56,18 @@ public abstract class Node : IReadOnlyNode, IDisposable
     private ChunkResolution? _lastResolution;
     private VecI? _lastChunkPos;
     private bool _keyFramesDirty;
-    private Surface? _lastCachedResult;
+    private Texture? _lastCachedResult;
     private bool _isDisposed;
 
-    public Surface? Execute(RenderingContext context)
+    public Texture? Execute(RenderingContext context)
     {
         var result = ExecuteInternal(context);
 
-        var copy = new Surface(result);
+        var copy = new Texture(result);
         return copy;
     }
 
-    internal Surface ExecuteInternal(RenderingContext context)
+    internal Texture ExecuteInternal(RenderingContext context)
     {
         if(_isDisposed) throw new ObjectDisposedException("Node was disposed before execution.");
         
@@ -76,14 +76,14 @@ public abstract class Node : IReadOnlyNode, IDisposable
         CachedResult = OnExecute(context);
         if (CachedResult is { IsDisposed: true })
         {
-            throw new ObjectDisposedException("Surface was disposed after execution.");
+            throw new ObjectDisposedException("Texture was disposed after execution.");
         }
 
         UpdateCache(context);
         return CachedResult;
     }
 
-    protected abstract Surface? OnExecute(RenderingContext context);
+    protected abstract Texture? OnExecute(RenderingContext context);
 
     protected virtual bool CacheChanged(RenderingContext context)
     {

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs

@@ -20,7 +20,7 @@ public class NoiseNode : Node
     private static readonly ColorFilter grayscaleFilter = ColorFilter.CreateColorMatrix(
         ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
     
-    public OutputProperty<Surface> Noise { get; }
+    public OutputProperty<Texture> Noise { get; }
     
     public InputProperty<NoiseType> NoiseType { get; }
     public InputProperty<VecI> Size { get; }
@@ -33,7 +33,7 @@ public class NoiseNode : Node
 
     public NoiseNode()
     {
-        Noise = CreateOutput<Surface>(nameof(Noise), "NOISE", null);
+        Noise = CreateOutput<Texture>(nameof(Noise), "NOISE", null);
         NoiseType = CreateInput(nameof(NoiseType), "NOISE_TYPE", Nodes.NoiseType.TurbulencePerlin);
         Size = CreateInput(nameof(Size), "SIZE", new VecI(64, 64));
         Scale = CreateInput(nameof(Scale), "SCALE", 10d);
@@ -41,7 +41,7 @@ public class NoiseNode : Node
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
     }
 
-    protected override Surface OnExecute(RenderingContext context)
+    protected override Texture OnExecute(RenderingContext context)
     {
         if (Math.Abs(previousScale - Scale.Value) > 0.000001
             || previousSeed != Seed.Value
@@ -81,9 +81,9 @@ public class NoiseNode : Node
             return null;
         }
         
-        var workingSurface = new Surface(size);
+        var workingSurface = new Texture(size);
        
-        workingSurface.DrawingSurface.Canvas.DrawPaint(paint);
+        workingSurface.Surface.Canvas.DrawPaint(paint);
 
         Noise.Value = workingSurface;
         

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs

@@ -11,10 +11,10 @@ public class OutputNode : Node, IBackgroundInput
     public const string InputPropertyName = "Background";
 
     public override string DisplayName { get; set; } = "OUTPUT_NODE";
-    public InputProperty<Surface?> Input { get; } 
+    public InputProperty<Texture?> Input { get; } 
     public OutputNode()
     {
-        Input = CreateInput<Surface>(InputPropertyName, "INPUT", null);
+        Input = CreateInput<Texture>(InputPropertyName, "INPUT", null);
     }
 
     public override Node CreateCopy()
@@ -22,10 +22,10 @@ public class OutputNode : Node, IBackgroundInput
         return new OutputNode();
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         return Input.Value;
     }
 
-    InputProperty<Surface?> IBackgroundInput.Background => Input;
+    InputProperty<Texture?> IBackgroundInput.Background => Input;
 }

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SampleImageNode.cs

@@ -12,7 +12,7 @@ public class SampleImageNode : Node
 {
     private Pixmap? pixmap;
 
-    public InputProperty<Surface?> Image { get; }
+    public InputProperty<Texture?> Image { get; }
 
     public FuncOutputProperty<VecD> Coordinate { get; }
 
@@ -22,7 +22,7 @@ public class SampleImageNode : Node
 
     public SampleImageNode()
     {
-        Image = CreateInput<Surface>(nameof(Surface), "IMAGE", null);
+        Image = CreateInput<Texture>(nameof(Texture), "IMAGE", null);
         Coordinate = CreateFuncOutput(nameof(Coordinate), "UV", ctx => ctx.Position);
         Color = CreateFuncOutput(nameof(Color), "COLOR", GetColor);
     }
@@ -42,10 +42,10 @@ public class SampleImageNode : Node
 
     internal void PreparePixmap()
     {
-        pixmap = Image.Value?.PeekPixels();
+        pixmap = Image.Value?.PeekReadOnlyPixels();
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Texture? OnExecute(RenderingContext context)
     {
         PreparePixmap();
         return Image.Value;

+ 44 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ShaderNode.cs

@@ -0,0 +1,44 @@
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+
+[NodeInfo("Shader")]
+public class ShaderNode : Node
+{
+    public override string DisplayName { get; set; } = "Shader Node";
+
+    public InputProperty<Texture> Input { get; set; }
+    public OutputProperty<Texture> Output { get; set; }
+    
+
+    private const string ShaderCode = @"
+       half4 main(float2 coord)
+       {
+           return half4(1.0, 0.0, 0.0, 1.0); 
+       } 
+";
+
+    private Paint paint = new Paint() { Shader = Shader.CreateFromSksl(ShaderCode, true, out _) };
+    
+    public ShaderNode()
+    {
+        Input = CreateInput<Texture>("Input", "INPUT", null);
+        Output = CreateOutput<Texture>("Output", "OUTPUT", null);
+    }
+    
+    protected override Texture? OnExecute(RenderingContext context)
+    {
+        if(Input.Value is null)
+            return null;
+        
+        Input.Value.Surface.Canvas.DrawPaint(paint);
+        return Input.Value;
+    }
+
+    public override Node CreateCopy()
+    {
+        return new ShaderNode();
+    }
+}

+ 21 - 20
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs

@@ -13,7 +13,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundInput
 {
-    public InputProperty<Surface?> Background { get; }
+    public InputProperty<Texture?> Background { get; }
     public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }
     public InputProperty<bool> ClipToPreviousMember { get; }
@@ -22,9 +22,9 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
     public InputProperty<bool> MaskIsVisible { get; }
     public InputProperty<Filter> Filters { get; }
 
-    public OutputProperty<Surface?> Output { get; }
+    public OutputProperty<Texture?> Output { get; }
 
-    public OutputProperty<Surface?> FilterlessOutput { get; }
+    public OutputProperty<Texture?> FilterlessOutput { get; }
 
     public string MemberName { get; set; } = "New Element"; // would be good to add localization here, it is set if node is created via node graph
     
@@ -34,13 +34,13 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         set => MemberName = value;
     }
 
-    protected Dictionary<(ChunkResolution, int), Surface> workingSurfaces = new Dictionary<(ChunkResolution, int), Surface>();
+    protected Dictionary<(ChunkResolution, int), Texture> workingSurfaces = new Dictionary<(ChunkResolution, int), Texture>();
     private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surfaces.BlendMode.DstIn };
     protected Paint blendPaint = new Paint();
 
     protected StructureNode()
     {
-        Background = CreateInput<Surface?>("Background", "BACKGROUND", null);
+        Background = CreateInput<Texture?>("Background", "BACKGROUND", null);
         Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
         IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
         ClipToPreviousMember = CreateInput<bool>("ClipToMemberBelow", "CLIP_TO_MEMBER_BELOW", false);
@@ -49,41 +49,41 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
         Filters = CreateInput<Filter>(nameof(Filters), "FILTERS", null);
 
-        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
-        FilterlessOutput = CreateOutput<Surface?>(nameof(FilterlessOutput), "WITHOUT_FILTERS", null);
+        Output = CreateOutput<Texture?>("Output", "OUTPUT", null);
+        FilterlessOutput = CreateOutput<Texture?>(nameof(FilterlessOutput), "WITHOUT_FILTERS", null);
     }
 
-    protected abstract override Surface? OnExecute(RenderingContext context);
+    protected abstract override Texture? OnExecute(RenderingContext context);
 
-    protected Surface TryInitWorkingSurface(VecI imageSize, RenderingContext context, int id)
+    protected Texture TryInitWorkingSurface(VecI imageSize, RenderingContext context, int id)
     {
         ChunkResolution targetResolution = context.ChunkResolution;
-        bool hasSurface = workingSurfaces.TryGetValue((targetResolution, id), out Surface workingSurface);
+        bool hasSurface = workingSurfaces.TryGetValue((targetResolution, id), out Texture workingSurface);
         VecI targetSize = (VecI)(imageSize * targetResolution.Multiplier());
 
         if (!hasSurface || workingSurface.Size != targetSize || workingSurface.IsDisposed)
         {
-            workingSurfaces[(targetResolution, id)] = new Surface(targetSize);
+            workingSurfaces[(targetResolution, id)] = new Texture(targetSize);
             workingSurface = workingSurfaces[(targetResolution, id)];
         }
 
         return workingSurface;
     }
 
-    protected void ApplyMaskIfPresent(Surface surface, RenderingContext context)
+    protected void ApplyMaskIfPresent(Texture surface, RenderingContext context)
     {
         if (Mask.Value != null && MaskIsVisible.Value)
         {
             Mask.Value.DrawMostUpToDateChunkOn(
                 context.ChunkToUpdate,
                 context.ChunkResolution,
-                surface.DrawingSurface,
+                surface.Surface,
                 context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
                 maskPaint);
         }
     }
 
-    protected void ApplyRasterClip(Surface surface, RenderingContext context)
+    protected void ApplyRasterClip(Texture surface, RenderingContext context)
     {
         if (ClipToPreviousMember.Value && Background.Value != null)
         {
@@ -92,7 +92,8 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
             VecI targetSize = new VecI(context.ChunkResolution.PixelSize());
             clippingRect = new RectI(chunkStart, targetSize);
 
-            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value, clippingRect);
+            //TODO: Implement this
+            // OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value, clippingRect);
         }
     }
 
@@ -107,24 +108,24 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         return (MaskIsVisible.Value && Mask.Value != null) || ClipToPreviousMember.Value;
     }
 
-    protected void DrawBackground(Surface workingSurface, RenderingContext context)
+    protected void DrawBackground(Texture workingSurface, RenderingContext context)
     {
         blendPaint.Color = Colors.White;
         DrawSurface(workingSurface, Background.Value, context, null); 
     }
 
-    protected void DrawSurface(Surface workingSurface, Surface source, RenderingContext context, Filter? filter)
+    protected void DrawSurface(Texture workingSurface, Texture source, RenderingContext context, Filter? filter)
     {
         // Maybe clip rect will allow to avoid snapshotting? Idk if it will be faster
         RectI sourceRect = CalculateSourceRect(source, workingSurface.Size, context);
         RectI targetRect = CalculateDestinationRect(context);
-        using var snapshot = source.DrawingSurface.Snapshot(sourceRect);
+        using var snapshot = source.Surface.Snapshot(sourceRect);
 
         blendPaint.SetFilters(filter);
-        workingSurface.DrawingSurface.Canvas.DrawImage(snapshot, targetRect.X, targetRect.Y, blendPaint);
+        workingSurface.Surface.Canvas.DrawImage(snapshot, targetRect.X, targetRect.Y, blendPaint);
     }
 
-    protected RectI CalculateSourceRect(Surface image, VecI targetSize, RenderingContext context)
+    protected RectI CalculateSourceRect(Texture image, VecI targetSize, RenderingContext context)
     {
         float multiplierToFit = image.Size.X / (float)targetSize.X;
         int chunkSize = context.ChunkResolution.PixelSize();

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

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

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

@@ -84,7 +84,7 @@ public static class FloodFillHelper
             if (!drawingChunks.ContainsKey(chunkPos))
             {
                 var chunk = Chunk.Create();
-                chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
+                chunk.Surface.Surface.Canvas.Clear(Colors.Transparent);
                 drawingChunks[chunkPos] = chunk;
             }
             var drawingChunk = drawingChunks[chunkPos];
@@ -95,7 +95,7 @@ public static class FloodFillHelper
             {
                 if (colorToReplace.A == 0 && !processedEmptyChunks.Contains(chunkPos))
                 {
-                    drawingChunk.Surface.DrawingSurface.Canvas.Clear(drawingColor);
+                    drawingChunk.Surface.Surface.Canvas.Clear(drawingColor);
                     for (int i = 0; i < chunkSize; i++)
                     {
                         if (chunkPos.Y > 0)
@@ -165,10 +165,10 @@ public static class FloodFillHelper
         byte[] pixelStates = new byte[chunkSize * chunkSize];
         DrawSelection(pixelStates, selection, globalSelectionBounds, chunkPos, chunkSize);
 
-        using var refPixmap = referenceChunk.Surface.DrawingSurface.PeekPixels();
+        using var refPixmap = referenceChunk.Surface.Surface.PeekPixels();
         Half* refArray = (Half*)refPixmap.GetPixels();
 
-        using var drawPixmap = drawingChunk.Surface.DrawingSurface.PeekPixels();
+        using var drawPixmap = drawingChunk.Surface.Surface.PeekPixels();
         Half* drawArray = (Half*)drawPixmap.GetPixels();
 
         Stack<VecI> toVisit = new();
@@ -195,18 +195,18 @@ public static class FloodFillHelper
         return pixelStates;
     }
 
-    public static Surface FillSelection(IReadOnlyDocument document, VectorPath selection)
+    public static Texture FillSelection(IReadOnlyDocument document, VectorPath selection)
     {
-        Surface surface = new Surface(document.Size);
+        Texture surface = new Texture(document.Size);
 
         var inverse = new VectorPath();
         inverse.AddRect(new RectI(new(0, 0), document.Size));
 
-        surface.DrawingSurface.Canvas.Clear(new Color(255, 255, 255, 255));
-        surface.DrawingSurface.Canvas.Flush();
-        surface.DrawingSurface.Canvas.ClipPath(inverse.Op(selection, VectorPathOp.Difference));
-        surface.DrawingSurface.Canvas.Clear(new Color(0, 0, 0, 0));
-        surface.DrawingSurface.Canvas.Flush();
+        surface.Surface.Canvas.Clear(new Color(255, 255, 255, 255));
+        surface.Surface.Canvas.Flush();
+        surface.Surface.Canvas.ClipPath(inverse.Op(selection, VectorPathOp.Difference));
+        surface.Surface.Canvas.Clear(new Color(0, 0, 0, 0));
+        surface.Surface.Canvas.Flush();
 
         return surface;
     }

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Drawing/PasteImage_UpdateableChange.cs

@@ -10,7 +10,7 @@ internal class PasteImage_UpdateableChange : UpdateableChange
     private readonly Guid memberGuid;
     private readonly bool ignoreClipsSymmetriesEtc;
     private readonly bool drawOnMask;
-    private readonly Surface imageToPaste;
+    private readonly Texture imageToPaste;
     private CommittedChunkStorage? savedChunks;
     private int? frame;
     private Guid? targetKeyFrameGuid;
@@ -19,13 +19,13 @@ internal class PasteImage_UpdateableChange : UpdateableChange
     private bool hasEnqueudImage = false;
 
     [GenerateUpdateableChangeActions]
-    public PasteImage_UpdateableChange(Surface image, ShapeCorners corners, Guid memberGuid, bool ignoreClipsSymmetriesEtc, bool isDrawingOnMask, int frame, Guid targetKeyFrameGuid)
+    public PasteImage_UpdateableChange(Texture image, ShapeCorners corners, Guid memberGuid, bool ignoreClipsSymmetriesEtc, bool isDrawingOnMask, int frame, Guid targetKeyFrameGuid)
     {
         this.corners = corners;
         this.memberGuid = memberGuid;
         this.ignoreClipsSymmetriesEtc = ignoreClipsSymmetriesEtc;
         this.drawOnMask = isDrawingOnMask;
-        this.imageToPaste = new Surface(image);
+        this.imageToPaste = new Texture(image);
         this.frame = frame;
         this.targetKeyFrameGuid = targetKeyFrameGuid;
     }

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

@@ -15,7 +15,7 @@ internal class TransformSelectedArea_UpdateableChange : UpdateableChange
     private bool keepOriginal;
     private ShapeCorners corners;
 
-    private Dictionary<Guid, (Surface surface, VecI pos)>? images;
+    private Dictionary<Guid, (Texture surface, VecI pos)>? images;
     private Matrix3X3 globalMatrix;
     private Dictionary<Guid, CommittedChunkStorage>? savedChunks;
 
@@ -74,7 +74,7 @@ internal class TransformSelectedArea_UpdateableChange : UpdateableChange
         return true;
     }
 
-    public OneOf<None, (Surface image, RectI extractedRect)> ExtractArea(ChunkyImage image, VectorPath path, RectI pathBounds)
+    public OneOf<None, (Texture image, RectI extractedRect)> ExtractArea(ChunkyImage image, VectorPath path, RectI pathBounds)
     {
         // get rid of transparent areas on edges
         var memberImageBounds = image.FindChunkAlignedMostUpToDateBounds();
@@ -90,11 +90,11 @@ internal class TransformSelectedArea_UpdateableChange : UpdateableChange
         clipPath.Transform(Matrix3X3.CreateTranslation(-pathBounds.X, -pathBounds.Y));
 
         // draw
-        Surface output = new(pathBounds.Size);
-        output.DrawingSurface.Canvas.Save();
-        output.DrawingSurface.Canvas.ClipPath(clipPath);
-        image.DrawMostUpToDateRegionOn(pathBounds, ChunkResolution.Full, output.DrawingSurface, VecI.Zero);
-        output.DrawingSurface.Canvas.Restore();
+        Texture output = new(pathBounds.Size);
+        output.Surface.Canvas.Save();
+        output.Surface.Canvas.ClipPath(clipPath);
+        image.DrawMostUpToDateRegionOn(pathBounds, ChunkResolution.Full, output.Surface, VecI.Zero);
+        output.Surface.Canvas.Restore();
 
         return (output, pathBounds);
     }
@@ -107,7 +107,7 @@ internal class TransformSelectedArea_UpdateableChange : UpdateableChange
         globalMatrix = OperationHelper.CreateMatrixFromPoints(corners, originalTightBounds.Size);
     }
 
-    private AffectedArea DrawImage(Document doc, Guid memberGuid, Surface image, VecI originalPos, ChunkyImage memberImage)
+    private AffectedArea DrawImage(Document doc, Guid memberGuid, Texture image, VecI originalPos, ChunkyImage memberImage)
     {
         var prevAffArea = memberImage.FindAffectedArea();
 

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs

@@ -68,9 +68,9 @@ public static class NodeOperations
         return node;
     }
 
-    public static List<ConnectProperty_ChangeInfo> AppendMember(InputProperty<Surface?> parentInput,
-        OutputProperty<Surface> toAddOutput,
-        InputProperty<Surface> toAddInput, Guid memberId)
+    public static List<ConnectProperty_ChangeInfo> AppendMember(InputProperty<Texture?> parentInput,
+        OutputProperty<Texture> toAddOutput,
+        InputProperty<Texture> toAddInput, Guid memberId)
     {
         List<ConnectProperty_ChangeInfo> changes = new();
         IOutputProperty? previouslyConnected = null;

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

@@ -72,19 +72,19 @@ internal sealed class FlipImage_Change : Change
             originalSurface.DrawingSurface,
             VecI.Zero);
 
-        using Surface flipped = new Surface(img.LatestSize);
+        using Texture flipped = new Texture(img.LatestSize);
 
         bool flipX = flipType == FlipType.Horizontal;
         bool flipY = flipType == FlipType.Vertical;
         
-        flipped.DrawingSurface.Canvas.Save();
-        flipped.DrawingSurface.Canvas.Scale(
+        flipped.Surface.Canvas.Save();
+        flipped.Surface.Canvas.Scale(
             flipX ? -1 : 1, 
             flipY ? -1 : 1, 
             flipX ? bounds.X + (bounds.Width / 2f) : 0,
             flipY ? bounds.Y + (bounds.Height / 2f) : 0f);
-        flipped.DrawingSurface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
-        flipped.DrawingSurface.Canvas.Restore();
+        flipped.Surface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
+        flipped.Surface.Canvas.Restore();
         
         img.EnqueueClear();
         img.EnqueueDrawImage(VecI.Zero, flipped);

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

@@ -62,11 +62,11 @@ internal class ResizeImage_Change : Change
         FilterQuality quality = ToFilterQuality(method, downscaling);
         using Paint paint = new() { FilterQuality = quality, BlendMode = BlendMode.Src, };
 
-        using Surface newSurface = new(newSize);
-        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();
+        using Texture newSurface = new(newSize);
+        newSurface.Surface.Canvas.Save();
+        newSurface.Surface.Canvas.Scale(newSize.X / (float)originalSize.X, newSize.Y / (float)originalSize.Y);
+        newSurface.Surface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
+        newSurface.Surface.Canvas.Restore();
 
         image.EnqueueResize(newSize);
         image.EnqueueClear();

+ 6 - 6
src/PixiEditor.ChangeableDocument/Changes/Root/RotateImage_Change.cs

@@ -89,7 +89,7 @@ internal sealed class RotateImage_Change : Change
             originalSurface.DrawingSurface,
             VecI.Zero);
 
-        using Surface flipped = new Surface(newSize);
+        using Texture flipped = new Texture(newSize);
 
         float translationX = newSize.X;
         float translationY = newSize.Y;
@@ -103,11 +103,11 @@ internal sealed class RotateImage_Change : Change
                 break;
         }
 
-        flipped.DrawingSurface.Canvas.Save();
-        flipped.DrawingSurface.Canvas.Translate(translationX, translationY);
-        flipped.DrawingSurface.Canvas.RotateRadians(RotationAngleToRadians(rotation), 0, 0);
-        flipped.DrawingSurface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
-        flipped.DrawingSurface.Canvas.Restore();
+        flipped.Surface.Canvas.Save();
+        flipped.Surface.Canvas.Translate(translationX, translationY);
+        flipped.Surface.Canvas.RotateRadians(RotationAngleToRadians(rotation), 0, 0);
+        flipped.Surface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
+        flipped.Surface.Canvas.Restore();
 
         if (membersToRotate.Count == 0)
         {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Selection/MagicWand/MagicWandHelper.cs

@@ -261,7 +261,7 @@ internal class MagicWandHelper
 
         bool[] pixelVisitedStates = new bool[chunkSize * chunkSize];
 
-        using var refPixmap = referenceChunk.Surface.DrawingSurface.PeekPixels();
+        using var refPixmap = referenceChunk.Surface.Surface.PeekPixels();
         Half* refArray = (Half*)refPixmap.GetPixels();
 
         Stack<VecI> toVisit = new();

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

@@ -46,9 +46,9 @@ internal class CreateStructureMember_Change : Change
 
         List<IChangeInfo> changes = new() { CreateChangeInfo(member) };
         
-        InputProperty<Surface> targetInput = parentNode.InputProperties.FirstOrDefault(x => 
-            x.ValueType == typeof(Surface) && 
-            x.Connection.Node is StructureNode) as InputProperty<Surface>;
+        InputProperty<Texture> targetInput = parentNode.InputProperties.FirstOrDefault(x => 
+            x.ValueType == typeof(Texture) && 
+            x.Connection.Node is StructureNode) as InputProperty<Texture>;
         
         
         
@@ -109,7 +109,7 @@ internal class CreateStructureMember_Change : Change
         return changes;
     }
 
-    private static void AppendFolder(InputProperty<Surface> backgroundInput, FolderNode folder, List<IChangeInfo> changes)
+    private static void AppendFolder(InputProperty<Texture> backgroundInput, FolderNode folder, List<IChangeInfo> changes)
     {
         var appened = NodeOperations.AppendMember(backgroundInput, folder.Output, folder.Background, folder.Id);
         changes.AddRange(appened);

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Structure/DuplicateLayer_Change.cs

@@ -40,9 +40,9 @@ internal class DuplicateLayer_Change : Change
         LayerNode clone = (LayerNode)existingLayer.Clone();
         clone.Id = duplicateGuid;
 
-        InputProperty<Surface?> targetInput = parent.InputProperties.FirstOrDefault(x =>
-            x.ValueType == typeof(Surface) &&
-            x.Connection.Node is StructureNode) as InputProperty<Surface?>;
+        InputProperty<Texture?> targetInput = parent.InputProperties.FirstOrDefault(x =>
+            x.ValueType == typeof(Texture) &&
+            x.Connection.Node is StructureNode) as InputProperty<Texture?>;
 
         List<IChangeInfo> operations = new();
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs

@@ -51,7 +51,7 @@ internal class MoveStructureMember_Change : Change
         
         Guid oldBackgroundId = sourceNode.Background.Node.Id;
 
-        InputProperty<Surface?> inputProperty = backgroundInput.Background;
+        InputProperty<Texture?> inputProperty = backgroundInput.Background;
 
         if (targetNode is FolderNode folder && putInsideFolder)
         {

+ 16 - 16
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -25,7 +25,7 @@ public class DocumentRenderer
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Surface? evaluated = Document.NodeGraph.Execute(context);
+            Texture? evaluated = Document.NodeGraph.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();
@@ -33,12 +33,12 @@ public class DocumentRenderer
 
             Chunk chunk = Chunk.Create(resolution);
 
-            chunk.Surface.DrawingSurface.Canvas.Save();
-            chunk.Surface.DrawingSurface.Canvas.Clear();
+            chunk.Surface.Surface.Canvas.Save();
+            chunk.Surface.Surface.Canvas.Clear();
 
             if (transformedClippingRect is not null)
             {
-                chunk.Surface.DrawingSurface.Canvas.ClipRect((RectD)transformedClippingRect);
+                chunk.Surface.Surface.Canvas.ClipRect((RectD)transformedClippingRect);
             }
 
             VecD pos = chunkPos;
@@ -59,11 +59,11 @@ public class DocumentRenderer
                 return new EmptyChunk();
             }
 
-            using var chunkSnapshot = evaluated.DrawingSurface.Snapshot((RectI)sourceRect);
+            using var chunkSnapshot = evaluated.Surface.Snapshot((RectI)sourceRect);
 
-            chunk.Surface.DrawingSurface.Canvas.DrawImage(chunkSnapshot, 0, 0, context.ReplacingPaintWithOpacity);
+            chunk.Surface.Surface.Canvas.DrawImage(chunkSnapshot, 0, 0, context.ReplacingPaintWithOpacity);
 
-            chunk.Surface.DrawingSurface.Canvas.Restore();
+            chunk.Surface.Surface.Canvas.Restore();
 
             return chunk;
         }
@@ -81,7 +81,7 @@ public class DocumentRenderer
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Surface? evaluated = node.Execute(context);
+            Texture? evaluated = node.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();
@@ -112,7 +112,7 @@ public class DocumentRenderer
         NodeGraph membersOnlyGraph = ConstructMembersOnlyGraph(layersToCombine, Document.NodeGraph);
         try
         {
-            Surface? evaluated = membersOnlyGraph.Execute(context);
+            Texture? evaluated = membersOnlyGraph.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();
@@ -147,7 +147,7 @@ public class DocumentRenderer
             }
         });
 
-        IInputProperty<Surface> lastInput = outputNode.Input;
+        IInputProperty<Texture> lastInput = outputNode.Input;
 
         foreach (var layer in layersInOrder)
         {
@@ -162,28 +162,28 @@ public class DocumentRenderer
     }
 
     private static OneOf<Chunk, EmptyChunk> ChunkFromResult(ChunkResolution resolution,
-        RectI? transformedClippingRect, Surface evaluated,
+        RectI? transformedClippingRect, Texture evaluated,
         RenderingContext context)
     {
         Chunk chunk = Chunk.Create(resolution);
 
-        chunk.Surface.DrawingSurface.Canvas.Save();
-        chunk.Surface.DrawingSurface.Canvas.Clear();
+        chunk.Surface.Surface.Canvas.Save();
+        chunk.Surface.Surface.Canvas.Clear();
 
         int x = 0;
         int y = 0;
 
         if (transformedClippingRect is not null)
         {
-            chunk.Surface.DrawingSurface.Canvas.ClipRect((RectD)transformedClippingRect);
+            chunk.Surface.Surface.Canvas.ClipRect((RectD)transformedClippingRect);
             x = transformedClippingRect.Value.X;
             y = transformedClippingRect.Value.Y;
         }
 
-        chunk.Surface.DrawingSurface.Canvas.DrawSurface(evaluated.DrawingSurface, x, y,
+        chunk.Surface.Surface.Canvas.DrawSurface(evaluated.Surface, x, y,
             context.ReplacingPaintWithOpacity);
 
-        chunk.Surface.DrawingSurface.Canvas.Restore();
+        chunk.Surface.Surface.Canvas.Restore();
 
         return chunk;
     }

+ 76 - 7
src/PixiEditor.DrawingApi.Core/Texture.cs

@@ -14,12 +14,15 @@ public class Texture : IDisposable
     public DrawingSurface Surface { get; }
 
     public event SurfaceChangedEventHandler? Changed;
-    
+
     public bool IsDisposed { get; private set; }
-    
+
     private bool pixmapUpToDate;
     private Pixmap pixmap;
 
+    private Paint nearestNeighborReplacingPaint =
+        new() { BlendMode = BlendMode.Src, FilterQuality = FilterQuality.None };
+
     public Texture(VecI size)
     {
         Size = size;
@@ -29,12 +32,27 @@ public class Texture : IDisposable
                 {
                     GpuBacked = true
                 });
-        
+
         Surface.Changed += SurfaceOnChanged;
     }
 
+    public Texture(Texture createFrom)
+    {
+        Size = createFrom.Size;
+
+        Surface =
+            DrawingSurface.Create(
+                new ImageInfo(Size.X, Size.Y, ColorType.RgbaF16, AlphaType.Premul, ColorSpace.CreateSrgb())
+                {
+                    GpuBacked = true
+                });
+
+        Surface.Canvas.DrawSurface(createFrom.Surface, 0, 0);
+    }
+
     private void SurfaceOnChanged(RectD? changedRect)
     {
+        pixmapUpToDate = false;
         Changed?.Invoke(changedRect);
     }
 
@@ -95,17 +113,17 @@ public class Texture : IDisposable
         return newTexture;
     }
 
-    public Color? GetSRGBPixel(VecI vecI)
+    public Color GetSRGBPixel(VecI vecI)
     {
         if (vecI.X < 0 || vecI.X >= Size.X || vecI.Y < 0 || vecI.Y >= Size.Y)
-            return null;
-        
+            return Color.Empty;
+
         if (!pixmapUpToDate)
         {
             pixmapUpToDate = true;
             pixmap = Surface.PeekPixels();
         }
-        
+
         return pixmap.GetPixelColor(vecI);
     }
 
@@ -118,4 +136,55 @@ public class Texture : IDisposable
         Surface.Changed -= SurfaceOnChanged;
         Surface.Dispose();
     }
+
+    public Pixmap? PeekReadOnlyPixels()
+    {
+        if (pixmapUpToDate)
+        {
+            return pixmap;
+        }
+
+        pixmap = Surface.PeekPixels();
+        pixmapUpToDate = true;
+
+        return pixmap;
+    }
+
+    public void CopyTo(Texture destination)
+    {
+        destination.Surface.Canvas.DrawSurface(Surface, 0, 0);
+    }
+
+    public unsafe bool IsFullyTransparent()
+    {
+        ulong* ptr = (ulong*)PeekReadOnlyPixels().GetPixels();
+        for (int i = 0; i < Size.X * Size.Y; i++)
+        {
+            // ptr[i] actually contains 4 16-bit floats. We only care about the first one which is alpha.
+            // An empty pixel can have alpha of 0 or -0 (not sure if -0 actually ever comes up). 0 in hex is 0x0, -0 in hex is 0x8000
+            if ((ptr[i] & 0x1111_0000_0000_0000) != 0 && (ptr[i] & 0x1111_0000_0000_0000) != 0x8000_0000_0000_0000)
+                return false;
+        }
+
+        return true;
+    }
+
+    public void DrawBytes(VecI surfaceSize, byte[] pixels, ColorType color, AlphaType alphaType)
+    {
+        if (surfaceSize != Size)
+            throw new ArgumentException("Surface size must match the size of the byte array");
+
+        using Image image = Image.FromPixels(new ImageInfo(Size.X, Size.Y, color, alphaType, ColorSpace.CreateSrgb()),
+            pixels);
+        Surface.Canvas.DrawImage(image, 0, 0);
+    }
+
+    public Texture ResizeNearestNeighbor(VecI newSize)
+    {
+        using Image image = Surface.Snapshot();
+        Texture newSurface = new(newSize);
+        newSurface.Surface.Canvas.DrawImage(image, new RectD(0, 0, newSize.X, newSize.Y),
+            nearestNeighborReplacingPaint);
+        return newSurface;
+    }
 }

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

@@ -279,7 +279,7 @@ internal class NodeGraphBuilder
         return this;
     }
 
-    public NodeGraphBuilder WithImageLayerNode(string name, Surface image, out int id)
+    public NodeGraphBuilder WithImageLayerNode(string name, Texture image, out int id)
     {
         AllNodes.Add(
             this.WithNodeOfType(typeof(ImageLayerNode))

+ 3 - 0
src/PixiEditor/Helpers/Extensions/BitmapExtensions.cs

@@ -3,6 +3,9 @@ using System.Runtime.InteropServices;
 using Avalonia;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using Bitmap = Avalonia.Media.Imaging.Bitmap;
 
 namespace PixiEditor.Helpers.Extensions;
 

+ 20 - 2
src/PixiEditor/Helpers/SurfaceHelpers.cs

@@ -10,7 +10,7 @@ namespace PixiEditor.Helpers;
 
 public static class SurfaceHelpers
 {
-    public static Surface FromBitmap(Bitmap original)
+    public static Texture FromBitmap(Bitmap original)
     {
         if(original.Format == null) throw new ArgumentException("Bitmap format must be non-null");
 
@@ -21,7 +21,7 @@ public static class SurfaceHelpers
         int stride = (original.PixelSize.Width * original.Format.Value.BitsPerPixel + 7) / 8;
         byte[] pixels = original.ExtractPixels();
 
-        Surface surface = new Surface(new VecI(original.PixelSize.Width, original.PixelSize.Height));
+        Texture surface = new Texture(new VecI(original.PixelSize.Width, original.PixelSize.Height));
         surface.DrawBytes(surface.Size, pixels, color, alpha);
         return surface;
     }
@@ -53,4 +53,22 @@ public static class SurfaceHelpers
 
         return buffer;
     }
+    
+    public static unsafe byte[] ToByteArray(this Texture texture, ColorType colorType = ColorType.Bgra8888, AlphaType alphaType = AlphaType.Premul)
+    {
+        int width = texture.Size.X;
+        int height = texture.Size.Y;
+        var imageInfo = new ImageInfo(width, height, colorType, alphaType, ColorSpace.CreateSrgb());
+
+        byte[] buffer = new byte[width * height * imageInfo.BytesPerPixel];
+        fixed (void* pointer = buffer)
+        {
+            if (!texture.Surface.ReadPixels(imageInfo, new IntPtr(pointer), imageInfo.RowBytes, 0, 0))
+            {
+                throw new InvalidOperationException("Could not read texture into buffer");
+            }
+        }
+
+        return buffer;
+    }
 }

+ 2 - 2
src/PixiEditor/Models/Clipboard/DataImage.cs

@@ -5,7 +5,7 @@ using PixiEditor.Numerics;
 
 namespace PixiEditor.Models.Clipboard;
 
-public record struct DataImage(string? name, Surface image, VecI position)
+public record struct DataImage(string? name, Texture image, VecI position)
 {
-    public DataImage(Surface image, VecI position) : this(null, image, position) { }
+    public DataImage(Texture image, VecI position) : this(null, image, position) { }
 }

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

@@ -38,7 +38,7 @@ internal static class ClipboardController
     {
         Clipboard = clipboard;
     }
-    
+
     public static readonly string TempCopyFilePath = Path.Join(
         Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
         "PixiEditor",
@@ -74,7 +74,7 @@ internal static class ClipboardController
             Directory.CreateDirectory(Path.GetDirectoryName(TempCopyFilePath)!);
             await using FileStream fileStream = new FileStream(TempCopyFilePath, FileMode.Create, FileAccess.Write);
             await pngStream.CopyToAsync(fileStream);
-            data.SetFileDropList(new [] { TempCopyFilePath });
+            data.SetFileDropList(new[] { TempCopyFilePath });
         }
 
         WriteableBitmap finalBitmap = actuallySurface.ToWriteableBitmap();
@@ -185,7 +185,7 @@ internal static class ClipboardController
         }
 
         var paths = data.GetFileDropList().Select(x => x.Path.LocalPath).ToList();
-        if(paths != null && data.TryGetRawTextPath(out string? textPath))
+        if (paths != null && data.TryGetRawTextPath(out string? textPath))
         {
             paths.Add(textPath);
         }
@@ -201,17 +201,17 @@ internal static class ClipboardController
                 continue;
             try
             {
-                Surface imported;
+                Texture imported;
 
                 if (Path.GetExtension(path) == ".pixi")
                 {
                     using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
 
-                    imported = Surface.Load(PixiParser.ReadPreview(stream));
+                    imported = Texture.Load(PixiParser.ReadPreview(stream));
                 }
                 else
                 {
-                    imported = Surface.Load(path);
+                    imported = Texture.Load(path);
                 }
 
                 string filename = Path.GetFullPath(path);
@@ -301,7 +301,7 @@ internal static class ClipboardController
     private static Bitmap FromPNG(IDataObject data)
     {
         object obj = data.Get("PNG");
-        if(obj is byte[] bytes)
+        if (obj is byte[] bytes)
         {
             using MemoryStream stream = new MemoryStream(bytes);
             return new Bitmap(stream);
@@ -316,8 +316,8 @@ internal static class ClipboardController
     }
 
     private static bool HasData(IDataObject dataObject, params string[] formats) => formats.Any(dataObject.Contains);
-    
-    private static bool TryExtractSingleImage(IDataObject data, [NotNullWhen(true)] out Surface? result)
+
+    private static bool TryExtractSingleImage(IDataObject data, [NotNullWhen(true)] out Texture? result)
     {
         try
         {

+ 1 - 1
src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs

@@ -93,7 +93,7 @@ internal class ActionAccumulator
             var affectedAreas = new AffectedAreasGatherer(document.AnimationHandler.ActiveFrameTime, internals.Tracker, optimizedChanges);
             List<IRenderInfo> renderResult = new();
             renderResult.AddRange(await canvasUpdater.UpdateGatheredChunks(affectedAreas, undoBoundaryPassed || viewportRefreshRequest));
-            renderResult.AddRange(await previewUpdater.UpdateGatheredChunks(affectedAreas, undoBoundaryPassed));
+            //renderResult.AddRange(await previewUpdater.UpdateGatheredChunks(affectedAreas, undoBoundaryPassed));
 
             if (undoBoundaryPassed)
             {

+ 4 - 4
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -421,7 +421,7 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// </summary>
     /// <param name="image">The image to paste</param>
     /// <param name="startPos">Where the transform should start</param>
-    public void PasteImageWithTransform(Surface image, VecI startPos)
+    public void PasteImageWithTransform(Texture image, VecI startPos)
     {
         if (Document.SelectedStructureMember is null)
             return;
@@ -433,7 +433,7 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// </summary>
     /// <param name="image">The image to paste</param>
     /// <param name="startPos">Where the transform should start</param>
-    public void PasteImageWithTransform(Surface image, VecI startPos, Guid memberGuid, bool drawOnMask)
+    public void PasteImageWithTransform(Texture image, VecI startPos, Guid memberGuid, bool drawOnMask)
     {
         Internals.ChangeController.TryStartExecutor(new PasteImageExecutor(image, startPos, memberGuid, drawOnMask));
     }
@@ -460,7 +460,7 @@ internal class DocumentOperationsModule : IDocumentOperations
             Internals.ChangeController.TryStopActiveExecutor();
     }
 
-    public void DrawImage(Surface image, ShapeCorners corners, Guid memberGuid, bool ignoreClipSymmetriesEtc, bool drawOnMask, int frame) =>
+    public void DrawImage(Texture image, ShapeCorners corners, Guid memberGuid, bool ignoreClipSymmetriesEtc, bool drawOnMask, int frame) =>
         DrawImage(image, corners, memberGuid, ignoreClipSymmetriesEtc, drawOnMask, frame, true);
 
     /// <summary>
@@ -472,7 +472,7 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// <param name="ignoreClipSymmetriesEtc">Ignore selection clipping and symmetry (See DrawingChangeHelper.ApplyClipsSymmetriesEtc of UpdateableDocument)</param>
     /// <param name="drawOnMask">Draw on the mask or on the image</param>
     /// <param name="finish">Is this a finished action</param>
-    private void DrawImage(Surface image, ShapeCorners corners, Guid memberGuid, bool ignoreClipSymmetriesEtc, bool drawOnMask, int atFrame, bool finish)
+    private void DrawImage(Texture image, ShapeCorners corners, Guid memberGuid, bool ignoreClipSymmetriesEtc, bool drawOnMask, int atFrame, bool finish)
     {
         if (Internals.ChangeController.IsChangeActive)
             return;

+ 3 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs

@@ -11,18 +11,18 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
 internal class PasteImageExecutor : UpdateableChangeExecutor
 {
-    private readonly Surface image;
+    private readonly Texture image;
     private readonly VecI pos;
     private bool drawOnMask;
     private Guid? memberGuid;
 
-    public PasteImageExecutor(Surface image, VecI pos)
+    public PasteImageExecutor(Texture image, VecI pos)
     {
         this.image = image;
         this.pos = pos;
     }
 
-    public PasteImageExecutor(Surface image, VecI pos, Guid memberGuid, bool drawOnMask)
+    public PasteImageExecutor(Texture image, VecI pos, Guid memberGuid, bool drawOnMask)
     {
         this.image = image;
         this.pos = pos;

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

@@ -31,15 +31,15 @@ internal class Importer : ObservableObject
     /// <param name="path">Path of the image.</param>
     /// <param name="size">New size of the image.</param>
     /// <returns>WriteableBitmap of imported image.</returns>
-    public static Surface? ImportImage(string path, VecI size)
+    public static Texture? ImportImage(string path, VecI size)
     {
         if (!Path.Exists(path))
             throw new MissingFileException();
 
-        Surface original;
+        Texture original;
         try
         {
-            original = Surface.Load(path);
+            original = Texture.Load(path);
         }
         catch (Exception e) when (e is ArgumentException or FileNotFoundException)
         {
@@ -51,7 +51,7 @@ internal class Importer : ObservableObject
             return original;
         }
 
-        Surface resized = original.ResizeNearestNeighbor(size);
+        Texture resized = original.ResizeNearestNeighbor(size);
         original.Dispose();
         return resized;
     }

+ 41 - 43
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -200,17 +200,22 @@ internal class CanvasUpdater
             Texture screenSurface = doc.Surfaces[resolution];
             foreach (var chunkPos in chunks)
             {
-                RenderChunk(chunkPos, screenSurface, resolution, globalClippingRectangle,
-                    globalScaledClippingRectangle);
-                RectI chunkRect = new(chunkPos * chunkSize, new(chunkSize, chunkSize));
-                if (globalScaledClippingRectangle is RectI rect)
-                    chunkRect = chunkRect.Intersect(rect);
-
-                infos.Add(new DirtyRect_RenderInfo(
-                    chunkRect.Pos,
-                    chunkRect.Size,
-                    resolution
-                ));
+                // TODO render on dedicated thread
+                Dispatcher.UIThread.Post(
+                    () =>
+                    {
+                        RenderChunk(chunkPos, screenSurface, resolution, globalClippingRectangle,
+                            globalScaledClippingRectangle);
+                        RectI chunkRect = new(chunkPos * chunkSize, new(chunkSize, chunkSize));
+                        if (globalScaledClippingRectangle is RectI rect)
+                            chunkRect = chunkRect.Intersect(rect);
+
+                        infos.Add(new DirtyRect_RenderInfo(
+                            chunkRect.Pos,
+                            chunkRect.Size,
+                            resolution
+                        ));
+                    }, DispatcherPriority.Render);
             }
         }
     }
@@ -221,50 +226,43 @@ internal class CanvasUpdater
         if (screenSurface is null || screenSurface.IsDisposed)
             return;
 
-
         doc.Renderer.RenderChunk(chunkPos, resolution, doc.AnimationHandler.ActiveFrameTime, globalClippingRectangle)
             .Switch(
                 (Chunk chunk) =>
                 {
-                    Dispatcher.UIThread.Post(() =>
-                    {
-                        if (screenSurface.IsDisposed) return;
+                    if (screenSurface.IsDisposed) return;
 
-                        if (globalScaledClippingRectangle is not null)
-                        {
-                            screenSurface.Surface.Canvas.Save();
-                            screenSurface.Surface.Canvas.ClipRect((RectD)globalScaledClippingRectangle);
-                        }
+                    if (globalScaledClippingRectangle is not null)
+                    {
+                        screenSurface.Surface.Canvas.Save();
+                        screenSurface.Surface.Canvas.ClipRect((RectD)globalScaledClippingRectangle);
+                    }
 
-                        screenSurface.Surface.Canvas.DrawSurface(
-                            chunk.Surface.DrawingSurface,
-                            chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
-                        chunk.Dispose();
+                    screenSurface.Surface.Canvas.DrawSurface(
+                        chunk.Surface.Surface,
+                        chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
+                    chunk.Dispose();
 
 
-                        if (globalScaledClippingRectangle is not null)
-                            screenSurface.Surface.Canvas.Restore();
-                    });
+                    if (globalScaledClippingRectangle is not null)
+                        screenSurface.Surface.Canvas.Restore();
                 },
                 (EmptyChunk _) =>
                 {
-                    Dispatcher.UIThread.Post(() =>
+                    if (screenSurface.IsDisposed) return;
+
+                    if (globalScaledClippingRectangle is not null)
                     {
-                        if (screenSurface.IsDisposed) return;
-
-                        if (globalScaledClippingRectangle is not null)
-                        {
-                            screenSurface.Surface.Canvas.Save();
-                            screenSurface.Surface.Canvas.ClipRect((RectD)globalScaledClippingRectangle);
-                        }
-
-                        var pos = chunkPos * resolution.PixelSize();
-                        screenSurface.Surface.Canvas.DrawRect(pos.X, pos.Y, resolution.PixelSize(),
-                            resolution.PixelSize(), ClearPaint);
-                        
-                        if (globalScaledClippingRectangle is not null)
-                            screenSurface.Surface.Canvas.Restore();
-                    });
+                        screenSurface.Surface.Canvas.Save();
+                        screenSurface.Surface.Canvas.ClipRect((RectD)globalScaledClippingRectangle);
+                    }
+
+                    var pos = chunkPos * resolution.PixelSize();
+                    screenSurface.Surface.Canvas.DrawRect(pos.X, pos.Y, resolution.PixelSize(),
+                        resolution.PixelSize(), ClearPaint);
+
+                    if (globalScaledClippingRectangle is not null)
+                        screenSurface.Surface.Canvas.Restore();
                 });
     }
 }

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

@@ -587,7 +587,7 @@ internal class MemberPreviewUpdater
 
                 if (rendered.IsT0)
                 {
-                    memberVM.PreviewSurface.Surface.Canvas.DrawSurface(rendered.AsT0.Surface.DrawingSurface, pos,
+                    memberVM.PreviewSurface.Surface.Canvas.DrawSurface(rendered.AsT0.Surface.Surface, pos,
                         scaling < smoothingThreshold ? SmoothReplacingPaint : ReplacingPaint);
                     rendered.AsT0.Dispose();
                 }
@@ -761,7 +761,7 @@ internal class MemberPreviewUpdater
 
                 RectI region = new RectI(0, 0, node.CachedResult.Size.X, node.CachedResult.Size.Y);
 
-                nodeVm.ResultPreview.Surface.Canvas.DrawSurface(node.CachedResult.DrawingSurface, 0, 0,
+                nodeVm.ResultPreview.Surface.Canvas.DrawSurface(node.CachedResult.Surface, 0, 0,
                     ReplacingPaint);
 
                 nodeVm.ResultPreview.Surface.Canvas.Restore();

+ 3 - 3
src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs

@@ -17,10 +17,10 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
         var encoder = Config.Encoder;
         surfaceFactory.Config = Config;
 
-        Surface surface = new Surface(original.LatestSize);
+        Texture surface = new Texture(original.LatestSize);
         original.DrawMostUpToDateRegionOn(
             new RectI(0, 0, original.LatestSize.X,
-                original.LatestSize.Y), ChunkResolution.Full, surface.DrawingSurface, new VecI(0, 0), new Paint());
+                original.LatestSize.Y), ChunkResolution.Full, surface.Surface, new VecI(0, 0), new Paint());
 
         return surfaceFactory.Serialize(surface);
     }
@@ -30,7 +30,7 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
         if (serialized is byte[] imgBytes)
         {
             surfaceFactory.Config = Config;
-            if (!surfaceFactory.TryDeserialize(imgBytes, out Surface surface))
+            if (!surfaceFactory.TryDeserialize(imgBytes, out Texture surface))
             {
                 original = null;
                 return false;

+ 6 - 6
src/PixiEditor/Models/Serialization/Factories/SurfaceSerializationFactory.cs

@@ -7,9 +7,9 @@ using PixiEditor.Parser.Skia;
 
 namespace PixiEditor.Models.Serialization.Factories;
 
-public class SurfaceSerializationFactory : SerializationFactory<byte[], Surface>
+public class SurfaceSerializationFactory : SerializationFactory<byte[], Texture>
 {
-    public override byte[] Serialize(Surface original)
+    public override byte[] Serialize(Texture original)
     {
         var encoder = Config.Encoder;
         byte[] result = encoder.Encode(original.ToByteArray(), original.Size.X, original.Size.Y);
@@ -17,7 +17,7 @@ public class SurfaceSerializationFactory : SerializationFactory<byte[], Surface>
         return result;
     }
 
-    public override bool TryDeserialize(object serialized, out Surface original)
+    public override bool TryDeserialize(object serialized, out Texture original)
     {
         if (serialized is byte[] imgBytes)
         {
@@ -30,13 +30,13 @@ public class SurfaceSerializationFactory : SerializationFactory<byte[], Surface>
     }
 
 
-    public static Surface DecodeSurface(byte[] imgBytes, ImageEncoder encoder)
+    public static Texture DecodeSurface(byte[] imgBytes, ImageEncoder encoder)
     {
         byte[] decoded =
             encoder.Decode(imgBytes, out SKImageInfo info);
         using Image img = Image.FromPixels(info.ToImageInfo(), decoded);
-        Surface surface = new Surface(img.Size);
-        surface.DrawingSurface.Canvas.DrawImage(img, 0, 0);
+        Texture surface = new Texture(img.Size);
+        surface.Surface.Canvas.DrawImage(img, 0, 0);
 
         return surface;
     }

+ 5 - 5
src/PixiEditor/Styles/Templates/NodeView.axaml

@@ -55,14 +55,14 @@
                             </Border>
                             <Border IsVisible="{Binding !!ResultPreview, RelativeSource={RelativeSource TemplatedParent}}"
                                 CornerRadius="0, 0, 4.5, 4.5" Grid.Row="2" ClipToBounds="True">
-                                <visuals:SurfaceControl Width="200" Height="200"
-                                                        Surface="{TemplateBinding ResultPreview}"
+                                <visuals:TextureControl Width="200" Height="200"
+                                                        Texture="{TemplateBinding ResultPreview}"
                                                         RenderOptions.BitmapInterpolationMode="None">
-                                    <visuals:SurfaceControl.Background>
+                                    <visuals:TextureControl.Background>
                                         <ImageBrush Source="/Images/CheckerTile.png"
                                                     TileMode="Tile" DestinationRect="0, 0, 25, 25" />
-                                    </visuals:SurfaceControl.Background>
-                                </visuals:SurfaceControl>
+                                    </visuals:TextureControl.Background>
+                                </visuals:TextureControl>
                             </Border>
                         </Grid>
                     </Border>

+ 1 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -512,7 +512,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                         continue;
                     using Chunk chunk = maybeChunk.AsT0;
                     finalSurface.DrawingSurface.Canvas.DrawSurface(
-                        chunk.Surface.DrawingSurface,
+                        chunk.Surface.Surface,
                         i * ChunkyImage.FullChunkSize, j * ChunkyImage.FullChunkSize);
                 }
             }

+ 3 - 2
src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs

@@ -63,13 +63,14 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         DataImage imageData = (data == null ? await ClipboardController.GetImagesFromClipboard() : ClipboardController.GetImage(data)).First();
         using var surface = imageData.image;
 
-        var bitmap = imageData.image.ToWriteableBitmap();
+        // TODO: Implement this
+        /*var bitmap = imageData.image.ToWriteableBitmap();
 
         byte[] pixels = bitmap.ExtractPixels();
 
         doc.Operations.ImportReferenceLayer(
             pixels.ToImmutableArray(),
-            imageData.image.Size);
+            imageData.image.Size);*/
 
         if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
         {

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs

@@ -267,7 +267,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     /// Opens a regular image file from path, creates a document from it, and adds it to the system.
     /// </summary>
-    private void OpenRegularImage(Surface surface, string path)
+    private void OpenRegularImage(Texture surface, string path)
     {
         DocumentViewModel doc = NewDocument(b => b
             .WithSize(surface.Size)

+ 0 - 1
src/PixiEditor/Views/Rendering/Scene.cs

@@ -527,7 +527,6 @@ internal class DrawSceneOperation : SkiaDrawOperation
 
         using var ctx = DrawingBackendApi.Current.RenderOnDifferentGrContext(lease.GrContext);
 
-
         var matrixValues = new float[ColorMatrix.Width * ColorMatrix.Height];
         ColorMatrix.TryGetMembers(matrixValues);
 

+ 20 - 5
src/PixiEditor/Views/Visuals/TextureControl.cs

@@ -21,6 +21,10 @@ public class TextureControl : Control
     public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<TextureControl, Stretch>(
         nameof(Stretch), Stretch.Uniform);
 
+    public static readonly StyledProperty<IBrush> BackgroundProperty =
+        AvaloniaProperty.Register<TextureControl, IBrush>(
+            nameof(Background));
+
     public Stretch Stretch
     {
         get => GetValue(StretchProperty);
@@ -33,6 +37,12 @@ public class TextureControl : Control
         set => SetValue(TextureProperty, value);
     }
 
+    public IBrush Background
+    {
+        get { return (IBrush)GetValue(BackgroundProperty); }
+        set { SetValue(BackgroundProperty, value); }
+    }
+
     static TextureControl()
     {
         AffectsRender<TextureControl>(TextureProperty, StretchProperty);
@@ -87,11 +97,16 @@ public class TextureControl : Control
 
     public override void Render(DrawingContext context)
     {
+        if (Background != null)
+        {
+            context.FillRectangle(Background, new Rect(0, 0, Bounds.Width, Bounds.Height));
+        }
+
         if (Texture == null)
         {
             return;
         }
-        
+
         Texture texture = Texture;
         texture.Surface.Flush();
         ICustomDrawOperation drawOperation = new DrawTextureOperation(
@@ -101,20 +116,20 @@ public class TextureControl : Control
 
         context.Custom(drawOperation);
     }
-    
+
     private void OnTextureChanged(AvaloniaPropertyChangedEventArgs<Texture> args)
     {
         if (args.OldValue.Value != null)
         {
             args.OldValue.Value.Changed -= TextureOnChanged;
         }
-        
+
         if (args.NewValue.Value != null)
         {
             args.NewValue.Value.Changed += TextureOnChanged;
         }
     }
-    
+
     private void TextureOnChanged(RectD? changedRect)
     {
         Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Render);
@@ -143,7 +158,7 @@ internal class DrawTextureOperation : SkiaDrawOperation
         {
             return;
         }
-        
+
         SKCanvas canvas = lease.SkCanvas;
 
         using var ctx = DrawingBackendApi.Current.RenderOnDifferentGrContext(lease.GrContext);