Browse Source

Image => Surface

flabbet 1 year ago
parent
commit
f85b704c8a
29 changed files with 111 additions and 121 deletions
  1. 6 1
      src/ChunkyImageLib/Surface.cs
  2. 3 3
      src/PixiEditor.AvaloniaUI/Models/Rendering/MemberPreviewUpdater.cs
  3. 10 5
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  4. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IBackgroundInput.cs
  5. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs
  6. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs
  7. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
  8. 6 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CircleNode.cs
  9. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineColorNode.cs
  10. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs
  11. 7 8
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  12. 5 24
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  13. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSizeNode.cs
  14. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs
  15. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs
  16. 14 15
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  17. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  18. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  19. 8 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  20. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  21. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  22. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateColorNode.cs
  23. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateVecDNode.cs
  24. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  25. 3 3
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs
  26. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  27. 0 1
      src/PixiEditor.ChangeableDocument/PixiEditor.ChangeableDocument.csproj
  28. 6 4
      src/PixiEditor.ChangeableDocument/Rendering/DocumentEvaluator.cs
  29. 1 1
      src/PixiEditor.UI.Common/Accents/Base.axaml

+ 6 - 1
src/ChunkyImageLib/Surface.cs

@@ -10,7 +10,7 @@ using PixiEditor.Numerics;
 
 namespace ChunkyImageLib;
 
-public class Surface : IDisposable, ICloneable
+public class Surface : IDisposable, ICloneable, IPixelsMap
 {
     private bool disposed;
     public IntPtr PixelBuffer { get; }
@@ -232,6 +232,11 @@ public class Surface : IDisposable, ICloneable
         Marshal.FreeHGlobal(PixelBuffer);
     }
 
+    public Pixmap PeekPixels()
+    {
+        return DrawingSurface.PeekPixels(); 
+    }
+
     public object Clone()
     {
         return new Surface(this);

+ 3 - 3
src/PixiEditor.AvaloniaUI/Models/Rendering/MemberPreviewUpdater.cs

@@ -698,8 +698,8 @@ internal class MemberPreviewUpdater
                      nodeVm.ResultPreview = new Surface(StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size));
                }
                
-               float scalingX = (float)nodeVm.ResultPreview.Size.X / node.CachedResult.Width;
-               float scalingY = (float)nodeVm.ResultPreview.Size.Y / node.CachedResult.Height;
+               float scalingX = (float)nodeVm.ResultPreview.Size.X / node.CachedResult.Size.X;
+               float scalingY = (float)nodeVm.ResultPreview.Size.Y / node.CachedResult.Size.Y;
                
                nodeVm.ResultPreview.DrawingSurface.Canvas.Save();
                nodeVm.ResultPreview.DrawingSurface.Canvas.Scale(scalingX, scalingY);
@@ -708,7 +708,7 @@ internal class MemberPreviewUpdater
 
                if (node.CachedResult != null)
                {
-                   nodeVm.ResultPreview.DrawingSurface.Canvas.DrawImage(node.CachedResult, new RectD(0, 0, node.CachedResult.Width, node.CachedResult.Height), ReplacingPaint);
+                   nodeVm.ResultPreview.DrawingSurface.Canvas.DrawSurface(node.CachedResult.DrawingSurface, 0, 0, ReplacingPaint);
                }
 
                nodeVm.ResultPreview.DrawingSurface.Canvas.Restore();

+ 10 - 5
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -84,7 +84,7 @@ internal class Document : IChangeable, IReadOnlyDocument, IDisposable
 
         using var paint = new Paint();
 
-        Image image;
+        Surface image;
         
         if (layer is IReadOnlyImageNode imageNode)
         {
@@ -95,17 +95,22 @@ internal class Document : IChangeable, IReadOnlyDocument, IDisposable
                 ChunkResolution.Full,
                 chunkSurface.DrawingSurface,
                 VecI.Zero);
-            
-            image = chunkSurface.DrawingSurface.Snapshot();
+
+            image = chunkSurface;
         }
         else
         {
             image = layer.Execute(new RenderingContext(frame));
         }
         
-        surface.DrawingSurface.Canvas.DrawImage(image, (RectD)tightBounds.Value, paint);
+        //todo: idk if it's correct
+        surface.DrawingSurface.Canvas.DrawSurface(image.DrawingSurface, 0, 0, paint);
+
+        var snapshot = surface.DrawingSurface.Snapshot();
+        surface.Dispose();
+        image.Dispose();
 
-        return surface.DrawingSurface.Snapshot();
+        return snapshot;
     }
 
     public RectI? GetChunkAlignedLayerBounds(Guid layerGuid, int frame)

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

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

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

@@ -11,11 +11,11 @@ public interface IReadOnlyNode
     public IReadOnlyCollection<IInputProperty> InputProperties { get; }
     public IReadOnlyCollection<IOutputProperty> OutputProperties { get; }
     public VecD Position { get; }
-    public Image? CachedResult { get; }
+    public Surface? CachedResult { get; }
     
     public string InternalName { get; }
 
-    public Image? Execute(RenderingContext context);
+    public Surface? Execute(RenderingContext context);
     public bool Validate();
     
     /// <summary>

+ 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 Image? Execute(RenderingContext context);
+    public Surface? Execute(RenderingContext context);
 }

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

@@ -96,7 +96,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         return true;
     }
 
-    public Image? Execute(RenderingContext context)
+    public Surface? Execute(RenderingContext context)
     {
         Stopwatch stopwatch = new();
         if (OutputNode == null) return null;

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

@@ -1,8 +1,5 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Surface;
-using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
 using PixiEditor.Numerics;
 
@@ -16,7 +13,7 @@ public class CircleNode : Node
     public InputProperty<Color> StrokeColor { get; }
     public InputProperty<Color> FillColor { get; }
     public InputProperty<int> StrokeWidth { get; }
-    public OutputProperty<Image> Output { get; }
+    public OutputProperty<Surface> Output { get; }
     
     public CircleNode() 
     {
@@ -26,10 +23,10 @@ public class CircleNode : 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<Image?>("Output", "OUTPUT", null);
+        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
     }
     
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         Surface workingSurface = new Surface(new VecI(Radius.Value * 2, Radius.Value * 2));
         
@@ -38,10 +35,8 @@ public class CircleNode : Node
         paint.Color = StrokeColor.Value;
         
         workingSurface.DrawingSurface.Canvas.DrawCircle(Radius.Value, Radius.Value, Radius.Value, paint);
-        
-        Output.Value = workingSurface.DrawingSurface.Snapshot();
-        
-        workingSurface.Dispose();
+
+        Output.Value = workingSurface;
         
         return Output.Value;
     }

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

@@ -38,7 +38,7 @@ public class CombineColorNode : Node
         return new Color((byte)r, (byte)g, (byte)b, (byte)a);
     }
     
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -11,7 +11,7 @@ public class CreateImageNode : Node
 {
     private Paint _paint = new();
     
-    public OutputProperty<Image> Output { get; }
+    public OutputProperty<Surface> Output { get; }
 
     public InputProperty<VecI> Size { get; }
     
@@ -19,19 +19,19 @@ public class CreateImageNode : Node
 
     public CreateImageNode()
     {
-        Output = CreateOutput<Image>(nameof(Output), "EMPTY_IMAGE", null);
+        Output = CreateOutput<Surface>(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 Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
-        using var surface = new Surface(Size.Value);
+        var surface = new Surface(Size.Value);
 
         _paint.Color = Fill.Value;
         surface.DrawingSurface.Canvas.DrawPaint(_paint);
 
-        Output.Value = surface.DrawingSurface.Snapshot();
+        Output.Value = surface;
 
         return Output.Value;
     }

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

@@ -8,11 +8,11 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public class FolderNode : StructureNode, IReadOnlyFolderNode
 {
-    public InputProperty<Image?> Content { get; }
+    public InputProperty<Surface?> Content { get; }
 
     public FolderNode()
     {
-        Content = CreateInput<Image?>("Content", "CONTENT", null);
+        Content = CreateInput<Surface?>("Content", "CONTENT", null);
     }
 
     public override bool Validate()
@@ -22,7 +22,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
 
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Content.Value == null)
         {
@@ -36,24 +36,23 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
         if (Background.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawImage(Background.Value, 0, 0);
+            workingSurface.DrawingSurface.Canvas.DrawSurface(Background.Value.DrawingSurface, 0, 0);
         }
 
         if (Content.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawImage(Content.Value, 0, 0);
+            workingSurface.DrawingSurface.Canvas.DrawSurface(Content.Value.DrawingSurface, 0, 0);
         }
 
-        Output.Value = workingSurface.DrawingSurface.Snapshot();
+        Output.Value = workingSurface;
         
-        workingSurface.Dispose();
         return Output.Value;
     }
 
     public override RectI? GetTightBounds(KeyFrameTime frameTime)
     {
         // TODO: Implement GetTightBounds
-        return RectI.Create(0, 0, Content.Value?.Width ?? 0, Content.Value?.Height ?? 0); 
+        return RectI.Create(0, 0, Content.Value?.Size.X ?? 0, Content.Value?.Size.Y ?? 0); 
         /*if (Children.Count == 0)
       {
           return null;

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

@@ -45,7 +45,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return true;
     }
 
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
@@ -58,9 +58,9 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         blendPaint.Color = new Color(255, 255, 255, (byte)Math.Round(Opacity.Value * 255));
         blendPaint.BlendMode = DrawingApi.Core.Surface.BlendMode.Src;
 
-        var finalSurface = RenderImage(frameImage, context);
+        var renderedSurface = RenderImage(frameImage, context);
 
-        Output.Value = finalSurface.DrawingSurface.Snapshot();
+        Output.Value = renderedSurface;
         
         return Output.Value;
     }
@@ -80,8 +80,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         if (Background.Value != null)
         {
             RectD sourceRect = CalculateSourceRect(Background.Value, targetSize, context);
-            RectD destRect = CalculateDestRect(Background.Value, targetSize, context);
-            workingSurface.DrawingSurface.Canvas.DrawImage(Background.Value, sourceRect, destRect, blendPaint);
+            workingSurface.DrawingSurface.Canvas.DrawSurface(Background.Value.DrawingSurface, (int)sourceRect.X, (int)sourceRect.Y, blendPaint);
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
         }
 
@@ -162,7 +161,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         }
     }
     
-    private RectD CalculateSourceRect(Image image, VecI targetSize, RenderingContext context)
+    private RectD CalculateSourceRect(Surface image, VecI targetSize, RenderingContext context)
     {
         if(context.Resolution == null || context.ChunkToUpdate == null)
         {
@@ -180,24 +179,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         
         return new RectD(x, y, width, height);
     }
-    
-    private RectD CalculateDestRect(Image image, VecI targetSize, RenderingContext context)
-    {
-        if(context.Resolution == null || context.ChunkToUpdate == null)
-        {
-            return new RectD(0, 0, targetSize.X, targetSize.Y);
-        }
-        
-        int chunkSize = context.Resolution.Value.PixelSize(); 
-        VecI chunkPos = context.ChunkToUpdate.Value;
-        
-        int x = (int)(chunkPos.X * chunkSize);
-        int y = (int)(chunkPos.Y * chunkSize);
-        int width = chunkSize;
-        int height = chunkSize;
-        
-        return new RectD(x, y, width, height);
-    }
 
     protected override bool CacheChanged(RenderingContext context)
     {

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSizeNode.cs

@@ -7,17 +7,17 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public class ImageSizeNode : Node
 {
-    public InputProperty<Image?> Image { get; }
+    public InputProperty<Surface?> Image { get; }
     
     public OutputProperty<VecI> Size { get; }
     
     public ImageSizeNode()
     {
-        Image = CreateInput<Image>(nameof(Image), "IMAGE", null);
+        Image = CreateInput<Surface>(nameof(Image), "IMAGE", null);
         Size = CreateOutput(nameof(Size), "SIZE", new VecI());
     }
     
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         Size.Value = Image.Value?.Size ?? new VecI();
 

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

@@ -18,7 +18,7 @@ public class ImageSpaceNode : Node
         Size = CreateFieldOutput(nameof(Size), "SIZE", ctx => ctx.Size);
     }
     
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         return null;
     }

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

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

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

@@ -8,15 +8,15 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public class MergeNode : Node, IBackgroundInput
 {
-    public InputProperty<Image?> Top { get; }
-    public InputProperty<Image?> Bottom { get; }
-    public OutputProperty<Image?> Output { get; }
+    public InputProperty<Surface?> Top { get; }
+    public InputProperty<Surface?> Bottom { get; }
+    public OutputProperty<Surface?> Output { get; }
     
     public MergeNode() 
     {
-        Top = CreateInput<Image?>("Top", "TOP", null);
-        Bottom = CreateInput<Image?>("Bottom", "BOTTOM", null);
-        Output = CreateOutput<Image?>("Output", "OUTPUT", null);
+        Top = CreateInput<Surface?>("Top", "TOP", null);
+        Bottom = CreateInput<Surface?>("Bottom", "BOTTOM", null);
+        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
     }
     
     public override bool Validate()
@@ -29,7 +29,7 @@ public class MergeNode : Node, IBackgroundInput
         return new MergeNode();
     }
 
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         if(Top.Value == null && Bottom.Value == null)
         {
@@ -37,26 +37,25 @@ public class MergeNode : Node, IBackgroundInput
             return null;
         }
         
-        int width = Top.Value?.Width ?? Bottom.Value.Width;
-        int height = Top.Value?.Height ?? Bottom.Value.Height;
+        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));
         
         if(Bottom.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawImage(Bottom.Value, 0, 0);
+            workingSurface.DrawingSurface.Canvas.DrawSurface(Bottom.Value.DrawingSurface, 0, 0);
         }
         
         if(Top.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawImage(Top.Value, 0, 0);
+            workingSurface.DrawingSurface.Canvas.DrawSurface(Top.Value.DrawingSurface, 0, 0);
         }
+
+        Output.Value = workingSurface;
         
-        Output.Value = workingSurface.DrawingSurface.Snapshot();
-        
-        workingSurface.Dispose();
         return Output.Value;
     }
 
-    InputProperty<Image> IBackgroundInput.Background => Bottom;
+    InputProperty<Surface> IBackgroundInput.Background => Bottom;
 }

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

@@ -12,7 +12,7 @@ public class ModifyImageLeftNode : Node
 {
     private Pixmap? pixmap;
     
-    public InputProperty<Image?> Image { get; }
+    public InputProperty<Surface?> Image { get; }
     
     public FieldOutputProperty<VecD> Coordinate { get; }
     
@@ -20,7 +20,7 @@ public class ModifyImageLeftNode : Node
     
     public ModifyImageLeftNode()
     {
-        Image = CreateInput<Image>(nameof(Image), "IMAGE", null);
+        Image = CreateInput<Surface>(nameof(Surface), "IMAGE", null);
         Coordinate = CreateFieldOutput(nameof(Coordinate), "COORDINATE", ctx => ctx.Position);
         Color = CreateFieldOutput(nameof(Color), "COLOR", GetColor);
     }
@@ -41,12 +41,12 @@ public class ModifyImageLeftNode : Node
         pixmap = Image.Value?.PeekPixels();
     }
 
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         return Image.Value;
     }
 
-    public override bool Validate() => Image.Value != null;
+    public override bool Validate() => Image.Connection != null;
 
     public override Node CreateCopy() => new ModifyImageLeftNode();
 }

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

@@ -17,16 +17,16 @@ public class ModifyImageRightNode : Node
     
     public FieldInputProperty<Color> Color { get; }
     
-    public OutputProperty<Image> Output { get; }
+    public OutputProperty<Surface> Output { get; }
     
     public ModifyImageRightNode(ModifyImageLeftNode startNode)
     {
         this.startNode = startNode;
         Color = CreateFieldInput(nameof(Color), "COLOR", new Color());
-        Output = CreateOutput<Image>(nameof(Output), "OUTPUT", null);
+        Output = CreateOutput<Surface>(nameof(Output), "OUTPUT", null);
     }
 
-    protected override Image? OnExecute(RenderingContext renderingContext)
+    protected override Surface? OnExecute(RenderingContext renderingContext)
     {
         if (startNode.Image.Value is not { Size: var size })
         {
@@ -38,7 +38,7 @@ public class ModifyImageRightNode : Node
         var width = size.X;
         var height = size.Y;
 
-        using var surface = new Surface(size);
+        var surface = new Surface(size);
 
         for (int y = 0; y < height; y++)
         {
@@ -52,7 +52,7 @@ public class ModifyImageRightNode : Node
             }
         }
 
-        Output.Value = surface.DrawingSurface.Snapshot();
+        Output.Value = surface;
 
         return Output.Value;
     }

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

@@ -20,7 +20,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
     public IReadOnlyCollection<InputProperty> InputProperties => inputs;
     public IReadOnlyCollection<OutputProperty> OutputProperties => outputs;
     public IReadOnlyCollection<IReadOnlyNode> ConnectedOutputNodes => _connectedNodes;
-    public Image? CachedResult { get; private set; }
+    public Surface? CachedResult { get; private set; }
 
     public virtual string InternalName { get; }
     
@@ -43,16 +43,21 @@ public abstract class Node : IReadOnlyNode, IDisposable
     private ChunkResolution? _lastResolution;
     private VecI? _lastChunkPos;
 
-    public Image? Execute(RenderingContext context)
+    public Surface? Execute(RenderingContext context)
     {
         if(!CacheChanged(context)) return CachedResult;
         
         CachedResult = OnExecute(context);
+        if (CachedResult is { IsDisposed: true })
+        {
+            throw new ObjectDisposedException("Surface was disposed after execution.");
+        }
+        
         UpdateCache(context);
         return CachedResult;
     }
 
-    protected abstract Image? OnExecute(RenderingContext context);
+    protected abstract Surface? OnExecute(RenderingContext context);
     public abstract bool Validate();
     
     protected virtual bool CacheChanged(RenderingContext context)

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

@@ -12,7 +12,7 @@ public class NoiseNode : Node
     private double previousScale = double.NaN;
     private Paint paint = new();
     
-    public OutputProperty<Image> Noise { get; }
+    public OutputProperty<Surface> Noise { get; }
 
     public InputProperty<VecI> Size { get; }
     
@@ -22,13 +22,13 @@ public class NoiseNode : Node
 
     public NoiseNode()
     {
-        Noise = CreateOutput<Image>(nameof(Noise), "NOISE", null);
+        Noise = CreateOutput<Surface>(nameof(Noise), "NOISE", null);
         Size = CreateInput(nameof(Size), "SIZE", new VecI());
         Scale = CreateInput(nameof(Scale), "SCALE", 0d);
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
     }
     
-    protected override Image OnExecute(RenderingContext context)
+    protected override Surface OnExecute(RenderingContext context)
     {
         if (Math.Abs(previousScale - Scale.Value) > 0.000001 || double.IsNaN(previousScale))
         {
@@ -40,11 +40,11 @@ public class NoiseNode : Node
         
         var size = Size.Value;
         
-        using var workingSurface = new Surface(size);
+        var workingSurface = new Surface(size);
         
         workingSurface.DrawingSurface.Canvas.DrawPaint(paint);
 
-        Noise.Value = workingSurface.DrawingSurface.Snapshot();
+        Noise.Value = workingSurface;
         
         return Noise.Value;
     }

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

@@ -7,10 +7,10 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public class OutputNode : Node, IBackgroundInput
 {
-    public InputProperty<Image?> Input { get; } 
+    public InputProperty<Surface?> Input { get; } 
     public OutputNode()
     {
-        Input = CreateInput<Image>("Background", "INPUT", null);
+        Input = CreateInput<Surface>("Background", "INPUT", null);
     }
     
     public override bool Validate()
@@ -23,10 +23,10 @@ public class OutputNode : Node, IBackgroundInput
         return new OutputNode();
     }
 
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         return Input.Value;
     }
 
-    InputProperty<Image?> IBackgroundInput.Background => Input;
+    InputProperty<Surface?> IBackgroundInput.Background => Input;
 }

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

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

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

@@ -21,7 +21,7 @@ public class SeparateVecDNode : Node
         Vector = CreateFieldInput("Vector", "VECTOR", new VecD(0, 0));
     }
 
-    protected override Image? OnExecute(RenderingContext context)
+    protected override Surface? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -9,7 +9,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundInput
 {
-    public InputProperty<Image?> Background { get; }
+    public InputProperty<Surface?> Background { get; }
     public InputProperty<float> Opacity { get; } 
     public InputProperty<bool> IsVisible { get; }
     public InputProperty<bool> ClipToPreviousMember { get; }
@@ -17,13 +17,13 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
     public InputProperty<ChunkyImage?> Mask { get; }
     public InputProperty<bool> MaskIsVisible { get; }
     
-    public OutputProperty<Image?> Output { get; }
+    public OutputProperty<Surface?> Output { get; }
     
     public string MemberName { get; set; } = string.Empty;
 
     protected StructureNode()
     {
-        Background = CreateInput<Image?>("Background", "BACKGROUND", null);
+        Background = CreateInput<Surface?>("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);
@@ -31,10 +31,10 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         Mask = CreateInput<ChunkyImage?>("Mask", "MASK", null);
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
         
-        Output = CreateOutput<Image?>("Output", "OUTPUT", null);
+        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
     }
 
-    protected abstract override Image? OnExecute(RenderingContext context);
+    protected abstract override Surface? OnExecute(RenderingContext context);
     public abstract override bool Validate();
 
     public abstract RectI? GetTightBounds(KeyFrameTime frameTime);

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

@@ -8,9 +8,9 @@ namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
 
 public static class NodeOperations
 {
-    public static List<ConnectProperty_ChangeInfo> AppendMember(InputProperty<Image?> parentInput,
-        OutputProperty<Image> toAddOutput,
-        InputProperty<Image> toAddInput, Guid memberId)
+    public static List<ConnectProperty_ChangeInfo> AppendMember(InputProperty<Surface?> parentInput,
+        OutputProperty<Surface> toAddOutput,
+        InputProperty<Surface> toAddInput, Guid memberId)
     {
         List<ConnectProperty_ChangeInfo> changes = new();
         IOutputProperty? previouslyConnected = null;

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

@@ -50,7 +50,7 @@ internal class MoveStructureMember_Change : Change
 
         List<IChangeInfo> changes = new();
 
-        InputProperty<Image?> inputProperty = backgroundInput.Background;
+        InputProperty<Surface?> inputProperty = backgroundInput.Background;
 
         if (targetNode is FolderNode folder && putInsideFolder)
         {

+ 0 - 1
src/PixiEditor.ChangeableDocument/PixiEditor.ChangeableDocument.csproj

@@ -4,7 +4,6 @@
     <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <WarningsAsErrors>Nullable</WarningsAsErrors>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <Configurations>Debug;Release;Steam;DevRelease;DevSteam</Configurations>
     <Platforms>AnyCPU;x86;x64</Platforms>

+ 6 - 4
src/PixiEditor.ChangeableDocument/Rendering/DocumentEvaluator.cs

@@ -14,7 +14,7 @@ public static class DocumentEvaluator
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Image? evaluated = graph.Execute(context);
+            Surface? evaluated = graph.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();
@@ -38,8 +38,10 @@ public static class DocumentEvaluator
             
             RectD sourceRect = new(x, y, width, height);
             RectD destRect = new(0, 0, chunk.PixelSize.X, chunk.PixelSize.Y);
+
+            using var chunkSnapshot = evaluated.DrawingSurface.Snapshot((RectI)sourceRect);
             
-            chunk.Surface.DrawingSurface.Canvas.DrawImage(evaluated, sourceRect, destRect, context.ReplacingPaintWithOpacity);
+            chunk.Surface.DrawingSurface.Canvas.DrawImage(chunkSnapshot, 0, 0, context.ReplacingPaintWithOpacity);
 
             chunk.Surface.DrawingSurface.Canvas.Restore();
 
@@ -59,7 +61,7 @@ public static class DocumentEvaluator
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Image? evaluated = node.Execute(context);
+            Surface? evaluated = node.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();
@@ -75,7 +77,7 @@ public static class DocumentEvaluator
                 chunk.Surface.DrawingSurface.Canvas.ClipRect((RectD)transformedClippingRect);
             }
             
-            chunk.Surface.DrawingSurface.Canvas.DrawImage(evaluated, transformedClippingRect.Value.X, transformedClippingRect.Value.Y, context.ReplacingPaintWithOpacity);
+            chunk.Surface.DrawingSurface.Canvas.DrawSurface(evaluated.DrawingSurface, transformedClippingRect.Value.X, transformedClippingRect.Value.Y, context.ReplacingPaintWithOpacity);
 
             chunk.Surface.DrawingSurface.Canvas.Restore();
 

+ 1 - 1
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -89,7 +89,7 @@
             <SolidColorBrush x:Key="SelectionFillBrush" Color="{StaticResource SelectionFillColor}"/>
             
             <SolidColorBrush x:Key="DefaultSocketBrush" Color="{StaticResource DefaultSocketColor}"/>
-            <SolidColorBrush x:Key="ImageSocketBrush" Color="{StaticResource ImageSocketColor}"/>
+            <SolidColorBrush x:Key="SurfaceSocketBrush" Color="{StaticResource ImageSocketColor}"/>
             <SolidColorBrush x:Key="BooleanSocketBrush" Color="{StaticResource BoolSocketColor}"/>
             <SolidColorBrush x:Key="SingleSocketBrush" Color="{StaticResource FloatSocketColor}"/>
             <SolidColorBrush x:Key="DoubleSocketBrush" Color="{StaticResource DoubleSocketColor}"/>