Browse Source

Folder node now works with all operations

flabbet 1 year ago
parent
commit
befa327e61

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

@@ -1,6 +1,7 @@
 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;
 using PixiEditor.Numerics;
 
@@ -24,35 +25,72 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
     protected override Surface? OnExecute(RenderingContext context)
     {
-        if (!IsVisible.Value || Content.Value == null)
+        if(Background.Value == null && Content.Value == null)
+        {
+            Output.Value = null;
+            return null;
+        }
+        
+        if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
             Output.Value = Background.Value;
             return Output.Value;
         }
+        
+        blendPaint.Color = new Color(255, 255, 255, 255);
+        blendPaint.BlendMode = DrawingApi.Core.Surface.BlendMode.Src;
 
-        VecI size = Content.Value?.Size ?? Background.Value.Size;
+        VecI size = Content.Value?.Size ?? Background.Value?.Size ?? VecI.Zero;
         
-        Surface workingSurface = new Surface(size);
+        var workingSurface = TryInitWorkingSurface(size, context);
 
-        if (Background.Value != null)
+        if (!HasOperations())
         {
-            workingSurface.DrawingSurface.Canvas.DrawSurface(Background.Value.DrawingSurface, 0, 0);
+            if (Background.Value != null)
+            {
+                DrawBackground(workingSurface, context);
+                blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
+            }
+            
+            if (Content.Value != null)
+            {
+                blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255)); 
+                DrawSurface(workingSurface, Content.Value, context);
+            }
+            
+            Output.Value = workingSurface;
+            return Output.Value;
         }
-
+        
         if (Content.Value != null)
         {
-            workingSurface.DrawingSurface.Canvas.DrawSurface(Content.Value.DrawingSurface, 0, 0);
+            DrawSurface(workingSurface, Content.Value, context);
+            
+            ApplyMaskIfPresent(workingSurface, context);
+            ApplyRasterClip(workingSurface, context);
+        }
+        
+        if (Background.Value != null)
+        {
+            Surface tempSurface = new Surface(workingSurface.Size);
+            DrawBackground(tempSurface, context);
+            
+            blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
+            blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
+            tempSurface.DrawingSurface.Canvas.DrawSurface(workingSurface.DrawingSurface, 0, 0, blendPaint);
+
+            Output.Value = tempSurface;
+            return tempSurface;
         }
 
         Output.Value = workingSurface;
-        
         return Output.Value;
     }
 
     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 RectI.Create(0, 0, Content.Value?.Size.X ?? 0, Content.Value?.Size.Y ?? 0);
         /*if (Children.Count == 0)
       {
           return null;

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

@@ -15,13 +15,9 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     private VecI size;
 
-    private Paint blendPaint = new Paint();
-    private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surface.BlendMode.DstIn };
     private static readonly Paint clearPaint = new() { BlendMode = DrawingApi.Core.Surface.BlendMode.Src, 
         Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent };
-
-    private Dictionary<ChunkResolution, Surface> workingSurfaces = new Dictionary<ChunkResolution, Surface>();
-
+    
     // Handled by overriden CacheChanged
     protected override bool AffectedByAnimation => true;
 
@@ -56,7 +52,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         var frameImage = GetFrameImage(context.FrameTime).Data;
 
-        blendPaint.Color = new Color(255, 255, 255, (byte)Math.Round(Opacity.Value * 255));
+        blendPaint.Color = new Color(255, 255, 255, 255);
         blendPaint.BlendMode = DrawingApi.Core.Surface.BlendMode.Src;
 
         var renderedSurface = RenderImage(frameImage, context);
@@ -68,30 +64,23 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     private Surface 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 (!hasSurface || workingSurface.Size != targetSize || workingSurface.IsDisposed)
-        {
-            workingSurfaces[targetResolution] = new Surface(targetSize);
-            workingSurface = workingSurfaces[targetResolution];
-        }
+        var workingSurface = TryInitWorkingSurface(frameImage.LatestSize, context);
 
         if (!HasOperations())
         {
+            bool canClear = Background.Value == null;
             if (Background.Value != null)
             {
                 DrawBackground(workingSurface, context);
                 blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             }
 
-            DrawLayer(frameImage, context, workingSurface);
+            DrawLayer(frameImage, context, workingSurface, canClear);
             Output.Value = workingSurface;
             return workingSurface;
         }
 
-        DrawLayer(frameImage, context, workingSurface);
+        DrawLayer(frameImage, context, workingSurface, true);
 
         // shit gets downhill with mask on big canvases, TODO: optimize
         ApplyMaskIfPresent(workingSurface, context);
@@ -112,51 +101,20 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return workingSurface;
     }
 
-    private void DrawBackground(Surface workingSurface, RenderingContext context)
-    {
-        RectI source = CalculateSourceRect(Background.Value, workingSurface.Size, context);
-        RectI target = CalculateDestinationRect(context);
-        using var snapshot = Background.Value.DrawingSurface.Snapshot(source);
-
-        workingSurface.DrawingSurface.Canvas.DrawImage(snapshot, target.X, target.Y, blendPaint);
-    }
-
-    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface)
+    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface, bool shouldClear)
     {
+        blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255)); 
         if (!frameImage.DrawMostUpToDateChunkOn(
                 context.ChunkToUpdate,
                 context.ChunkResolution,
                 workingSurface.DrawingSurface,
                 context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
-                blendPaint))
+                blendPaint) && shouldClear)
         {
             workingSurface.DrawingSurface.Canvas.DrawRect(CalculateDestinationRect(context), clearPaint);
         }
     }
 
-    private bool IsEmptyMask()
-    {
-        return Mask.Value != null && MaskIsVisible.Value && !Mask.Value.LatestOrCommittedChunkExists();
-    }
-
-    private bool HasOperations()
-    {
-        return (MaskIsVisible.Value && Mask.Value != null) || ClipToPreviousMember.Value;
-    }
-
-    private void ApplyRasterClip(Surface surface, RenderingContext context)
-    {
-        if (ClipToPreviousMember.Value)
-        {
-            RectI? clippingRect = null;
-            VecI chunkStart = context.ChunkToUpdate * context.ChunkResolution.PixelSize();
-            VecI size = new VecI(context.ChunkResolution.PixelSize());
-            clippingRect = new RectI(chunkStart, size);
-
-            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value, clippingRect);
-        }
-    }
-
     private ImageFrame GetFrameImage(KeyFrameTime frame)
     {
         var imageFrame = keyFrames.LastOrDefault(x => x.IsInFrame(frame.Frame));
@@ -169,46 +127,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return frameImage as ImageFrame;
     }
 
-    private void ApplyMaskIfPresent(Surface surface, RenderingContext context)
-    {
-        if (Mask.Value != null && MaskIsVisible.Value)
-        {
-            Mask.Value.DrawMostUpToDateChunkOn(
-                context.ChunkToUpdate,
-                context.ChunkResolution,
-                surface.DrawingSurface,
-                context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
-                maskPaint);
-        }
-    }
-
-    private RectI CalculateSourceRect(Surface image, VecI targetSize, RenderingContext context)
-    {
-        float multiplierToFit = image.Size.X / (float)targetSize.X;
-        int chunkSize = context.ChunkResolution.PixelSize();
-        VecI chunkPos = context.ChunkToUpdate;
-
-        int x = (int)(chunkPos.X * chunkSize * multiplierToFit);
-        int y = (int)(chunkPos.Y * chunkSize * multiplierToFit);
-        int width = (int)(chunkSize * multiplierToFit);
-        int height = (int)(chunkSize * multiplierToFit);
-
-        return new RectI(x, y, width, height);
-    }
-
-    private RectI CalculateDestinationRect(RenderingContext context)
-    {
-        int chunkSize = context.ChunkResolution.PixelSize();
-        VecI chunkPos = context.ChunkToUpdate;
-
-        int x = chunkPos.X * chunkSize;
-        int y = chunkPos.Y * chunkSize;
-        int width = chunkSize;
-        int height = chunkSize;
-
-        return new RectI(x, y, width, height);
-    }
-
     protected override bool CacheChanged(RenderingContext context)
     {
         var frame = GetFrameImage(context.FrameTime);
@@ -291,8 +209,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     public override void Dispose()
     {
         base.Dispose();
-        blendPaint.Dispose();
-        maskPaint.Dispose();
         clearPaint.Dispose();
         foreach (var surface in workingSurfaces.Values)
         {

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

@@ -1,8 +1,10 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using ChunkyImageLib.Operations;
+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.DrawingApi.Core.Surface.PaintImpl;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -10,17 +12,21 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundInput
 {
     public InputProperty<Surface?> Background { get; }
-    public InputProperty<float> Opacity { get; } 
+    public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }
     public InputProperty<bool> ClipToPreviousMember { get; }
-    public InputProperty<BlendMode> BlendMode { get; } 
+    public InputProperty<BlendMode> BlendMode { get; }
     public InputProperty<ChunkyImage?> Mask { get; }
     public InputProperty<bool> MaskIsVisible { get; }
-    
+
     public OutputProperty<Surface?> Output { get; }
-    
+
     public string MemberName { get; set; } = string.Empty;
 
+    protected Dictionary<ChunkResolution, Surface> workingSurfaces = new Dictionary<ChunkResolution, Surface>();
+    private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surface.BlendMode.DstIn };
+    protected Paint blendPaint = new Paint();
+
     protected StructureNode()
     {
         Background = CreateInput<Surface?>("Background", "BACKGROUND", null);
@@ -30,12 +36,112 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         BlendMode = CreateInput<BlendMode>("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
         Mask = CreateInput<ChunkyImage?>("Mask", "MASK", null);
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
-        
+
         Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
     }
 
     protected abstract override Surface? OnExecute(RenderingContext context);
     public abstract override bool Validate();
 
+    protected Surface TryInitWorkingSurface(VecI imageSize, RenderingContext context)
+    {
+        ChunkResolution targetResolution = context.ChunkResolution;
+        bool hasSurface = workingSurfaces.TryGetValue(targetResolution, out Surface workingSurface);
+        VecI targetSize = (VecI)(imageSize * targetResolution.Multiplier());
+
+        if (!hasSurface || workingSurface.Size != targetSize || workingSurface.IsDisposed)
+        {
+            workingSurfaces[targetResolution] = new Surface(targetSize);
+            workingSurface = workingSurfaces[targetResolution];
+        }
+
+        return workingSurface;
+    }
+
+    protected void ApplyMaskIfPresent(Surface surface, RenderingContext context)
+    {
+        if (Mask.Value != null && MaskIsVisible.Value)
+        {
+            Mask.Value.DrawMostUpToDateChunkOn(
+                context.ChunkToUpdate,
+                context.ChunkResolution,
+                surface.DrawingSurface,
+                context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
+                maskPaint);
+        }
+    }
+
+    protected void ApplyRasterClip(Surface surface, RenderingContext context)
+    {
+        if (ClipToPreviousMember.Value && Background.Value != null)
+        {
+            RectI? clippingRect = null;
+            VecI chunkStart = context.ChunkToUpdate * context.ChunkResolution.PixelSize();
+            VecI targetSize = new VecI(context.ChunkResolution.PixelSize());
+            clippingRect = new RectI(chunkStart, targetSize);
+
+            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value, clippingRect);
+        }
+    }
+
+
+    protected bool IsEmptyMask()
+    {
+        return Mask.Value != null && MaskIsVisible.Value && !Mask.Value.LatestOrCommittedChunkExists();
+    }
+
+    protected bool HasOperations()
+    {
+        return (MaskIsVisible.Value && Mask.Value != null) || ClipToPreviousMember.Value;
+    }
+
+    protected void DrawBackground(Surface workingSurface, RenderingContext context)
+    {
+        DrawSurface(workingSurface, Background.Value, context); 
+    }
+
+    protected void DrawSurface(Surface workingSurface, Surface source, RenderingContext context)
+    {
+        RectI sourceRect = CalculateSourceRect(source, workingSurface.Size, context);
+        RectI targetRect = CalculateDestinationRect(context);
+        using var snapshot = source.DrawingSurface.Snapshot(sourceRect);
+
+        workingSurface.DrawingSurface.Canvas.DrawImage(snapshot, targetRect.X, targetRect.Y, blendPaint);
+    }
+
+    protected RectI CalculateSourceRect(Surface image, VecI targetSize, RenderingContext context)
+    {
+        float multiplierToFit = image.Size.X / (float)targetSize.X;
+        int chunkSize = context.ChunkResolution.PixelSize();
+        VecI chunkPos = context.ChunkToUpdate;
+
+        int x = (int)(chunkPos.X * chunkSize * multiplierToFit);
+        int y = (int)(chunkPos.Y * chunkSize * multiplierToFit);
+        int width = (int)(chunkSize * multiplierToFit);
+        int height = (int)(chunkSize * multiplierToFit);
+
+        return new RectI(x, y, width, height);
+    }
+
+    protected RectI CalculateDestinationRect(RenderingContext context)
+    {
+        int chunkSize = context.ChunkResolution.PixelSize();
+        VecI chunkPos = context.ChunkToUpdate;
+
+        int x = chunkPos.X * chunkSize;
+        int y = chunkPos.Y * chunkSize;
+        int width = chunkSize;
+        int height = chunkSize;
+
+        return new RectI(x, y, width, height);
+    }
+
     public abstract RectI? GetTightBounds(KeyFrameTime frameTime);
+
+    public override void Dispose()
+    {
+        base.Dispose();
+        maskPaint.Dispose();
+        blendPaint.Dispose();
+    }
 }