ソースを参照

chunk based nodes somewhat working

flabbet 1 年間 前
コミット
94fe6a6992
26 ファイル変更212 行追加150 行削除
  1. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IBackgroundInput.cs
  2. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs
  3. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs
  4. 24 8
      src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
  5. 30 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CircleNode.cs
  6. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineColorNode.cs
  7. 16 8
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs
  8. 7 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  9. 15 22
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  10. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSizeNode.cs
  11. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs
  12. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs
  13. 9 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MatrixTransformNode.cs
  14. 12 10
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  15. 6 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  16. 7 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  17. 39 19
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  18. 5 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  19. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  20. 5 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateChannelsNode.cs
  21. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateColorNode.cs
  22. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateVecDNode.cs
  23. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  24. 3 3
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs
  25. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  26. 9 14
      src/PixiEditor.ChangeableDocument/Rendering/DocumentEvaluator.cs

+ 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<Chunk?> 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 Surface? CachedResult { get; }
+    public ChunkyImage? CachedResult { get; }
     
     public string InternalName { get; }
 
-    public Surface? Execute(RenderingContext context);
+    public Chunk? 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 Surface? Execute(RenderingContext context);
+    public Chunk? Execute(RenderingContext context);
 }

+ 24 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
+using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
@@ -83,20 +84,20 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
 
     public bool TryTraverse(Action<IReadOnlyNode> action)
     {
-        if(OutputNode == null) return false;
-        
+        if (OutputNode == null) return false;
+
         var queue = CalculateExecutionQueue(OutputNode, false);
-        
+
         while (queue.Count > 0)
         {
             var node = queue.Dequeue();
             action(node);
         }
-        
+
         return true;
     }
 
-    public Surface? Execute(RenderingContext context)
+    public Chunk? Execute(RenderingContext context)
     {
         Stopwatch stopwatch = new();
         if (OutputNode == null) return null;
@@ -106,12 +107,27 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         while (queue.Count > 0)
         {
             var node = queue.Dequeue();
-            
+
             stopwatch.Restart();
-            node.Execute(context);
+            if (node is Node typedNode)
+            {
+                typedNode.ExecuteInternal(context);
+            }
+            else
+            {
+                node.Execute(context);
+            }
+
             Console.WriteLine($"{node.GetType().Name} took {stopwatch.ElapsedMilliseconds}ms to execute");
         }
 
-        return OutputNode.Input.Value;
+        if (OutputNode?.Input.Value != null)
+        {
+            Chunk result = Chunk.Create(context.ChunkResolution);
+            result.Surface.DrawingSurface.Canvas.DrawSurface(OutputNode.Input.Value.Surface.DrawingSurface, 0, 0);
+            return result;
+        }
+
+        return null;
     }
 }

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

@@ -12,7 +12,9 @@ public class CircleNode : Node
     public InputProperty<Color> StrokeColor { get; }
     public InputProperty<Color> FillColor { get; }
     public InputProperty<int> StrokeWidth { get; }
-    public OutputProperty<Surface> Output { get; }
+    public OutputProperty<Chunk> Output { get; }
+    
+    private ChunkyImage? workingImage;
     
     public CircleNode()
     {
@@ -20,23 +22,40 @@ 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<Surface?>("Output", "OUTPUT", null);
+        Output = CreateOutput<Chunk?>("Output", "OUTPUT", null);
     }
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
-        var radius = Radius.Value / 2;
-        var strokeWidth = StrokeWidth.Value;
-        var strokeOffset = new VecI(strokeWidth / 2);
+        var radius = Radius.Value;
+        VecI targetDimensions = radius * 2;
+        
+        if(workingImage is null || workingImage.LatestSize.X != targetDimensions.X || workingImage.LatestSize.Y != targetDimensions.Y)
+        {
+            workingImage?.Dispose();
+            workingImage = new ChunkyImage(targetDimensions);
+        }
         
-        Surface workingSurface = new Surface(Radius.Value + strokeOffset * 2);
+        RectI location = new RectI(VecI.Zero, targetDimensions);
         
+        workingImage.EnqueueDrawEllipse(location, StrokeColor.Value, FillColor.Value, StrokeWidth.Value);
+        workingImage.CommitChanges();
+
+        Chunk output = Chunk.Create(context.ChunkResolution);
+        workingImage.DrawMostUpToDateChunkOn(context.ChunkToUpdate, context.ChunkResolution,
+            output.Surface.DrawingSurface, VecI.Zero);
+
+        Output.Value = output;
+        return output;
+
+        /*Surface workingSurface = new Surface(Radius.Value + strokeOffset * 2);
+
         using Paint paint = new Paint();
         paint.Color = FillColor.Value;
         paint.Style = PaintStyle.Fill;
-        
+
         workingSurface.DrawingSurface.Canvas.DrawOval(radius + strokeOffset, radius, paint);
-        
+
         paint.Color = StrokeColor.Value;
         paint.StrokeWidth = strokeWidth;
         paint.Style = PaintStyle.Stroke;
@@ -44,8 +63,8 @@ public class CircleNode : Node
         workingSurface.DrawingSurface.Canvas.DrawOval(radius + strokeOffset, radius, paint);
 
         Output.Value = workingSurface;
-        
-        return Output.Value;
+
+        return Output.Value;*/
     }
 
     public override bool Validate()

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

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

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

@@ -11,7 +11,7 @@ public class CreateImageNode : Node
 {
     private Paint _paint = new();
     
-    public OutputProperty<Surface> Output { get; }
+    public OutputProperty<ChunkyImage> Output { get; }
 
     public InputProperty<VecI> Size { get; }
     
@@ -19,21 +19,29 @@ public class CreateImageNode : Node
 
     public CreateImageNode()
     {
-        Output = CreateOutput<Surface>(nameof(Output), "EMPTY_IMAGE", null);
+        Output = CreateOutput<ChunkyImage>(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)
+    private ChunkyImage? Image { get; set; }
+    
+    protected override Chunk? OnExecute(RenderingContext context)
     {
-        var surface = new Surface(Size.Value);
-
+        if(Image == null || Image.LatestSize != Size.Value)
+        {
+            Image = new ChunkyImage(Size.Value);
+        }
+        
         _paint.Color = Fill.Value;
-        surface.DrawingSurface.Canvas.DrawPaint(_paint);
+        Image.EnqueueDrawPaint(_paint);
+
+        Chunk result = Chunk.Create(context.ChunkResolution);
+        Image.DrawMostUpToDateChunkOn(context.ChunkToUpdate, context.ChunkResolution, result.Surface.DrawingSurface, VecI.Zero);
 
-        Output.Value = surface;
+        Output.Value = Image;
 
-        return Output.Value;
+        return result;
     }
 
     public override bool Validate() => Size.Value is { X: > 0, Y: > 0 };

+ 7 - 7
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<Surface?> Content { get; }
+    public InputProperty<Chunk?> Content { get; }
 
     public FolderNode()
     {
-        Content = CreateInput<Surface?>("Content", "CONTENT", null);
+        Content = CreateInput<Chunk?>("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 Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Content.Value == null)
         {
@@ -30,9 +30,9 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
             return Output.Value;
         }
 
-        VecI size = Content.Value?.Size ?? Background.Value.Size;
+        //VecI size = Content.Value?.Size ?? Background.Value.Size;
         
-        Surface workingSurface = new Surface(size);
+        /*Surface workingSurface = new Surface(size);
 
         if (Background.Value != null)
         {
@@ -44,7 +44,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
             workingSurface.DrawingSurface.Canvas.DrawSurface(Content.Value.DrawingSurface, 0, 0);
         }
 
-        Output.Value = workingSurface;
+        Output.Value = workingSurface;*/
         
         return Output.Value;
     }
@@ -52,7 +52,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
     public override RectI? GetTightBounds(KeyFrameTime frameTime)
     {
         // TODO: Implement GetTightBounds
-        return RectI.Create(0, 0, Content.Value?.Size.X ?? 0, Content.Value?.Size.Y ?? 0); 
+        return CachedResult.FindTightCommittedBounds(ChunkResolution.Full);
         /*if (Children.Count == 0)
       {
           return null;

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

@@ -18,9 +18,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     private Paint blendPaint = new Paint();
     private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surface.BlendMode.DstIn };
-
-    private Dictionary<ChunkResolution, Surface> workingSurfaces = new Dictionary<ChunkResolution, Surface>();
-
+    
     // Handled by overriden CacheChanged
     protected override bool AffectedByAnimation => false;
 
@@ -45,7 +43,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return true;
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
@@ -65,37 +63,32 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return Output.Value;
     }
 
-    private Surface RenderImage(ChunkyImage frameImage, RenderingContext context)
+    private Chunk RenderImage(ChunkyImage frameImage, RenderingContext context)
     {
         ChunkResolution targetResolution = context.ChunkResolution;
-        bool hasSurface = workingSurfaces.TryGetValue(targetResolution, out Surface workingSurface);
-        VecI targetSize = (VecI)(frameImage.LatestSize * targetResolution.Multiplier());
 
-        if (Background.Value != null)
+        /*if (Background.Value != null)
         {
             workingSurface = Background.Value;
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
-        }
-        else if (!hasSurface || workingSurface.Size != targetSize || workingSurface.IsDisposed)
-        {
-            workingSurfaces[targetResolution] = new Surface(targetSize);
-            workingSurface = workingSurfaces[targetResolution];
-        }
+        }*/
+
+        Chunk workingChunk = Chunk.Create(context.ChunkResolution); 
 
-        DrawLayer(frameImage, context, workingSurface);
+        DrawLayer(frameImage, context, workingChunk);
 
-        ApplyMaskIfPresent(workingSurface, context);
-        ApplyRasterClip(workingSurface, context);
-        return workingSurface;
+        /*ApplyMaskIfPresent(workingChunk, context);
+        ApplyRasterClip(workingChunk, context);*/
+        return workingChunk;
     }
 
-    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface)
+    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Chunk workingSurface)
     {
        frameImage.DrawMostUpToDateChunkOn(
                 context.ChunkToUpdate,
                 context.ChunkResolution,
-                workingSurface.DrawingSurface,
-                context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
+                workingSurface.Surface.DrawingSurface,
+                VecI.Zero,
                 blendPaint);
     }
 
@@ -113,7 +106,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             VecI size = new VecI(context.ChunkResolution.PixelSize());
             clippingRect = new RectI(chunkStart, size);
 
-            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value, clippingRect);
+            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value.Surface, clippingRect);
         }
     }
 

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

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

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

@@ -8,17 +8,17 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public class ImageSpaceNode : Node
 {
-    public FieldOutputProperty<VecD> Position { get; }
+    public FieldOutputProperty<VecD> PositionField { get; }
     
     public FieldOutputProperty<VecI> Size { get; }
 
     public ImageSpaceNode()
     {
-        Position = CreateFieldOutput(nameof(Position), "PIXEL_COORDINATE", ctx => ctx.Position);
+        PositionField = CreateFieldOutput(nameof(PositionField), "PIXEL_COORDINATE", ctx => ctx.Position);
         Size = CreateFieldOutput(nameof(Size), "SIZE", ctx => ctx.Size);
     }
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
         return null;
     }

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

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

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

@@ -14,24 +14,24 @@ public class MatrixTransformNode : Node
     
     private Paint paint;
     
-    public OutputProperty<Surface> Transformed { get; }
+    public OutputProperty<Chunk> Transformed { get; }
     
-    public InputProperty<Surface?> Input { get; }
+    public InputProperty<Chunk?> Input { get; }
     
     public InputProperty<ColorMatrix> Matrix { get; }
 
     public MatrixTransformNode()
     {
-        Transformed = CreateOutput<Surface>(nameof(Transformed), "TRANSFORMED", null);
-        Input = CreateInput<Surface>(nameof(Input), "INPUT", null);
+        Transformed = CreateOutput<Chunk>(nameof(Transformed), "TRANSFORMED", null);
+        Input = CreateInput<Chunk>(nameof(Input), "INPUT", null);
         Matrix = CreateInput(nameof(Matrix), "MATRIX", previousMatrix);
 
         paint = new Paint { ColorFilter = ColorFilter.CreateColorMatrix(previousMatrix) };
     }
     
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
-        var currentMatrix = Matrix.Value;
+        /*var currentMatrix = Matrix.Value;
         if (previousMatrix != currentMatrix)
         {
             paint.ColorFilter = ColorFilter.CreateColorMatrix(Matrix.Value);
@@ -44,7 +44,9 @@ public class MatrixTransformNode : Node
 
         Transformed.Value = workingSurface;
         
-        return Transformed.Value;
+        return Transformed.Value;*/
+        
+        return null;
     }
 
     public override bool Validate() => Input.Value != null;

+ 12 - 10
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<Surface?> Top { get; }
-    public InputProperty<Surface?> Bottom { get; }
-    public OutputProperty<Surface?> Output { get; }
+    public InputProperty<Chunk?> Top { get; }
+    public InputProperty<Chunk?> Bottom { get; }
+    public OutputProperty<Chunk?> Output { get; }
     
     public MergeNode() 
     {
-        Top = CreateInput<Surface?>("Top", "TOP", null);
-        Bottom = CreateInput<Surface?>("Bottom", "BOTTOM", null);
-        Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
+        Top = CreateInput<Chunk?>("Top", "TOP", null);
+        Bottom = CreateInput<Chunk?>("Bottom", "BOTTOM", null);
+        Output = CreateOutput<Chunk?>("Output", "OUTPUT", null);
     }
     
     public override bool Validate()
@@ -29,9 +29,9 @@ public class MergeNode : Node, IBackgroundInput
         return new MergeNode();
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
-        if(Top.Value == null && Bottom.Value == null)
+        /*if(Top.Value == null && Bottom.Value == null)
         {
             Output.Value = null;
             return null;
@@ -54,8 +54,10 @@ public class MergeNode : Node, IBackgroundInput
 
         Output.Value = workingSurface;
         
-        return Output.Value;
+        return Output.Value;*/
+        
+        return null;
     }
 
-    InputProperty<Surface> IBackgroundInput.Background => Bottom;
+    InputProperty<Chunk> IBackgroundInput.Background => Bottom;
 }

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

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

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

@@ -18,18 +18,18 @@ public class ModifyImageRightNode : Node
     
     public FieldInputProperty<Color> Color { get; }
     
-    public OutputProperty<Surface> Output { get; }
+    public OutputProperty<Chunk> Output { get; }
     
     public ModifyImageRightNode(ModifyImageLeftNode startNode)
     {
         this.startNode = startNode;
         Color = CreateFieldInput(nameof(Color), "COLOR", new Color());
-        Output = CreateOutput<Surface>(nameof(Output), "OUTPUT", null);
+        Output = CreateOutput<Chunk>(nameof(Output), "OUTPUT", null);
     }
 
-    protected override Surface? OnExecute(RenderingContext renderingContext)
+    protected override Chunk? OnExecute(RenderingContext renderingContext)
     {
-        if (startNode.Image.Value is not { Size: var size })
+        /*if (startNode.Image.Value is not { Size: var size })
         {
             return null;
         }
@@ -55,7 +55,9 @@ public class ModifyImageRightNode : Node
 
         Output.Value = surface;
 
-        return Output.Value;
+        return Output.Value;*/
+        
+        return null;
     }
 
     public override bool Validate() => true;

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

@@ -3,7 +3,6 @@ using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -21,21 +20,21 @@ public abstract class Node : IReadOnlyNode, IDisposable
     public IReadOnlyCollection<InputProperty> InputProperties => inputs;
     public IReadOnlyCollection<OutputProperty> OutputProperties => outputs;
     public IReadOnlyCollection<IReadOnlyNode> ConnectedOutputNodes => _connectedNodes;
-    public Surface? CachedResult { get; private set; }
+    public ChunkyImage? CachedResult { get; private set; }
 
     public virtual string InternalName { get; }
-    
+
     protected virtual bool AffectedByAnimation { get; }
-    
+
     protected virtual bool AffectedByChunkResolution { get; }
 
     protected virtual bool AffectedByChunkToUpdate { get; }
-    
+
     protected Node()
     {
         InternalName = $"PixiEditor.{GetType().Name}";
     }
-    
+
     IReadOnlyCollection<IInputProperty> IReadOnlyNode.InputProperties => inputs;
     IReadOnlyCollection<IOutputProperty> IReadOnlyNode.OutputProperties => outputs;
     public VecD Position { get; set; }
@@ -43,39 +42,58 @@ public abstract class Node : IReadOnlyNode, IDisposable
     private KeyFrameTime _lastFrameTime = new KeyFrameTime(-1);
     private ChunkResolution? _lastResolution;
     private VecI? _lastChunkPos;
+    private ChunkyImage _cache;
+    private Chunk? lastRenderedChunk;
 
-    public Surface? Execute(RenderingContext context)
+    public Chunk? Execute(RenderingContext context)
     {
-        if(!CacheChanged(context)) return CachedResult;
+        var result = ExecuteInternal(context);
+        
+        // copy the result to avoid leaking the internal chunks, that are mostly caches used by nodes.
+        Chunk returnCopy = Chunk.Create(context.ChunkResolution);
+        returnCopy.Surface.DrawingSurface.Canvas.DrawSurface(result.Surface.DrawingSurface, 0, 0);
         
-        CachedResult = OnExecute(context);
-        if (CachedResult is { IsDisposed: true })
+        return returnCopy;
+    }
+
+    internal Chunk ExecuteInternal(RenderingContext context)
+    {
+        if (CachedResult == null)
         {
-            throw new ObjectDisposedException("Surface was disposed after execution.");
+            CachedResult = new ChunkyImage(context.DocumentSize);
         }
-        
+
+        if (!CacheChanged(context))
+        {
+            return lastRenderedChunk;
+        }
+
+        lastRenderedChunk = OnExecute(context);
+
+        CachedResult.SetCommitedChunk(lastRenderedChunk, context.ChunkToUpdate, context.ChunkResolution);
+
         UpdateCache(context);
-        return CachedResult;
+        return lastRenderedChunk;
     }
 
-    protected abstract Surface? OnExecute(RenderingContext context);
+    protected abstract Chunk? OnExecute(RenderingContext context);
     public abstract bool Validate();
-    
+
     protected virtual bool CacheChanged(RenderingContext context)
     {
         return (!context.FrameTime.Equals(_lastFrameTime) && AffectedByAnimation)
-               || (context.ChunkResolution != _lastResolution && AffectedByChunkResolution)
-               || (context.ChunkToUpdate != _lastChunkPos && AffectedByChunkToUpdate)
+               || (context.ChunkResolution != _lastResolution /*&& AffectedByChunkResolution*/)
+               || (context.ChunkToUpdate != _lastChunkPos /*&& AffectedByChunkToUpdate*/)
                || inputs.Any(x => x.CacheChanged);
     }
-    
+
     protected virtual void UpdateCache(RenderingContext context)
     {
         foreach (var input in inputs)
         {
             input.UpdateCache();
         }
-        
+
         _lastFrameTime = context.FrameTime;
         _lastResolution = context.ChunkResolution;
         _lastChunkPos = context.ChunkToUpdate;
@@ -216,6 +234,8 @@ public abstract class Node : IReadOnlyNode, IDisposable
                 disposable.Dispose();
             }
         }
+
+        CachedResult?.Dispose();
     }
 
     public abstract Node CreateCopy();

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

@@ -28,9 +28,9 @@ public class NoiseNode : Node
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
     }
     
-    protected override Surface OnExecute(RenderingContext context)
+    protected override Chunk OnExecute(RenderingContext context)
     {
-        if (Math.Abs(previousScale - Scale.Value) > 0.000001 || double.IsNaN(previousScale))
+        /*if (Math.Abs(previousScale - Scale.Value) > 0.000001 || double.IsNaN(previousScale))
         {
             var shader = Shader.CreatePerlinNoiseTurbulence((float)(1d / Scale.Value), (float)(1d / Scale.Value), 4, (float)Seed.Value);
             paint.Shader = shader;
@@ -46,7 +46,9 @@ public class NoiseNode : Node
 
         Noise.Value = workingSurface;
         
-        return Noise.Value;
+        return Noise.Value;*/
+        
+        return null;
     }
 
     public override bool Validate() => Size.Value is { X: > 0, Y: > 0 };

+ 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<Surface?> Input { get; } 
+    public InputProperty<Chunk?> Input { get; } 
     public OutputNode()
     {
-        Input = CreateInput<Surface>("Background", "INPUT", null);
+        Input = CreateInput<Chunk>("Background", "INPUT", null);
     }
     
     public override bool Validate()
@@ -23,10 +23,10 @@ public class OutputNode : Node, IBackgroundInput
         return new OutputNode();
     }
 
-    protected override Surface? OnExecute(RenderingContext context)
+    protected override Chunk? OnExecute(RenderingContext context)
     {
         return Input.Value;
     }
 
-    InputProperty<Surface?> IBackgroundInput.Background => Input;
+    InputProperty<Chunk?> IBackgroundInput.Background => Input;
 }

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

@@ -44,9 +44,9 @@ public class SeparateChannelsNode : Node
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
     
-    protected override Surface OnExecute(RenderingContext context)
+    protected override Chunk OnExecute(RenderingContext context)
     {
-        var image = Image.Value;
+        /*var image = Image.Value;
         var grayscale = Grayscale.Value;
 
         var red = !grayscale ? _redFilter : _redGrayscaleFilter;
@@ -73,7 +73,9 @@ public class SeparateChannelsNode : Node
         previewSurface.DrawingSurface.Canvas.DrawSurface(Blue.Value.DrawingSurface, bluePos, context.ReplacingPaintWithOpacity);
         previewSurface.DrawingSurface.Canvas.DrawSurface(Alpha.Value.DrawingSurface, alphaPos, context.ReplacingPaintWithOpacity);
         
-        return previewSurface;
+        return previewSurface;*/
+        
+        return null;
     }
 
     private Surface GetImage(Surface image, ColorFilter filter)

+ 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 Surface? OnExecute(RenderingContext context)
+    protected override Chunk? 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 Surface? OnExecute(RenderingContext context)
+    protected override Chunk? 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<Surface?> Background { get; }
+    public InputProperty<Chunk?> 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<Surface?> Output { get; }
+    public OutputProperty<Chunk?> Output { get; }
     
     public string MemberName { get; set; } = string.Empty;
 
     protected StructureNode()
     {
-        Background = CreateInput<Surface?>("Background", "BACKGROUND", null);
+        Background = CreateInput<Chunk?>("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<Surface?>("Output", "OUTPUT", null);
+        Output = CreateOutput<Chunk?>("Output", "OUTPUT", null);
     }
 
-    protected abstract override Surface? OnExecute(RenderingContext context);
+    protected abstract override Chunk? 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<Surface?> parentInput,
-        OutputProperty<Surface> toAddOutput,
-        InputProperty<Surface> toAddInput, Guid memberId)
+    public static List<ConnectProperty_ChangeInfo> AppendMember(InputProperty<Chunk?> parentInput,
+        OutputProperty<Chunk> toAddOutput,
+        InputProperty<Chunk> 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<Surface?> inputProperty = backgroundInput.Background;
+        InputProperty<Chunk?> inputProperty = backgroundInput.Background;
 
         if (targetNode is FolderNode folder && putInsideFolder)
         {

+ 9 - 14
src/PixiEditor.ChangeableDocument/Rendering/DocumentEvaluator.cs

@@ -21,13 +21,13 @@ public class DocumentEvaluator
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Surface? evaluated = Document.NodeGraph.Execute(context);
-            if (evaluated is null)
+            Chunk? chunk = Document.NodeGraph.Execute(context);
+            if (chunk is null)
             {
                 return new EmptyChunk();
             }
-
-            Chunk chunk = Chunk.Create(resolution);
+            
+            /*Chunk chunk = Chunk.Create(resolution);
 
             chunk.Surface.DrawingSurface.Canvas.Save();
             chunk.Surface.DrawingSurface.Canvas.Clear();
@@ -49,7 +49,7 @@ public class DocumentEvaluator
             
             chunk.Surface.DrawingSurface.Canvas.DrawImage(chunkSnapshot, 0, 0, context.ReplacingPaintWithOpacity);
 
-            chunk.Surface.DrawingSurface.Canvas.Restore();
+            chunk.Surface.DrawingSurface.Canvas.Restore();*/
 
             return chunk;
         }
@@ -67,25 +67,20 @@ public class DocumentEvaluator
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Surface? evaluated = node.Execute(context);
-            if (evaluated is null)
+            Chunk? chunk = node.Execute(context);
+            if (chunk is null)
             {
                 return new EmptyChunk();
             }
 
-            Chunk chunk = Chunk.Create(resolution);
-
-            chunk.Surface.DrawingSurface.Canvas.Save();
-            chunk.Surface.DrawingSurface.Canvas.Clear();
-
-            if (transformedClippingRect is not null)
+            /*if (transformedClippingRect is not null)
             {
                 chunk.Surface.DrawingSurface.Canvas.ClipRect((RectD)transformedClippingRect);
             }
             
             chunk.Surface.DrawingSurface.Canvas.DrawSurface(evaluated.DrawingSurface, transformedClippingRect.Value.X, transformedClippingRect.Value.Y, context.ReplacingPaintWithOpacity);
 
-            chunk.Surface.DrawingSurface.Canvas.Restore();
+            chunk.Surface.DrawingSurface.Canvas.Restore();*/
 
             return chunk;
         }