Browse Source

Folders wip

flabbet 10 months ago
parent
commit
2ff518be9e
19 changed files with 193 additions and 94 deletions
  1. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IRenderInput.cs
  2. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/SceneObjectRenderContext.cs
  3. 49 27
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  4. 12 13
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
  5. 15 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  6. 5 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  7. 35 17
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  8. 0 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
  9. 19 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/RenderInputProperty.cs
  10. 30 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/RenderOutputProperty.cs
  11. 4 4
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs
  12. 7 7
      src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs
  13. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs
  14. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/DuplicateLayer_Change.cs
  15. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  16. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/RasterizeMember_Change.cs
  17. 1 1
      src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs
  18. 3 3
      src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs
  19. 1 1
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IBackgroundInput.cs → src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IRenderInput.cs

@@ -4,7 +4,7 @@ using PixiEditor.DrawingApi.Core.Surfaces;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 
-public interface IBackgroundInput
+public interface IRenderInput
 {
 {
-    InputProperty<DrawingSurface?> Background { get; }
+    RenderInputProperty RenderTarget { get; }
 }
 }

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

@@ -8,9 +8,11 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 public class SceneObjectRenderContext : RenderContext
 public class SceneObjectRenderContext : RenderContext
 {
 {
     public RectD LocalBounds { get; }
     public RectD LocalBounds { get; }
+    public bool RenderSurfaceIsScene { get; }
 
 
-    public SceneObjectRenderContext(DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI docSize) : base(surface, frameTime, chunkResolution, docSize)
+    public SceneObjectRenderContext(DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI docSize, bool renderSurfaceIsScene) : base(surface, frameTime, chunkResolution, docSize)
     {
     {
         LocalBounds = localBounds;
         LocalBounds = localBounds;
+        RenderSurfaceIsScene = renderSurfaceIsScene;
     }
     }
 }
 }

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

@@ -11,11 +11,24 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 [NodeInfo("Folder")]
 [NodeInfo("Folder")]
 public class FolderNode : StructureNode, IReadOnlyFolderNode
 public class FolderNode : StructureNode, IReadOnlyFolderNode
 {
 {
-    public InputProperty<DrawingSurface?> Content { get; }
+    public RenderInputProperty Content { get; }
 
 
     public FolderNode()
     public FolderNode()
     {
     {
-        Content = CreateInput<DrawingSurface?>("Content", "CONTENT", null);
+        Content = CreateRenderInput("Content", "CONTENT", ctx =>
+        {
+            RectD? bounds = new RectD(VecI.Zero, ctx.DocumentSize);
+            
+            // Folder doesn't need to do anything if no operations are present
+            if (bounds == null || !HasOperations()) 
+            {
+                return Output.GetFirstRenderTarget(ctx);
+            }
+
+            VecI size = (VecI)bounds.Value.Size;
+            var outputWorkingSurface = RequestTexture(0, size, false);
+            return outputWorkingSurface.DrawingSurface;
+        });
     }
     }
 
 
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
@@ -23,18 +36,20 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
 
     protected override void OnExecute(RenderContext context)
     protected override void OnExecute(RenderContext context)
     {
     {
+        base.OnExecute(context);
+
         /*if(Background.Value == null && Content.Value == null)
         /*if(Background.Value == null && Content.Value == null)
         {
         {
             Output.Value = null;
             Output.Value = null;
             return;
             return;
         }
         }
-        
+
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
         {
             Output.Value = Background.Value;
             Output.Value = Background.Value;
             return;
             return;
         }
         }
-        
+
         blendPaint.Color = new Color(255, 255, 255, 255);
         blendPaint.Color = new Color(255, 255, 255, 255);
         blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
         blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
 
 
@@ -44,10 +59,10 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
         }
         }
 
 
         VecI size = Content.Value?.Size ?? VecI.Zero;
         VecI size = Content.Value?.Size ?? VecI.Zero;
-        
-        var outputWorkingSurface = RequestTexture(0, size); 
-        var filterlessWorkingSurface = RequestTexture(1, size); 
-        
+
+        var outputWorkingSurface = RequestTexture(0, size);
+        var filterlessWorkingSurface = RequestTexture(1, size);
+
         if (Background.Value != null)
         if (Background.Value != null)
         {
         {
             DrawBackground(filterlessWorkingSurface.DrawingSurface, context);
             DrawBackground(filterlessWorkingSurface.DrawingSurface, context);
@@ -56,7 +71,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
 
         if (Content.Value != null)
         if (Content.Value != null)
         {
         {
-            blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255)); 
+            blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
             DrawSurface(filterlessWorkingSurface.DrawingSurface, Content.Value, context, null);
             DrawSurface(filterlessWorkingSurface.DrawingSurface, Content.Value, context, null);
         }
         }
 
 
@@ -71,30 +86,30 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
                 DrawBackground(outputWorkingSurface.DrawingSurface, context);
                 DrawBackground(outputWorkingSurface.DrawingSurface, context);
                 blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
                 blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             }
             }
-            
+
             if (Content.Value != null)
             if (Content.Value != null)
             {
             {
-                blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255)); 
+                blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
                 DrawSurface(outputWorkingSurface.DrawingSurface, Content.Value.DrawingSurface, context, Filters.Value);
                 DrawSurface(outputWorkingSurface.DrawingSurface, Content.Value.DrawingSurface, context, Filters.Value);
             }
             }
-            
+
             Output.Value = outputWorkingSurface.DrawingSurface;
             Output.Value = outputWorkingSurface.DrawingSurface;
         }
         }
-        
+
         if (Content.Value != null)
         if (Content.Value != null)
         {
         {
             DrawSurface(outputWorkingSurface.DrawingSurface, Content.Value.DrawingSurface, context, Filters.Value);
             DrawSurface(outputWorkingSurface.DrawingSurface, Content.Value.DrawingSurface, context, Filters.Value);
-            
+
             ApplyMaskIfPresent(outputWorkingSurface, context);
             ApplyMaskIfPresent(outputWorkingSurface, context);
         }
         }
-        
+
         if (Background.Value != null)
         if (Background.Value != null)
         {
         {
             Texture tempSurface = RequestTexture(2, outputWorkingSurface.Size);
             Texture tempSurface = RequestTexture(2, outputWorkingSurface.Size);
             DrawBackground(tempSurface.DrawingSurface, context);
             DrawBackground(tempSurface.DrawingSurface, context);
-            
+
             ApplyRasterClip(outputWorkingSurface, tempSurface);
             ApplyRasterClip(outputWorkingSurface, tempSurface);
-            
+
             blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
             blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             tempSurface.DrawingSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
             tempSurface.DrawingSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
@@ -106,18 +121,29 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
         Output.Value = outputWorkingSurface.DrawingSurface;*/
         Output.Value = outputWorkingSurface.DrawingSurface;*/
     }
     }
 
 
-    public override VecD ScenePosition => throw new NotImplementedException(); 
-    public override VecD SceneSize => throw new NotImplementedException();
+    public override VecD ScenePosition => Content.Value?.DeviceClipBounds.Size / 2f ?? VecD.Zero;
+    public override VecD SceneSize => Content.Value?.DeviceClipBounds.Size ?? VecD.Zero;
 
 
     public override void Render(SceneObjectRenderContext sceneContext)
     public override void Render(SceneObjectRenderContext sceneContext)
     {
     {
-        throw new NotImplementedException();
+        RectD bounds = RectD.Create(VecI.Zero, sceneContext.DocumentSize); 
+        
+        if(Content.Connection == null || !HasOperations())
+        {
+            return;
+        }
+        
+        VecI size = (VecI)bounds.Size;
+        var outputWorkingSurface = RequestTexture(0, size, false);
+        
+        sceneContext.RenderSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
+        outputWorkingSurface.DrawingSurface.Canvas.Clear();
     }
     }
 
 
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
     {
     {
         RectI bounds = new RectI();
         RectI bounds = new RectI();
-        if(Content.Connection != null)
+        if (Content.Connection != null)
         {
         {
             Content.Connection.Node.TraverseBackwards((n) =>
             Content.Connection.Node.TraverseBackwards((n) =>
             {
             {
@@ -132,15 +158,11 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
 
                 return true;
                 return true;
             });
             });
-            
+
             return (RectD)bounds;
             return (RectD)bounds;
         }
         }
-        
+
         return null;
         return null;
-        // TODO: Implement this
-        /*
-        return (RectD)RectI.Create(0, 0, Content.Value?.Size.X ?? 0, Content.Value?.Size.Y ?? 0);
-    */
     }
     }
 
 
     public HashSet<Guid> GetLayerNodeGuids()
     public HashSet<Guid> GetLayerNodeGuids()

+ 12 - 13
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

@@ -18,19 +18,18 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
     {
     {
     }
     }
 
 
-
     public override void Render(SceneObjectRenderContext sceneContext)
     public override void Render(SceneObjectRenderContext sceneContext)
     {
     {
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
         {
-            Output.Value = sceneContext.TargetSurface;
+            Output.Value = sceneContext.RenderSurface;
             return;
             return;
         }
         }
 
 
         blendPaint.Color = new Color(255, 255, 255, 255);
         blendPaint.Color = new Color(255, 255, 255, 255);
         blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.SrcOver;
         blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.SrcOver;
 
 
-        DrawingSurface target = sceneContext.TargetSurface;
+        DrawingSurface target = sceneContext.RenderSurface;
 
 
         VecI targetSize = GetTargetSize(sceneContext);
         VecI targetSize = GetTargetSize(sceneContext);
 
 
@@ -61,7 +60,7 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
         {
         {
             if (!HasOperations())
             if (!HasOperations())
             {
             {
-                if (Background.Value != null)
+                if (RenderTarget.Value != null)
                 {
                 {
                     blendPaint.BlendMode = RenderContext.GetDrawingBlendMode(BlendMode.Value);
                     blendPaint.BlendMode = RenderContext.GetDrawingBlendMode(BlendMode.Value);
                 }
                 }
@@ -74,14 +73,14 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
             outputWorkingSurface.DrawingSurface.Canvas.Clear();
             outputWorkingSurface.DrawingSurface.Canvas.Clear();
 
 
             DrawLayerOnTexture(context, outputWorkingSurface.DrawingSurface, true);
             DrawLayerOnTexture(context, outputWorkingSurface.DrawingSurface, true);
-            
+
             ApplyMaskIfPresent(outputWorkingSurface.DrawingSurface, context);
             ApplyMaskIfPresent(outputWorkingSurface.DrawingSurface, context);
 
 
-            if (Background.Value != null)
+            if (RenderTarget.Value != null)
             {
             {
-                Texture tempSurface = TryInitWorkingSurface(size, context.ChunkResolution, 4); 
+                Texture tempSurface = TryInitWorkingSurface(size, context.ChunkResolution, 4);
                 tempSurface.DrawingSurface.Canvas.Clear();
                 tempSurface.DrawingSurface.Canvas.Clear();
-                if (Background.Connection.Node is LayerNode layerNode)
+                if (RenderTarget.Connection.Node is LayerNode layerNode)
                 {
                 {
                     // TODO: This probably should work with StructureMembers not Layers only
                     // TODO: This probably should work with StructureMembers not Layers only
                     DrawPreviousLayer(tempSurface.DrawingSurface, layerNode, context);
                     DrawPreviousLayer(tempSurface.DrawingSurface, layerNode, context);
@@ -98,12 +97,13 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
             DrawWithResolution(outputWorkingSurface.DrawingSurface, renderOnto, context.ChunkResolution, size);
             DrawWithResolution(outputWorkingSurface.DrawingSurface, renderOnto, context.ChunkResolution, size);
         }
         }
     }
     }
-    
-    protected internal virtual void DrawLayerOnTexture(SceneObjectRenderContext ctx, DrawingSurface workingSurface, bool useFilters)
+
+    protected internal virtual void DrawLayerOnTexture(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
+        bool useFilters)
     {
     {
         int scaled = workingSurface.Canvas.Save();
         int scaled = workingSurface.Canvas.Save();
         workingSurface.Canvas.Translate(ScenePosition);
         workingSurface.Canvas.Translate(ScenePosition);
-        
+
         DrawLayerOnto(ctx, workingSurface, useFilters);
         DrawLayerOnto(ctx, workingSurface, useFilters);
 
 
         workingSurface.Canvas.RestoreToCount(scaled);
         workingSurface.Canvas.RestoreToCount(scaled);
@@ -113,11 +113,10 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
     {
     {
         int scaled = target.Canvas.Save();
         int scaled = target.Canvas.Save();
         float multiplier = (float)resolution.InvertedMultiplier();
         float multiplier = (float)resolution.InvertedMultiplier();
-        VecD shiftToCenter = SceneSize - (size * resolution.Multiplier()); 
         target.Canvas.Scale(multiplier, multiplier);
         target.Canvas.Scale(multiplier, multiplier);
 
 
         target.Canvas.DrawSurface(source, 0, 0, blendPaint);
         target.Canvas.DrawSurface(source, 0, 0, blendPaint);
-        
+
         target.Canvas.RestoreToCount(scaled);
         target.Canvas.RestoreToCount(scaled);
     }
     }
 
 

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

@@ -250,6 +250,21 @@ public abstract class Node : IReadOnlyNode, IDisposable
         return property;
         return property;
     }
     }
 
 
+    protected void AddOutputProperty(OutputProperty property)
+    {
+        outputs.Add(property);
+    }
+
+    protected void AddInputProperty(InputProperty property)
+    {
+        if (InputProperties.Any(x => x.InternalPropertyName == property.InternalPropertyName))
+        {
+            throw new InvalidOperationException($"Input with name {property.InternalPropertyName} already exists.");
+        }
+
+        inputs.Add(property);
+    }
+
     public virtual void Dispose()
     public virtual void Dispose()
     {
     {
         _isDisposed = true;
         _isDisposed = true;

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

@@ -7,14 +7,15 @@ using PixiEditor.DrawingApi.Core.Surfaces;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 
 [NodeInfo("Output")]
 [NodeInfo("Output")]
-public class OutputNode : Node, IBackgroundInput
+public class OutputNode : Node, IRenderInput
 {
 {
     public const string InputPropertyName = "Background";
     public const string InputPropertyName = "Background";
 
 
-    public InputProperty<DrawingSurface?> Input { get; } 
+    public RenderInputProperty Input { get; } 
     public OutputNode()
     public OutputNode()
     {
     {
-        Input = CreateInput<DrawingSurface>(InputPropertyName, "INPUT", null);
+        Input = new RenderInputProperty(this, InputPropertyName, "BACKGROUND", null, (ctx) => ctx.RenderSurface);
+        AddInputProperty(Input);
     }
     }
 
 
     public override Node CreateCopy()
     public override Node CreateCopy()
@@ -26,5 +27,5 @@ public class OutputNode : Node, IBackgroundInput
     {
     {
     }
     }
 
 
-    InputProperty<DrawingSurface?> IBackgroundInput.Background => Input;
+    RenderInputProperty IRenderInput.RenderTarget => Input;
 }
 }

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

@@ -13,13 +13,13 @@ using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 
-public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundInput
+public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput
 {
 {
     public abstract VecD ScenePosition { get; }
     public abstract VecD ScenePosition { get; }
     public abstract VecD SceneSize { get; }
     public abstract VecD SceneSize { get; }
 
 
     public const string DefaultMemberName = "DEFAULT_MEMBER_NAME";
     public const string DefaultMemberName = "DEFAULT_MEMBER_NAME";
-    public InputProperty<DrawingSurface?> Background { get; }
+    public RenderInputProperty RenderTarget { get; }
     public InputProperty<float> Opacity { get; }
     public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }
     public InputProperty<bool> IsVisible { get; }
     public bool ClipToPreviousMember { get; set; }
     public bool ClipToPreviousMember { get; set; }
@@ -27,9 +27,9 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
     public InputProperty<Texture?> CustomMask { get; }
     public InputProperty<Texture?> CustomMask { get; }
     public InputProperty<bool> MaskIsVisible { get; }
     public InputProperty<bool> MaskIsVisible { get; }
     public InputProperty<Filter> Filters { get; }
     public InputProperty<Filter> Filters { get; }
-    public OutputProperty<DrawingSurface?> Output { get; }
+    public RenderOutputProperty Output { get; }
 
 
-    public OutputProperty<Texture?> FilterlessOutput { get; }
+    public OutputProperty<DrawingSurface?> FilterlessOutput { get; }
 
 
     public ChunkyImage? EmbeddedMask { get; set; }
     public ChunkyImage? EmbeddedMask { get; set; }
 
 
@@ -54,7 +54,8 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
 
 
     protected StructureNode()
     protected StructureNode()
     {
     {
-        Background = CreateInput<DrawingSurface?>("Background", "BACKGROUND", null);
+        RenderTarget = CreateRenderInput("Background", "BACKGROUND", (context => Output.GetFirstRenderTarget(context)));
+        
         Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
         Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
         IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
         IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
         BlendMode = CreateInput("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
         BlendMode = CreateInput("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
@@ -62,32 +63,49 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
         Filters = CreateInput<Filter>(nameof(Filters), "FILTERS", null);
         Filters = CreateInput<Filter>(nameof(Filters), "FILTERS", null);
 
 
-        Output = CreateOutput<DrawingSurface?>("Output", "OUTPUT", null);
-        FilterlessOutput = CreateOutput<Texture?>(nameof(FilterlessOutput), "WITHOUT_FILTERS", null);
+        Output = CreateRenderOutput("Output", "OUTPUT");
+        FilterlessOutput = CreateOutput<DrawingSurface?>(nameof(FilterlessOutput), "WITHOUT_FILTERS", null);
 
 
         MemberName = DefaultMemberName;
         MemberName = DefaultMemberName;
     }
     }
 
 
+    protected RenderOutputProperty? CreateRenderOutput(string internalName, string displayName)
+    {
+        RenderOutputProperty prop = new RenderOutputProperty(this, internalName, displayName, null);
+        AddOutputProperty(prop);
+
+        return prop;
+    }
+
+    protected RenderInputProperty CreateRenderInput(string internalName, string displayName,
+        Func<RenderContext, DrawingSurface> renderTarget)
+    {
+        RenderInputProperty prop = new RenderInputProperty(this, internalName, displayName, null, renderTarget);
+        AddInputProperty(prop);
+
+        return prop;
+    }
+
     protected override bool AffectedByChunkResolution => true;
     protected override bool AffectedByChunkResolution => true;
 
 
     protected override void OnExecute(RenderContext context)
     protected override void OnExecute(RenderContext context)
     {
     {
         RectD localBounds = new RectD(0, 0, SceneSize.X, SceneSize.Y);
         RectD localBounds = new RectD(0, 0, SceneSize.X, SceneSize.Y);
 
 
-        DrawingSurface sceneSurface = Background.Value ?? context.TargetSurface;
+        DrawingSurface renderTarget = RenderTarget.Value ?? Output.GetFirstRenderTarget(context);
 
 
-        int savedNum = sceneSurface.Canvas.Save();
+        int savedNum = renderTarget.Canvas.Save();
 
 
-        sceneSurface.Canvas.ClipRect(new RectD(ScenePosition - (SceneSize / 2f), SceneSize));
+        renderTarget.Canvas.ClipRect(new RectD(ScenePosition - (SceneSize / 2f), SceneSize));
 
 
-        SceneObjectRenderContext renderObjectContext = new SceneObjectRenderContext(sceneSurface, localBounds,
-            context.FrameTime, context.ChunkResolution, context.DocumentSize);
+        SceneObjectRenderContext renderObjectContext = new SceneObjectRenderContext(renderTarget, localBounds,
+            context.FrameTime, context.ChunkResolution, context.DocumentSize, renderTarget == context.RenderSurface);
 
 
         Render(renderObjectContext);
         Render(renderObjectContext);
 
 
-        sceneSurface.Canvas.RestoreToCount(savedNum);
+        renderTarget.Canvas.RestoreToCount(savedNum);
 
 
-        Output.Value = sceneSurface;
+        Output.Value = renderTarget;
     }
     }
 
 
     public abstract void Render(SceneObjectRenderContext sceneContext);
     public abstract void Render(SceneObjectRenderContext sceneContext);
@@ -102,7 +120,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
             }
             }
             else if (EmbeddedMask != null)
             else if (EmbeddedMask != null)
             {
             {
-                surface.Canvas.DrawSurface(renderedMasks[context.ChunkResolution].DrawingSurface, 0, 0, maskPaint); 
+                surface.Canvas.DrawSurface(renderedMasks[context.ChunkResolution].DrawingSurface, 0, 0, maskPaint);
             }
             }
         }
         }
     }
     }
@@ -148,7 +166,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         {
         {
             cache[resolution] = new Texture(targetSize);
             cache[resolution] = new Texture(targetSize);
         }
         }
-        
+
         if (cache[resolution].Size != targetSize)
         if (cache[resolution].Size != targetSize)
         {
         {
             cache[resolution].Dispose();
             cache[resolution].Dispose();
@@ -167,7 +185,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
 
 
     protected void ApplyRasterClip(DrawingSurface toClip, DrawingSurface clipSource)
     protected void ApplyRasterClip(DrawingSurface toClip, DrawingSurface clipSource)
     {
     {
-        if (ClipToPreviousMember && Background.Value != null)
+        if (ClipToPreviousMember && RenderTarget.Value != null)
         {
         {
             toClip.Canvas.DrawSurface(clipSource, 0, 0, maskPaint);
             toClip.Canvas.DrawSurface(clipSource, 0, 0, maskPaint);
         }
         }

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

@@ -37,13 +37,6 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
 
 
     private int lastCacheHash;
     private int lastCacheHash;
 
 
-    protected override void OnExecute(RenderContext context)
-    {
-        base.OnExecute(context);
-        
-        Output.Value = context.TargetSurface;
-    }
-
     public override VecD ScenePosition => ShapeData?.TransformedAABB.Center ?? VecD.Zero;
     public override VecD ScenePosition => ShapeData?.TransformedAABB.Center ?? VecD.Zero;
     public override VecD SceneSize => ShapeData?.TransformedAABB.Size ?? VecD.Zero;
     public override VecD SceneSize => ShapeData?.TransformedAABB.Size ?? VecD.Zero;
 
 

+ 19 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/RenderInputProperty.cs

@@ -0,0 +1,19 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.Surfaces;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph;
+
+public class RenderInputProperty : InputProperty<DrawingSurface?>
+{
+    Func<RenderContext, DrawingSurface> getRenderTarget;
+    internal RenderInputProperty(Node node, string internalName, string displayName, DrawingSurface? defaultValue, Func<RenderContext, DrawingSurface> getRenderTarget) : base(node, internalName, displayName, defaultValue)
+    {
+        this.getRenderTarget = getRenderTarget;
+    }
+
+    public DrawingSurface GetRenderTarget(RenderContext context)
+    {
+        return getRenderTarget(context);
+    }
+}

+ 30 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/RenderOutputProperty.cs

@@ -0,0 +1,30 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.Surfaces;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph;
+
+public class RenderOutputProperty : OutputProperty<DrawingSurface>
+{
+    internal RenderOutputProperty(Node node, string internalName, string displayName, DrawingSurface defaultValue) : base(node, internalName, displayName, defaultValue)
+    {
+        
+    }
+    
+    public DrawingSurface GetFirstRenderTarget(RenderContext ctx)
+    {
+        foreach (var connection in Connections)
+        {
+            if (connection is RenderInputProperty renderInput)
+            {
+                var target = renderInput.GetRenderTarget(ctx);
+                if (target != null)
+                {
+                    return target;
+                }
+            }
+        }
+
+        return null;
+    }
+}

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

@@ -100,13 +100,13 @@ public static class NodeOperations
     {
     {
         List<IChangeInfo> changes = new();
         List<IChangeInfo> changes = new();
 
 
-        if (structureNode.Background.Connection != null)
+        if (structureNode.RenderTarget.Connection != null)
         {
         {
             // connect connection to next input if possible
             // connect connection to next input if possible
 
 
             var connections = structureNode.Output.Connections.ToArray();
             var connections = structureNode.Output.Connections.ToArray();
 
 
-            var output = structureNode.Background.Connection;
+            var output = structureNode.RenderTarget.Connection;
 
 
             foreach (var input in connections)
             foreach (var input in connections)
             {
             {
@@ -115,9 +115,9 @@ public static class NodeOperations
                     output.InternalPropertyName, input.InternalPropertyName));
                     output.InternalPropertyName, input.InternalPropertyName));
             }
             }
 
 
-            structureNode.Background.Connection.DisconnectFrom(structureNode.Background);
+            structureNode.RenderTarget.Connection.DisconnectFrom(structureNode.RenderTarget);
             changes.Add(new ConnectProperty_ChangeInfo(null, structureNode.Id, null,
             changes.Add(new ConnectProperty_ChangeInfo(null, structureNode.Id, null,
-                structureNode.Background.InternalPropertyName));
+                structureNode.RenderTarget.InternalPropertyName));
         }
         }
 
 
         var outputs = structureNode.Output.Connections.ToArray();
         var outputs = structureNode.Output.Connections.ToArray();

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

@@ -58,7 +58,7 @@ internal class CreateStructureMember_Change : Change
         {
         {
             document.NodeGraph.AddNode(member);
             document.NodeGraph.AddNode(member);
             List<ConnectProperty_ChangeInfo> connectPropertyChangeInfo =
             List<ConnectProperty_ChangeInfo> connectPropertyChangeInfo =
-                NodeOperations.AppendMember(targetInput, member.Output, member.Background, member.Id);
+                NodeOperations.AppendMember(targetInput, member.Output, member.RenderTarget, member.Id);
             changes.AddRange(connectPropertyChangeInfo);
             changes.AddRange(connectPropertyChangeInfo);
         }
         }
 
 
@@ -81,13 +81,13 @@ internal class CreateStructureMember_Change : Change
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document document)
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document document)
     {
     {
         var container = document.FindNodeOrThrow<Node>(parentGuid);
         var container = document.FindNodeOrThrow<Node>(parentGuid);
-        if (container is not IBackgroundInput backgroundInput)
+        if (container is not IRenderInput backgroundInput)
         {
         {
             throw new InvalidOperationException("Parent folder is not a valid container.");
             throw new InvalidOperationException("Parent folder is not a valid container.");
         }
         }
 
 
         StructureNode child = document.FindMemberOrThrow(newMemberGuid);
         StructureNode child = document.FindMemberOrThrow(newMemberGuid);
-        var childBackgroundConnection = child.Background.Connection;
+        var childBackgroundConnection = child.RenderTarget.Connection;
         child.Dispose();
         child.Dispose();
 
 
         document.NodeGraph.RemoveNode(child);
         document.NodeGraph.RemoveNode(child);
@@ -96,10 +96,10 @@ internal class CreateStructureMember_Change : Change
 
 
         if (childBackgroundConnection != null)
         if (childBackgroundConnection != null)
         {
         {
-            childBackgroundConnection?.ConnectTo(backgroundInput.Background);
+            childBackgroundConnection?.ConnectTo(backgroundInput.RenderTarget);
             ConnectProperty_ChangeInfo change = new(childBackgroundConnection.Node.Id,
             ConnectProperty_ChangeInfo change = new(childBackgroundConnection.Node.Id,
-                backgroundInput.Background.Node.Id, childBackgroundConnection.InternalPropertyName,
-                backgroundInput.Background.InternalPropertyName);
+                backgroundInput.RenderTarget.Node.Id, childBackgroundConnection.InternalPropertyName,
+                backgroundInput.RenderTarget.InternalPropertyName);
             changes.Add(change);
             changes.Add(change);
         }
         }
 
 
@@ -108,7 +108,7 @@ internal class CreateStructureMember_Change : Change
 
 
     private static void AppendFolder(InputProperty<DrawingSurface> backgroundInput, FolderNode folder, List<IChangeInfo> changes)
     private static void AppendFolder(InputProperty<DrawingSurface> backgroundInput, FolderNode folder, List<IChangeInfo> changes)
     {
     {
-        var appened = NodeOperations.AppendMember(backgroundInput, folder.Output, folder.Background, folder.Id);
+        var appened = NodeOperations.AppendMember(backgroundInput, folder.Output, folder.RenderTarget, folder.Id);
         changes.AddRange(appened);
         changes.AddRange(appened);
     }
     }
 
 

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

@@ -44,7 +44,7 @@ internal class DeleteStructureMember_Change : Change
     {
     {
         StructureNode node = document.FindMember(memberGuid);
         StructureNode node = document.FindMember(memberGuid);
 
 
-        var bgConnection = node.Background.Connection;
+        var bgConnection = node.RenderTarget.Connection;
         var outputConnections = node.Output.Connections.ToArray();
         var outputConnections = node.Output.Connections.ToArray();
 
 
         document.NodeGraph.RemoveNode(node);
         document.NodeGraph.RemoveNode(node);

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

@@ -51,7 +51,7 @@ internal class DuplicateLayer_Change : Change
 
 
         operations.Add(CreateLayer_ChangeInfo.FromLayer(clone));
         operations.Add(CreateLayer_ChangeInfo.FromLayer(clone));
 
 
-        operations.AddRange(NodeOperations.AppendMember(targetInput, clone.Output, clone.Background, clone.Id));
+        operations.AddRange(NodeOperations.AppendMember(targetInput, clone.Output, clone.RenderTarget, clone.Id));
 
 
         ignoreInUndo = false;
         ignoreInUndo = false;
 
 

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

@@ -45,14 +45,14 @@ internal class MoveStructureMember_Change : Change
     {
     {
         var sourceNode = document.FindMember(sourceNodeGuid);
         var sourceNode = document.FindMember(sourceNodeGuid);
         var targetNode = document.FindNode(targetNodeGuid);
         var targetNode = document.FindNode(targetNodeGuid);
-        if (sourceNode is null || targetNode is not IBackgroundInput backgroundInput)
+        if (sourceNode is null || targetNode is not IRenderInput backgroundInput)
             return [];
             return [];
 
 
         List<IChangeInfo> changes = new();
         List<IChangeInfo> changes = new();
         
         
-        Guid oldBackgroundId = sourceNode.Background.Node.Id;
+        Guid oldBackgroundId = sourceNode.RenderTarget.Node.Id;
 
 
-        InputProperty<DrawingSurface?> inputProperty = backgroundInput.Background;
+        InputProperty<DrawingSurface?> inputProperty = backgroundInput.RenderTarget;
 
 
         if (targetNode is FolderNode folder && putInsideFolder)
         if (targetNode is FolderNode folder && putInsideFolder)
         {
         {
@@ -63,7 +63,7 @@ internal class MoveStructureMember_Change : Change
         
         
         changes.AddRange(NodeOperations.DetachStructureNode(sourceNode));
         changes.AddRange(NodeOperations.DetachStructureNode(sourceNode));
         changes.AddRange(NodeOperations.AppendMember(inputProperty, sourceNode.Output,
         changes.AddRange(NodeOperations.AppendMember(inputProperty, sourceNode.Output,
-            sourceNode.Background,
+            sourceNode.RenderTarget,
             sourceNode.Id));
             sourceNode.Id));
         
         
         changes.Add(changeInfo);
         changes.Add(changeInfo);

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

@@ -60,7 +60,7 @@ internal class RasterizeMember_Change : Change
         InputProperty<DrawingSurface>? outputConnectedInput =
         InputProperty<DrawingSurface>? outputConnectedInput =
             outputConnection?.Connections.FirstOrDefault(x => x is InputProperty<DrawingSurface>) as InputProperty<DrawingSurface>;
             outputConnection?.Connections.FirstOrDefault(x => x is InputProperty<DrawingSurface>) as InputProperty<DrawingSurface>;
 
 
-        InputProperty<DrawingSurface> backgroundInput = imageLayer.Background;
+        InputProperty<DrawingSurface> backgroundInput = imageLayer.RenderTarget;
         OutputProperty<DrawingSurface> toAddOutput = imageLayer.Output;
         OutputProperty<DrawingSurface> toAddOutput = imageLayer.Output;
 
 
         List<IChangeInfo> changeInfos = new();
         List<IChangeInfo> changeInfos = new();

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

@@ -266,7 +266,7 @@ public class DocumentRenderer
             membersOnlyGraph.AddNode(clone);
             membersOnlyGraph.AddNode(clone);
 
 
             clone.Output.ConnectTo(lastInput);
             clone.Output.ConnectTo(lastInput);
-            lastInput = clone.Background;
+            lastInput = clone.RenderTarget;
         }
         }
 
 
         return membersOnlyGraph;
         return membersOnlyGraph;

+ 3 - 3
src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs

@@ -22,13 +22,13 @@ public class RenderContext : IDisposable
     public ChunkResolution ChunkResolution { get; }
     public ChunkResolution ChunkResolution { get; }
     public VecI DocumentSize { get; set; }
     public VecI DocumentSize { get; set; }
     
     
-    public DrawingSurface TargetSurface { get; set; }
+    public DrawingSurface RenderSurface { get; set; }
 
 
     public bool IsDisposed { get; private set; }
     public bool IsDisposed { get; private set; }
     
     
-    public RenderContext(DrawingSurface targetSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI docSize)
+    public RenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI docSize)
     {
     {
-        TargetSurface = targetSurface;
+        RenderSurface = renderSurface;
         FrameTime = frameTime;
         FrameTime = frameTime;
         ChunkResolution = chunkResolution;
         ChunkResolution = chunkResolution;
         DocumentSize = docSize;
         DocumentSize = docSize;

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

@@ -347,7 +347,7 @@ internal class MemberPreviewUpdater
         }
         }
 
 
         return combinedBounds;*/
         return combinedBounds;*/
-        return (RectI)folder.GetTightBounds(frame);
+        return (RectI?)folder.GetTightBounds(frame);
     }
     }
 
 
     /// <summary>
     /// <summary>