Browse Source

Improved previews and fixed image operation

Krzysztof Krysiński 5 months ago
parent
commit
baef212efb

+ 7 - 25
src/ChunkyImageLib/Operations/ImageOperation.cs

@@ -82,36 +82,18 @@ internal class ImageOperation : IMirroredDrawOperation
     {
         //customPaint.FilterQuality = chunk.Resolution != ChunkResolution.Full;
         float scaleMult = (float)targetChunk.Resolution.Multiplier();
-        VecD trans = chunkPos * ChunkPool.FullChunkSize;
-
-        Matrix3X3 scaleTrans;
-        bool posBiggerThanSize = trans.X >= toPaint.Size.X || trans.Y >= toPaint.Size.Y;
-        if (posBiggerThanSize)
-        {
-            scaleTrans = Matrix3X3.CreateScaleTranslation(scaleMult, scaleMult, (float)-trans.X * scaleMult,
-                (float)-trans.Y * scaleMult);
-        }
-        else
-        {
-            scaleTrans = Matrix3X3.CreateScale(scaleMult, scaleMult);
-        }
+        VecD trans = -chunkPos * ChunkPool.FullChunkSize;
 
+        var scaleTrans = Matrix3X3.CreateScaleTranslation(scaleMult, scaleMult, (float)trans.X * scaleMult, (float)trans.Y * scaleMult);
         var finalMatrix = Matrix3X3.Concat(scaleTrans, transformMatrix);
 
+        using var snapshot = toPaint.DrawingSurface.Snapshot();
+        ShapeCorners chunkCorners = new ShapeCorners(new RectD(VecD.Zero, targetChunk.PixelSize));
+        RectD rect = chunkCorners.WithMatrix(finalMatrix.Invert()).AABBBounds;
+
         targetChunk.Surface.DrawingSurface.Canvas.Save();
         targetChunk.Surface.DrawingSurface.Canvas.SetMatrix(finalMatrix);
-
-        if (posBiggerThanSize)
-        {
-            targetChunk.Surface.DrawingSurface.Canvas.DrawSurface(toPaint.DrawingSurface, 0, 0, customPaint);
-        }
-        else
-        {
-            RectI snapshotRect = new RectI((VecI)(trans * scaleMult), new VecI(targetChunk.Resolution.PixelSize()));
-            using var snapshot = toPaint.DrawingSurface.Snapshot(snapshotRect);
-            targetChunk.Surface.DrawingSurface.Canvas.DrawImage(snapshot, 0, 0, customPaint);
-        }
-
+        targetChunk.Surface.DrawingSurface.Canvas.DrawImage(snapshot, rect, rect, customPaint);
         targetChunk.Surface.DrawingSurface.Canvas.Restore();
     }
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -364,7 +364,7 @@ internal class Document : IChangeable, IReadOnlyDocument
     /// <param name="guid">The <see cref="StructureNode.Id"/> of the member</param>
     public List<StructureNode> FindMemberPath(Guid guid)
     {
-        if (NodeGraph.OutputNode == null) return [];
+        //if (NodeGraph.OutputNode == null) return [];
 
         var list = new List<StructureNode>();
         var targetNode = FindNode(guid);

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

@@ -34,7 +34,8 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     {
         if (keyFrames.Count == 0)
         {
-            keyFrames.Add(new KeyFrameData(Guid.NewGuid(), 0, 0, ImageLayerKey) { Data = new ChunkyImage(size, colorSpace) });
+            keyFrames.Add(
+                new KeyFrameData(Guid.NewGuid(), 0, 0, ImageLayerKey) { Data = new ChunkyImage(size, colorSpace) });
         }
 
         this.startSize = size;
@@ -125,13 +126,39 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
             if (keyFrame != null)
             {
-                return (RectD?)GetLayerImageByKeyFrameGuid(keyFrame.KeyFrameGuid).FindChunkAlignedCommittedBounds();
+                var kf = GetLayerImageByKeyFrameGuid(keyFrame.KeyFrameGuid);
+                if (kf == null)
+                {
+                    return null;
+                }
+
+                RectI? bounds = kf.FindChunkAlignedCommittedBounds(); // Don't use tight bounds, very expensive
+                if (bounds.HasValue)
+                {
+                    return new RectD(bounds.Value.X, bounds.Value.Y,
+                        Math.Min(bounds.Value.Width, kf.CommittedSize.X),
+                        Math.Min(bounds.Value.Height, kf.CommittedSize.Y));
+                }
             }
         }
 
         try
         {
-            return (RectD?)GetLayerImageAtFrame(frame).FindChunkAlignedCommittedBounds();
+            var kf = GetLayerImageAtFrame(frame);
+            if (kf == null)
+            {
+                return null;
+            }
+
+            var bounds = kf.FindChunkAlignedCommittedBounds(); // Don't use tight bounds, very expensive
+            if (bounds.HasValue)
+            {
+                return new RectD(bounds.Value.X, bounds.Value.Y,
+                    Math.Min(bounds.Value.Width, kf.CommittedSize.X),
+                    Math.Min(bounds.Value.Height, kf.CommittedSize.Y));
+            }
+
+            return null;
         }
         catch (ObjectDisposedException)
         {
@@ -197,7 +224,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         {
             return keyFrames[0];
         }
-        
+
         var imageFrame = keyFrames.OrderBy(x => x.StartFrame).LastOrDefault(x => x.IsInFrame(frame.Frame));
         if (imageFrame?.Data is not ChunkyImage)
         {
@@ -229,8 +256,10 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     {
         var image = new ImageLayerNode(startSize, colorSpace)
         {
-            MemberName = this.MemberName, LockTransparency = this.LockTransparency,
-            ClipToPreviousMember = this.ClipToPreviousMember, EmbeddedMask = this.EmbeddedMask?.CloneFromCommitted()
+            MemberName = this.MemberName,
+            LockTransparency = this.LockTransparency,
+            ClipToPreviousMember = this.ClipToPreviousMember,
+            EmbeddedMask = this.EmbeddedMask?.CloneFromCommitted()
         };
 
         image.keyFrames.Clear();
@@ -258,7 +287,8 @@ 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)
+    public override void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime,
+        ColorSpace processColorSpace)
     {
         base.RenderChunk(chunkPos, resolution, frameTime, processColorSpace);
 

+ 0 - 10
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Matrix/Matrix3X3BaseNode.cs

@@ -48,14 +48,4 @@ public abstract class Matrix3X3BaseNode : RenderNode, IRenderInput
     }
 
     protected abstract Matrix3X3 CalculateMatrix(Matrix3X3 input);
-
-    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
-    {
-        return null;
-    }
-
-    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
-    {
-        return false;
-    }
 }

+ 14 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/RenderNode.cs

@@ -17,6 +17,8 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
 
     private TextureCache textureCache = new();
 
+    private VecI lastDocumentSize = VecI.Zero;
+
     public RenderNode()
     {
         Painter painter = new Painter(Paint);
@@ -34,6 +36,8 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
                 output.ChainToPainterValue();
             }
         }
+
+        lastDocumentSize = context.DocumentSize;
     }
 
     private void Paint(RenderContext context, DrawingSurface surface)
@@ -58,10 +62,17 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
 
     protected abstract void OnPaint(RenderContext context, DrawingSurface surface);
 
-    public abstract RectD? GetPreviewBounds(int frame, string elementToRenderName = "");
+    public virtual RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        return new RectD(0, 0, lastDocumentSize.X, lastDocumentSize.Y);
+    }
 
-    public abstract bool RenderPreview(DrawingSurface renderOn, RenderContext context,
-        string elementToRenderName);
+    public virtual bool RenderPreview(DrawingSurface renderOn, RenderContext context,
+        string elementToRenderName)
+    {
+        OnPaint(context, renderOn);
+        return true;
+    }
 
     protected Texture RequestTexture(int id, VecI size, ColorSpace processingCs, bool clear = true)
     {

+ 1 - 1
src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs

@@ -143,7 +143,7 @@ internal class ActionAccumulator
                         undoBoundaryPassed || viewportRefreshRequest);
                 }*/
 
-                previewUpdater.UpdatePreviews(undoBoundaryPassed || changeFrameRequest || viewportRefreshRequest,
+                previewUpdater.UpdatePreviews(
                     affectedAreas.ImagePreviewAreas.Keys,
                     affectedAreas.MaskPreviewAreas.Keys,
                     affectedAreas.ChangedNodes, affectedAreas.ChangedKeyFrames);

+ 2 - 3
src/PixiEditor/Models/Rendering/AffectedAreasGatherer.cs

@@ -209,7 +209,9 @@ internal class AffectedAreasGatherer
     {
         ChangedNodes ??= new List<Guid>();
         if (!ChangedNodes.Contains(nodeId))
+        {
             ChangedNodes.Add(nodeId);
+        }
     }
     
     private void AddAllNodesToImagePreviews()
@@ -331,9 +333,6 @@ internal class AffectedAreasGatherer
     private void AddToImagePreviews(Guid memberGuid, AffectedArea area, bool ignoreSelf = false)
     {
         var path = tracker.Document.FindMemberPath(memberGuid);
-        int minCount = ignoreSelf ? 2 : 1;
-        if (path.Count < minCount)
-            return;
         for (int i = ignoreSelf ? 1 : 0; i < path.Count; i++)
         {
             var member = path[i];

+ 56 - 37
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -1,21 +1,13 @@
 #nullable enable
 
-using System.Diagnostics.CodeAnalysis;
-using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
-using PixiEditor.ChangeableDocument.Rendering;
-using Drawie.Backend.Core;
-using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.ImageData;
-using Drawie.Backend.Core.Surfaces.PaintImpl;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
-using PixiEditor.Parser;
 
 namespace PixiEditor.Models.Rendering;
 
@@ -33,13 +25,12 @@ internal class MemberPreviewUpdater
         AnimationKeyFramePreviewRenderer = new AnimationKeyFramePreviewRenderer(internals);
     }
 
-    public void UpdatePreviews(bool rerenderPreviews, IEnumerable<Guid> membersToUpdate,
+    public void UpdatePreviews(IEnumerable<Guid> membersToUpdate,
         IEnumerable<Guid> masksToUpdate, IEnumerable<Guid> nodesToUpdate, IEnumerable<Guid> keyFramesToUpdate)
     {
-        if (!rerenderPreviews)
-        {
+        if (!membersToUpdate.Any() && !masksToUpdate.Any() && !nodesToUpdate.Any() &&
+            !keyFramesToUpdate.Any())
             return;
-        }
 
         UpdatePreviewPainters(membersToUpdate, masksToUpdate, nodesToUpdate, keyFramesToUpdate);
     }
@@ -243,45 +234,73 @@ internal class MemberPreviewUpdater
         if (outputNode is null)
             return;
 
-        var executionQueue =
+        var allNodes =
             internals.Tracker.Document.NodeGraph
                 .AllNodes; //internals.Tracker.Document.NodeGraph.CalculateExecutionQueue(outputNode);
 
         if (nodesGuids.Length == 0)
             return;
 
-        foreach (var node in executionQueue)
+        List<Guid> actualRepaintedNodes = new();
+        foreach (var guid in nodesGuids)
+        {
+            QueueRepaintNode(actualRepaintedNodes, guid, allNodes);
+        }
+    }
+
+    private void QueueRepaintNode(List<Guid> actualRepaintedNodes, Guid guid,
+        IReadOnlyCollection<IReadOnlyNode> allNodes)
+    {
+        if (actualRepaintedNodes.Contains(guid))
+            return;
+
+        var nodeVm = doc.StructureHelper.FindNode<INodeHandler>(guid);
+        if (nodeVm == null)
+        {
+            return;
+        }
+
+        actualRepaintedNodes.Add(guid);
+        IReadOnlyNode node = allNodes.FirstOrDefault(x => x.Id == guid);
+        if (node is null)
+            return;
+
+        RequestRepaintNode(node, nodeVm);
+
+        nodeVm.TraverseForwards(next =>
         {
-            if (node is null)
-                continue;
+            if (next is not INodeHandler nextVm)
+                return true;
 
-            if (!nodesGuids.Contains(node.Id))
-                continue;
+            var nextNode = allNodes.FirstOrDefault(x => x.Id == next.Id);
 
-            var nodeVm = doc.StructureHelper.FindNode<INodeHandler>(node.Id);
+            if (nextNode is null || actualRepaintedNodes.Contains(next.Id))
+                return true;
+
+            RequestRepaintNode(nextNode, nextVm);
+            actualRepaintedNodes.Add(next.Id);
+            return true;
+        });
+    }
 
-            if (nodeVm == null)
+    private void RequestRepaintNode(IReadOnlyNode node, INodeHandler nodeVm)
+    {
+        if (node is IPreviewRenderable renderable)
+        {
+            if (nodeVm.ResultPainter == null)
             {
-                continue;
+                nodeVm.ResultPainter = new PreviewPainter(doc.Renderer, renderable,
+                    doc.AnimationHandler.ActiveFrameTime,
+                    doc.SizeBindable, internals.Tracker.Document.ProcessingColorSpace);
+                nodeVm.ResultPainter.Repaint();
             }
-
-            if (node is IPreviewRenderable renderable)
+            else
             {
-                if (nodeVm.ResultPainter == null)
-                {
-                    nodeVm.ResultPainter = new PreviewPainter(doc.Renderer, renderable,
-                        doc.AnimationHandler.ActiveFrameTime,
-                        doc.SizeBindable, internals.Tracker.Document.ProcessingColorSpace);
-                    nodeVm.ResultPainter.Repaint();
-                }
-                else
-                {
-                    nodeVm.ResultPainter.FrameTime = doc.AnimationHandler.ActiveFrameTime;
-                    nodeVm.ResultPainter.DocumentSize = doc.SizeBindable;
-                    nodeVm.ResultPainter.ProcessingColorSpace = internals.Tracker.Document.ProcessingColorSpace;
+                nodeVm.ResultPainter.FrameTime = doc.AnimationHandler.ActiveFrameTime;
+                nodeVm.ResultPainter.DocumentSize = doc.SizeBindable;
+                nodeVm.ResultPainter.ProcessingColorSpace = internals.Tracker.Document.ProcessingColorSpace;
 
-                    nodeVm.ResultPainter?.Repaint();
-                }
+                nodeVm.ResultPainter?.Repaint();
             }
         }
     }