2
0
Эх сурвалжийг харах

Merge branch 'refs/heads/node-backend' into funcy-nodes

CPKreuz 1 жил өмнө
parent
commit
8f3cdef409
34 өөрчлөгдсөн 180 нэмэгдсэн , 86 устгасан
  1. 16 0
      src/ChunkyImageLib/ChunkyImage.cs
  2. 2 2
      src/ChunkyImageLib/Operations/OperationHelper.cs
  3. 2 2
      src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.Serialization.cs
  4. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateFolder_ChangeInfo.cs
  5. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateLayer_ChangeInfo.cs
  6. 22 1
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  7. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs
  8. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs
  9. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyStructureNode.cs
  10. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
  11. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CircleNode.cs
  12. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineColorNode.cs
  13. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs
  14. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  15. 37 27
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  16. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSizeNode.cs
  17. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs
  18. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs
  19. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  20. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  21. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  22. 17 9
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  23. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  24. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateColorNode.cs
  25. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateVecDNode.cs
  26. 4 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  27. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Properties/LayerStructure/StructureMemberClipToMemberBelow_Change.cs
  28. 4 4
      src/PixiEditor.ChangeableDocument/Rendering/DocumentEvaluator.cs
  29. 17 10
      src/PixiEditor.ChangeableDocument/Rendering/RenderingContext.cs
  30. 1 1
      src/PixiEditor.DrawingApi.Core/Surface/DrawingSurface.cs
  31. 8 0
      src/PixiEditor.DrawingApi.Core/Surface/IPixelsMap.cs
  32. 1 1
      src/PixiEditor.DrawingApi.Core/Surface/ImageData/Image.cs
  33. 8 0
      src/PixiEditor.DrawingApi.Skia/Implementations/SkiaImageImplementation.cs
  34. 0 1
      src/PixiEditor.DrawingApi.Skia/Implementations/SkiaShaderImplementation.cs

+ 16 - 0
src/ChunkyImageLib/ChunkyImage.cs

@@ -377,6 +377,22 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable
         }
     }
 
+    public bool LatestOrCommittedChunkExists()
+    {
+        lock (lockObject)
+        {
+            ThrowIfDisposed();
+            var chunks = FindAllChunks();
+            foreach (var chunk in chunks)
+            {
+                if (LatestOrCommittedChunkExists(chunk))
+                    return true;
+            }
+        }
+        
+        return false;
+    }
+
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
     public bool DrawCommittedChunkOn(VecI chunkPos, ChunkResolution resolution, DrawingSurface surface, VecI pos, Paint? paint = null)
     {

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

@@ -22,7 +22,7 @@ public static class OperationHelper
     /// <summary>
     /// toModify[x,y].Alpha = Math.Min(toModify[x,y].Alpha, toGetAlphaFrom[x,y].Alpha)
     /// </summary>
-    public static unsafe void ClampAlpha(DrawingSurface toModify, DrawingSurface toGetAlphaFrom, RectI? clippingRect = null)
+    public static unsafe void ClampAlpha(IPixelsMap toModify, IPixelsMap toGetAlphaFrom, RectI? clippingRect = null)
     {
         if (clippingRect is not null)
         {
@@ -59,7 +59,7 @@ public static class OperationHelper
         }
     }
 
-    private static unsafe void ClampAlphaWithClippingRect(DrawingSurface toModify, DrawingSurface toGetAlphaFrom, RectI clippingRect)
+    private static unsafe void ClampAlphaWithClippingRect(IPixelsMap toModify, IPixelsMap toGetAlphaFrom, RectI clippingRect)
     {
         using Pixmap map = toModify.PeekPixels();
         using Pixmap refMap = toGetAlphaFrom.PeekPixels();

+ 2 - 2
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -112,7 +112,7 @@ internal partial class DocumentViewModel
             BlendMode = (BlendMode)(int)folder.BlendMode.Value,
             Enabled = folder.IsVisible.Value,
             Opacity = folder.Opacity.Value,
-            ClipToMemberBelow = folder.ClipToMemberBelow.Value,
+            ClipToMemberBelow = folder.ClipToPreviousMember.Value,
             Mask = GetMask(folder.Mask.Value, folder.MaskIsVisible.Value)
         };
     }
@@ -128,7 +128,7 @@ internal partial class DocumentViewModel
         {
             Width = result?.Width ?? 0, Height = result?.Height ?? 0, OffsetX = tightBounds?.X ?? 0, OffsetY = tightBounds?.Y ?? 0,
             Enabled = layer.IsVisible.Value, BlendMode = (BlendMode)(int)layer.BlendMode.Value, ImageBytes = bytes,
-            ClipToMemberBelow = layer.ClipToMemberBelow.Value, Name = layer.MemberName,
+            ClipToMemberBelow = layer.ClipToPreviousMember.Value, Name = layer.MemberName,
             Guid = layer.Id,
             LockAlpha = layer is ITransparencyLockable { LockTransparency: true },
             Opacity = layer.Opacity.Value, Mask = GetMask(layer.Mask.Value, layer.MaskIsVisible.Value)

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateFolder_ChangeInfo.cs

@@ -32,7 +32,7 @@ public record class CreateFolder_ChangeInfo : CreateStructureMember_ChangeInfo
             parentGuid,
             folder.Opacity.Value,
             folder.IsVisible.Value,
-            folder.ClipToMemberBelow.Value,
+            folder.ClipToPreviousMember.Value,
             folder.MemberName,
             folder.BlendMode.Value,
             folder.Id,

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateLayer_ChangeInfo.cs

@@ -38,7 +38,7 @@ public record class CreateLayer_ChangeInfo : CreateStructureMember_ChangeInfo
             parentGuid,
             layer.Opacity.Value,
             layer.IsVisible.Value,
-            layer.ClipToMemberBelow.Value,
+            layer.ClipToPreviousMember.Value,
             layer.MemberName,
             layer.BlendMode.Value,
             layer.Id,

+ 22 - 1
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
@@ -82,7 +83,27 @@ internal class Document : IChangeable, IReadOnlyDocument, IDisposable
         Surface surface = new Surface(tightBounds.Value.Size);
 
         using var paint = new Paint();
-        surface.DrawingSurface.Canvas.DrawImage(layer.Execute(frame), (RectD)tightBounds.Value, paint);
+
+        Image image;
+        
+        if (layer is IReadOnlyImageNode imageNode)
+        {
+            var chunkyImage = imageNode.GetLayerImageAtFrame(frame);
+            using Surface chunkSurface = new Surface(chunkyImage.CommittedSize);
+            chunkyImage.DrawCommittedRegionOn(
+                new RectI(0, 0, chunkyImage.CommittedSize.X, chunkyImage.CommittedSize.Y), 
+                ChunkResolution.Full,
+                chunkSurface.DrawingSurface,
+                VecI.Zero);
+            
+            image = chunkSurface.DrawingSurface.Snapshot();
+        }
+        else
+        {
+            image = layer.Execute(new RenderingContext(frame));
+        }
+        
+        surface.DrawingSurface.Canvas.DrawImage(image, (RectD)tightBounds.Value, paint);
 
         return surface.DrawingSurface.Snapshot();
     }

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

@@ -1,4 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -14,7 +15,7 @@ public interface IReadOnlyNode
     
     public string InternalName { get; }
 
-    public Image? Execute(KeyFrameTime frame);
+    public Image? Execute(RenderingContext context);
     public bool Validate();
     
     /// <summary>

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

@@ -1,4 +1,5 @@
-using PixiEditor.DrawingApi.Core.Surface.ImageData;
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.Surface.ImageData;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
@@ -9,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(int frame);
+    public Image? Execute(RenderingContext context);
 }

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

@@ -8,7 +8,7 @@ public interface IReadOnlyStructureNode : IReadOnlyNode
 {
     public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }
-    public InputProperty<bool> ClipToMemberBelow { get; }
+    public InputProperty<bool> ClipToPreviousMember { get; }
     public InputProperty<BlendMode> BlendMode { get; }
     public InputProperty<ChunkyImage?> Mask { get; }
     public InputProperty<bool> MaskIsVisible { get; }

+ 3 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs

@@ -2,6 +2,7 @@
 using System.Diagnostics;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
@@ -95,7 +96,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         return true;
     }
 
-    public Image? Execute(int frame)
+    public Image? Execute(RenderingContext context)
     {
         Stopwatch stopwatch = new();
         if (OutputNode == null) return null;
@@ -107,7 +108,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
             var node = queue.Dequeue();
             
             stopwatch.Restart();
-            node.Execute(frame);
+            node.Execute(context);
             Console.WriteLine($"{node.GetType().Name} took {stopwatch.ElapsedMilliseconds}ms to execute");
         }
 

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

@@ -1,4 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
@@ -28,7 +29,7 @@ public class CircleNode : Node
         Output = CreateOutput<Image?>("Output", "OUTPUT", null);
     }
     
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         Surface workingSurface = new Surface(new VecI(Radius.Value * 2, Radius.Value * 2));
         

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 
@@ -37,7 +38,7 @@ public class CombineColorNode : Node
         return new Color((byte)r, (byte)g, (byte)b, (byte)a);
     }
     
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -1,4 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
@@ -23,7 +24,7 @@ public class CreateImageNode : Node
         Fill = CreateInput(nameof(Fill), "FILL", new Color(0, 0, 0, 255));
     }
     
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         using var surface = new Surface(Size.Value);
 

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -21,7 +22,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
 
-    protected override Image? OnExecute(KeyFrameTime frame)
+    protected override Image? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Content.Value == null)
         {

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

@@ -1,4 +1,5 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using ChunkyImageLib.Operations;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
@@ -16,6 +17,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     private VecI size;
     
     private Paint blendPaint = new Paint();
+    private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surface.BlendMode.DstIn };
 
     public ImageLayerNode(VecI size)
     {
@@ -34,15 +36,15 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return true;
     }
 
-    protected override Image? OnExecute(KeyFrameTime frame)
+    protected override Image? OnExecute(RenderingContext context)
     {
-        if (!IsVisible.Value || Opacity.Value <= 0)
+        if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
             Output.Value = Background.Value;
             return Output.Value;
         }
 
-        var frameImage = GetFrameImage(frame).Image;
+        var frameImage = GetFrameImage(context.FrameTime).Image;
 
         Surface workingSurface;
         
@@ -56,29 +58,23 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             workingSurface.DrawingSurface.Canvas.DrawImage(Background.Value, 0, 0, blendPaint);
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             
-            using ChunkyImage img = new ChunkyImage(targetSize);
-            img.EnqueueDrawUpToDateChunkyImage(VecI.Zero, frameImage);
-
-            ApplyMaskIfPresent(img);
-            img.CommitChanges();
-
-            img.DrawMostUpToDateRegionOn(
+            frameImage.DrawMostUpToDateRegionOn(
                 new RectI(0, 0, frameImage.LatestSize.X, frameImage.LatestSize.Y), 
                 ChunkResolution.Full, workingSurface.DrawingSurface, VecI.Zero, blendPaint);
+            
+            ApplyMaskIfPresent(workingSurface);
+            ApplyRasterClip(workingSurface);
         }
         else
         {
             workingSurface = new Surface(frameImage.LatestSize);
             
-            using ChunkyImage img = new ChunkyImage(frameImage.LatestSize);
-            img.EnqueueDrawUpToDateChunkyImage(VecI.Zero, frameImage);
-            
-            ApplyMaskIfPresent(img);
-            img.CommitChanges();
-            
-            img.DrawMostUpToDateRegionOn(
+            frameImage.DrawMostUpToDateRegionOn(
                 new RectI(0, 0, frameImage.LatestSize.X, frameImage.LatestSize.Y), 
                 ChunkResolution.Full, workingSurface.DrawingSurface, VecI.Zero, blendPaint);
+            
+            ApplyMaskIfPresent(workingSurface);
+            ApplyRasterClip(workingSurface);
         }
 
 
@@ -88,6 +84,19 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return Output.Value;
     }
 
+    private bool IsEmptyMask()
+    {
+        return Mask.Value != null && MaskIsVisible.Value && !Mask.Value.LatestOrCommittedChunkExists();
+    }
+    
+    private void ApplyRasterClip(Surface surface)
+    {
+        if (ClipToPreviousMember.Value)
+        {
+            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value);
+        }
+    }
+
     private ImageFrame GetFrameImage(KeyFrameTime frame)
     {
         var imageFrame = frames.FirstOrDefault(x => x.IsInFrame(frame.Frame));
@@ -95,24 +104,25 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return frameImage;
     }
 
-    private void ApplyMaskIfPresent(ChunkyImage img)
+    private void ApplyMaskIfPresent(Surface surface)
     {
-        if (Mask.Value != null)
+        if (Mask.Value != null && MaskIsVisible.Value)
         {
-            img.EnqueueApplyMask(Mask.Value);
+            RectI region = new RectI(0, 0, size.X, size.Y);
+            Mask.Value.DrawMostUpToDateRegionOn(region, ChunkResolution.Full, surface.DrawingSurface, VecI.Zero, maskPaint);
         }
     }
 
-    protected override bool CacheChanged(KeyFrameTime frameTime)
+    protected override bool CacheChanged(RenderingContext context)
     {
-        var frame = GetFrameImage(frameTime);
-        return base.CacheChanged(frameTime) || frame?.RequiresUpdate == true;
+        var frame = GetFrameImage(context.FrameTime);
+        return base.CacheChanged(context) || frame?.RequiresUpdate == true;
     }
 
-    protected override void UpdateCache(KeyFrameTime time)
+    protected override void UpdateCache(RenderingContext context)
     {
-        base.UpdateCache(time);
-        var imageFrame = GetFrameImage(time);
+        base.UpdateCache(context);
+        var imageFrame = GetFrameImage(context.FrameTime);
         if (imageFrame is not null && imageFrame.RequiresUpdate)
         {
             imageFrame.RequiresUpdate = false; 

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

@@ -1,4 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -16,7 +17,7 @@ public class ImageSizeNode : Node
         Size = CreateOutput(nameof(Size), "SIZE", new VecI());
     }
     
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         Size.Value = Image.Value?.Size ?? new VecI();
 

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -17,7 +18,7 @@ public class ImageSpaceNode : Node
         Size = CreateFieldOutput(nameof(Size), "SIZE", ctx => ctx.Size);
     }
     
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -1,6 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -48,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(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -28,7 +29,7 @@ public class MergeNode : Node, IBackgroundInput
         return new MergeNode();
     }
 
-    protected override Image? OnExecute(KeyFrameTime frame)
+    protected override Image? OnExecute(RenderingContext context)
     {
         if(Top.Value == null && Bottom.Value == null)
         {

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
@@ -40,7 +41,7 @@ public class ModifyImageLeftNode : Node
         pixmap = Image.Value?.PeekPixels();
     }
 
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return Image.Value;
     }

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
@@ -25,7 +26,7 @@ public class ModifyImageRightNode : Node
         Output = CreateOutput<Image>(nameof(Output), "OUTPUT", null);
     }
 
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext renderingContext)
     {
         if (startNode.Image.Value is not { Size: var size })
         {

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

@@ -1,6 +1,7 @@
 using System.Diagnostics;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -33,32 +34,39 @@ public abstract class Node : IReadOnlyNode, IDisposable
     public VecD Position { get; set; }
 
     private KeyFrameTime _lastFrameTime = new KeyFrameTime(-1);
+    private ChunkResolution? _lastResolution;
+    private VecI? _lastChunkPos;
 
-    public Image? Execute(KeyFrameTime frameTime)
+    public Image? Execute(RenderingContext context)
     {
-        if(!CacheChanged(frameTime)) return CachedResult;
+        if(!CacheChanged(context)) return CachedResult;
         
-        CachedResult = OnExecute(frameTime);
-        UpdateCache(frameTime);
+        CachedResult = OnExecute(context);
+        UpdateCache(context);
         return CachedResult;
     }
 
-    protected abstract Image? OnExecute(KeyFrameTime frameTime);
+    protected abstract Image? OnExecute(RenderingContext context);
     public abstract bool Validate();
     
-    protected virtual bool CacheChanged(KeyFrameTime frameTime)
+    protected virtual bool CacheChanged(RenderingContext context)
     {
-        return !frameTime.Equals(_lastFrameTime) || inputs.Any(x => x.CacheChanged);
+        return !context.FrameTime.Equals(_lastFrameTime)
+               || context.Resolution != _lastResolution
+               || context.ChunkToUpdate != _lastChunkPos
+               || inputs.Any(x => x.CacheChanged);
     }
     
-    protected virtual void UpdateCache(KeyFrameTime time)
+    protected virtual void UpdateCache(RenderingContext context)
     {
         foreach (var input in inputs)
         {
             input.UpdateCache();
         }
         
-        _lastFrameTime = time;
+        _lastFrameTime = context.FrameTime;
+        _lastResolution = context.Resolution;
+        _lastChunkPos = context.ChunkToUpdate;
     }
 
     public void RemoveKeyFrame(Guid keyFrameGuid)

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -22,7 +23,7 @@ public class OutputNode : Node, IBackgroundInput
         return new OutputNode();
     }
 
-    protected override Image? OnExecute(KeyFrameTime frame)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return Input.Value;
     }

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 
@@ -26,7 +27,7 @@ public class SeparateColorNode : Node
         A = CreateFieldOutput(nameof(A), "A", ctx => Color.Value(ctx).A / 255d);
     }
 
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -20,7 +21,7 @@ public class SeparateVecDNode : Node
         Vector = CreateFieldInput("Vector", "VECTOR", new VecD(0, 0));
     }
 
-    protected override Image? OnExecute(KeyFrameTime frameTime)
+    protected override Image? OnExecute(RenderingContext context)
     {
         return null;
     }

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

@@ -1,6 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Numerics;
 
@@ -11,7 +12,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
     public InputProperty<Image?> Background { get; }
     public InputProperty<float> Opacity { get; } 
     public InputProperty<bool> IsVisible { get; }
-    public InputProperty<bool> ClipToMemberBelow { get; }
+    public InputProperty<bool> ClipToPreviousMember { get; }
     public InputProperty<BlendMode> BlendMode { get; } 
     public InputProperty<ChunkyImage?> Mask { get; }
     public InputProperty<bool> MaskIsVisible { get; }
@@ -25,7 +26,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         Background = CreateInput<Image?>("Background", "BACKGROUND", null);
         Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
         IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
-        ClipToMemberBelow = CreateInput<bool>("ClipToMemberBelow", "CLIP_TO_MEMBER_BELOW", false);
+        ClipToPreviousMember = CreateInput<bool>("ClipToMemberBelow", "CLIP_TO_MEMBER_BELOW", false);
         BlendMode = CreateInput<BlendMode>("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
         Mask = CreateInput<ChunkyImage?>("Mask", "MASK", null);
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
@@ -33,7 +34,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         Output = CreateOutput<Image?>("Output", "OUTPUT", null);
     }
 
-    protected abstract override Image? OnExecute(KeyFrameTime frameTime);
+    protected abstract override Image? OnExecute(RenderingContext context);
     public abstract override bool Validate();
 
     public abstract RectI? GetTightBounds(KeyFrameTime frameTime);

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Properties/LayerStructure/StructureMemberClipToMemberBelow_Change.cs

@@ -16,16 +16,16 @@ internal class StructureMemberClipToMemberBelow_Change : Change
 
     public override bool InitializeAndValidate(Document target)
     {
-        if (!target.TryFindMember(memberGuid, out var member) || member.ClipToMemberBelow.NonOverridenValue == newValue)
+        if (!target.TryFindMember(memberGuid, out var member) || member.ClipToPreviousMember.NonOverridenValue == newValue)
             return false;
-        originalValue = member.ClipToMemberBelow.NonOverridenValue;
+        originalValue = member.ClipToPreviousMember.NonOverridenValue;
         return true;
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
     {
         var member = target.FindMemberOrThrow(memberGuid);
-        member.ClipToMemberBelow.NonOverridenValue = newValue;
+        member.ClipToPreviousMember.NonOverridenValue = newValue;
         ignoreInUndo = false;
         return new StructureMemberClipToMemberBelow_ChangeInfo(memberGuid, newValue);
     }
@@ -33,7 +33,7 @@ internal class StructureMemberClipToMemberBelow_Change : Change
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         var member = target.FindMemberOrThrow(memberGuid);
-        member.ClipToMemberBelow.NonOverridenValue = originalValue;
+        member.ClipToPreviousMember.NonOverridenValue = originalValue;
         return new StructureMemberClipToMemberBelow_ChangeInfo(memberGuid, originalValue);
     }
 

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

@@ -9,12 +9,12 @@ public static class DocumentEvaluator
     public static OneOf<Chunk, EmptyChunk> RenderChunk(VecI chunkPos, ChunkResolution resolution,
         IReadOnlyNodeGraph graph, int frame, RectI? globalClippingRect = null)
     {
-        using RenderingContext context = new();
+        using RenderingContext context = new(frame, chunkPos, resolution);
         try
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Image? evaluated = graph.Execute(frame);
+            Image? evaluated = graph.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();
@@ -54,12 +54,12 @@ public static class DocumentEvaluator
     public static OneOf<Chunk, EmptyChunk> RenderChunk(VecI chunkPos, ChunkResolution resolution,
         IReadOnlyNode node, int frame, RectI? globalClippingRect = null)
     {
-        using RenderingContext context = new();
+        using RenderingContext context = new(frame, chunkPos, resolution);
         try
         {
             RectI? transformedClippingRect = TransformClipRect(globalClippingRect, resolution, chunkPos);
 
-            Image? evaluated = node.Execute(frame);
+            Image? evaluated = node.Execute(context);
             if (evaluated is null)
             {
                 return new EmptyChunk();

+ 17 - 10
src/PixiEditor.ChangeableDocument/Rendering/RenderingContext.cs

@@ -1,27 +1,34 @@
-using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
+using PixiEditor.Numerics;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 using DrawingApiBlendMode = PixiEditor.DrawingApi.Core.Surface.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Rendering;
-internal class RenderingContext : IDisposable
+public class RenderingContext : IDisposable
 {
     public Paint BlendModePaint = new () { BlendMode = DrawingApiBlendMode.SrcOver };
     public Paint BlendModeOpacityPaint = new () { BlendMode = DrawingApiBlendMode.SrcOver };
     public Paint ReplacingPaintWithOpacity = new () { BlendMode = DrawingApiBlendMode.Src };
 
-    public void UpdateFromMember(IReadOnlyStructureNode member)
-    {
-        Color opacityColor = new(255, 255, 255, (byte)Math.Round(member.Opacity.Value * 255));
-        DrawingApiBlendMode blendMode = GetDrawingBlendMode(member.BlendMode.Value);
+    public KeyFrameTime FrameTime { get; }
+    public VecI? ChunkToUpdate { get; }
+    public ChunkResolution? Resolution { get; }
 
-        BlendModeOpacityPaint.Color = opacityColor;
-        BlendModeOpacityPaint.BlendMode = blendMode;
-        BlendModePaint.BlendMode = blendMode;
-        ReplacingPaintWithOpacity.Color = opacityColor;
+    public RenderingContext(KeyFrameTime frameTime)
+    {
+        FrameTime = frameTime;
+    }
+    
+    public RenderingContext(KeyFrameTime frameTime, VecI chunkToUpdate, ChunkResolution resolution)
+    {
+        FrameTime = frameTime;
+        ChunkToUpdate = chunkToUpdate;
+        Resolution = resolution;
     }
 
     public static DrawingApiBlendMode GetDrawingBlendMode(BlendMode blendMode)

+ 1 - 1
src/PixiEditor.DrawingApi.Core/Surface/DrawingSurface.cs

@@ -7,7 +7,7 @@ using PixiEditor.Numerics;
 
 namespace PixiEditor.DrawingApi.Core.Surface
 {
-    public class DrawingSurface : NativeObject
+    public class DrawingSurface : NativeObject, IPixelsMap
     {
         public override object Native => DrawingBackendApi.Current.SurfaceImplementation.GetNativeSurface(ObjectPointer);
         public Canvas Canvas { get; private set; }

+ 8 - 0
src/PixiEditor.DrawingApi.Core/Surface/IPixelsMap.cs

@@ -0,0 +1,8 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Core.Surface;
+
+public interface IPixelsMap
+{
+    public Pixmap PeekPixels();
+}

+ 1 - 1
src/PixiEditor.DrawingApi.Core/Surface/ImageData/Image.cs

@@ -12,7 +12,7 @@ namespace PixiEditor.DrawingApi.Core.Surface.ImageData
     ///     <para />
     ///     <para>An image always has a non-zero dimensions. If there is a request to create a new image, either directly or via a surface, and either of the requested dimensions are zero, then <see langword="null" /> will be returned.</para>
     /// </remarks>
-    public class Image : NativeObject, ICloneable
+    public class Image : NativeObject, ICloneable, IPixelsMap
     {
         public override object Native => DrawingBackendApi.Current.ImageImplementation.GetNativeImage(ObjectPointer);
 

+ 8 - 0
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaImageImplementation.cs

@@ -14,6 +14,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         private readonly SkObjectImplementation<SKData> _imgImplementation;
         private readonly SkiaPixmapImplementation _pixmapImplementation;
         private SkObjectImplementation<SKSurface>? _surfaceImplementation;
+        private SkiaColorSpaceImplementation colorSpaceImpl;
         
         public SkiaImageImplementation(SkObjectImplementation<SKData> imgDataImplementation, SkiaPixmapImplementation pixmapImplementation)
         {
@@ -77,6 +78,13 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             ManagedInstances[nativeImg.Handle] = nativeImg;
             return new Image(nativeImg.Handle);
         }
+        
+        public Pixmap PeekPixels(Image image)
+        {
+            var native = ManagedInstances[image.ObjectPointer];
+            var pixmap = native.PeekPixels();
+            return _pixmapImplementation.CreateFrom(pixmap);
+        }
 
         public void GetColorShifts(ref int platformColorAlphaShift, ref int platformColorRedShift, ref int platformColorGreenShift,
             ref int platformColorBlueShift)

+ 0 - 1
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaShaderImplementation.cs

@@ -24,7 +24,6 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         public Shader? CreateFromSksl(string sksl, bool isOpaque, out string errors)
         {
             SKRuntimeEffect effect = SKRuntimeEffect.Create(sksl, out errors);
-            
             if (string.IsNullOrEmpty(errors))
             {
                 SKShader shader = effect.ToShader(isOpaque);