Browse Source

Added mask preview

flabbet 10 months ago
parent
commit
f0c6d07f1f
21 changed files with 110 additions and 225 deletions
  1. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs
  2. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Filters.cs
  3. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  4. 7 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  5. 0 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
  6. 23 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  7. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
  8. 5 62
      src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs
  9. 1 1
      src/PixiEditor/Models/Handlers/IStructureMemberHandler.cs
  10. 10 27
      src/PixiEditor/Models/Rendering/CanvasUpdater.cs
  11. 39 92
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  12. 4 2
      src/PixiEditor/Models/Rendering/PreviewPainter.cs
  13. 0 3
      src/PixiEditor/Models/Rendering/RenderInfos/CanvasPreviewDirty_RenderInfo.cs
  14. 0 7
      src/PixiEditor/Models/Rendering/RenderInfos/DirtyRect_RenderInfo.cs
  15. 0 5
      src/PixiEditor/Models/Rendering/RenderInfos/IRenderInfo.cs
  16. 0 3
      src/PixiEditor/Models/Rendering/RenderInfos/MaskPreviewDirty_RenderInfo.cs
  17. 0 3
      src/PixiEditor/Models/Rendering/RenderInfos/NodePreviewDirty_RenderInfo.cs
  18. 0 3
      src/PixiEditor/Models/Rendering/RenderInfos/PreviewDirty_RenderInfo.cs
  19. 4 4
      src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs
  20. 4 1
      src/PixiEditor/Views/Layers/FolderControl.axaml
  21. 5 4
      src/PixiEditor/Views/Layers/LayerControl.axaml

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

@@ -6,5 +6,6 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 public interface IPreviewRenderable
 {
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame);
+    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName);
 }

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

@@ -37,4 +37,5 @@ public static class Filters
     /// </summary>
     public static readonly ColorFilter AverageGrayscaleFilter =
         ColorFilter.CreateColorMatrix(ColorMatrix.AverageGrayscale + ColorMatrix.OpaqueAlphaOffset);
+    
 }

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

@@ -164,7 +164,8 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
         }
     }
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame)
+    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName)
     {
         if (Content.Connection != null)
         {
@@ -174,7 +175,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
                 IReadOnlyNode node = executionQueue.Dequeue();
                 if (node is IPreviewRenderable previewRenderable)
                 {
-                    previewRenderable.RenderPreview(renderOn, resolution, frame);
+                    previewRenderable.RenderPreview(renderOn, resolution, frame, elementToRenderName);
                 }
             }
         }

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

@@ -111,8 +111,14 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         workingSurface.Canvas.RestoreToCount(saved);
     }
 
-    public override bool RenderPreview(DrawingSurface renderOnto, ChunkResolution resolution, int frame)
+    public override bool RenderPreview(DrawingSurface renderOnto, ChunkResolution resolution, int frame,
+        string elementToRenderName)
     {
+        if (elementToRenderName == nameof(EmbeddedMask))
+        {
+            return base.RenderPreview(renderOnto, resolution, frame, elementToRenderName);
+        }
+        
         var img = GetLayerImageAtFrame(frame);
 
         if (img is null)

+ 0 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

@@ -167,8 +167,6 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
         return workingSurface;
     }
 
-    public abstract bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame);
-
     void IClipSource.DrawOnTexture(SceneObjectRenderContext context, DrawingSurface drawOnto)
     {
         DrawLayerOnTexture(context, drawOnto, false);

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

@@ -13,7 +13,7 @@ using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
-public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput
+public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput, IPreviewRenderable
 {
     public abstract VecD ScenePosition { get; }
     public abstract VecD SceneSize { get; }
@@ -49,6 +49,8 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput
 
     private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surfaces.BlendMode.DstIn };
     protected Paint blendPaint = new Paint() { BlendMode = DrawingApi.Core.Surfaces.BlendMode.SrcOver };
+    protected Paint maskPreviewPaint = new Paint() { BlendMode = DrawingApi.Core.Surfaces.BlendMode.SrcOver, 
+        ColorFilter = Nodes.Filters.AlphaGrayscaleFilter };
 
     private int maskCacheHash = 0;
 
@@ -283,4 +285,24 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput
         maskPaint.Dispose();
         blendPaint.Dispose();
     }
+
+    public virtual bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName)
+    {
+        if (elementToRenderName != nameof(EmbeddedMask))
+        {
+            return false;
+        }
+        
+        var img = EmbeddedMask;
+
+        if (img is null)
+        {
+            return false;
+        }
+        
+        renderOn.Canvas.DrawSurface(renderedMask.DrawingSurface, VecI.Zero, maskPreviewPaint);
+
+        return true;
+    }
 }

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

@@ -65,7 +65,8 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
         Rasterize(workingSurface, ctx.ChunkResolution, paint);
     }
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame)
+    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName)
     {
         if (ShapeData == null)
         {

+ 5 - 62
src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs

@@ -9,8 +9,6 @@ using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Rendering;
-using PixiEditor.Models.Rendering.RenderInfos;
-using PixiEditor.Numerics;
 
 namespace PixiEditor.Models.DocumentModels;
 #nullable enable
@@ -108,27 +106,18 @@ internal class ActionAccumulator
             // update the contents of the bitmaps
             var affectedAreas = new AffectedAreasGatherer(document.AnimationHandler.ActiveFrameTime, internals.Tracker,
                 optimizedChanges);
-            List<IRenderInfo> renderResult = new();
             if (DrawingBackendApi.Current.IsHardwareAccelerated)
             {
-                renderResult.AddRange(canvasUpdater.UpdateGatheredChunksSync(affectedAreas,
-                    undoBoundaryPassed || viewportRefreshRequest));
+                canvasUpdater.UpdateGatheredChunksSync(affectedAreas,
+                    undoBoundaryPassed || viewportRefreshRequest);
             }
             else
             {
-                renderResult.AddRange(await canvasUpdater.UpdateGatheredChunks(affectedAreas,
-                    undoBoundaryPassed || viewportRefreshRequest));
+                await canvasUpdater.UpdateGatheredChunks(affectedAreas,
+                    undoBoundaryPassed || viewportRefreshRequest);
             }
 
-            renderResult.AddRange(previewUpdater.UpdatePreviews(undoBoundaryPassed, affectedAreas.ImagePreviewAreas.Keys));
-
-            if (undoBoundaryPassed)
-            {
-                //ClearDirtyRects();
-            }
-
-            // add dirty rectangles
-            AddDirtyRects(renderResult);
+            previewUpdater.UpdatePreviews(undoBoundaryPassed, affectedAreas.ImagePreviewAreas.Keys);
 
             // force refresh viewports for better responsiveness
             foreach (var (_, value) in internals.State.Viewports)
@@ -154,50 +143,4 @@ internal class ActionAccumulator
 
         return true;
     }
-
-    private void AddDirtyRects(List<IRenderInfo> changes)
-    {
-        foreach (IRenderInfo renderInfo in changes)
-        {
-            switch (renderInfo)
-            {
-                case DirtyRect_RenderInfo info:
-                {
-                    //TODO: Validate if it's required
-                }
-                    break;
-                case PreviewDirty_RenderInfo info:
-                {
-                    /*var bitmap = document.StructureHelper.Find(info.GuidValue)?.PreviewPainter;
-                    if (bitmap is null)
-                        continue;
-                    bitmap.AddDirtyRect(new RectI(0, 0, bitmap.Size.X, bitmap.Size.Y));*/
-                }
-                    break;
-                case MaskPreviewDirty_RenderInfo info:
-                {
-                    /*var bitmap = document.StructureHelper.Find(info.GuidValue)?.MaskPreviewSurface;
-                    if (bitmap is null)
-                        continue;
-                    bitmap.AddDirtyRect(new RectI(0, 0, bitmap.Size.X, bitmap.Size.Y));*/
-                }
-                    break;
-                case CanvasPreviewDirty_RenderInfo:
-                {
-                    document.PreviewSurface.AddDirtyRect(new RectI(0, 0, document.PreviewSurface.Size.X,
-                        document.PreviewSurface.Size.Y));
-                }
-                    break;
-                case NodePreviewDirty_RenderInfo info:
-                {
-                    /*var node = document.StructureHelper.Find(info.NodeId);
-                    if (node is null || node.PreviewPainter is null)
-                        continue;
-                    node.PreviewPainter.AddDirtyRect(new RectI(0, 0, node.PreviewPainter.Size.X,
-                        node.PreviewPainter.Size.Y));*/
-                }
-                    break;
-            }
-        }
-    }
 }

+ 1 - 1
src/PixiEditor/Models/Handlers/IStructureMemberHandler.cs

@@ -14,7 +14,7 @@ namespace PixiEditor.Models.Handlers;
 internal interface IStructureMemberHandler : INodeHandler
 {
     public bool HasMaskBindable { get; }
-    public PreviewPainter? MaskPreviewSurface { get; set; }
+    public PreviewPainter? MaskPreviewPainter { get; set; }
     public PreviewPainter? PreviewPainter { get; set; }
     public bool MaskIsVisibleBindable { get; set; }
     public StructureMemberSelectionType Selection { get; set; }

+ 10 - 27
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -1,20 +1,12 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Avalonia.Threading;
-using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.Operations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Handlers;
-using PixiEditor.Models.Rendering.RenderInfos;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.Models.Rendering;
@@ -70,19 +62,19 @@ internal class CanvasUpdater
     /// <summary>
     /// Don't call this outside ActionAccumulator
     /// </summary>
-    public async Task<List<IRenderInfo>> UpdateGatheredChunks
+    public async Task UpdateGatheredChunks
         (AffectedAreasGatherer chunkGatherer, bool rerenderDelayed)
-    {
-        return await Task.Run(() => Render(chunkGatherer, rerenderDelayed)).ConfigureAwait(true);
+    { 
+        await Task.Run(() => Render(chunkGatherer, rerenderDelayed)).ConfigureAwait(true);
     }
 
     /// <summary>
     /// Don't call this outside ActionAccumulator
     /// </summary>
-    public List<IRenderInfo> UpdateGatheredChunksSync
+    public void UpdateGatheredChunksSync
         (AffectedAreasGatherer chunkGatherer, bool rerenderDelayed)
     {
-        return Render(chunkGatherer, rerenderDelayed);
+        Render(chunkGatherer, rerenderDelayed);
     }
 
     private Dictionary<ChunkResolution, HashSet<VecI>> FindChunksVisibleOnViewports(bool onDelayed, bool all)
@@ -160,7 +152,7 @@ internal class CanvasUpdater
         }
     }
 
-    private List<IRenderInfo> Render(AffectedAreasGatherer chunkGatherer, bool rerenderDelayed)
+    private void Render(AffectedAreasGatherer chunkGatherer, bool rerenderDelayed)
     {
         Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender =
             FindGlobalChunksToRerender(chunkGatherer, rerenderDelayed);
@@ -190,16 +182,13 @@ internal class CanvasUpdater
         }
 
         if (!anythingToUpdate)
-            return new();
+            return;
 
-        List<IRenderInfo> infos = new();
-        UpdateMainImage(chunksToRerender, updatingStoredChunks ? null : chunkGatherer.MainImageArea.GlobalArea.Value,
-            infos);
-        return infos;
+        UpdateMainImage(chunksToRerender, updatingStoredChunks ? null : chunkGatherer.MainImageArea.GlobalArea.Value);
     }
 
     private void UpdateMainImage(Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender,
-        RectI? globalClippingRectangle, List<IRenderInfo> infos)
+        RectI? globalClippingRectangle)
     {
         if (chunksToRerender.Count == 0)
             return;
@@ -218,12 +207,6 @@ internal class CanvasUpdater
                 RectI chunkRect = new(chunkPos * chunkSize, new(chunkSize, chunkSize));
                 if (globalScaledClippingRectangle is RectI rect)
                     chunkRect = chunkRect.Intersect(rect);
-
-                infos.Add(new DirtyRect_RenderInfo(
-                    chunkRect.Pos,
-                    chunkRect.Size,
-                    resolution
-                ));
             }
         }
     }

+ 39 - 92
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -4,133 +4,58 @@ using System.Diagnostics.CodeAnalysis;
 using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Handlers;
-using PixiEditor.Models.Rendering.RenderInfos;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.Models.Rendering;
 
 internal class MemberPreviewUpdater
 {
-    private const float smoothingThreshold = 1.5f;
-
     private readonly IDocument doc;
     private readonly DocumentInternalParts internals;
 
-    private static readonly Paint SmoothReplacingPaint = new()
-    {
-        BlendMode = BlendMode.Src, FilterQuality = FilterQuality.Medium, IsAntiAliased = true
-    };
-
-    private static readonly Paint ReplacingPaint = new() { BlendMode = BlendMode.Src };
-
-    private static readonly Paint ClearPaint =
-        new() { BlendMode = BlendMode.Src, Color = DrawingApi.Core.ColorsImpl.Colors.Transparent };
-
     public MemberPreviewUpdater(IDocument doc, DocumentInternalParts internals)
     {
         this.doc = doc;
         this.internals = internals;
     }
 
-    public List<IRenderInfo> UpdatePreviews(bool rerenderPreviews, IEnumerable<Guid> keys)
+    public void UpdatePreviews(bool rerenderPreviews, IEnumerable<Guid> keys)
     {
         if (!rerenderPreviews)
-            return new List<IRenderInfo>();
-
-        var renderInfos = UpdatePreviewPainters(keys);
-
-        return renderInfos;
-    }
-
-    /// <summary>
-    /// Finds the current committed tight bounds for a layer.
-    /// </summary>
-    private RectI? FindLayerTightBounds(IReadOnlyLayerNode layer, int frame, bool forMask)
-    {
-        if (layer.EmbeddedMask is null && forMask)
-            throw new InvalidOperationException();
-
-        if (layer.EmbeddedMask is not null && forMask)
-            return FindImageTightBoundsFast(layer.EmbeddedMask);
-
-        if (layer is IReadOnlyImageNode raster)
-        {
-            return FindImageTightBoundsFast(raster.GetLayerImageAtFrame(frame));
-        }
-
-        return (RectI?)layer.GetTightBounds(frame);
-    }
-
-    /// <summary>
-    /// Finds the current committed tight bounds for a folder recursively.
-    /// </summary>
-    private RectI? FindFolderTightBounds(IReadOnlyFolderNode folder, int frame, bool forMask)
-    {
-        if (forMask)
-        {
-            if (folder.EmbeddedMask is null)
-                throw new InvalidOperationException();
-            return FindImageTightBoundsFast(folder.EmbeddedMask);
-        }
-
-        return (RectI?)folder.GetTightBounds(frame);
-    }
-
-    /// <summary>
-    /// Finds the current committed tight bounds for an image in a reasonably efficient way.
-    /// Looks at the low-res chunks for large images, meaning the resulting bounds aren't 100% precise.
-    /// </summary>
-    private RectI? FindImageTightBoundsFast(IReadOnlyChunkyImage targetImage)
-    {
-        RectI? bounds = targetImage.FindChunkAlignedCommittedBounds();
-        if (bounds is null)
-            return null;
+            return;
 
-        int biggest = bounds.Value.Size.LongestAxis;
-        ChunkResolution resolution = biggest switch
-        {
-            > ChunkyImage.FullChunkSize * 9 => ChunkResolution.Eighth,
-            > ChunkyImage.FullChunkSize * 5 => ChunkResolution.Quarter,
-            > ChunkyImage.FullChunkSize * 3 => ChunkResolution.Half,
-            _ => ChunkResolution.Full,
-        };
-        return targetImage.FindTightCommittedBounds(resolution);
+        UpdatePreviewPainters(keys);
     }
 
     /// <summary>
     /// Re-renders changed chunks using <see cref="mainPreviewAreasAccumulator"/> and <see cref="maskPreviewAreasAccumulator"/> along with the passed lists of bitmaps that need full re-render.
     /// </summary>
-    /// <param name="members"></param>
-    private List<IRenderInfo> UpdatePreviewPainters(IEnumerable<Guid> members)
+    /// <param name="members">Members that should be rerendered</param>
+    private void UpdatePreviewPainters(IEnumerable<Guid> members)
     {
-        List<IRenderInfo> infos = new();
-
-        RenderWholeCanvasPreview(infos);
-        RenderMainPreviews(infos, members);
-        RenderMaskPreviews(infos);
-        RenderNodePreviews(infos);
-
-        return infos;
+        RenderWholeCanvasPreview();
+        RenderMainPreviews(members);
+        RenderMaskPreviews();
+        RenderNodePreviews();
     }
 
     /// <summary>
     /// Re-renders the preview of the whole canvas which is shown as the tab icon
     /// </summary>
-    private void RenderWholeCanvasPreview(List<IRenderInfo> infos)
+    private void RenderWholeCanvasPreview()
     {
         var previewSize = StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size);
         float scaling = (float)previewSize.X / doc.SizeBindable.X;
-
-        //infos.Add(new CanvasPreviewDirty_RenderInfo());
     }
 
-    private void RenderMainPreviews(List<IRenderInfo> infos, IEnumerable<Guid> members)
+    private void RenderMainPreviews(IEnumerable<Guid> members)
     {
         Guid[] memberGuids = members.ToArray();
         foreach (var node in doc.NodeGraphHandler.AllNodes)
@@ -139,14 +64,15 @@ internal class MemberPreviewUpdater
             {
                 if (!memberGuids.Contains(node.Id))
                     continue;
-                
+
                 if (structureMemberHandler.PreviewPainter == null)
                 {
                     var member = internals.Tracker.Document.FindMember(node.Id);
                     if (member is not IPreviewRenderable previewRenderable)
                         continue;
 
-                    structureMemberHandler.PreviewPainter = new PreviewPainter(previewRenderable, structureMemberHandler.TightBounds);
+                    structureMemberHandler.PreviewPainter =
+                        new PreviewPainter(previewRenderable, structureMemberHandler.TightBounds);
                     structureMemberHandler.PreviewPainter.Repaint();
                 }
                 else
@@ -255,12 +181,33 @@ internal class MemberPreviewUpdater
         });*/
     }
 
-    private void RenderMaskPreviews(List<IRenderInfo> infos)
+    private void RenderMaskPreviews()
     {
-        //infos.Add(new MaskPreviewDirty_RenderInfo(guid));
+        foreach (var node in doc.NodeGraphHandler.AllNodes)
+        {
+            if (node is IStructureMemberHandler structureMemberHandler)
+            {
+                var member = internals.Tracker.Document.FindMember(node.Id);
+                if (member is not IPreviewRenderable previewRenderable)
+                    continue;
+                
+                if (structureMemberHandler.MaskPreviewPainter == null)
+                {
+                    structureMemberHandler.MaskPreviewPainter = new PreviewPainter(previewRenderable,
+                        member.EmbeddedMask != null ? new RectD(VecD.Zero, member.EmbeddedMask.LatestSize) : null,
+                        nameof(StructureNode.EmbeddedMask));
+                    structureMemberHandler.MaskPreviewPainter.Repaint();
+                }
+                else
+                {
+                    structureMemberHandler.MaskPreviewPainter.Bounds = member.EmbeddedMask != null ? new RectD(VecD.Zero, member.EmbeddedMask.LatestSize) : null;
+                    structureMemberHandler.MaskPreviewPainter.Repaint();
+                }
+            }
+        }
     }
 
-    private void RenderNodePreviews(List<IRenderInfo> infos)
+    private void RenderNodePreviews()
     {
         /*using RenderingContext previewContext = new(doc.AnimationHandler.ActiveFrameTime,  VecI.Zero, ChunkResolution.Full, doc.SizeBindable);
 

+ 4 - 2
src/PixiEditor/Models/Rendering/PreviewPainter.cs

@@ -9,13 +9,15 @@ namespace PixiEditor.Models.Rendering;
 public class PreviewPainter
 {
     public RectD? Bounds { get; set; }
+    public string ElementToRenderName { get; set; }
     public IPreviewRenderable PreviewRenderable { get; set; }
     public event Action RequestRepaint;
     
-    public PreviewPainter(IPreviewRenderable previewRenderable, RectD? tightBounds)
+    public PreviewPainter(IPreviewRenderable previewRenderable, RectD? tightBounds, string elementToRenderName = "")
     {
         PreviewRenderable = previewRenderable;
         Bounds = tightBounds;
+        ElementToRenderName = elementToRenderName;
     }
 
     public void Paint(DrawingSurface renderOn, ChunkResolution resolution, KeyFrameTime frame) 
@@ -25,7 +27,7 @@ public class PreviewPainter
             return;
         }
 
-        PreviewRenderable.RenderPreview(renderOn, resolution, frame.Frame);
+        PreviewRenderable.RenderPreview(renderOn, resolution, frame.Frame, ElementToRenderName);
     }
 
     public void Repaint()

+ 0 - 3
src/PixiEditor/Models/Rendering/RenderInfos/CanvasPreviewDirty_RenderInfo.cs

@@ -1,3 +0,0 @@
-namespace PixiEditor.Models.Rendering.RenderInfos;
-#nullable enable
-internal record CanvasPreviewDirty_RenderInfo : IRenderInfo;

+ 0 - 7
src/PixiEditor/Models/Rendering/RenderInfos/DirtyRect_RenderInfo.cs

@@ -1,7 +0,0 @@
-using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.Models.Rendering.RenderInfos;
-#nullable enable
-public record class DirtyRect_RenderInfo(VecI Pos, VecI Size, ChunkResolution Resolution) : IRenderInfo;

+ 0 - 5
src/PixiEditor/Models/Rendering/RenderInfos/IRenderInfo.cs

@@ -1,5 +0,0 @@
-namespace PixiEditor.Models.Rendering.RenderInfos;
-#nullable enable
-public interface IRenderInfo
-{
-}

+ 0 - 3
src/PixiEditor/Models/Rendering/RenderInfos/MaskPreviewDirty_RenderInfo.cs

@@ -1,3 +0,0 @@
-namespace PixiEditor.Models.Rendering.RenderInfos;
-#nullable enable
-public record class MaskPreviewDirty_RenderInfo(Guid GuidValue) : IRenderInfo;

+ 0 - 3
src/PixiEditor/Models/Rendering/RenderInfos/NodePreviewDirty_RenderInfo.cs

@@ -1,3 +0,0 @@
-namespace PixiEditor.Models.Rendering.RenderInfos;
-
-public record NodePreviewDirty_RenderInfo(Guid NodeId) : IRenderInfo;

+ 0 - 3
src/PixiEditor/Models/Rendering/RenderInfos/PreviewDirty_RenderInfo.cs

@@ -1,3 +0,0 @@
-namespace PixiEditor.Models.Rendering.RenderInfos;
-#nullable enable
-public record class PreviewDirty_RenderInfo(Guid GuidValue) : IRenderInfo;

+ 4 - 4
src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs

@@ -145,7 +145,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
     }
 
     private PreviewPainter? previewSurface;
-    private PreviewPainter? maskPreviewSurface;
+    private PreviewPainter? _maskPreviewPainter;
 
     public PreviewPainter? PreviewPainter
     {
@@ -153,10 +153,10 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         set => SetProperty(ref previewSurface, value);
     }
 
-    public PreviewPainter? MaskPreviewSurface
+    public PreviewPainter? MaskPreviewPainter
     {
-        get => maskPreviewSurface;
-        set => SetProperty(ref maskPreviewSurface, value);
+        get => _maskPreviewPainter;
+        set => SetProperty(ref _maskPreviewPainter, value);
     }
 
     IDocument IStructureMemberHandler.Document => Document;

+ 4 - 1
src/PixiEditor/Views/Layers/FolderControl.axaml

@@ -92,7 +92,10 @@
                                 </ImageBrush>
                             </Border.Background>
                             <Grid IsHitTestVisible="False">
-                                <visuals:TextureControl Texture="{Binding Folder.MaskPreviewSurface, ElementName=folderControl}" Stretch="Uniform" Width="30" Height="30"
+                                <visuals:PreviewPainterControl 
+                                    PreviewPainter="{Binding Folder.MaskPreviewPainter, ElementName=folderControl}" 
+                                    Width="30" Height="30"
+                                    FrameToRender="{Binding Path=Folder.Document.AnimationDataViewModel.ActiveFrameBindable, ElementName=folderControl}"
                                     RenderOptions.BitmapInterpolationMode="None" IsHitTestVisible="False"/>
                                 <Path 
                                 Data="M 2 0 L 10 8 L 18 0 L 20 2 L 12 10 L 20 18 L 18 20 L 10 12 L 2 20 L 0 18 L 8 10 L 0 2 Z" 

+ 5 - 4
src/PixiEditor/Views/Layers/LayerControl.axaml

@@ -106,10 +106,11 @@
                             </MultiBinding>
                         </Border.BorderBrush>
                         <Grid IsHitTestVisible="False">
-                            <visuals:TextureControl Texture="{Binding Layer.MaskPreviewSurface,ElementName=uc}"
-                                                    Stretch="Uniform" Width="30" Height="30"
-                                                    RenderOptions.BitmapInterpolationMode="None"
-                                                    IsHitTestVisible="False" />
+                             <visuals:PreviewPainterControl 
+                                    PreviewPainter="{Binding Layer.MaskPreviewPainter, ElementName=uc}" 
+                                    Width="30" Height="30"
+                                    FrameToRender="{Binding Path=Layer.Document.AnimationHandler.ActiveFrameBindable, ElementName=uc}"
+                                    RenderOptions.BitmapInterpolationMode="None" IsHitTestVisible="False"/>
                             <Path
                                 Data="M 2 0 L 10 8 L 18 0 L 20 2 L 12 10 L 20 18 L 18 20 L 10 12 L 2 20 L 0 18 L 8 10 L 0 2 Z"
                                 Fill="{DynamicResource ThemeAccentBrush}" HorizontalAlignment="Center"