Sfoglia il codice sorgente

Implemented more Node previews

Krzysztof Krysiński 2 settimane fa
parent
commit
c39b5b278b
37 ha cambiato i file con 310 aggiunte e 494 eliminazioni
  1. 0 12
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs
  2. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyLayerNode.cs
  3. 11 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs
  4. 2 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs
  5. 1 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CreateImageNode.cs
  6. 3 6
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Effects/OutlineNode.cs
  7. 5 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs
  8. 5 16
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  9. 7 8
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  10. 4 18
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
  11. 4 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Matrix/Matrix3X3BaseNode.cs
  12. 4 31
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  13. 1 10
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  14. 1 16
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  15. 3 9
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  16. 12 25
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  17. 47 12
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/RenderNode.cs
  18. 1 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ShaderNode.cs
  19. 8 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShapeNode.cs
  20. 6 13
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  21. 0 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/TileNode.cs
  22. 16 15
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
  23. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Workspace/CustomOutputNode.cs
  24. 0 17
      src/PixiEditor.ChangeableDocument/Helpers/PreviewUtils.cs
  25. 1 59
      src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs
  26. 19 3
      src/PixiEditor.ChangeableDocument/Rendering/PreviewRenderRequest.cs
  27. 39 0
      src/PixiEditor.ChangeableDocument/Rendering/PreviewUtility.cs
  28. 27 0
      src/PixiEditor/Helpers/Converters/ResultPreviewIsPresentConverter.cs
  29. 0 1
      src/PixiEditor/Models/Handlers/IDocument.cs
  30. 3 3
      src/PixiEditor/Models/Rendering/AnimationPreviewRenderer.cs
  31. 43 45
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  32. 0 100
      src/PixiEditor/Models/Rendering/PreviewRenderer.cs
  33. 1 4
      src/PixiEditor/Models/Rendering/SceneRenderer.cs
  34. 1 1
      src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs
  35. 15 4
      src/PixiEditor/Styles/Templates/NodeView.axaml
  36. 1 4
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  37. 17 3
      src/PixiEditor/Views/Visuals/PreviewTextureControl.cs

+ 0 - 12
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs

@@ -1,12 +0,0 @@
-using Drawie.Backend.Core.Surfaces;
-using Drawie.Numerics;
-using PixiEditor.ChangeableDocument.Rendering;
-
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
-
-public interface IPreviewRenderable
-{
-    public RectD? GetPreviewBounds(int frame, string elementToRenderName = "");
-    public bool RenderPreview(DrawingSurface renderOn, RenderContext context,
-        string elementToRenderName);
-}

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

@@ -1,5 +1,5 @@
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
-public interface IReadOnlyLayerNode : IReadOnlyStructureNode, IPreviewRenderable
+public interface IReadOnlyLayerNode : IReadOnlyStructureNode
 {
 }

+ 11 - 11
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -82,9 +82,10 @@ public class CombineChannelsNode : RenderNode
         surface.Canvas.RestoreToCount(saved);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    public override RectD? GetPreviewBounds(RenderContext ctx, string elementToRenderName = "")
     {
-        RectD? redBounds = PreviewUtils.FindPreviewBounds(Red.Connection, frame, elementToRenderName);
+        int frame = ctx.FrameTime.Frame;
+        /*RectD? redBounds = PreviewUtils.FindPreviewBounds(Red.Connection, frame, elementToRenderName);
         RectD? greenBounds = PreviewUtils.FindPreviewBounds(Green.Connection, frame, elementToRenderName);
         RectD? blueBounds = PreviewUtils.FindPreviewBounds(Blue.Connection, frame, elementToRenderName);
         RectD? alphaBounds = PreviewUtils.FindPreviewBounds(Alpha.Connection, frame, elementToRenderName);
@@ -116,19 +117,18 @@ public class CombineChannelsNode : RenderNode
             finalBounds = finalBounds?.Union(alphaBounds.Value) ?? alphaBounds.Value;
         }
         
-        return finalBounds;
+        return finalBounds;*/
+        return null;
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    protected override bool ShouldRenderPreview(string elementToRenderName)
     {
-        if (Red.Value == null && Green.Value == null && Blue.Value == null && Alpha.Value == null)
-        {
-            return false;
-        }
+        return Red.Value != null || Green.Value != null || Blue.Value != null || Alpha.Value != null;
+    }
 
-        OnPaint(context, renderOn); 
-        
-        return true;
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    {
+        OnPaint(context, renderOn);
     }
 
     public override Node CreateCopy() => new CombineChannelsNode();

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

@@ -10,7 +10,7 @@ using Drawie.Numerics;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
 [NodeInfo("SeparateChannels")]
-public class SeparateChannelsNode : Node, IRenderInput, IPreviewRenderable
+public class SeparateChannelsNode : Node, IRenderInput
 {
     private readonly Paint _paint = new();
     
@@ -96,8 +96,7 @@ public class SeparateChannelsNode : Node, IRenderInput, IPreviewRenderable
     RenderInputProperty IRenderInput.Background => Image;
     public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
     {
-        RectD? bounds = PreviewUtils.FindPreviewBounds(Image.Connection, frame, elementToRenderName);
-        return bounds;
+        return null;
     }
 
     public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)

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

@@ -13,7 +13,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 [NodeInfo("CreateImage")]
-public class CreateImageNode : Node, IPreviewRenderable
+public class CreateImageNode : Node
 {
     public OutputProperty<Texture> Output { get; }
 
@@ -107,16 +107,6 @@ public class CreateImageNode : Node, IPreviewRenderable
         textureCache.Dispose();
     }
 
-    public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        if (Size.Value.X <= 0 || Size.Value.Y <= 0)
-        {
-            return null;
-        }
-
-        return new RectD(0, 0, Size.Value.X, Size.Value.Y);
-    }
-
     public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         if (Size.Value.X <= 0 || Size.Value.Y <= 0)

+ 3 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Effects/OutlineNode.cs

@@ -32,7 +32,6 @@ public class OutlineNode : RenderNode, IRenderInput
     private ImageFilter filter;
 
     private OutlineType? lastType = null;
-    private VecI lastDocumentSize;
 
     protected override bool ExecuteOnlyOnCacheChange => true;
 
@@ -52,7 +51,6 @@ public class OutlineNode : RenderNode, IRenderInput
     protected override void OnExecute(RenderContext context)
     {
         base.OnExecute(context);
-        lastDocumentSize = context.RenderOutputSize;
 
         Kernel finalKernel = Type.Value switch
         {
@@ -118,18 +116,17 @@ public class OutlineNode : RenderNode, IRenderInput
         Background?.Value?.Paint(context, surface);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    public override RectD? GetPreviewBounds(RenderContext ctx, string elementToRenderName = "")
     {
-        return new RectD(0, 0, lastDocumentSize.X, lastDocumentSize.Y);
+        return new RectD(0, 0, ctx.DocumentSize.X, ctx.DocumentSize.Y);
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         int saved = renderOn.Canvas.Save();
         renderOn.Canvas.Scale((float)context.ChunkResolution.Multiplier());
         OnPaint(context, renderOn);
         renderOn.Canvas.RestoreToCount(saved);
-        return true;
     }
 
     public override Node CreateCopy()

+ 5 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs

@@ -119,8 +119,11 @@ public sealed class ApplyFilterNode : RenderNode, IRenderInput
         finalSurface.Canvas.RestoreToCount(saved);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "") =>
-        PreviewUtils.FindPreviewBounds(Background.Connection, frame, elementToRenderName);
+    public override RectD? GetPreviewBounds(RenderContext ctx, string elementToRenderName = "") =>
+        null;
+        /*
+        PreviewUtils.FindPreviewBounds(Background.Connection, ctx.FrameTime.Frame, elementToRenderName);
+        */
 
     public override Node CreateCopy() => new ApplyFilterNode();
 

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

@@ -238,22 +238,13 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource
         return guids;
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
-    {
-        if (elementFor == nameof(EmbeddedMask))
-        {
-            return base.GetPreviewBounds(frame, elementFor);
-        }
-
-        return GetApproxBounds(frame);
-    }
-
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
     {
         if (elementToRenderName == nameof(EmbeddedMask))
         {
-            return base.RenderPreview(renderOn, context, elementToRenderName);
+            base.RenderPreview(renderOn, context, elementToRenderName);
+            return;
         }
 
         if (Content.Connection != null)
@@ -268,14 +259,12 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource
                     continue;
                 }
 
-                if (node is IPreviewRenderable previewRenderable)
+                /*if (node is IPreviewRenderable previewRenderable)
                 {
                     previewRenderable.RenderPreview(renderOn, context, elementToRenderName);
-                }
+                }*/
             }
         }
-
-        return true;
     }
 
     void IClipSource.DrawClipSource(SceneObjectRenderContext context, DrawingSurface drawOnto)

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

@@ -155,7 +155,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         workingSurface.Canvas.RestoreToCount(saved);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
+    /*public override RectD? GetPreviewBounds(int frame, string elementFor = "")
     {
         if (IsDisposed)
         {
@@ -211,19 +211,20 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         {
             return null;
         }
-    }
+    }*/
 
-    public override bool RenderPreview(DrawingSurface renderOnto, RenderContext context,
+    public override void RenderPreview(DrawingSurface renderOnto, RenderContext context,
         string elementToRenderName)
     {
         if (IsDisposed)
         {
-            return false;
+            return;
         }
 
         if (elementToRenderName == nameof(EmbeddedMask))
         {
-            return base.RenderPreview(renderOnto, context, elementToRenderName);
+            base.RenderPreview(renderOnto, context, elementToRenderName);
+            return;
         }
 
         var img = GetLayerImageAtFrame(context.FrameTime.Frame);
@@ -247,15 +248,13 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         if (img is null)
         {
-            return false;
+            return;
         }
 
         img.DrawCommittedRegionOn(
             new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
             context.ChunkResolution,
             renderOnto, VecI.Zero, replacePaint, context.DesiredSamplingOptions);
-
-        return true;
     }
 
     private KeyFrameData GetFrameWithImage(KeyFrameTime frame)

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

@@ -244,8 +244,6 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
         if (texture == null || texture.IsDisposed)
             return;
 
-
-
         int saved = texture.DrawingSurface.Canvas.Save();
 
         RenderContext previewCtx = ctx.Clone();
@@ -253,30 +251,17 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
         var approxBounds = GetApproxBounds(ctx.FrameTime);
 
         VecD size = approxBounds?.Size ?? ctx.DocumentSize;
-        VecD densityVec = size.Divide(texture.Size);
-        double density = Math.Min(densityVec.X, densityVec.Y);
-        ChunkResolution resolution = density switch
-        {
-            > 8.01 => ChunkResolution.Eighth,
-            > 4.01 => ChunkResolution.Quarter,
-            > 2.01 => ChunkResolution.Half,
-            _ => ChunkResolution.Full
-        };
 
-        previewCtx.ChunkResolution = resolution;
+        previewCtx.ChunkResolution = PreviewUtility.CalculateResolution(size, texture.Size);
 
         if (approxBounds.HasValue)
         {
             var bounds = approxBounds.Value;
 
-            // target size = texture size
             var targetW = texture.Size.X;
             var targetH = texture.Size.Y;
 
-            // scale so that bounds fits inside target
-            double scaleX = (double)targetW / bounds.Width;
-            double scaleY = (double)targetH / bounds.Height;
-            double uniformScale = Math.Min(scaleX, scaleY);
+            double uniformScale = PreviewUtility.CalculateUniformScaling(bounds.Size, texture.Size).X;
 
             // scaled content size
             double scaledW = bounds.Width * uniformScale;
@@ -288,12 +273,13 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
 
             var canvas = texture.DrawingSurface.Canvas;
             canvas.Translate((float)offsetX, (float)offsetY);
-            uniformScale *= resolution.InvertedMultiplier();
+            uniformScale *= previewCtx.ChunkResolution.InvertedMultiplier();
             canvas.Scale((float)uniformScale, (float)uniformScale);
 
             previewCtx.DesiredSamplingOptions = uniformScale < 1.0 ? SamplingOptions.Bilinear : SamplingOptions.Default;
         }
 
+        texture.DrawingSurface.Canvas.Clear();
         RenderPreview(texture.DrawingSurface, previewCtx, elementToRender);
 
         texture.DrawingSurface.Canvas.RestoreToCount(saved);

+ 4 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Matrix/Matrix3X3BaseNode.cs

@@ -50,20 +50,17 @@ public abstract class Matrix3X3BaseNode : RenderNode, IRenderInput
         surface.Canvas.RestoreToCount(layer);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    public override RectD? GetPreviewBounds(RenderContext ctx, string elementToRenderName = "")
     {
         if (Background.Value == null)
             return null;
 
-        return base.GetPreviewBounds(frame, elementToRenderName);
+        return base.GetPreviewBounds(ctx, elementToRenderName);
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    protected override bool ShouldRenderPreview(string elementToRenderName)
     {
-        if (Background.Value == null)
-            return false;
-
-        return base.RenderPreview(renderOn, context, elementToRenderName);
+        return Background.Value != null;
     }
 
     protected abstract Float3x3 CalculateMatrix(FuncContext ctx, Float3x3 input);

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

@@ -70,46 +70,19 @@ public class MergeNode : RenderNode
         Top.Value?.Paint(context, target);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    protected override bool ShouldRenderPreview(string elementToRenderName)
     {
-        if (Top.Value == null && Bottom.Value == null)
-        {
-            return null;
-        }
-
-        RectD? totalBounds = null;
-
-        if (Top.Connection != null && Top.Connection.Node is IPreviewRenderable topPreview)
-        {
-            var topBounds = topPreview.GetPreviewBounds(frame, elementToRenderName);
-            if (topBounds != null)
-            {
-                totalBounds = totalBounds?.Union(topBounds.Value) ?? topBounds;
-            }
-        }
-
-        if (Bottom.Connection != null && Bottom.Connection.Node is IPreviewRenderable bottomPreview)
-        {
-            var bottomBounds = bottomPreview.GetPreviewBounds(frame, elementToRenderName);
-            if (bottomBounds != null)
-            {
-                totalBounds = totalBounds?.Union(bottomBounds.Value) ?? bottomBounds;
-            }
-        }
-
-        return totalBounds;
+        return Top.Value != null || Bottom.Value != null;
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         if (Top.Value == null && Bottom.Value == null)
         {
-            return false;
+            return;
         }
 
         Merge(renderOn, context);
-
-        return true;
     }
 
     public override void Dispose()

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

@@ -12,7 +12,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 [NodeInfo("ModifyImageLeft")]
 [PairNode(typeof(ModifyImageRightNode), "ModifyImageZone", true)]
-public class ModifyImageLeftNode : Node, IPairNode, IPreviewRenderable
+public class ModifyImageLeftNode : Node, IPairNode
 {
     public InputProperty<Texture?> Image { get; }
 
@@ -51,15 +51,6 @@ public class ModifyImageLeftNode : Node, IPairNode, IPreviewRenderable
     }
 
     public override Node CreateCopy() => new ModifyImageLeftNode();
-    public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        if(Image.Value == null)
-        {
-            return null;
-        } 
-        
-        return new RectD(0, 0, Image.Value.Size.X, Image.Value.Size.Y);
-    }
 
     public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {

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

@@ -114,28 +114,13 @@ public class ModifyImageRightNode : RenderNode, IPairNode, ICustomShaderNode
         builder.Dispose();
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        var startNode = FindStartNode();
-        if (startNode != null)
-        {
-            return startNode.GetPreviewBounds(frame, elementToRenderName);
-        }
-
-        return null;
-    }
-
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         var startNode = FindStartNode();
         if (drawingPaint != null && startNode is { Image.Value: not null })
         {
             renderOn.Canvas.DrawRect(0, 0, startNode.Image.Value.Size.X, startNode.Image.Value.Size.Y, drawingPaint);
-
-            return true;
         }
-
-        return false;
     }
 
     public override void Dispose()

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

@@ -117,29 +117,23 @@ public class NoiseNode : RenderNode
         workingSurface.Canvas.RestoreToCount(saved);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        return new RectD(0, 0, 128, 128); 
-    }
-
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         var shader = SelectShader();
         if (shader == null)
         {
-            return false;
+            return;
         }
 
         if (paint.Shader != voronoiShader)
         {
             paint?.Shader?.Dispose();
         }
+
         paint.Shader = shader;
         paint.ColorFilter = grayscaleFilter;
         
         RenderNoise(renderOn);
-
-        return true;
     }
 
     private Shader SelectShader()

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

@@ -8,7 +8,7 @@ using Drawie.Numerics;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 [NodeInfo("Output")]
-public class OutputNode : Node, IRenderInput, IPreviewRenderable
+public class OutputNode : Node, IRenderInput
 {
     public const string UniqueName = "PixiEditor.Output";
     public const string InputPropertyName = "Background";
@@ -34,33 +34,20 @@ public class OutputNode : Node, IRenderInput, IPreviewRenderable
 
         Input.Value?.Paint(context, context.RenderSurface);
         lastDocumentSize = context.DocumentSize;
-    }
-
-    RenderInputProperty IRenderInput.Background => Input;
 
-    public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        if (lastDocumentSize == null)
+        var previews = context.GetPreviewTexturesForNode(Id);
+        if (previews is null) return;
+        foreach (var request in previews)
         {
-            return null;
-        }
-
-        return new RectD(0, 0, lastDocumentSize.Value.X, lastDocumentSize.Value.Y);
-    }
+            var texture = request.Texture;
+            if (texture is null) continue;
 
-    public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
-    {
-        if (Input.Value == null)
-        {
-            return false;
+            int saved = texture.DrawingSurface.Canvas.Save();
+            texture.DrawingSurface.Canvas.Scale((float)context.ChunkResolution.Multiplier());
+            Input.Value?.Paint(context, texture.DrawingSurface);
+            texture.DrawingSurface.Canvas.RestoreToCount(saved);
         }
-
-        int saved = renderOn.Canvas.Save();
-        renderOn.Canvas.Scale((float)context.ChunkResolution.Multiplier());
-        Input.Value.Paint(context, renderOn);
-
-        renderOn.Canvas.RestoreToCount(saved);
-
-        return true;
     }
+
+    RenderInputProperty IRenderInput.Background => Input;
 }

+ 47 - 12
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/RenderNode.cs

@@ -11,7 +11,7 @@ using PixiEditor.ChangeableDocument.Changes.Structure;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
-public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
+public abstract class RenderNode : Node, IHighDpiRenderNode
 {
     public RenderOutputProperty Output { get; }
 
@@ -49,10 +49,12 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
         DrawingSurface target = surface;
         bool useIntermediate = !AllowHighDpiRendering
                                && context.RenderOutputSize is { X: > 0, Y: > 0 }
-                               && (surface.DeviceClipBounds.Size != context.RenderOutputSize || (RendersInAbsoluteCoordinates && !surface.Canvas.TotalMatrix.IsIdentity));
+                               && (surface.DeviceClipBounds.Size != context.RenderOutputSize ||
+                                   (RendersInAbsoluteCoordinates && !surface.Canvas.TotalMatrix.IsIdentity));
         if (useIntermediate)
         {
-            Texture intermediate = textureCache.RequestTexture(-6451, context.RenderOutputSize, context.ProcessingColorSpace);
+            Texture intermediate =
+                textureCache.RequestTexture(-6451, context.RenderOutputSize, context.ProcessingColorSpace);
             target = intermediate.DrawingSurface;
         }
 
@@ -81,22 +83,56 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
                 surface.Canvas.Restore();
             }
         }
+
+        RenderPreviews(context);
     }
 
     protected abstract void OnPaint(RenderContext context, DrawingSurface surface);
 
-    public virtual RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    private void RenderPreviews(RenderContext ctx)
+    {
+        var previewToRender = ctx.GetPreviewTexturesForNode(Id);
+        if (previewToRender == null || previewToRender.Count == 0)
+            return;
+
+        foreach (var preview in previewToRender)
+        {
+            if (!ShouldRenderPreview(preview.ElementToRender))
+                continue;
+
+            if (preview.Texture == null)
+                continue;
+
+            int saved = preview.Texture.DrawingSurface.Canvas.Save();
+            preview.Texture.DrawingSurface.Canvas.Clear();
+
+            VecD scaling = PreviewUtility.CalculateUniformScaling(ctx.DocumentSize, preview.Texture.Size);
+            RenderContext adjusted =
+                PreviewUtility.CreatePreviewContext(ctx, scaling, ctx.DocumentSize, preview.Texture.Size);
+
+            preview.Texture.DrawingSurface.Canvas.Scale((float)scaling.X, (float)scaling.Y);
+
+            RenderPreview(preview.Texture.DrawingSurface, adjusted, preview.ElementToRender);
+            preview.Texture.DrawingSurface.Canvas.RestoreToCount(saved);
+        }
+    }
+
+    protected virtual bool ShouldRenderPreview(string elementToRenderName)
     {
-        return new RectD(0, 0, lastDocumentSize.X, lastDocumentSize.Y);
+        return true;
     }
 
-    public virtual bool RenderPreview(DrawingSurface renderOn, RenderContext context,
+    public virtual RectD? GetPreviewBounds(RenderContext ctx, string elementToRenderName)
+    {
+        return null;
+    }
+
+    public virtual void RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
     {
         int saved = renderOn.Canvas.Save();
         OnPaint(context, renderOn);
         renderOn.Canvas.RestoreToCount(saved);
-        return true;
     }
 
     protected Texture RequestTexture(int id, VecI size, ColorSpace processingCs, bool clear = true)
@@ -110,19 +146,18 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
         additionalData["AllowHighDpiRendering"] = AllowHighDpiRendering;
     }
 
-    internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data, List<IChangeInfo> infos)
+    internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data,
+        List<IChangeInfo> infos)
     {
         base.DeserializeAdditionalData(target, data, infos);
 
-        if(data.TryGetValue("AllowHighDpiRendering", out var value))
+        if (data.TryGetValue("AllowHighDpiRendering", out var value))
             AllowHighDpiRendering = (bool)value;
     }
 
     public override void Dispose()
     {
         base.Dispose();
-        textureCache.Dispose(); 
+        textureCache.Dispose();
     }
-
-   
 }

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

@@ -207,15 +207,9 @@ public class ShaderNode : RenderNode, IRenderInput, ICustomShaderNode
         }
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        return new RectD(0, 0, lastDocumentSize.X, lastDocumentSize.Y);
-    }
-
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         OnPaint(context, renderOn);
-        return true;
     }
 
     public override Node CreateCopy()

+ 8 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShapeNode.cs

@@ -34,20 +34,23 @@ public class RasterizeShapeNode : RenderNode
 
     public override Node CreateCopy() => new RasterizeShapeNode();
 
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    public override RectD? GetPreviewBounds(RenderContext ctx, string elementToRenderName = "")
     {
         return Data?.Value?.TransformedAABB;
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    protected override bool ShouldRenderPreview(string elementToRenderName)
+    {
+        return Data.Value != null && Data.Value.IsValid();
+    }
+
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         var shape = Data.Value;
 
         if (shape == null || !shape.IsValid())
-            return false;
+            return;
 
         shape.RasterizeTransformed(renderOn.Canvas);
-
-        return true;
     }
 }

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

@@ -285,37 +285,30 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
         }
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
+    protected override bool ShouldRenderPreview(string elementToRenderName)
     {
-        if (elementFor == nameof(EmbeddedMask) && EmbeddedMask != null)
+        if (elementToRenderName == nameof(EmbeddedMask))
         {
-            return new RectD(VecD.Zero, EmbeddedMask.LatestSize);
+            return true;
         }
 
-        return null;
+        return EmbeddedMask != null;
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
     {
-        if (elementToRenderName != nameof(EmbeddedMask))
-        {
-            return false;
-        }
-
         var img = EmbeddedMask;
 
         if (img is null)
         {
-            return false;
+            return;
         }
 
         img.DrawMostUpToDateRegionOn(
             new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
             context.ChunkResolution,
             renderOn, VecI.Zero, maskPreviewPaint, drawPaintOnEmpty: true);
-
-        return true;
     }
 
     public override void Dispose()

+ 0 - 11
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/TileNode.cs

@@ -69,15 +69,4 @@ public class TileNode : RenderNode
     {
         return new TileNode();
     }
-
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        return null;
-    }
-
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
-    {
-        return false;
-    }
-
 }

+ 16 - 15
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs

@@ -90,31 +90,35 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
         Rasterize(workingSurface, paint);
     }
 
-    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
+    protected override bool ShouldRenderPreview(string elementToRenderName)
     {
-        if (elementFor == nameof(EmbeddedMask))
+        if(RenderableShapeData == null)
         {
-            base.GetPreviewBounds(frame, elementFor);
-        }
-        else
-        {
-            return RenderableShapeData?.TransformedVisualAABB;
+            return false;
         }
 
-        return null;
+        VecI tightBoundsSize = (VecI)RenderableShapeData.TransformedVisualAABB.Size;
+
+        VecI translation = new VecI(
+            (int)Math.Max(RenderableShapeData.TransformedAABB.TopLeft.X, 0),
+            (int)Math.Max(RenderableShapeData.TransformedAABB.TopLeft.Y, 0));
+
+        VecI size = tightBoundsSize + translation;
+        return size.X > 0 && size.Y > 0;
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
+    public override void RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
     {
         if (elementToRenderName == nameof(EmbeddedMask))
         {
-            return base.RenderPreview(renderOn, context, elementToRenderName);
+            base.RenderPreview(renderOn, context, elementToRenderName);
+            return;
         }
 
         if (RenderableShapeData == null)
         {
-            return false;
+            return;
         }
 
         using var paint = new Paint();
@@ -129,16 +133,13 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
 
         if (size.X == 0 || size.Y == 0)
         {
-            return false;
+            return;
         }
 
-
         int savedCount = renderOn.Canvas.Save();
         renderOn.Canvas.Scale((float)context.ChunkResolution.Multiplier());
         Rasterize(renderOn, paint);
         renderOn.Canvas.RestoreToCount(savedCount);
-
-        return true;
     }
 
     public override RectD? GetApproxBounds(KeyFrameTime frameTime)

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

@@ -6,7 +6,7 @@ using PixiEditor.ChangeableDocument.Rendering;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Workspace;
 
 [NodeInfo("CustomOutput")]
-public class CustomOutputNode : Node, IRenderInput, IPreviewRenderable
+public class CustomOutputNode : Node, IRenderInput
 {
     public const string OutputNamePropertyName = "OutputName";
     public const string IsDefaultExportPropertyName = "IsDefaultExport";

+ 0 - 17
src/PixiEditor.ChangeableDocument/Helpers/PreviewUtils.cs

@@ -1,17 +0,0 @@
-using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
-using Drawie.Numerics;
-
-namespace PixiEditor.ChangeableDocument.Helpers;
-
-public static class PreviewUtils
-{
-    public static RectD? FindPreviewBounds(IOutputProperty? connectionProperty, int frame, string elementToRenderName)
-    {
-        if (connectionProperty is { Node: IPreviewRenderable previousPreview })
-        {
-            return previousPreview.GetPreviewBounds(frame, elementToRenderName);
-        }
-
-        return null;
-    }
-}

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

@@ -17,7 +17,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Workspace;
 
 namespace PixiEditor.ChangeableDocument.Rendering;
 
-public class DocumentRenderer : IPreviewRenderable, IDisposable
+public class DocumentRenderer : IDisposable
 {
     private Texture renderTexture;
 
@@ -176,28 +176,6 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
         return membersOnlyGraph;
     }
 
-    RectD? IPreviewRenderable.GetPreviewBounds(int frame, string elementNameToRender = "") =>
-        new(0, 0, Document.Size.X, Document.Size.Y);
-
-    bool IPreviewRenderable.RenderPreview(DrawingSurface renderOn, RenderContext context,
-        string elementToRenderName)
-    {
-        IsBusy = true;
-
-        /*
-        renderOn.Canvas.Clear();
-        int savedCount = renderOn.Canvas.Save();
-        renderOn.Canvas.Scale((float)context.ChunkResolution.Multiplier());
-        context.RenderSurface = renderOn;
-        Document.NodeGraph.Execute(context);
-        renderOn.Canvas.RestoreToCount(savedCount);
-        */
-
-        IsBusy = false;
-
-        return true;
-    }
-
     public void RenderDocument(DrawingSurface toRenderOn, KeyFrameTime frameTime, VecI renderSize,
         string? customOutput = null)
     {
@@ -324,41 +302,5 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
     {
         renderTexture?.Dispose();
         renderTexture = null;
-
-        /*foreach (var request in renderRequests)
-        {
-            if (request.TaskCompletionSource == null) continue;
-
-            request.TaskCompletionSource.TrySetCanceled();
-        }*/
-    }
-}
-
-public struct RenderRequest
-{
-    public RenderContext Context { get; set; }
-    public DrawingSurface RenderOn { get; set; }
-    public IReadOnlyNodeGraph? NodeGraph { get; set; } // TODO: Implement async rendering for stuff other than previews
-    public IPreviewRenderable? PreviewRenderable { get; set; }
-    public string ElementToRenderName { get; set; }
-    public TaskCompletionSource<bool> TaskCompletionSource { get; set; }
-
-    public RenderRequest(TaskCompletionSource<bool> completionSource, RenderContext context, DrawingSurface renderOn,
-        IReadOnlyNodeGraph nodeGraph)
-    {
-        TaskCompletionSource = completionSource;
-        Context = context;
-        RenderOn = renderOn;
-        NodeGraph = nodeGraph;
-    }
-
-    public RenderRequest(TaskCompletionSource<bool> completionSource, RenderContext context, DrawingSurface renderOn,
-        IPreviewRenderable previewRenderable, string elementToRenderName)
-    {
-        TaskCompletionSource = completionSource;
-        Context = context;
-        RenderOn = renderOn;
-        PreviewRenderable = previewRenderable;
-        ElementToRenderName = elementToRenderName;
     }
 }

+ 19 - 3
src/PixiEditor.ChangeableDocument/Rendering/PreviewRenderRequest.cs

@@ -4,13 +4,29 @@ namespace PixiEditor.ChangeableDocument.Rendering;
 
 public record struct PreviewRenderRequest
 {
-    public Texture? Texture { get; set; }
+    public Texture? Texture
+    {
+        get
+        {
+            if (!accessedTexture)
+            {
+                texture = textureCreateFunc();
+                accessedTexture = true;
+            }
+
+            return texture;
+        }
+    }
     public string? ElementToRender { get; set; }
     public Action TextureUpdatedAction { get; set; }
 
-    public PreviewRenderRequest(Texture? texture, Action textureUpdatedAction, string? elementToRender = null)
+    private Func<Texture?> textureCreateFunc;
+    private Texture? texture;
+    private bool accessedTexture = false;
+
+    public PreviewRenderRequest(Func<Texture?> textureCreateFunc, Action textureUpdatedAction, string? elementToRender = null)
     {
-        Texture = texture;
+        this.textureCreateFunc = textureCreateFunc;
         TextureUpdatedAction = textureUpdatedAction;
         ElementToRender = elementToRender;
     }

+ 39 - 0
src/PixiEditor.ChangeableDocument/Rendering/PreviewUtility.cs

@@ -0,0 +1,39 @@
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Rendering;
+
+public static class PreviewUtility
+{
+    public static ChunkResolution CalculateResolution(VecD size, VecD textureSize)
+    {
+        VecD densityVec = size.Divide(textureSize);
+        double density = Math.Min(densityVec.X, densityVec.Y);
+        return density switch
+        {
+            > 8.01 => ChunkResolution.Eighth,
+            > 4.01 => ChunkResolution.Quarter,
+            > 2.01 => ChunkResolution.Half,
+            _ => ChunkResolution.Full
+        };
+    }
+
+    public static VecD CalculateUniformScaling(VecD originalSize, VecD targetSize)
+    {
+        if (originalSize.X == 0 || originalSize.Y == 0)
+            return new VecD(1);
+
+        VecD scale = targetSize.Divide(originalSize);
+        double uniformScale = Math.Min(scale.X, scale.Y);
+        return new VecD(uniformScale, uniformScale);
+    }
+
+    public static RenderContext CreatePreviewContext(RenderContext ctx, VecD scaling, VecD renderSize, VecD textureSize)
+    {
+        var clone = ctx.Clone();
+        clone.ChunkResolution = CalculateResolution(renderSize, textureSize);
+        clone.DesiredSamplingOptions = scaling.X > 1 ? SamplingOptions.Default : SamplingOptions.Bilinear;
+
+        return clone;
+    }
+}

+ 27 - 0
src/PixiEditor/Helpers/Converters/ResultPreviewIsPresentConverter.cs

@@ -0,0 +1,27 @@
+using System.Globalization;
+using PixiEditor.ViewModels.Document;
+
+namespace PixiEditor.Helpers.Converters;
+
+internal class ResultPreviewIsPresentConverter : SingleInstanceMultiValueConverter<ResultPreviewIsPresentConverter>
+{
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is TexturePreview preview)
+        {
+            return preview.Preview != null;
+        }
+
+        return false;
+    }
+
+    public override object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
+    {
+        if (values[0] is TexturePreview preview)
+        {
+            return preview.Preview != null;
+        }
+
+        return false;
+    }
+}

+ 0 - 1
src/PixiEditor/Models/Handlers/IDocument.cs

@@ -49,7 +49,6 @@ internal interface IDocument : IHandler, Extensions.CommonApi.Documents.IDocumen
     public DocumentRenderer Renderer { get; }
     public ISnappingHandler SnappingHandler { get; }
     public IReadOnlyCollection<Guid> SelectedMembers { get; }
-    public PreviewRenderer PreviewRenderer { get; }
     public Dictionary<Guid, Texture> SceneTextures { get; }
     public Texture DocumentTexture { get; }
     public SceneRenderer SceneRenderer { get; }

+ 3 - 3
src/PixiEditor/Models/Rendering/AnimationPreviewRenderer.cs

@@ -8,7 +8,7 @@ using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.Models.Rendering;
 
-internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals) : IPreviewRenderable
+internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals)
 {
     public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
     {
@@ -22,7 +22,7 @@ internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals)
         return null;
     }
 
-    public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    /*public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
         if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(
                 Guid.Parse(elementToRenderName),
@@ -38,5 +38,5 @@ internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals)
         }
         
         return false;
-    }
+    }*/
 }

+ 43 - 45
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -21,7 +21,6 @@ internal class MemberPreviewUpdater
 {
     private readonly IDocument doc;
     private readonly DocumentInternalParts internals;
-    private PreviewRenderer renderer => doc.PreviewRenderer;
 
     private AnimationKeyFramePreviewRenderer AnimationKeyFramePreviewRenderer { get; }
 
@@ -135,18 +134,21 @@ internal class MemberPreviewUpdater
                 if (textureSize.X <= 0 || textureSize.Y <= 0)
                     continue;
 
-                if (structureMemberHandler.Preview.Preview == null || structureMemberHandler.Preview.Preview.IsDisposed ||
-                    structureMemberHandler.Preview.Preview.Size != textureSize)
+                Texture? CreateTextureForLayer()
                 {
-                    structureMemberHandler.Preview.Preview?.Dispose();
-                    structureMemberHandler.Preview.Preview = Texture.ForDisplay(textureSize);
-                }
-                else
-                {
-                    structureMemberHandler.Preview.Preview?.DrawingSurface.Canvas.Clear();
+                    if (structureMemberHandler.Preview.Preview == null ||
+                        structureMemberHandler.Preview.Preview.IsDisposed ||
+                        structureMemberHandler.Preview.Preview.Size != textureSize)
+                    {
+                        structureMemberHandler.Preview.Preview?.Dispose();
+                        structureMemberHandler.Preview.Preview = Texture.ForDisplay(textureSize);
+                    }
+
+                    return structureMemberHandler.Preview.Preview;
                 }
 
-                previewTextures[node.Id].Add(new PreviewRenderRequest(structureMemberHandler.Preview.Preview, structureMemberHandler.Preview.InvokeTextureUpdated));
+                previewTextures[node.Id].Add(new PreviewRenderRequest(CreateTextureForLayer,
+                    structureMemberHandler.Preview.InvokeTextureUpdated));
             }
         }
     }
@@ -242,10 +244,6 @@ internal class MemberPreviewUpdater
                 if (!members.Contains(node.Id))
                     continue;
 
-                var member = internals.Tracker.Document.FindMember(node.Id);
-                if (member is not IPreviewRenderable previewRenderable)
-                    continue;
-
                 if (structureMemberHandler.MaskPreview == null)
                 {
                     structureMemberHandler.MaskPreview = new TexturePreview(node.Id, RequestRender);
@@ -265,18 +263,20 @@ internal class MemberPreviewUpdater
                 if (textureSize.X <= 0 || textureSize.Y <= 0)
                     continue;
 
-                if (structureMemberHandler.MaskPreview.Preview == null || structureMemberHandler.MaskPreview.Preview.IsDisposed ||
-                    structureMemberHandler.MaskPreview.Preview.Size != textureSize)
+                Texture? CreateTextureForMask()
                 {
-                    structureMemberHandler.MaskPreview.Preview?.Dispose();
-                    structureMemberHandler.MaskPreview.Preview = Texture.ForDisplay(textureSize);
-                }
-                else
-                {
-                    structureMemberHandler.MaskPreview.Preview?.DrawingSurface.Canvas.Clear();
+                    if (structureMemberHandler.MaskPreview.Preview == null ||
+                        structureMemberHandler.MaskPreview.Preview.IsDisposed ||
+                        structureMemberHandler.MaskPreview.Preview.Size != textureSize)
+                    {
+                        structureMemberHandler.MaskPreview.Preview?.Dispose();
+                        structureMemberHandler.MaskPreview.Preview = Texture.ForDisplay(textureSize);
+                    }
+
+                    return structureMemberHandler.MaskPreview.Preview;
                 }
 
-                previewTextures[node.Id].Add(new PreviewRenderRequest(structureMemberHandler.MaskPreview.Preview,
+                previewTextures[node.Id].Add(new PreviewRenderRequest(CreateTextureForMask,
                     structureMemberHandler.MaskPreview.InvokeTextureUpdated));
             }
         }
@@ -345,40 +345,38 @@ internal class MemberPreviewUpdater
         if (previews == null)
             return;
 
-        if (node is IPreviewRenderable renderable)
+        nodeVm.Preview ??= new TexturePreview(node.Id, RequestRender);
+        if (nodeVm.Preview.Listeners.Count == 0)
         {
-            nodeVm.Preview ??= new TexturePreview(node.Id, RequestRender);
-            if (nodeVm.Preview.Listeners.Count == 0)
-            {
-                nodeVm.Preview.Preview?.Dispose();
-                return;
-            }
+            nodeVm.Preview.Preview?.Dispose();
+            return;
+        }
 
-            if (!previews.ContainsKey(node.Id))
-                previews[node.Id] = new List<PreviewRenderRequest>();
+        if (!previews.ContainsKey(node.Id))
+            previews[node.Id] = new List<PreviewRenderRequest>();
 
-            if (previews.TryGetValue(node.Id, out var existingPreviews) &&
-                existingPreviews.Any(x => string.IsNullOrEmpty(x.ElementToRender)))
-                return;
+        if (previews.TryGetValue(node.Id, out var existingPreviews) &&
+            existingPreviews.Any(x => string.IsNullOrEmpty(x.ElementToRender)))
+            return;
 
-            VecI textureSize = nodeVm.Preview.GetMaxListenerSize();
-            if (textureSize.X <= 0 || textureSize.Y <= 0)
-                return;
+        VecI textureSize = nodeVm.Preview.GetMaxListenerSize();
+        if (textureSize.X <= 0 || textureSize.Y <= 0)
+            return;
 
+        Texture? CreateTextureForNode()
+        {
             if (nodeVm.Preview.Preview == null || nodeVm.Preview.Preview.IsDisposed ||
                 nodeVm.Preview.Preview.Size != textureSize)
             {
                 nodeVm.Preview.Preview?.Dispose();
                 nodeVm.Preview.Preview = Texture.ForDisplay(textureSize);
             }
-            else
-            {
-                nodeVm.Preview.Preview?.DrawingSurface.Canvas.Clear();
-            }
 
-            previews[node.Id]
-                .Add(new PreviewRenderRequest(nodeVm.Preview.Preview, nodeVm.Preview.InvokeTextureUpdated));
-        }
+            return nodeVm.Preview.Preview;
+        };
+
+        previews[node.Id]
+            .Add(new PreviewRenderRequest(CreateTextureForNode, nodeVm.Preview.InvokeTextureUpdated));
     }
 
     private void RequestRender(Guid id)

+ 0 - 100
src/PixiEditor/Models/Rendering/PreviewRenderer.cs

@@ -1,100 +0,0 @@
-using ChunkyImageLib.DataHolders;
-using Drawie.Backend.Core;
-using Drawie.Backend.Core.Bridge;
-using Drawie.Backend.Core.Surfaces;
-using Drawie.Numerics;
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-using PixiEditor.ChangeableDocument.Rendering;
-
-namespace PixiEditor.Models.Rendering;
-
-public class PreviewRenderer
-{
-    private Queue<RenderRequest> renderRequests = new();
-
-    private bool isExecuting = false;
-
-    public IReadOnlyDocument Document { get; }
-
-    public PreviewRenderer(IReadOnlyDocument document)
-    {
-        Document = document;
-    }
-
-    public async Task RenderPreviews(KeyFrameTime frameTime)
-    {
-        await ExecuteRenderRequests(frameTime);
-    }
-
-    public async Task<bool> QueueRenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn,
-        RenderContext context,
-        string elementToRenderName)
-    {
-        if (previewRenderable is Node { IsDisposed: true }) return false;
-        TaskCompletionSource<bool> tcs = new();
-        RenderRequest request = new(tcs, context, renderOn, previewRenderable, elementToRenderName);
-
-        renderRequests.Enqueue(request);
-
-        return await tcs.Task;
-    }
-
-    private async Task ExecuteRenderRequests(KeyFrameTime frameTime)
-    {
-        isExecuting = true;
-        using var ctx = DrawingBackendApi.Current?.RenderingDispatcher.EnsureContext();
-
-        ChunkResolution highestResolution = renderRequests.MaxBy(x => 8 - (int)x.Context.ChunkResolution).Context.ChunkResolution;
-        using Texture docSizeTex = Texture.ForDisplay(Document.Size);
-        RenderContext context = new RenderContext(docSizeTex.DrawingSurface, frameTime, highestResolution,
-            Document.Size,
-            Document.Size, Document.ProcessingColorSpace, SamplingOptions.Default);
-
-        Document.NodeGraph.Execute(context);
-
-        while (renderRequests.Count > 0)
-        {
-            RenderRequest request = renderRequests.Dequeue();
-
-            /*if (frameTime.Frame != lastExecutedGraphFrame && request.PreviewRenderable != this)
-            {
-                using Texture executeSurface = Texture.ForDisplay(new VecI(1));
-                RenderDocument(executeSurface.DrawingSurface, frameTime, VecI.One);
-            }*/
-
-            try
-            {
-                bool result = true;
-                if (request.PreviewRenderable != null)
-                {
-                    if (request.PreviewRenderable.GetType() == typeof(DocumentRenderer))
-                    {
-                        var renderOn = request.RenderOn;
-                        int saved = renderOn.Canvas.Save();
-                        renderOn.Canvas.Scale((float)request.Context.ChunkResolution.Multiplier());
-                        renderOn.Canvas.Clear();
-                        renderOn.Canvas.DrawSurface(docSizeTex.DrawingSurface, 0, 0);
-                        renderOn.Canvas.RestoreToCount(saved);
-                        result = true;
-                    }
-                    else
-                    {
-                        result = request.PreviewRenderable.RenderPreview(request.RenderOn, request.Context,
-                            request.ElementToRenderName);
-                    }
-                }
-
-                request.TaskCompletionSource.SetResult(result);
-            }
-            catch (Exception e)
-            {
-                request.TaskCompletionSource.SetException(e);
-            }
-        }
-
-        isExecuting = false;
-    }
-}

+ 1 - 4
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -29,18 +29,15 @@ internal class SceneRenderer : IDisposable
     private int lastGraphCacheHash = -1;
     private KeyFrameTime lastFrameTime;
     private Dictionary<Guid, bool> lastFramesVisibility = new();
-    private PreviewRenderer previewRenderer;
 
     private ChunkResolution? lastResolution;
 
     private TextureCache textureCache = new();
 
-    public SceneRenderer(IReadOnlyDocument trackerDocument, IDocument documentViewModel,
-        PreviewRenderer previewRenderer)
+    public SceneRenderer(IReadOnlyDocument trackerDocument, IDocument documentViewModel)
     {
         Document = trackerDocument;
         DocumentViewModel = documentViewModel;
-        this.previewRenderer = previewRenderer;
     }
 
     public async Task RenderAsync(Dictionary<Guid, ViewportInfo> stateViewports, AffectedArea affectedArea,

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs

@@ -36,7 +36,7 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
     {
         SurfaceSerializationFactory surfaceFactory = new();
         surfaceFactory.Config = Config;
-        if (IsFilePreVersion(serializerData, new Version(2, 0, 1, 12)))
+        if (IsFilePreVersion(serializerData, new Version(2, 0, 1, 12)) || serializerData == default)
         {
             if (serialized is byte[] imgBytes)
             {

+ 15 - 4
src/PixiEditor/Styles/Templates/NodeView.axaml

@@ -3,7 +3,7 @@
                     xmlns:nodes="clr-namespace:PixiEditor.Views.Nodes"
                     xmlns:visuals="clr-namespace:PixiEditor.Views.Visuals"
                     xmlns:ui="clr-namespace:PixiEditor.UI.Common.Localization;assembly=PixiEditor.UI.Common"
-                    >
+                    xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters">
     <ControlTheme TargetType="nodes:NodeView" x:Key="{x:Type nodes:NodeView}">
         <Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}" />
         <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}" />
@@ -47,7 +47,8 @@
                                         </ControlTheme>
                                     </ItemsControl.ItemContainerTheme>
                                 </ItemsControl>
-                                <ItemsControl Name="PART_Inputs" ItemsSource="{TemplateBinding Inputs}" ClipToBounds="False">
+                                <ItemsControl Name="PART_Inputs" ItemsSource="{TemplateBinding Inputs}"
+                                              ClipToBounds="False">
                                     <ItemsControl.ItemContainerTheme>
                                         <ControlTheme TargetType="ContentPresenter">
                                             <Setter Property="DataContext" Value="." />
@@ -56,14 +57,24 @@
                                 </ItemsControl>
                             </StackPanel>
                         </Border>
-                        <Border IsVisible="{Binding ResultPreview, RelativeSource={RelativeSource TemplatedParent}}"
-                                CornerRadius="0, 0, 4.5, 4.5" Grid.Row="2" ClipToBounds="True">
+                        <Border
+                            CornerRadius="0, 0, 4.5, 4.5" Grid.Row="2" ClipToBounds="True">
+                            <Border.IsVisible>
+                                <MultiBinding Converter="{converters:ResultPreviewIsPresentConverter}">
+                                    <Binding Path="ResultPreview"
+                                             RelativeSource="{RelativeSource TemplatedParent}" />
+                                    <Binding Path="ResultPreview.Preview"
+                                             RelativeSource="{RelativeSource TemplatedParent}" />
+                                </MultiBinding>
+                            </Border.IsVisible>
                             <Panel RenderOptions.BitmapInterpolationMode="None" Width="200" Height="200">
                                 <Panel.Background>
                                     <ImageBrush Source="/Images/CheckerTile.png"
                                                 TileMode="Tile" DestinationRect="0, 0, 25, 25" />
                                 </Panel.Background>
                                 <visuals:PreviewTextureControl
+                                    Width="200"
+                                    Height="200"
                                     TexturePreview="{TemplateBinding ResultPreview}"
                                     RenderOptions.BitmapInterpolationMode="None">
                                 </visuals:PreviewTextureControl>

+ 1 - 4
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -215,8 +215,6 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
     public AnimationDataViewModel AnimationDataViewModel { get; }
     public TextOverlayViewModel TextOverlayViewModel { get; }
 
-    public PreviewRenderer PreviewRenderer { get; }
-
     public IReadOnlyCollection<IStructureMemberHandler> SoftSelectedStructureMembers => softSelectedStructureMembers;
     private DocumentInternalParts Internals { get; }
     INodeGraphHandler IDocument.NodeGraphHandler => NodeGraph;
@@ -293,8 +291,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         ReferenceLayerViewModel = new(this, Internals);
 
         Renderer = new DocumentRenderer(Internals.Tracker.Document);
-        PreviewRenderer = new PreviewRenderer(Internals.Tracker.Document);
-        SceneRenderer = new SceneRenderer(Internals.Tracker.Document, this, PreviewRenderer);
+        SceneRenderer = new SceneRenderer(Internals.Tracker.Document, this);
     }
 
     /// <summary>

+ 17 - 3
src/PixiEditor/Views/Visuals/PreviewTextureControl.cs

@@ -29,11 +29,26 @@ public class PreviewTextureControl : DrawieControl
         base.OnAttachedToVisualTree(e);
         if (TexturePreview != null)
         {
-            TexturePreview.Attach(this, () => new VecI((int)Math.Ceiling(Bounds.Size.Width), (int)Math.Ceiling(Bounds.Size.Height)));
+            TexturePreview.Attach(this, () =>
+            {
+                return GetBounds();
+            });
             TexturePreview.TextureUpdated += QueueNextFrame;
         }
     }
 
+    private VecI GetBounds()
+    {
+        double width = double.IsPositive(Width) ? Width : Bounds.Width;
+        double height = double.IsPositive(Height) ? Height : Bounds.Height;
+        if (double.IsNaN(width) || double.IsInfinity(width))
+            width = Bounds.Width;
+        if (double.IsNaN(height) || double.IsInfinity(height))
+            height = Bounds.Height;
+
+        return new VecI((int)Math.Ceiling(width), (int)Math.Ceiling(height));
+    }
+
     protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
     {
         base.OnDetachedFromVisualTree(e);
@@ -61,8 +76,7 @@ public class PreviewTextureControl : DrawieControl
             args.OldValue.Value?.Detach(control);
             if(args.OldValue.Value != null)
                 args.OldValue.Value.TextureUpdated -= control.QueueNextFrame;
-            args.NewValue.Value?.Attach(control, () =>
-                new VecI((int)Math.Ceiling(control.Bounds.Size.Width), (int)Math.Ceiling(control.Bounds.Size.Height)));
+            args.NewValue.Value?.Attach(control, () => control.GetBounds());
             if(args.NewValue.Value != null)
                 args.NewValue.Value.TextureUpdated += control.QueueNextFrame;
         }