Browse Source

Improved rendering, fixed svg serialization for namespaces, fixed magic wand with masks

flabbet 7 months ago
parent
commit
29f83daf8f

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

@@ -22,7 +22,12 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
         AllowHighDpiRendering = true;
         AllowHighDpiRendering = true;
     }
     }
 
 
-    public override Node CreateCopy() => new FolderNode { MemberName = MemberName, ClipToPreviousMember = this.ClipToPreviousMember };
+    public override Node CreateCopy() => new FolderNode
+    {
+        MemberName = MemberName, 
+        ClipToPreviousMember = this.ClipToPreviousMember,
+        EmbeddedMask = this.EmbeddedMask?.CloneFromCommitted()
+    };
 
 
     public override VecD GetScenePosition(KeyFrameTime time) =>
     public override VecD GetScenePosition(KeyFrameTime time) =>
         documentSize / 2f; 
         documentSize / 2f; 

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

@@ -230,7 +230,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         var image = new ImageLayerNode(startSize, colorSpace)
         var image = new ImageLayerNode(startSize, colorSpace)
         {
         {
             MemberName = this.MemberName, LockTransparency = this.LockTransparency,
             MemberName = this.MemberName, LockTransparency = this.LockTransparency,
-            ClipToPreviousMember = this.ClipToPreviousMember
+            ClipToPreviousMember = this.ClipToPreviousMember, EmbeddedMask = this.EmbeddedMask?.CloneFromCommitted()
         };
         };
 
 
         image.keyFrames.Clear();
         image.keyFrames.Clear();

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -252,14 +252,14 @@ internal class CombineStructureMembersOnto_Change : Change
         var toDrawOnImage = ((ImageLayerNode)targetLayer).GetLayerImageAtFrame(frame);
         var toDrawOnImage = ((ImageLayerNode)targetLayer).GetLayerImageAtFrame(frame);
         toDrawOnImage.EnqueueClear();
         toDrawOnImage.EnqueueClear();
 
 
-        Texture tempTexture = new Texture(target.Size);
+        Texture tempTexture = Texture.ForProcessing(target.Size, target.ProcessingColorSpace);
 
 
         DocumentRenderer renderer = new(target);
         DocumentRenderer renderer = new(target);
 
 
         AffectedArea affArea = new();
         AffectedArea affArea = new();
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
         {
-            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full);
+            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full, target.Size);
 
 
             toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
             toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
 
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs

@@ -62,7 +62,7 @@ internal class FloodFillChunkCache : IDisposable
             
             
             chunk.Surface.DrawingSurface.Canvas.Translate(-chunkPos.X, -chunkPos.Y);
             chunk.Surface.DrawingSurface.Canvas.Translate(-chunkPos.X, -chunkPos.Y);
             
             
-            document.Renderer.RenderLayers(chunk.Surface.DrawingSurface, membersToRender, frame, ChunkResolution.Full);
+            document.Renderer.RenderLayers(chunk.Surface.DrawingSurface, membersToRender, frame, ChunkResolution.Full, chunk.Surface.Size);
             
             
             chunk.Surface.DrawingSurface.Canvas.Restore();
             chunk.Surface.DrawingSurface.Canvas.Restore();
             
             

+ 65 - 28
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -21,7 +21,7 @@ public class DocumentRenderer : IPreviewRenderable
     };
     };
 
 
     private Texture renderTexture;
     private Texture renderTexture;
-    
+
     public DocumentRenderer(IReadOnlyDocument document)
     public DocumentRenderer(IReadOnlyDocument document)
     {
     {
         Document = document;
         Document = document;
@@ -47,28 +47,47 @@ public class DocumentRenderer : IPreviewRenderable
         }
         }
     }
     }
 
 
-    public void RenderLayers(DrawingSurface toDrawOn, HashSet<Guid> layersToCombine, int frame,
-        ChunkResolution resolution)
+    public void RenderLayers(DrawingSurface toRenderOn, HashSet<Guid> layersToCombine, int frame,
+        ChunkResolution resolution, VecI renderSize)
     {
     {
         IsBusy = true;
         IsBusy = true;
-        RenderContext context = new(toDrawOn, frame, resolution, Document.Size, Document.ProcessingColorSpace);
+
+        if (renderTexture == null || renderTexture.Size != renderSize)
+        {
+            renderTexture?.Dispose();
+            renderTexture = Texture.ForProcessing(renderSize, Document.ProcessingColorSpace);
+        }
+
+        renderTexture.DrawingSurface.Canvas.Save();
+        renderTexture.DrawingSurface.Canvas.Clear();
+
+        renderTexture.DrawingSurface.Canvas.SetMatrix(toRenderOn.Canvas.TotalMatrix);
+        toRenderOn.Canvas.Save();
+        toRenderOn.Canvas.SetMatrix(Matrix3X3.Identity);
+
+        RenderContext context = new(renderTexture.DrawingSurface, frame, resolution, Document.Size,
+            Document.ProcessingColorSpace);
         context.FullRerender = true;
         context.FullRerender = true;
         IReadOnlyNodeGraph membersOnlyGraph = ConstructMembersOnlyGraph(layersToCombine, Document.NodeGraph);
         IReadOnlyNodeGraph membersOnlyGraph = ConstructMembersOnlyGraph(layersToCombine, Document.NodeGraph);
         try
         try
         {
         {
             membersOnlyGraph.Execute(context);
             membersOnlyGraph.Execute(context);
+            toRenderOn.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
         }
         }
         catch (ObjectDisposedException)
         catch (ObjectDisposedException)
         {
         {
         }
         }
         finally
         finally
         {
         {
+            renderTexture.DrawingSurface.Canvas.Restore();
+            toRenderOn.Canvas.Restore();
             IsBusy = false;
             IsBusy = false;
         }
         }
     }
     }
 
 
 
 
-    public void RenderLayer(DrawingSurface renderOn, Guid layerId, ChunkResolution resolution, KeyFrameTime frameTime)
+    public void RenderLayer(DrawingSurface toRenderOn, Guid layerId, ChunkResolution resolution, KeyFrameTime frameTime,
+        VecI renderSize)
     {
     {
         var node = Document.FindMember(layerId);
         var node = Document.FindMember(layerId);
 
 
@@ -79,26 +98,44 @@ public class DocumentRenderer : IPreviewRenderable
 
 
         IsBusy = true;
         IsBusy = true;
 
 
-        RenderContext context = new(renderOn, frameTime, resolution, Document.Size, Document.ProcessingColorSpace);
+        if (renderTexture == null || renderTexture.Size != renderSize)
+        {
+            renderTexture?.Dispose();
+            renderTexture = Texture.ForProcessing(renderSize, Document.ProcessingColorSpace);
+        }
+
+        renderTexture.DrawingSurface.Canvas.Save();
+        renderTexture.DrawingSurface.Canvas.Clear();
+
+        renderTexture.DrawingSurface.Canvas.SetMatrix(toRenderOn.Canvas.TotalMatrix);
+        toRenderOn.Canvas.Save();
+        toRenderOn.Canvas.SetMatrix(Matrix3X3.Identity);
+
+        RenderContext context = new(renderTexture.DrawingSurface, frameTime, resolution, Document.Size, Document.ProcessingColorSpace);
         context.FullRerender = true;
         context.FullRerender = true;
 
 
-        node.RenderForOutput(context, renderOn, null);
+        node.RenderForOutput(context, toRenderOn, null);
+        
+        renderTexture.DrawingSurface.Canvas.Restore();
+        toRenderOn.Canvas.Restore();
+        
         IsBusy = false;
         IsBusy = false;
     }
     }
-    
-    public void RenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+
+    public void RenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn, RenderContext context,
+        string elementToRenderName)
     {
     {
         if (IsBusy)
         if (IsBusy)
         {
         {
             return;
             return;
         }
         }
-        
+
         IsBusy = true;
         IsBusy = true;
-        
-        if(previewRenderable is Node { IsDisposed: true }) return;
-        
+
+        if (previewRenderable is Node { IsDisposed: true }) return;
+
         previewRenderable.RenderPreview(renderOn, context, elementToRenderName);
         previewRenderable.RenderPreview(renderOn, context, elementToRenderName);
-        
+
         IsBusy = false;
         IsBusy = false;
     }
     }
 
 
@@ -132,9 +169,9 @@ public class DocumentRenderer : IPreviewRenderable
                 LayerNode clone = (LayerNode)layer.Clone();
                 LayerNode clone = (LayerNode)layer.Clone();
                 membersOnlyGraph.AddNode(clone);
                 membersOnlyGraph.AddNode(clone);
 
 
-                
+
                 IInputProperty targetInput = GetTargetInput(input, fullGraph, membersOnlyGraph, nodeMapping);
                 IInputProperty targetInput = GetTargetInput(input, fullGraph, membersOnlyGraph, nodeMapping);
-                
+
                 clone.Output.ConnectTo(targetInput);
                 clone.Output.ConnectTo(targetInput);
                 nodeMapping[layer.Id] = clone.Id;
                 nodeMapping[layer.Id] = clone.Id;
             }
             }
@@ -144,7 +181,7 @@ public class DocumentRenderer : IPreviewRenderable
                 membersOnlyGraph.AddNode(clone);
                 membersOnlyGraph.AddNode(clone);
 
 
                 var targetInput = GetTargetInput(input, fullGraph, membersOnlyGraph, nodeMapping);
                 var targetInput = GetTargetInput(input, fullGraph, membersOnlyGraph, nodeMapping);
-                
+
                 clone.Output.ConnectTo(targetInput);
                 clone.Output.ConnectTo(targetInput);
                 nodeMapping[folder.Id] = clone.Id;
                 nodeMapping[folder.Id] = clone.Id;
             }
             }
@@ -196,38 +233,38 @@ public class DocumentRenderer : IPreviewRenderable
         renderTexture.DrawingSurface.Canvas.SetMatrix(toRenderOn.Canvas.TotalMatrix);
         renderTexture.DrawingSurface.Canvas.SetMatrix(toRenderOn.Canvas.TotalMatrix);
         toRenderOn.Canvas.Save();
         toRenderOn.Canvas.Save();
         toRenderOn.Canvas.SetMatrix(Matrix3X3.Identity);
         toRenderOn.Canvas.SetMatrix(Matrix3X3.Identity);
-        
+
         RenderContext context =
         RenderContext context =
             new(renderTexture.DrawingSurface, frameTime, ChunkResolution.Full, Document.Size,
             new(renderTexture.DrawingSurface, frameTime, ChunkResolution.Full, Document.Size,
                 Document.ProcessingColorSpace) { FullRerender = true };
                 Document.ProcessingColorSpace) { FullRerender = true };
         Document.NodeGraph.Execute(context);
         Document.NodeGraph.Execute(context);
 
 
         toRenderOn.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
         toRenderOn.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
-        
+
         renderTexture.DrawingSurface.Canvas.Restore();
         renderTexture.DrawingSurface.Canvas.Restore();
         toRenderOn.Canvas.Restore();
         toRenderOn.Canvas.Restore();
-        
+
         IsBusy = false;
         IsBusy = false;
     }
     }
-    
-    private static IInputProperty GetTargetInput(IInputProperty? input, 
+
+    private static IInputProperty GetTargetInput(IInputProperty? input,
         IReadOnlyNodeGraph sourceGraph,
         IReadOnlyNodeGraph sourceGraph,
         NodeGraph membersOnlyGraph,
         NodeGraph membersOnlyGraph,
         Dictionary<Guid, Guid> nodeMapping)
         Dictionary<Guid, Guid> nodeMapping)
     {
     {
         if (input == null)
         if (input == null)
         {
         {
-            if(membersOnlyGraph.OutputNode is IRenderInput inputNode) return inputNode.Background;
+            if (membersOnlyGraph.OutputNode is IRenderInput inputNode) return inputNode.Background;
 
 
             return null;
             return null;
         }
         }
-        
+
         if (nodeMapping.ContainsKey(input.Node?.Id ?? Guid.Empty))
         if (nodeMapping.ContainsKey(input.Node?.Id ?? Guid.Empty))
         {
         {
             return membersOnlyGraph.Nodes.First(x => x.Id == nodeMapping[input.Node.Id])
             return membersOnlyGraph.Nodes.First(x => x.Id == nodeMapping[input.Node.Id])
                 .GetInputProperty(input.InternalPropertyName);
                 .GetInputProperty(input.InternalPropertyName);
         }
         }
-        
+
         var sourceNode = sourceGraph.AllNodes.First(x => x.Id == input.Node.Id);
         var sourceNode = sourceGraph.AllNodes.First(x => x.Id == input.Node.Id);
 
 
         IInputProperty? found = null;
         IInputProperty? found = null;
@@ -235,17 +272,17 @@ public class DocumentRenderer : IPreviewRenderable
         {
         {
             if (n is StructureNode structureNode)
             if (n is StructureNode structureNode)
             {
             {
-                if(nodeMapping.TryGetValue(structureNode.Id, out var value))
+                if (nodeMapping.TryGetValue(structureNode.Id, out var value))
                 {
                 {
                     Node mappedNode = membersOnlyGraph.Nodes.First(x => x.Id == value);
                     Node mappedNode = membersOnlyGraph.Nodes.First(x => x.Id == value);
                     found = mappedNode.GetInputProperty(input.InternalPropertyName);
                     found = mappedNode.GetInputProperty(input.InternalPropertyName);
                     return false;
                     return false;
                 }
                 }
             }
             }
-            
+
             return true;
             return true;
         });
         });
-        
+
         return found ?? (membersOnlyGraph.OutputNode as IRenderInput)?.Background;
         return found ?? (membersOnlyGraph.OutputNode as IRenderInput)?.Background;
     }
     }
 }
 }

BIN
src/PixiEditor.Extensions.Sdk/build/PixiEditor.Api.CGlueMSBuild.dll


BIN
src/PixiEditor.Extensions.Sdk/build/PixiEditor.Extensions.MSPackageBuilder.dll


+ 1 - 1
src/PixiEditor.SVG/Elements/SvgImage.cs

@@ -12,7 +12,7 @@ public class SvgImage : SvgElement
     public SvgProperty<SvgNumericUnit> Width { get; } = new("width");
     public SvgProperty<SvgNumericUnit> Width { get; } = new("width");
     public SvgProperty<SvgNumericUnit> Height { get; } = new("height");
     public SvgProperty<SvgNumericUnit> Height { get; } = new("height");
         
         
-    public SvgProperty<SvgStringUnit> Href { get; } = new("xlink:href");
+    public SvgProperty<SvgStringUnit> Href { get; } = new("href", "xlink", "http://www.w3.org/1999/xlink");
     public SvgProperty<SvgLinkUnit> Mask { get; } = new("mask");
     public SvgProperty<SvgLinkUnit> Mask { get; } = new("mask");
     public SvgProperty<SvgEnumUnit<SvgImageRenderingType>> ImageRendering { get; } = new("image-rendering");
     public SvgProperty<SvgEnumUnit<SvgImageRenderingType>> ImageRendering { get; } = new("image-rendering");
 
 

+ 3 - 3
src/PixiEditor.SVG/SvgDocument.cs

@@ -61,12 +61,12 @@ public class SvgDocument : SvgElement, IElementContainer, ITransformable, IFilla
 
 
         Dictionary<string, string> usedNamespaces = new();
         Dictionary<string, string> usedNamespaces = new();
 
 
-        GatherRequiredNamespaces(usedNamespaces, Children);
+        /*GatherRequiredNamespaces(usedNamespaces, Children);
 
 
         foreach (var usedNamespace in usedNamespaces)
         foreach (var usedNamespace in usedNamespaces)
         {
         {
-            document.Root.Add(new XAttribute($"xmlns:{usedNamespace.Key}", usedNamespace.Value));
-        }
+            document.Root.Add(XNamespace.Xmlns + usedNamespace.Key, usedNamespace.Value);
+        }*/
 
 
         AppendProperties(document.Root);
         AppendProperties(document.Root);
 
 

+ 12 - 1
src/PixiEditor.SVG/SvgElement.cs

@@ -25,7 +25,18 @@ public class SvgElement(string tagName)
                 SvgProperty prop = (SvgProperty)property.GetValue(this);
                 SvgProperty prop = (SvgProperty)property.GetValue(this);
                 if (prop?.Unit != null)
                 if (prop?.Unit != null)
                 {
                 {
-                    element.Add(new XAttribute(prop.SvgName, prop.Unit.ToXml()));
+                    if (!string.IsNullOrEmpty(prop.NamespaceName) && !string.IsNullOrEmpty(prop.NamespaceUri))
+                    {
+                        XAttribute nsAttribute = new XAttribute(XNamespace.Xmlns + prop.NamespaceName, prop.NamespaceUri);
+                        element.Add(nsAttribute);
+                        
+                        XName name = XNamespace.Get(prop.NamespaceUri) + prop.SvgName;
+                        element.Add(new XAttribute(name, prop.Unit.ToXml()));
+                    }
+                    else
+                    {
+                        element.Add(new XAttribute(prop.SvgName, prop.Unit.ToXml()));
+                    }
                 }
                 }
             }
             }
         }
         }

+ 14 - 1
src/PixiEditor.SVG/SvgProperty.cs

@@ -1,4 +1,5 @@
-using PixiEditor.SVG.Units;
+using System.Xml.Linq;
+using PixiEditor.SVG.Units;
 
 
 namespace PixiEditor.SVG;
 namespace PixiEditor.SVG;
 
 
@@ -8,7 +9,15 @@ public abstract class SvgProperty
     {
     {
         SvgName = svgName;
         SvgName = svgName;
     }
     }
+    
+    protected SvgProperty(string svgName, string? namespaceName, string? namespaceUri) : this(svgName)
+    {
+        NamespaceName = namespaceName;
+        NamespaceUri = namespaceUri;
+    }
 
 
+    public string? NamespaceName { get; set; }
+    public string? NamespaceUri { get; set; }
     public string SvgName { get; set; }
     public string SvgName { get; set; }
     public ISvgUnit? Unit { get; set; }
     public ISvgUnit? Unit { get; set; }
 }
 }
@@ -24,4 +33,8 @@ public class SvgProperty<T> : SvgProperty where T : struct, ISvgUnit
     public SvgProperty(string svgName) : base(svgName)
     public SvgProperty(string svgName) : base(svgName)
     {
     {
     }
     }
+    
+    public SvgProperty(string svgName, string? namespaceName, string? namespaceUri) : base(svgName, namespaceName, namespaceUri)
+    {
+    }
 }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/Public/DocumentStructureModule.cs

@@ -132,7 +132,7 @@ internal class DocumentStructureModule
     ///     Returns all layers in the document.
     ///     Returns all layers in the document.
     /// </summary>
     /// </summary>
     /// <returns>List of ILayerHandlers. Empty if no layers found.</returns>
     /// <returns>List of ILayerHandlers. Empty if no layers found.</returns>
-    public List<ILayerHandler> GetAllLayers()
+    public List<ILayerHandler> GetAllLayers(bool includeFoldersWithMask = false)
     {
     {
         List<ILayerHandler> layers = new List<ILayerHandler>();
         List<ILayerHandler> layers = new List<ILayerHandler>();
 
 

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

@@ -253,7 +253,8 @@ internal partial class DocumentViewModel
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
         {
             using Surface surface = new Surface(SizeBindable);
             using Surface surface = new Surface(SizeBindable);
-            Renderer.RenderLayer(surface.DrawingSurface, imageNode.Id, ChunkResolution.Full, atTime.Frame);
+            Renderer.RenderLayer(surface.DrawingSurface, imageNode.Id, ChunkResolution.Full, atTime.Frame,
+                SizeBindable);
 
 
             toSave = surface.DrawingSurface.Snapshot((RectI)tightBounds.Value);
             toSave = surface.DrawingSurface.Snapshot((RectI)tightBounds.Value);
         });
         });

+ 25 - 39
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -160,7 +160,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
 
     public bool IsChangeFeatureActive<T>() where T : IExecutorFeature =>
     public bool IsChangeFeatureActive<T>() where T : IExecutorFeature =>
         Internals.ChangeController.IsChangeOfTypeActive<T>();
         Internals.ChangeController.IsChangeOfTypeActive<T>();
-    
+
     public T? TryGetExecutorFeature<T>() where T : IExecutorFeature =>
     public T? TryGetExecutorFeature<T>() where T : IExecutorFeature =>
         Internals.ChangeController.TryGetExecutorFeature<T>();
         Internals.ChangeController.TryGetExecutorFeature<T>();
 
 
@@ -537,7 +537,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
     public OneOf<Error, None, (Surface, RectI)> TryExtractAreaFromSelected(
     public OneOf<Error, None, (Surface, RectI)> TryExtractAreaFromSelected(
         RectI bounds)
         RectI bounds)
     {
     {
-        var selectedLayers = ExtractSelectedLayers();
+        var selectedLayers = ExtractSelectedLayers(true);
         if (selectedLayers.Count == 0)
         if (selectedLayers.Count == 0)
             return new Error();
             return new Error();
         if (bounds.IsZeroOrNegativeArea)
         if (bounds.IsZeroOrNegativeArea)
@@ -547,23 +547,15 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
 
         for (int i = 0; i < selectedLayers.Count; i++)
         for (int i = 0; i < selectedLayers.Count; i++)
         {
         {
-            var layerVm = StructureHelper.Find(selectedLayers[i]);
-            IReadOnlyStructureNode? layer = Internals.Tracker.Document.FindMember(layerVm.Id);
+            var memberVm = StructureHelper.Find(selectedLayers[i]);
+            IReadOnlyStructureNode? layer = Internals.Tracker.Document.FindMember(memberVm.Id);
             if (layer is null)
             if (layer is null)
                 return new Error();
                 return new Error();
 
 
             RectI? memberImageBounds;
             RectI? memberImageBounds;
             try
             try
             {
             {
-                if (layer is IReadOnlyImageNode imgNode)
-                {
-                    memberImageBounds = imgNode.GetLayerImageAtFrame(AnimationDataViewModel.ActiveFrameBindable)
-                        .FindChunkAlignedMostUpToDateBounds();
-                }
-                else
-                {
-                    memberImageBounds = (RectI?)layer.GetTightBounds(AnimationDataViewModel.ActiveFrameTime);
-                }
+                memberImageBounds = (RectI?)layer.GetTightBounds(AnimationDataViewModel.ActiveFrameTime);
             }
             }
             catch (ObjectDisposedException)
             catch (ObjectDisposedException)
             {
             {
@@ -604,28 +596,18 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
 
         using Paint paint = new Paint() { BlendMode = BlendMode.SrcOver };
         using Paint paint = new Paint() { BlendMode = BlendMode.SrcOver };
 
 
-        foreach (var layer in selectedLayers)
+        DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
         {
             try
             try
             {
             {
-                var layerVm = Internals.Tracker.Document.FindMember(layer);
-
-                DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
-                {
-                    using Surface toPaintOn = new Surface(SizeBindable);
-
-                    Renderer.RenderLayer(toPaintOn.DrawingSurface, layerVm.Id, ChunkResolution.Full,
-                        AnimationDataViewModel.ActiveFrameTime);
-                    using Image snapshot = toPaintOn.DrawingSurface.Snapshot(finalBounds);
-                    output.DrawingSurface.Canvas.DrawImage(snapshot, 0, 0, paint);
-                });
+                Renderer.RenderLayers(output.DrawingSurface, selectedLayers.ToHashSet(),
+                    AnimationDataViewModel.ActiveFrameBindable, ChunkResolution.Full, finalBounds.Size);
             }
             }
             catch (ObjectDisposedException)
             catch (ObjectDisposedException)
             {
             {
-                output.Dispose();
-                return new Error();
+                output?.Dispose();
             }
             }
-        }
+        });
 
 
         output.DrawingSurface.Canvas.Restore();
         output.DrawingSurface.Canvas.Restore();
         return (output, finalBounds);
         return (output, finalBounds);
@@ -695,14 +677,11 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
             // via a passthrough action to avoid all the try catches
             // via a passthrough action to avoid all the try catches
             if (scope == DocumentScope.AllLayers)
             if (scope == DocumentScope.AllLayers)
             {
             {
-                VecI chunkPos = OperationHelper.GetChunkPos(pos, ChunkyImage.FullChunkSize);
-                using Texture tmpTexture = Texture.ForProcessing(SizeBindable);
+                using Surface tmpSurface = new Surface(SizeBindable);
                 HashSet<Guid> layers = StructureHelper.GetAllMembers().Select(x => x.Id).ToHashSet();
                 HashSet<Guid> layers = StructureHelper.GetAllMembers().Select(x => x.Id).ToHashSet();
-                Renderer.RenderLayers(tmpTexture.DrawingSurface, layers, frameTime.Frame, ChunkResolution.Full);
-                
-                using Surface tmpSurface = new Surface(tmpTexture.Size);
-                tmpSurface.DrawingSurface.Canvas.DrawImage(tmpTexture.DrawingSurface.Snapshot(), 0, 0);
-                
+                Renderer.RenderLayers(tmpSurface.DrawingSurface, layers, frameTime.Frame, ChunkResolution.Full,
+                    SizeBindable);
+
                 return tmpSurface.GetSrgbPixel(pos);
                 return tmpSurface.GetSrgbPixel(pos);
             }
             }
 
 
@@ -723,7 +702,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
             {
             {
                 return layer.GetLayerImageAtFrame(frameTime.Frame).GetMostUpToDatePixel(pos);
                 return layer.GetLayerImageAtFrame(frameTime.Frame).GetMostUpToDatePixel(pos);
             }
             }
-            
+
             return Colors.Transparent;
             return Colors.Transparent;
         }
         }
         catch (ObjectDisposedException)
         catch (ObjectDisposedException)
@@ -848,13 +827,20 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
     {
     {
         var result = new List<Guid>();
         var result = new List<Guid>();
         List<Guid> selectedMembers = GetSelectedMembers();
         List<Guid> selectedMembers = GetSelectedMembers();
-        var allLayers = StructureHelper.GetAllLayers();
+        var allLayers = StructureHelper.GetAllMembers();
         foreach (var member in allLayers)
         foreach (var member in allLayers)
         {
         {
-            if (selectedMembers.Contains(member.Id))
+            if (member is ILayerHandler && selectedMembers.Contains(member.Id))
             {
             {
                 result.Add(member.Id);
                 result.Add(member.Id);
             }
             }
+            else if (member is IFolderHandler folder)
+            {
+                if (includeFoldersWithMask && folder.HasMaskBindable && selectedMembers.Contains(folder.Id))
+                    result.Add(folder.Id);
+
+                ExtractSelectedLayers(folder, result, includeFoldersWithMask);
+            }
         }
         }
 
 
         return result;
         return result;
@@ -894,7 +880,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
 
         int firstFrame = AnimationDataViewModel.FirstFrame;
         int firstFrame = AnimationDataViewModel.FirstFrame;
         int lastFrame = AnimationDataViewModel.LastFrame;
         int lastFrame = AnimationDataViewModel.LastFrame;
-        
+
         int framesCount = lastFrame - firstFrame;
         int framesCount = lastFrame - firstFrame;
 
 
         Image[] images = new Image[framesCount];
         Image[] images = new Image[framesCount];