Bläddra i källkod

Implemented layer preview

Krzysztof Krysiński 2 veckor sedan
förälder
incheckning
3423e63aac
26 ändrade filer med 184 tillägg och 796 borttagningar
  1. 2 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs
  2. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyStructureNode.cs
  3. 4 38
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  4. 32 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
  5. 1 42
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  6. 15 1
      src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs
  7. 6 2
      src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs
  8. 1 1
      src/PixiEditor/Models/Handlers/ICelHandler.cs
  9. 0 2
      src/PixiEditor/Models/Handlers/IDocument.cs
  10. 1 1
      src/PixiEditor/Models/Handlers/INodeHandler.cs
  11. 2 2
      src/PixiEditor/Models/Handlers/IStructureMemberHandler.cs
  12. 39 44
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  13. 0 284
      src/PixiEditor/Models/Rendering/PreviewPainter.cs
  14. 19 49
      src/PixiEditor/Models/Rendering/SceneRenderer.cs
  15. 3 3
      src/PixiEditor/Styles/Templates/KeyFrame.axaml
  16. 3 4
      src/PixiEditor/Styles/Templates/NodeView.axaml
  17. 2 2
      src/PixiEditor/Styles/Templates/TimelineGroupHeader.axaml
  18. 9 8
      src/PixiEditor/ViewModels/Document/CelViewModel.cs
  19. 4 15
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  20. 17 19
      src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs
  21. 7 8
      src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs
  22. 5 7
      src/PixiEditor/ViewModels/SubViewModels/ViewportWindowViewModel.cs
  23. 4 6
      src/PixiEditor/Views/Layers/FolderControl.axaml
  24. 4 6
      src/PixiEditor/Views/Layers/LayerControl.axaml
  25. 3 21
      src/PixiEditor/Views/Nodes/NodeView.cs
  26. 0 227
      src/PixiEditor/Views/Visuals/PreviewPainterControl.cs

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

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

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

@@ -8,7 +8,7 @@ using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
-public interface IReadOnlyStructureNode : IReadOnlyNode, ISceneObject, IChunkRenderable
+public interface IReadOnlyStructureNode : IReadOnlyNode, ISceneObject
 {
     public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }

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

@@ -31,8 +31,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     private ColorSpace colorSpace;
     private ChunkyImage layerImage => keyFrames[0]?.Data as ChunkyImage;
 
-    private int renderedSurfaceFrame = -1;
-
     public ImageLayerNode(VecI size, ColorSpace colorSpace)
     {
         if (keyFrames.Count == 0)
@@ -266,30 +264,10 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             return false;
         }
 
-        if (renderedSurfaceFrame == cacheFrame)
-        {
-            int saved = renderOnto.Canvas.Save();
-            renderOnto.Canvas.Scale((float)context.ChunkResolution.Multiplier());
-            /*if (context.DesiredSamplingOptions == SamplingOptions.Default)
-            {
-                renderOnto.Canvas.DrawSurface(
-                    fullResrenderedSurface.DrawingSurface, 0, 0, blendPaint);
-            }
-            else
-            {
-                using var snapshot = fullResrenderedSurface.DrawingSurface.Snapshot();
-                renderOnto.Canvas.DrawImage(snapshot, 0, 0, context.DesiredSamplingOptions, blendPaint);
-            }*/
-
-            renderOnto.Canvas.RestoreToCount(saved);
-        }
-        else
-        {
-            img.DrawMostUpToDateRegionOn(
-                new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
-                context.ChunkResolution,
-                renderOnto, VecI.Zero, blendPaint, context.DesiredSamplingOptions);
-        }
+        img.DrawCommittedRegionOn(
+            new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
+            context.ChunkResolution,
+            renderOnto, VecI.Zero, replacePaint, context.DesiredSamplingOptions);
 
         return true;
     }
@@ -353,18 +331,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     void IReadOnlyImageNode.ForEveryFrame(Action<IReadOnlyChunkyImage> action) => ForEveryFrame(action);
 
-    public override void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime,
-        ColorSpace processColorSpace)
-    {
-        return;
-        base.RenderChunk(chunkPos, resolution, frameTime, processColorSpace);
-
-        var img = GetLayerImageAtFrame(frameTime.Frame);
-
-        //RenderChunkyImageChunk(chunkPos, resolution, img, 85, processColorSpace, ref fullResrenderedSurface);
-        renderedSurfaceFrame = frameTime.Frame;
-    }
-
     public void ForEveryFrame(Action<ChunkyImage> action)
     {
         foreach (var frame in keyFrames)

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

@@ -33,6 +33,8 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
 
         RenderContent(sceneContext, sceneContext.RenderSurface,
             sceneContext.TargetPropertyOutput == Output);
+
+        RenderPreviews(sceneContext);
     }
 
     private void RenderContent(SceneObjectRenderContext context, DrawingSurface renderOnto, bool useFilters)
@@ -223,6 +225,36 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
     protected abstract void DrawWithFilters(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
         Paint paint);
 
+    private void RenderPreviews(RenderContext ctx)
+    {
+        var previewTexture = ctx.GetPreviewTexture(Id);
+        if (previewTexture == null || previewTexture.IsDisposed)
+            return;
+
+        int saved = previewTexture.DrawingSurface.Canvas.Save();
+
+        VecD densityVec = ((VecD)ctx.DocumentSize).Divide(previewTexture.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
+        };
+
+        RenderContext previewCtx = ctx.Clone();
+        previewCtx.ChunkResolution = resolution;
+
+        VecD scaling = new VecD((double)previewTexture.Size.X / ctx.DocumentSize.X,
+            (double)previewTexture.Size.Y / ctx.DocumentSize.Y) * resolution.InvertedMultiplier();
+        previewTexture.DrawingSurface.Canvas.Scale((float)scaling.X, (float)scaling.Y);
+
+        RenderPreview(previewTexture.DrawingSurface, previewCtx, "");
+
+        previewTexture.DrawingSurface.Canvas.RestoreToCount(saved);
+    }
+
     protected Texture TryInitWorkingSurface(VecI imageSize, ChunkResolution resolution, ColorSpace processingCs, int id)
     {
         ChunkResolution targetResolution = resolution;

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

@@ -188,6 +188,7 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
         renderObjectContext.FullRerender = context.FullRerender;
         renderObjectContext.AffectedArea = context.AffectedArea;
         renderObjectContext.VisibleDocumentRegion = context.VisibleDocumentRegion;
+        renderObjectContext.PreviewTextures = context.PreviewTextures;
         return renderObjectContext;
     }
 
@@ -231,48 +232,6 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
             ClipToPreviousMember ? 1 : 0);
     }
 
-    public virtual void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime,
-        ColorSpace processingColorSpace)
-    {
-        RenderChunkyImageChunk(chunkPos, resolution, EmbeddedMask, 55, processingColorSpace, ref renderedMask);
-    }
-
-    protected void RenderChunkyImageChunk(VecI chunkPos, ChunkResolution resolution, ChunkyImage img,
-        int textureId, ColorSpace processingColorSpace,
-        ref Texture? renderSurface)
-    {
-        if (img is null)
-        {
-            return;
-        }
-
-        VecI targetSize = img.LatestSize;
-
-        if (targetSize.X <= 0 || targetSize.Y <= 0)
-        {
-            return;
-        }
-
-        using var ctx = DrawingBackendApi.Current.RenderingDispatcher.EnsureContext();
-        renderSurface = RequestTexture(textureId, targetSize, processingColorSpace, false);
-
-        int saved = renderSurface.DrawingSurface.Canvas.Save();
-
-        if (!img.DrawMostUpToDateChunkOn(
-                chunkPos,
-                ChunkResolution.Full,
-                renderSurface.DrawingSurface,
-                chunkPos * ChunkResolution.Full.PixelSize(),
-                replacePaint))
-        {
-            var chunkSize = ChunkResolution.Full.PixelSize();
-            renderSurface.DrawingSurface.Canvas.DrawRect(new RectD(chunkPos * chunkSize, new VecD(chunkSize)),
-                clearPaint);
-        }
-
-        renderSurface.DrawingSurface.Canvas.RestoreToCount(saved);
-    }
-
     protected void ApplyRasterClip(DrawingSurface toClip, DrawingSurface clipSource)
     {
         if (ClipToPreviousMember && Background.Value != null)

+ 15 - 1
src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs

@@ -1,4 +1,5 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using Drawie.Backend.Core;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
@@ -24,6 +25,7 @@ public class RenderContext
     public ColorSpace ProcessingColorSpace { get; set; }
     public string? TargetOutput { get; set; }
     public AffectedArea AffectedArea { get; set; }
+    public Dictionary<Guid, Texture>? PreviewTextures { get; set; }
 
 
     public RenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution,
@@ -39,6 +41,15 @@ public class RenderContext
         DesiredSamplingOptions = desiredSampling;
     }
 
+    public Texture? GetPreviewTexture(Guid guid)
+    {
+        if (PreviewTextures is null)
+            return null;
+        PreviewTextures.TryGetValue(guid, out Texture? texture);
+        PreviewTextures.Remove(guid);
+        return texture;
+    }
+
     public static DrawingApiBlendMode GetDrawingBlendMode(BlendMode blendMode)
     {
         return blendMode switch
@@ -71,6 +82,9 @@ public class RenderContext
         {
             FullRerender = FullRerender,
             TargetOutput = TargetOutput,
+            AffectedArea = AffectedArea,
+            PreviewTextures = PreviewTextures,
+            VisibleDocumentRegion = VisibleDocumentRegion
         };
     }
 }

+ 6 - 2
src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs

@@ -1,5 +1,6 @@
 using System.Diagnostics;
 using Avalonia.Threading;
+using Drawie.Backend.Core;
 using PixiEditor.ChangeableDocument;
 using PixiEditor.ChangeableDocument.Actions;
 using PixiEditor.ChangeableDocument.Actions.Generated;
@@ -140,14 +141,14 @@ internal class ActionAccumulator
                 bool updateDelayed = undoBoundaryPassed || viewportRefreshRequest || changeFrameRequest ||
                                      document.SizeBindable.LongestAxis <= LiveUpdatePerformanceThreshold;
 
-                await document.SceneRenderer.RenderAsync(internals.State.Viewports, affectedAreas.MainImageArea, !previewsDisabled && updateDelayed);
+                Dictionary<Guid, Texture>? previewTextures = null;
 
                 if (!previewsDisabled)
                 {
                     if (undoBoundaryPassed || viewportRefreshRequest || changeFrameRequest ||
                         document.SizeBindable.LongestAxis <= LiveUpdatePerformanceThreshold)
                     {
-                        previewUpdater.UpdatePreviews(
+                        previewTextures = previewUpdater.UpdatePreviews(
                             affectedAreas.ChangedMembers,
                             affectedAreas.ChangedMasks,
                             affectedAreas.ChangedNodes, affectedAreas.ChangedKeyFrames,
@@ -155,6 +156,9 @@ internal class ActionAccumulator
                             undoBoundaryPassed || refreshPreviewsRequest);
                     }
                 }
+
+                await document.SceneRenderer.RenderAsync(internals.State.Viewports, affectedAreas.MainImageArea,
+                    !previewsDisabled && updateDelayed, previewTextures);
             }
         }
         catch (Exception e)

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

@@ -6,7 +6,7 @@ namespace PixiEditor.Models.Handlers;
 
 internal interface ICelHandler : IDisposable
 {
-    public PreviewPainter? PreviewPainter { get; set; }
+    public Texture? PreviewTexture { get; }
     public int StartFrameBindable { get; }
     public int DurationBindable { get; }
     public bool IsSelected { get; set; }

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

@@ -33,7 +33,6 @@ internal interface IDocument : IHandler, Extensions.CommonApi.Documents.IDocumen
     public VectorPath SelectionPathBindable { get; }
     public INodeGraphHandler NodeGraphHandler { get; }
     public DocumentStructureModule StructureHelper { get; }
-    public PreviewPainter? PreviewPainter { get; set; }
     public bool AllChangesSaved { get; }
     public string CoordinatesString { get; set; }
     public IReadOnlyCollection<IStructureMemberHandler> SoftSelectedStructureMembers { get; }
@@ -50,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 PreviewPainter? MiniPreviewPainter { get; set; }
     public PreviewRenderer PreviewRenderer { get; }
     public Dictionary<Guid, Texture> SceneTextures { get; }
     public Texture DocumentTexture { get; }

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

@@ -22,7 +22,7 @@ public interface INodeHandler : INotifyPropertyChanged, IDisposable
     public NodeMetadata Metadata { get; set; }
     public ObservableRangeCollection<INodePropertyHandler> Inputs { get; }
     public ObservableRangeCollection<INodePropertyHandler> Outputs { get; }
-    public PreviewPainter? ResultPainter { get; set; }
+    public Texture? Preview { get; set; }
     public VecD PositionBindable { get; set; }
     public Rect UiSize { get; set; }
     public bool IsNodeSelected { get; set; }

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

@@ -14,8 +14,8 @@ namespace PixiEditor.Models.Handlers;
 internal interface IStructureMemberHandler : INodeHandler
 {
     public bool HasMaskBindable { get; }
-    public PreviewPainter? MaskPreviewPainter { get; set; }
-    public PreviewPainter? PreviewPainter { get; set; }
+    public Texture? MaskPreview { get; set; }
+    public Texture? Preview { get; set; }
     public bool MaskIsVisibleBindable { get; set; }
     public StructureMemberSelectionType Selection { get; set; }
     public float OpacityBindable { get; set; }

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

@@ -1,5 +1,7 @@
 #nullable enable
 
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -28,15 +30,16 @@ internal class MemberPreviewUpdater
         AnimationKeyFramePreviewRenderer = new AnimationKeyFramePreviewRenderer(internals);
     }
 
-    public void UpdatePreviews(HashSet<Guid> membersToUpdate,
+    public Dictionary<Guid, Texture>? UpdatePreviews(HashSet<Guid> membersToUpdate,
         HashSet<Guid> masksToUpdate, HashSet<Guid> nodesToUpdate, HashSet<Guid> keyFramesToUpdate,
         bool ignoreAnimationPreviews, bool renderMiniPreviews)
     {
         if (!membersToUpdate.Any() && !masksToUpdate.Any() && !nodesToUpdate.Any() &&
             !keyFramesToUpdate.Any())
-            return;
+            return null;
 
-        UpdatePreviewPainters(membersToUpdate, masksToUpdate, nodesToUpdate, keyFramesToUpdate, ignoreAnimationPreviews,
+        return UpdatePreviewPainters(membersToUpdate, masksToUpdate, nodesToUpdate, keyFramesToUpdate,
+            ignoreAnimationPreviews,
             renderMiniPreviews);
     }
 
@@ -45,30 +48,35 @@ internal class MemberPreviewUpdater
     /// </summary>
     /// <param name="members">Members that should be rendered</param>
     /// <param name="masksToUpdate">Masks that should be rendered</param>
-    private void UpdatePreviewPainters(HashSet<Guid> members, HashSet<Guid> masksToUpdate,
+    private Dictionary<Guid, Texture> UpdatePreviewPainters(HashSet<Guid> members, HashSet<Guid> masksToUpdate,
         HashSet<Guid> nodesToUpdate, HashSet<Guid> keyFramesToUpdate, bool ignoreAnimationPreviews,
         bool renderLowPriorityPreviews)
     {
-        RenderWholeCanvasPreview(renderLowPriorityPreviews);
+        Dictionary<Guid, Texture> previewTextures = new();
+        //RenderWholeCanvasPreview(renderLowPriorityPreviews);
         if (renderLowPriorityPreviews)
         {
-            RenderLayersPreview(members);
-            RenderMaskPreviews(masksToUpdate);
+            RenderLayersPreview(members, previewTextures);
+            //RenderMaskPreviews(masksToUpdate);
         }
 
         if (!ignoreAnimationPreviews)
         {
-            RenderAnimationPreviews(members, keyFramesToUpdate);
+            //RenderAnimationPreviews(members, keyFramesToUpdate);
         }
 
-        RenderNodePreviews(nodesToUpdate);
+        //RenderNodePreviews(nodesToUpdate);
+
+        return previewTextures;
     }
 
     /// <summary>
     /// Re-renders the preview of the whole canvas which is shown as the tab icon
     /// </summary>
+    /// <param name="memberGuids"></param>
+    /// <param name="previewTextures"></param>
     /// <param name="renderMiniPreviews">Decides whether to re-render mini previews for the document</param>
-    private void RenderWholeCanvasPreview(bool renderMiniPreviews)
+    /*private void RenderWholeCanvasPreview(bool renderMiniPreviews)
     {
         var previewSize = StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size);
         //float scaling = (float)previewSize.X / doc.SizeBindable.X;
@@ -86,17 +94,15 @@ internal class MemberPreviewUpdater
             doc.SizeBindable, internals.Tracker.Document.ProcessingColorSpace);
 
         UpdateDocPreviewPainter(doc.MiniPreviewPainter);
-    }
-
-    private void UpdateDocPreviewPainter(PreviewPainter painter)
+    }*/
+    /*private void UpdateDocPreviewPainter(PreviewPainter painter)
     {
         painter.DocumentSize = doc.SizeBindable;
         painter.ProcessingColorSpace = internals.Tracker.Document.ProcessingColorSpace;
         painter.FrameTime = doc.AnimationHandler.ActiveFrameTime;
         painter.Repaint();
-    }
-
-    private void RenderLayersPreview(HashSet<Guid> memberGuids)
+    }*/
+    private void RenderLayersPreview(HashSet<Guid> memberGuids, Dictionary<Guid, Texture> previewTextures)
     {
         foreach (var node in doc.NodeGraphHandler.AllNodes)
         {
@@ -105,32 +111,21 @@ internal class MemberPreviewUpdater
                 if (!memberGuids.Contains(node.Id))
                     continue;
 
-                if (structureMemberHandler.PreviewPainter == null)
+                if (structureMemberHandler.Preview == null)
                 {
                     var member = internals.Tracker.Document.FindMember(node.Id);
                     if (member is not IPreviewRenderable previewRenderable)
                         continue;
 
-                    structureMemberHandler.PreviewPainter =
-                        new PreviewPainter(renderer, previewRenderable,
-                            doc.AnimationHandler.ActiveFrameTime, doc.SizeBindable,
-                            internals.Tracker.Document.ProcessingColorSpace);
-                    structureMemberHandler.PreviewPainter.Repaint();
+                    structureMemberHandler.Preview = Texture.ForDisplay(new VecI(30, 30));
                 }
-                else
-                {
-                    structureMemberHandler.PreviewPainter.FrameTime = doc.AnimationHandler.ActiveFrameTime;
-                    structureMemberHandler.PreviewPainter.DocumentSize = doc.SizeBindable;
-                    structureMemberHandler.PreviewPainter.ProcessingColorSpace =
-                        internals.Tracker.Document.ProcessingColorSpace;
 
-                    structureMemberHandler.PreviewPainter.Repaint();
-                }
+                previewTextures[node.Id] = structureMemberHandler.Preview;
             }
         }
     }
 
-    private void RenderAnimationPreviews(HashSet<Guid> memberGuids, HashSet<Guid> keyFramesGuids)
+    /*private void RenderAnimationPreviews(HashSet<Guid> memberGuids, HashSet<Guid> keyFramesGuids)
     {
         foreach (var keyFrame in doc.AnimationHandler.KeyFrames)
         {
@@ -154,7 +149,7 @@ internal class MemberPreviewUpdater
                 RenderGroupPreview(groupHandler);
             }
         }
-    }
+    }*/
 
     private bool IsInFrame(ICelHandler cel)
     {
@@ -162,7 +157,7 @@ internal class MemberPreviewUpdater
                cel.StartFrameBindable + cel.DurationBindable > doc.AnimationHandler.ActiveFrameBindable;
     }
 
-    private void RenderFramePreview(ICelHandler cel)
+    /*private void RenderFramePreview(ICelHandler cel)
     {
         if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(cel.Id, out KeyFrame _))
         {
@@ -182,9 +177,9 @@ internal class MemberPreviewUpdater
 
             cel.PreviewPainter.Repaint();
         }
-    }
+    }*/
 
-    private void RenderGroupPreview(ICelGroupHandler groupHandler)
+    /*private void RenderGroupPreview(ICelGroupHandler groupHandler)
     {
         var group = internals.Tracker.Document.AnimationData.KeyFrames.FirstOrDefault(x => x.Id == groupHandler.Id);
         if (group != null)
@@ -209,9 +204,9 @@ internal class MemberPreviewUpdater
 
             groupHandler.PreviewPainter.Repaint();
         }
-    }
+    }*/
 
-    private void RenderMaskPreviews(HashSet<Guid> members)
+    /*private void RenderMaskPreviews(HashSet<Guid> members)
     {
         foreach (var node in doc.NodeGraphHandler.AllNodes)
         {
@@ -245,9 +240,9 @@ internal class MemberPreviewUpdater
                 structureMemberHandler.MaskPreviewPainter.Repaint();
             }
         }
-    }
+    }*/
 
-    private void RenderNodePreviews(HashSet<Guid> nodesGuids)
+    /*private void RenderNodePreviews(HashSet<Guid> nodesGuids)
     {
         var outputNode = internals.Tracker.Document.NodeGraph.OutputNode;
 
@@ -266,9 +261,9 @@ internal class MemberPreviewUpdater
         {
             QueueRepaintNode(actualRepaintedNodes, guid, allNodes);
         }
-    }
+    }*/
 
-    private void QueueRepaintNode(List<Guid> actualRepaintedNodes, Guid guid,
+    /*private void QueueRepaintNode(List<Guid> actualRepaintedNodes, Guid guid,
         IReadOnlyCollection<IReadOnlyNode> allNodes)
     {
         if (actualRepaintedNodes.Contains(guid))
@@ -301,9 +296,9 @@ internal class MemberPreviewUpdater
             actualRepaintedNodes.Add(next.Id);
             return Traverse.Further;
         });
-    }
+    }*/
 
-    private void RequestRepaintNode(IReadOnlyNode node, INodeHandler nodeVm)
+    /*private void RequestRepaintNode(IReadOnlyNode node, INodeHandler nodeVm)
     {
         if (node is IPreviewRenderable renderable)
         {
@@ -324,5 +319,5 @@ internal class MemberPreviewUpdater
                 nodeVm.ResultPainter?.Repaint();
             }
         }
-    }
+    }*/
 }

+ 0 - 284
src/PixiEditor/Models/Rendering/PreviewPainter.cs

@@ -1,284 +0,0 @@
-using Avalonia;
-using Avalonia.Threading;
-using ChunkyImageLib.DataHolders;
-using Drawie.Backend.Core;
-using Drawie.Backend.Core.ColorsImpl;
-using Drawie.Backend.Core.Numerics;
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
-using Drawie.Backend.Core.Surfaces;
-using Drawie.Backend.Core.Surfaces.ImageData;
-using Drawie.Backend.Core.Surfaces.PaintImpl;
-using Drawie.Numerics;
-using PixiEditor.ChangeableDocument.Rendering;
-
-namespace PixiEditor.Models.Rendering;
-
-public class PreviewPainter : IDisposable
-{
-    public string ElementToRenderName { get; set; }
-    public IPreviewRenderable PreviewRenderable { get; set; }
-    public ColorSpace ProcessingColorSpace { get; set; }
-    public KeyFrameTime FrameTime { get; set; }
-    public VecI DocumentSize { get; set; }
-    public PreviewRenderer Renderer { get; set; }
-
-    public bool AllowPartialResolutions { get; set; } = true;
-
-    public bool CanRender => canRender;
-
-    public event Action<bool>? CanRenderChanged;
-
-    private Dictionary<int, Texture> renderTextures = new();
-    private Dictionary<int, PainterInstance> painterInstances = new();
-
-    private HashSet<int> dirtyTextures = new HashSet<int>();
-    private HashSet<int> repaintingTextures = new HashSet<int>();
-
-    private Dictionary<int, VecI> pendingResizes = new();
-    private HashSet<int> pendingRemovals = new();
-
-    private bool canRender;
-
-    private int lastRequestId = 0;
-
-    public PreviewPainter(PreviewRenderer renderer, IPreviewRenderable previewRenderable, KeyFrameTime frameTime,
-        VecI documentSize, ColorSpace processingColorSpace, string elementToRenderName = "")
-    {
-        PreviewRenderable = previewRenderable;
-        ElementToRenderName = elementToRenderName;
-        ProcessingColorSpace = processingColorSpace;
-        FrameTime = frameTime;
-        DocumentSize = documentSize;
-        Renderer = renderer;
-    }
-
-    public void Paint(DrawingSurface renderOn, int painterId)
-    {
-        if (!renderTextures.TryGetValue(painterId, out Texture? renderTexture))
-        {
-            return;
-        }
-
-        if (renderTexture == null || renderTexture.IsDisposed)
-        {
-            return;
-        }
-
-        renderOn.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
-    }
-
-    public PainterInstance AttachPainterInstance()
-    {
-        int requestId = lastRequestId++;
-
-        PainterInstance painterInstance = new() { RequestId = requestId };
-
-        painterInstances[requestId] = painterInstance;
-
-        return painterInstance;
-    }
-
-    public void ChangeRenderTextureSize(int requestId, VecI size)
-    {
-        if (size.X <= 0 || size.Y <= 0)
-        {
-            return;
-        }
-
-        if (repaintingTextures.Contains(requestId))
-        {
-            pendingResizes[requestId] = size;
-            return;
-        }
-
-        if (renderTextures.ContainsKey(requestId))
-        {
-            renderTextures[requestId].Dispose();
-        }
-
-        renderTextures[requestId] = Texture.ForProcessing(size, ProcessingColorSpace);
-    }
-
-    public void RemovePainterInstance(int requestId)
-    {
-        painterInstances.Remove(requestId);
-        dirtyTextures.Remove(requestId);
-
-        if (repaintingTextures.Contains(requestId))
-        {
-            pendingRemovals.Add(requestId);
-            return;
-        }
-
-        if (renderTextures.TryGetValue(requestId, out var renderTexture))
-        {
-            renderTexture?.Dispose();
-            renderTextures.Remove(requestId);
-        }
-    }
-
-    public void Repaint()
-    {
-        foreach (var texture in renderTextures)
-        {
-            dirtyTextures.Add(texture.Key);
-        }
-
-        RepaintDirty();
-    }
-
-    public void RepaintFor(int requestId)
-    {
-        dirtyTextures.Add(requestId);
-        RepaintDirty();
-    }
-
-    private void RepaintDirty()
-    {
-        var dirtyArray = dirtyTextures.ToArray();
-        bool couldRender = canRender;
-        canRender = PreviewRenderable?.GetPreviewBounds(FrameTime.Frame, ElementToRenderName) != null &&
-                    painterInstances.Count > 0;
-        if (couldRender != canRender)
-        {
-            CanRenderChanged?.Invoke(canRender);
-        }
-
-        if (!CanRender)
-        {
-            return;
-        }
-
-        foreach (var texture in dirtyArray)
-        {
-            if (!renderTextures.TryGetValue(texture, out var renderTexture))
-            {
-                continue;
-            }
-
-            if (!painterInstances.TryGetValue(texture, out var painterInstance))
-            {
-                repaintingTextures.Remove(texture);
-                dirtyTextures.Remove(texture);
-                continue;
-            }
-
-            repaintingTextures.Add(texture);
-
-            renderTexture.DrawingSurface.Canvas.Clear();
-            renderTexture.DrawingSurface.Canvas.Save();
-
-            Matrix3X3? matrix = painterInstance.RequestMatrix?.Invoke();
-            VecI bounds = painterInstance.RequestRenderBounds?.Invoke() ?? VecI.Zero;
-
-            ChunkResolution finalResolution = FindResolution(bounds);
-            SamplingOptions samplingOptions = FindSamplingOptions(matrix);
-
-            renderTexture.DrawingSurface.Canvas.SetMatrix(matrix ?? Matrix3X3.Identity);
-            renderTexture.DrawingSurface.Canvas.Scale((float)finalResolution.InvertedMultiplier());
-
-            RenderContext context = new(renderTexture.DrawingSurface, FrameTime, finalResolution,
-                DocumentSize,
-                DocumentSize,
-                ProcessingColorSpace, samplingOptions);
-
-            dirtyTextures.Remove(texture);
-            Renderer.QueueRenderNodePreview(PreviewRenderable, renderTexture.DrawingSurface, context, ElementToRenderName)
-                .ContinueWith(_ =>
-                {
-                    Dispatcher.UIThread.Invoke(() =>
-                    {
-                        if (pendingRemovals.Contains(texture))
-                        {
-                            if (!renderTexture.IsDisposed)
-                            {
-                                try
-                                {
-                                    renderTexture.Dispose();
-                                }
-                                catch (Exception) { }
-                            }
-
-                            renderTextures.Remove(texture);
-                            pendingRemovals.Remove(texture);
-                            pendingResizes.Remove(texture);
-                            dirtyTextures.Remove(texture);
-                            return;
-                        }
-
-                        if (renderTexture is { IsDisposed: false })
-                        {
-                            try
-                            {
-                                renderTexture.DrawingSurface.Canvas.Restore();
-                            }
-                            catch (Exception)
-                            {
-                                repaintingTextures.Remove(texture);
-                                dirtyTextures.Remove(texture);
-                                pendingResizes.Remove(texture);
-                                return;
-                            }
-                        }
-
-                        painterInstance.RequestRepaint?.Invoke();
-                        repaintingTextures.Remove(texture);
-
-                        if (pendingResizes.Remove(texture, out VecI size))
-                        {
-                            ChangeRenderTextureSize(texture, size);
-                            dirtyTextures.Add(texture);
-                        }
-
-                        if (repaintingTextures.Count == 0 && dirtyTextures.Count > 0)
-                        {
-                            RepaintDirty();
-                        }
-                    });
-                });
-        }
-    }
-
-    private ChunkResolution FindResolution(VecI bounds)
-    {
-        if (bounds.X <= 0 || bounds.Y <= 0 || !AllowPartialResolutions)
-        {
-            return ChunkResolution.Full;
-        }
-
-        double density = DocumentSize.X / (double)bounds.X;
-        if (density > 8.01)
-            return ChunkResolution.Eighth;
-        if (density > 4.01)
-            return ChunkResolution.Quarter;
-        if (density > 2.01)
-            return ChunkResolution.Half;
-        return ChunkResolution.Full;
-    }
-
-    private SamplingOptions FindSamplingOptions(Matrix3X3? matrix)
-    {
-        Matrix3X3 mtx = matrix ?? Matrix3X3.Identity;
-        return mtx.ScaleX < 1f || mtx.ScaleY < 1f
-            ? SamplingOptions.Bilinear
-            : SamplingOptions.Default;
-    }
-
-    public void Dispose()
-    {
-        foreach (var texture in renderTextures)
-        {
-            texture.Value.Dispose();
-        }
-    }
-}
-
-public class PainterInstance
-{
-    public int RequestId { get; set; }
-    public Func<VecI> RequestRenderBounds;
-
-    public Func<Matrix3X3?>? RequestMatrix;
-    public Action RequestRepaint;
-}

+ 19 - 49
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -44,7 +44,7 @@ internal class SceneRenderer : IDisposable
     }
 
     public async Task RenderAsync(Dictionary<Guid, ViewportInfo> stateViewports, AffectedArea affectedArea,
-        bool updateDelayed)
+        bool updateDelayed, Dictionary<Guid, Texture>? previewTextures)
     {
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
@@ -57,17 +57,7 @@ internal class SceneRenderer : IDisposable
 
                 if (viewport.Value.RealDimensions.ShortestAxis <= 0) continue;
 
-                var rendered = RenderScene(
-                    viewport.Key,
-                    (VecI)viewport.Value.RealDimensions,
-                    viewport.Value.Transform,
-                    viewport.Value.Resolution,
-                    affectedArea,
-                    viewport.Value.VisibleDocumentRegion,
-                    viewport.Value.Sampling,
-                    viewport.Value.RenderOutput.Equals("DEFAULT", StringComparison.InvariantCultureIgnoreCase)
-                        ? null
-                        : viewport.Value.RenderOutput);
+                var rendered = RenderScene(viewport.Value, affectedArea, previewTextures);
                 if (DocumentViewModel.SceneTextures.TryGetValue(viewport.Key, out var texture) && texture != rendered)
                 {
                     texture.Dispose();
@@ -79,24 +69,29 @@ internal class SceneRenderer : IDisposable
         }, DispatcherPriority.Background);
     }
 
-    public Texture? RenderScene(Guid viewportId, VecI renderTargetSize, Matrix3X3 targetMatrix,
-        ChunkResolution resolution,
-        AffectedArea affectedArea,
-        RectI? visibleDocumentRegion,
-        SamplingOptions samplingOptions,
-        string? targetOutput = null)
+    public Texture? RenderScene(ViewportInfo viewport, AffectedArea affectedArea, Dictionary<Guid, Texture>? previewTextures = null)
     {
         /*if (Document.Renderer.IsBusy || DocumentViewModel.Busy ||
             target.DeviceClipBounds.Size.ShortestAxis <= 0) return;*/
         //RenderOnionSkin(target, resolution, samplingOptions, targetOutput);
 
         /*TODO:
-         - Onion skinning
-         - Previews generation
+         - [ ] Onion skinning
+         - [ ] Previews generation
          - Rendering optimizer
-         - Render thread and proper locking/synchronization
+         - [?] Render thread and proper locking/synchronization
          */
 
+        VecI renderTargetSize = (VecI)viewport.RealDimensions;
+        Matrix3X3 targetMatrix = viewport.Transform;
+        Guid viewportId = viewport.Id;
+        ChunkResolution resolution = viewport.Resolution;
+        SamplingOptions samplingOptions = viewport.Sampling;
+        RectI? visibleDocumentRegion = viewport.VisibleDocumentRegion;
+        string? targetOutput = viewport.RenderOutput.Equals("DEFAULT", StringComparison.InvariantCultureIgnoreCase)
+            ? null
+            : viewport.RenderOutput;
+
         IReadOnlyNodeGraph finalGraph = RenderingUtils.SolveFinalNodeGraph(targetOutput, Document);
         bool shouldRerender =
             ShouldRerender(renderTargetSize, targetMatrix, resolution, viewportId, targetOutput, finalGraph);
@@ -104,10 +99,8 @@ internal class SceneRenderer : IDisposable
         if (shouldRerender)
         {
             return RenderGraph(renderTargetSize, targetMatrix, viewportId, resolution, samplingOptions, affectedArea,
-                visibleDocumentRegion, targetOutput,
-                finalGraph);
+                visibleDocumentRegion, targetOutput, finalGraph, previewTextures);
         }
-        //previewRenderer.RenderPreviews(DocumentViewModel.AnimationHandler.ActiveFrameTime);
 
         var cachedTexture = DocumentViewModel.SceneTextures[viewportId];
         return cachedTexture;
@@ -119,7 +112,7 @@ internal class SceneRenderer : IDisposable
         AffectedArea area,
         RectI? visibleDocumentRegion,
         string? targetOutput,
-        IReadOnlyNodeGraph finalGraph)
+        IReadOnlyNodeGraph finalGraph, Dictionary<Guid, Texture>? previewTextures)
     {
         DrawingSurface renderTarget = null;
         Texture? renderTexture = null;
@@ -135,9 +128,6 @@ internal class SceneRenderer : IDisposable
             renderTarget.Canvas.Save();
             renderTexture.DrawingSurface.Canvas.Save();
             renderTexture.DrawingSurface.Canvas.Scale((float)resolution.Multiplier());
-
-            /*restoreCanvasTo = target.Canvas.Save();
-            target.Canvas.Scale((float)resolution.InvertedMultiplier());*/
         }
         else
         {
@@ -145,11 +135,6 @@ internal class SceneRenderer : IDisposable
                 Document.ProcessingColorSpace);
 
             renderTarget = renderTexture.DrawingSurface;
-
-            /*restoreCanvasTo = target.Canvas.Save();
-            renderTarget.Canvas.Save();*/
-
-            /*target.Canvas.SetMatrix(Matrix3X3.Identity);*/
             renderTarget.Canvas.SetMatrix(targetMatrix);
         }
 
@@ -158,26 +143,11 @@ internal class SceneRenderer : IDisposable
         context.TargetOutput = targetOutput;
         context.AffectedArea = area;
         context.VisibleDocumentRegion = visibleDocumentRegion;
+        context.PreviewTextures = previewTextures;
         finalGraph.Execute(context);
 
         renderTarget.Canvas.Restore();
 
-        /*if (renderTexture != null)
-        {
-            target.Canvas.Clear();
-            if (samplingOptions == SamplingOptions.Default)
-            {
-                target.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
-            }
-            else
-            {
-                using var snapshot = renderTexture.DrawingSurface.Snapshot();
-                target.Canvas.DrawImage(snapshot, 0, 0, samplingOptions);
-            }
-
-            target.Canvas.RestoreToCount(restoreCanvasTo);
-        }*/
-
         return renderTexture;
     }
 

+ 3 - 3
src/PixiEditor/Styles/Templates/KeyFrame.axaml

@@ -36,10 +36,10 @@
                                 </ImageBrush.Transform>
                             </ImageBrush>
                         </Border.Background>
-                        <visuals:PreviewPainterControl
-                            PreviewPainter="{Binding Item.PreviewPainter, RelativeSource={RelativeSource TemplatedParent}}"
+                        <visuals:TextureControl
+                            Texture="{Binding Item.PreviewTexture, RelativeSource={RelativeSource TemplatedParent}}"
                             Width="60" Height="60" RenderOptions.BitmapInterpolationMode="None">
-                            </visuals:PreviewPainterControl>
+                        </visuals:TextureControl>
                     </Border>
                 </Grid>
             </ControlTemplate>

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

@@ -63,11 +63,10 @@
                                     <ImageBrush Source="/Images/CheckerTile.png"
                                                 TileMode="Tile" DestinationRect="0, 0, 25, 25" />
                                 </Panel.Background>
-                                <visuals:PreviewPainterControl
-                                    PreviewPainter="{TemplateBinding ResultPreview}"
-                                    FrameToRender="{TemplateBinding ActiveFrame}"
+                                <visuals:TextureControl
+                                    Texture="{TemplateBinding ResultPreview}"
                                     RenderOptions.BitmapInterpolationMode="None">
-                                </visuals:PreviewPainterControl>
+                                </visuals:TextureControl>
                             </Panel>
                         </Border>
                     </Grid>

+ 2 - 2
src/PixiEditor/Styles/Templates/TimelineGroupHeader.axaml

@@ -25,8 +25,8 @@
                                     </ImageBrush.Transform>
                                 </ImageBrush>
                             </Border.Background>
-                            <visuals:PreviewPainterControl
-                                PreviewPainter="{Binding Item.PreviewPainter, RelativeSource={RelativeSource TemplatedParent}}"
+                            <visuals:TextureControl
+                                Texture="{Binding Item.PreviewTexture, RelativeSource={RelativeSource TemplatedParent}}"
                                 Width="60" Height="60" RenderOptions.BitmapInterpolationMode="None"/>
                         </Border>
                         <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="{Binding Item.LayerName, RelativeSource={RelativeSource TemplatedParent}}" />

+ 9 - 8
src/PixiEditor/ViewModels/Document/CelViewModel.cs

@@ -10,12 +10,18 @@ namespace PixiEditor.ViewModels.Document;
 
 internal abstract class CelViewModel : ObservableObject, ICelHandler
 {
-    private PreviewPainter? previewPainter;
     private int startFrameBindable;
     private int durationBindable;
     private bool isVisibleBindable = true;
     private bool isSelected;
     private bool isCollapsed;
+    private Texture? previewTexture;
+
+    public Texture? PreviewTexture
+    {
+        get => previewTexture;
+        set => SetProperty(ref previewTexture, value);
+    }
 
     public bool IsCollapsed
     {
@@ -29,11 +35,6 @@ internal abstract class CelViewModel : ObservableObject, ICelHandler
 
     IDocument ICelHandler.Document => Document;
 
-    public PreviewPainter? PreviewPainter
-    {
-        get => previewPainter;
-        set => SetProperty(ref previewPainter, value);
-    }
 
     public virtual int StartFrameBindable
     {
@@ -147,7 +148,7 @@ internal abstract class CelViewModel : ObservableObject, ICelHandler
 
     public void Dispose()
     {
-        PreviewPainter?.Dispose();
-        PreviewPainter = null;
+        PreviewTexture?.Dispose();
+        PreviewTexture = null;
     }
 }

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

@@ -185,25 +185,14 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
     public IStructureMemberHandler? SelectedStructureMember { get; private set; } = null;
 
-    private PreviewPainter miniPreviewPainter;
+    private Texture? miniPreviewTexture;
 
-    public PreviewPainter MiniPreviewPainter
+    public Texture MiniPreviewTexture
     {
-        get => miniPreviewPainter;
+        get => miniPreviewTexture;
         set
         {
-            SetProperty(ref miniPreviewPainter, value);
-        }
-    }
-
-    private PreviewPainter previewSurface;
-
-    public PreviewPainter PreviewPainter
-    {
-        get => previewSurface;
-        set
-        {
-            SetProperty(ref previewSurface, value);
+            SetProperty(ref miniPreviewTexture, value);
         }
     }
 

+ 17 - 19
src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs

@@ -76,6 +76,21 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         OnPropertyChanged(nameof(MaskIsVisibleBindable));
     }
 
+    private Texture? maskPreview;
+    private Texture? preview;
+
+    public Texture? Preview
+    {
+        get => preview;
+        set => SetProperty(ref preview, value);
+    }
+
+    public Texture? MaskPreview
+    {
+        get => maskPreview;
+        set => SetProperty(ref maskPreview, value);
+    }
+
     public bool MaskIsVisibleBindable
     {
         get => maskIsVisible;
@@ -167,30 +182,13 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         set => SetProperty(ref selection, value);
     }
 
-    private PreviewPainter? previewSurface;
-    private PreviewPainter? _maskPreviewPainter;
-
-    public PreviewPainter? PreviewPainter
-    {
-        get => previewSurface;
-        set => SetProperty(ref previewSurface, value);
-    }
-
-    public PreviewPainter? MaskPreviewPainter
-    {
-        get => _maskPreviewPainter;
-        set => SetProperty(ref _maskPreviewPainter, value);
-    }
-
     IDocument IStructureMemberHandler.Document => Document;
 
     public override void Dispose()
     {
         base.Dispose();
-        PreviewPainter?.Dispose();
-        MaskPreviewPainter?.Dispose();
-        PreviewPainter = null;
-        MaskPreviewPainter = null;
+        Preview?.Dispose();
+        MaskPreview?.Dispose();
     }
 }
 

+ 7 - 8
src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs

@@ -30,14 +30,19 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
     private IBrush? categoryBrush;
     private string? nodeNameBindable;
     private VecD position;
+    private Texture? resultTexture;
     private ObservableRangeCollection<INodePropertyHandler> inputs;
     private ObservableRangeCollection<INodePropertyHandler> outputs;
-    private PreviewPainter resultPainter;
     private bool isSelected;
     private string? icon;
 
     protected Guid id;
 
+    public Texture? Preview
+    {
+        get => resultTexture;
+        set => SetProperty(ref resultTexture, value);
+    }
     public IReadOnlyDictionary<string, INodePropertyHandler> InputPropertyMap => inputPropertyMap;
     public IReadOnlyDictionary<string, INodePropertyHandler> OutputPropertyMap => outputPropertyMap;
 
@@ -163,12 +168,6 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
         }
     }
 
-    public PreviewPainter ResultPainter
-    {
-        get => resultPainter;
-        set => SetProperty(ref resultPainter, value);
-    }
-
     public bool IsNodeSelected
     {
         get => isSelected;
@@ -507,7 +506,7 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
 
     public virtual void Dispose()
     {
-        ResultPainter?.Dispose();
+        Preview?.Dispose();
     }
 
     public NodePropertyViewModel FindInputProperty(string propName)

+ 5 - 7
src/PixiEditor/ViewModels/SubViewModels/ViewportWindowViewModel.cs

@@ -146,7 +146,7 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         }
     }
 
-    private PreviewPainterControl previewPainterControl;
+    private TextureControl previewPainterControl;
 
     public void IndexChanged()
     {
@@ -175,9 +175,8 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         PixiEditorSettings.Scene.PrimaryBackgroundColor.ValueChanged += UpdateBackgroundBitmap;
         PixiEditorSettings.Scene.SecondaryBackgroundColor.ValueChanged += UpdateBackgroundBitmap;
 
-        previewPainterControl = new PreviewPainterControl(
-            Document.MiniPreviewPainter,
-            Document.AnimationDataViewModel.ActiveFrameTime.Frame);
+        previewPainterControl = new TextureControl();
+        previewPainterControl.Texture = Document.MiniPreviewTexture;
         TabCustomizationSettings.Icon = previewPainterControl;
     }
 
@@ -188,10 +187,9 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         {
             OnPropertyChanged(nameof(Title));
         }
-        else if (e.PropertyName == nameof(DocumentViewModel.MiniPreviewPainter))
+        else if (e.PropertyName == nameof(DocumentViewModel.MiniPreviewTexture))
         {
-            previewPainterControl.PreviewPainter = Document.MiniPreviewPainter;
-            previewPainterControl.FrameToRender = Document.AnimationDataViewModel.ActiveFrameTime.Frame;
+            previewPainterControl.Texture = Document.MiniPreviewTexture;
         }
         else if (e.PropertyName == nameof(DocumentViewModel.AllChangesSaved))
         {

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

@@ -71,10 +71,9 @@
                                     </ImageBrush.Transform>
                                 </ImageBrush>
                             </Border.Background>
-                            <visuals:PreviewPainterControl
-                                PreviewPainter="{Binding Folder.PreviewPainter, ElementName=folderControl}"
+                            <visuals:TextureControl
+                                Texture="{Binding Folder.Preview, ElementName=folderControl}"
                                 ClipToBounds="True"
-                                FrameToRender="{Binding Manager.ActiveDocument.AnimationDataViewModel.ActiveFrameBindable, ElementName=folderControl}"
                                 Width="30" Height="30" RenderOptions.BitmapInterpolationMode="None" />
                         </Border>
                         <Border
@@ -92,11 +91,10 @@
                                 </ImageBrush>
                             </Border.Background>
                             <Grid IsHitTestVisible="False">
-                                <visuals:PreviewPainterControl
-                                    PreviewPainter="{Binding Folder.MaskPreviewPainter, ElementName=folderControl}"
+                                <visuals:TextureControl
+                                    Texture="{Binding Folder.MaskPreview, ElementName=folderControl}"
                                     Width="30" Height="30"
                                     ClipToBounds="True"
-                                    FrameToRender="{Binding Manager.ActiveDocument.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"

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

@@ -80,10 +80,9 @@
                                 <Binding ElementName="uc" Path="Layer.HasMaskBindable" />
                             </MultiBinding>
                         </Border.BorderBrush>
-                        <visuals:PreviewPainterControl 
+                        <visuals:TextureControl
                             ClipToBounds="True" 
-                            PreviewPainter="{Binding Layer.PreviewPainter, ElementName=uc}"
-                            FrameToRender="{Binding Manager.ActiveDocument.AnimationDataViewModel.ActiveFrameBindable, ElementName=uc}"
+                            Texture="{Binding Layer.Preview, ElementName=uc}"
                             Width="30" Height="30"
                             RenderOptions.BitmapInterpolationMode="None" IsHitTestVisible="False" />
                     </Border>
@@ -107,11 +106,10 @@
                             </MultiBinding>
                         </Border.BorderBrush>
                         <Grid IsHitTestVisible="False">
-                             <visuals:PreviewPainterControl 
-                                    PreviewPainter="{Binding Layer.MaskPreviewPainter, ElementName=uc}" 
+                             <visuals:TextureControl
+                                    Texture="{Binding Layer.MaskPreview, ElementName=uc}"
                                     Width="30" Height="30"
                                     ClipToBounds="True"
-                                    FrameToRender="{Binding Path=Manager.ActiveDocument.AnimationDataViewModel.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"

+ 3 - 21
src/PixiEditor/Views/Nodes/NodeView.cs

@@ -41,8 +41,8 @@ public class NodeView : TemplatedControl
         AvaloniaProperty.Register<NodeView, ObservableRangeCollection<INodePropertyHandler>>(
             nameof(Outputs));
 
-    public static readonly StyledProperty<PreviewPainter> ResultPreviewProperty =
-        AvaloniaProperty.Register<NodeView, PreviewPainter>(
+    public static readonly StyledProperty<Texture> ResultPreviewProperty =
+        AvaloniaProperty.Register<NodeView, Texture>(
             nameof(ResultPreview));
 
     public static readonly StyledProperty<bool> IsSelectedProperty = AvaloniaProperty.Register<NodeView, bool>(
@@ -94,7 +94,7 @@ public class NodeView : TemplatedControl
         set => SetValue(IsSelectedProperty, value);
     }
 
-    public PreviewPainter ResultPreview
+    public Texture ResultPreview
     {
         get => GetValue(ResultPreviewProperty);
         set => SetValue(ResultPreviewProperty, value);
@@ -177,7 +177,6 @@ public class NodeView : TemplatedControl
     static NodeView()
     {
         IsSelectedProperty.Changed.Subscribe(NodeSelectionChanged);
-        ResultPreviewProperty.Changed.Subscribe(PainterChanged);
     }
 
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
@@ -312,23 +311,6 @@ public class NodeView : TemplatedControl
         }
     }
 
-    private static void PainterChanged(AvaloniaPropertyChangedEventArgs<PreviewPainter> e)
-    {
-        if (e.Sender is NodeView nodeView)
-        {
-            if (e.OldValue.Value is not null)
-            {
-                e.OldValue.Value.CanRenderChanged -= nodeView.ResultPreview_CanRenderChanged;
-            }
-
-            if (e.NewValue.Value is not null)
-            {
-                e.NewValue.Value.CanRenderChanged += nodeView.ResultPreview_CanRenderChanged;
-                nodeView.ResultPreview_CanRenderChanged(e.NewValue.Value.CanRender);
-            }
-        }
-    }
-
     private void ResultPreview_CanRenderChanged(bool canRender)
     {
         if (CanRenderPreview != canRender)

+ 0 - 227
src/PixiEditor/Views/Visuals/PreviewPainterControl.cs

@@ -1,227 +0,0 @@
-using Avalonia;
-using Avalonia.Interactivity;
-using Avalonia.LogicalTree;
-using ChunkyImageLib.DataHolders;
-using Drawie.Backend.Core.Numerics;
-using Drawie.Backend.Core.Surfaces;
-using Drawie.Interop.Avalonia.Core.Controls;
-using PixiEditor.Models.Rendering;
-using Drawie.Numerics;
-
-namespace PixiEditor.Views.Visuals;
-
-public class PreviewPainterControl : DrawieControl
-{
-    public static readonly StyledProperty<int> FrameToRenderProperty =
-        AvaloniaProperty.Register<PreviewPainterControl, int>("FrameToRender");
-
-    public static readonly StyledProperty<PreviewPainter> PreviewPainterProperty =
-        AvaloniaProperty.Register<PreviewPainterControl, PreviewPainter>(
-            nameof(PreviewPainter));
-
-    public static readonly StyledProperty<VecI> CustomRenderSizeProperty =
-        AvaloniaProperty.Register<PreviewPainterControl, VecI>(
-            nameof(CustomRenderSize));
-
-    public VecI CustomRenderSize
-    {
-        get => GetValue(CustomRenderSizeProperty);
-        set => SetValue(CustomRenderSizeProperty, value);
-    }
-
-    public PreviewPainter PreviewPainter
-    {
-        get => GetValue(PreviewPainterProperty);
-        set => SetValue(PreviewPainterProperty, value);
-    }
-
-    public int FrameToRender
-    {
-        get { return (int)GetValue(FrameToRenderProperty); }
-        set { SetValue(FrameToRenderProperty, value); }
-    }
-
-    private PainterInstance? painterInstance;
-
-    static PreviewPainterControl()
-    {
-        PreviewPainterProperty.Changed.Subscribe(PainterChanged);
-        BoundsProperty.Changed.Subscribe(UpdatePainterBounds);
-        CustomRenderSizeProperty.Changed.Subscribe(UpdatePainterBounds);
-    }
-
-    public PreviewPainterControl()
-    {
-    }
-
-    public PreviewPainterControl(PreviewPainter previewPainter, int frameToRender)
-    {
-        PreviewPainter = previewPainter;
-        FrameToRender = frameToRender;
-    }
-
-    protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
-    {
-        base.OnDetachedFromVisualTree(e);
-        if (PreviewPainter != null && painterInstance != null)
-        {
-            PreviewPainter.RemovePainterInstance(painterInstance.RequestId);
-            painterInstance.RequestMatrix = null;
-            painterInstance.RequestRepaint = null;
-            painterInstance.RequestRenderBounds = null;
-            painterInstance = null;
-        }
-    }
-
-    protected override void OnLoaded(RoutedEventArgs e)
-    {
-        base.OnLoaded(e);
-        if (PreviewPainter != null && painterInstance == null)
-        {
-            painterInstance = PreviewPainter.AttachPainterInstance();
-            VecI finalSize = GetFinalSize();
-            if (finalSize is { X: > 0, Y: > 0 })
-            {
-                PreviewPainter.ChangeRenderTextureSize(painterInstance.RequestId, finalSize);
-            }
-
-            painterInstance.RequestMatrix = OnPainterRequestMatrix;
-            painterInstance.RequestRepaint = OnPainterRenderRequest;
-            painterInstance.RequestRenderBounds = OnPainterRequestBounds;
-
-            PreviewPainter.RepaintFor(painterInstance.RequestId);
-        }
-    }
-
-    private static void PainterChanged(AvaloniaPropertyChangedEventArgs<PreviewPainter> args)
-    {
-        var sender = args.Sender as PreviewPainterControl;
-        if (args.OldValue.Value != null)
-        {
-            if (sender.painterInstance != null)
-            {
-                args.OldValue.Value.RemovePainterInstance(sender.painterInstance.RequestId);
-            }
-
-
-            sender.painterInstance = null;
-        }
-
-        if (args.NewValue.Value != null)
-        {
-            sender.painterInstance = args.NewValue.Value.AttachPainterInstance();
-            VecI finalSize = sender.GetFinalSize();
-            if (finalSize is { X: > 0, Y: > 0 })
-            {
-                sender.PreviewPainter.ChangeRenderTextureSize(sender.painterInstance.RequestId, finalSize);
-            }
-
-            sender.painterInstance.RequestMatrix = sender.OnPainterRequestMatrix;
-            sender.painterInstance.RequestRepaint = sender.OnPainterRenderRequest;
-            sender.painterInstance.RequestRenderBounds = sender.OnPainterRequestBounds;
-
-            args.NewValue.Value.RepaintFor(sender.painterInstance.RequestId);
-        }
-        else
-        {
-            sender.painterInstance = null;
-        }
-    }
-
-    private void OnPainterRenderRequest()
-    {
-        QueueNextFrame();
-    }
-
-    public override void Draw(DrawingSurface surface)
-    {
-        if (PreviewPainter == null || painterInstance == null)
-        {
-            return;
-        }
-
-        if (CustomRenderSize.ShortestAxis > 0)
-        {
-            surface.Canvas.Save();
-            VecI finalSize = GetFinalSize();
-            surface.Canvas.Scale(
-                (float)Bounds.Width / finalSize.X,
-                (float)Bounds.Height / finalSize.Y);
-        }
-
-        PreviewPainter.Paint(surface, painterInstance.RequestId);
-
-        if (CustomRenderSize.ShortestAxis > 0)
-        {
-            surface.Canvas.Restore();
-        }
-    }
-
-    private Matrix3X3 UniformScale(float x, float y, RectD previewBounds)
-    {
-        VecI finalSize = GetFinalSize();
-        float scaleX = finalSize.X / x;
-        float scaleY = finalSize.Y / y;
-        var scale = Math.Min(scaleX, scaleY);
-        float dX = (float)finalSize.X / 2 / scale - x / 2;
-        dX -= (float)previewBounds.X;
-        float dY = (float)finalSize.Y / 2 / scale - y / 2;
-        dY -= (float)previewBounds.Y;
-        Matrix3X3 matrix = Matrix3X3.CreateScale(scale, scale);
-        return matrix.Concat(Matrix3X3.CreateTranslation(dX, dY));
-    }
-
-    private VecI GetFinalSize()
-    {
-        VecI finalSize = CustomRenderSize.ShortestAxis > 0
-            ? CustomRenderSize
-            : new VecI((int)Bounds.Width, (int)Bounds.Height);
-        if (Bounds.Width < finalSize.X && Bounds.Height < finalSize.Y)
-        {
-            finalSize = new VecI((int)Bounds.Width, (int)Bounds.Height);
-        }
-
-        return finalSize;
-    }
-
-    private static void UpdatePainterBounds(AvaloniaPropertyChangedEventArgs args)
-    {
-        var sender = args.Sender as PreviewPainterControl;
-
-        if (sender?.PreviewPainter == null)
-        {
-            return;
-        }
-
-        if (sender.painterInstance != null)
-        {
-            VecI finalSize = sender.GetFinalSize();
-            if (finalSize is { X: > 0, Y: > 0 })
-            {
-                sender.PreviewPainter.ChangeRenderTextureSize(sender.painterInstance.RequestId, finalSize);
-                sender.PreviewPainter.RepaintFor(sender.painterInstance.RequestId);
-            }
-        }
-    }
-
-    private Matrix3X3? OnPainterRequestMatrix()
-    {
-        RectD? previewBounds =
-            PreviewPainter?.PreviewRenderable?.GetPreviewBounds(FrameToRender, PreviewPainter.ElementToRenderName);
-
-        if (previewBounds == null || previewBounds.Value.IsZeroOrNegativeArea)
-        {
-            return null;
-        }
-
-        float x = (float)(previewBounds?.Width ?? 0);
-        float y = (float)(previewBounds?.Height ?? 0);
-
-        return UniformScale(x, y, previewBounds.Value);
-    }
-
-    private VecI OnPainterRequestBounds()
-    {
-        return GetFinalSize();
-    }
-}