Browse Source

Key frames previews wip

flabbet 10 months ago
parent
commit
60d9ee3fda

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Animations/KeyFrame.cs

@@ -1,6 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Animations;
 

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Animations/RasterKeyFrame.cs

@@ -1,6 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Animations;
 

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

@@ -6,7 +6,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 public interface IPreviewRenderable
 {
-    public RectD? GetPreviewBounds(string elementToRenderName = "", int frame = 0); 
+    public RectD? GetPreviewBounds(int frame, string elementToRenderName = ""); 
     public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
         string elementToRenderName);
 }

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

@@ -164,11 +164,11 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
         }
     }
 
-    public override RectD? GetPreviewBounds(string elementFor = "", int frame = 0)
+    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
     {
         if (elementFor == nameof(EmbeddedMask))
         {
-            return base.GetPreviewBounds(elementFor);
+            return base.GetPreviewBounds(frame, elementFor);
         }
 
         return GetTightBounds(frame);

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

@@ -111,13 +111,23 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         workingSurface.Canvas.RestoreToCount(saved);
     }
 
-    public override RectD? GetPreviewBounds(string elementFor = "", int frame = 0)
+    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
     {
         if (elementFor == nameof(EmbeddedMask))
         {
-            return base.GetPreviewBounds(elementFor);
+            return base.GetPreviewBounds(frame, elementFor);
         }
-        
+
+        if (Guid.TryParse(elementFor, out Guid guid))
+        {
+            var keyFrame = keyFrames.FirstOrDefault(x => x.KeyFrameGuid == guid);
+
+            if (keyFrame != null)
+            {
+                return (RectD?)GetLayerImageByKeyFrameGuid(keyFrame.KeyFrameGuid).FindTightCommittedBounds();
+            }
+        }
+
         return (RectD?)GetLayerImageAtFrame(frame).FindTightCommittedBounds();
     }
 
@@ -128,8 +138,18 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         {
             return base.RenderPreview(renderOnto, resolution, frame, elementToRenderName);
         }
-        
+
         var img = GetLayerImageAtFrame(frame);
+        
+        if (Guid.TryParse(elementToRenderName, out Guid guid))
+        {
+            var keyFrame = keyFrames.FirstOrDefault(x => x.KeyFrameGuid == guid);
+
+            if (keyFrame != null)
+            {
+                img = GetLayerImageByKeyFrameGuid(keyFrame.KeyFrameGuid);
+            }
+        }
 
         if (img is null)
         {

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

@@ -286,7 +286,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput
         blendPaint.Dispose();
     }
 
-    public virtual RectD? GetPreviewBounds(string elementFor = "", int frame = 0)
+    public virtual RectD? GetPreviewBounds(int frame, string elementFor = "")
     {
         if (elementFor == nameof(EmbeddedMask) && EmbeddedMask != null)
         {

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

@@ -300,7 +300,8 @@ public class DocumentRenderer : IPreviewRenderable
         return chunk;
     }
 
-    public RectD? GetPreviewBounds(string elementNameToRender = "", int frame = 0) => new(0, 0, Document.Size.X, Document.Size.Y); 
+    public RectD? GetPreviewBounds(int frame, string elementNameToRender = "") => 
+        new(0, 0, Document.Size.X, Document.Size.Y); 
 
     public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
     {

+ 2 - 1
src/PixiEditor/Models/Handlers/IKeyFrameHandler.cs

@@ -1,11 +1,12 @@
 using ChunkyImageLib;
 using PixiEditor.DrawingApi.Core;
+using PixiEditor.Models.Rendering;
 
 namespace PixiEditor.Models.Handlers;
 
 internal interface IKeyFrameHandler
 {
-    public Texture? PreviewSurface { get; set; }
+    public PreviewPainter? PreviewPainter { get; set; }
     public int StartFrameBindable { get; }
     public int DurationBindable { get; }
     public bool IsSelected { get; set; }

+ 49 - 0
src/PixiEditor/Models/Rendering/AnimationPreviewRenderer.cs

@@ -0,0 +1,49 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.Models.DocumentModels;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.Rendering;
+
+internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals) : IPreviewRenderable
+{
+    private readonly DocumentInternalParts internals = internals;
+    
+    public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(
+                Guid.Parse(elementToRenderName),
+                out KeyFrame keyFrame))
+        {
+            var nodeId = keyFrame.NodeId;
+            var node = internals.Tracker.Document.NodeGraph.AllNodes.FirstOrDefault(x => x.Id == nodeId);
+            
+            if (node is IPreviewRenderable previewRenderable)
+            {
+                return previewRenderable.GetPreviewBounds(0, elementToRenderName); 
+            }
+        }
+        
+        return null;
+    }
+
+    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    {
+        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(
+                Guid.Parse(elementToRenderName),
+                out KeyFrame keyFrame))
+        {
+            var nodeId = keyFrame.NodeId;
+            var node = internals.Tracker.Document.NodeGraph.AllNodes.FirstOrDefault(x => x.Id == nodeId);
+            
+            if (node is IPreviewRenderable previewRenderable)
+            {
+                return previewRenderable.RenderPreview(renderOn, resolution, frame, elementToRenderName);
+            }
+        }
+        
+        return false;
+    }
+}

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

@@ -3,6 +3,7 @@
 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;
@@ -20,11 +21,14 @@ internal class MemberPreviewUpdater
 {
     private readonly IDocument doc;
     private readonly DocumentInternalParts internals;
+    
+    private AnimationKeyFramePreviewRenderer AnimationKeyFramePreviewRenderer { get; }
 
     public MemberPreviewUpdater(IDocument doc, DocumentInternalParts internals)
     {
         this.doc = doc;
         this.internals = internals;
+        AnimationKeyFramePreviewRenderer = new AnimationKeyFramePreviewRenderer(internals);
     }
 
     public void UpdatePreviews(bool rerenderPreviews, IEnumerable<Guid> membersToUpdate,
@@ -41,15 +45,17 @@ 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(IEnumerable<Guid> members, IEnumerable<Guid> masksToUpdate, IEnumerable<Guid> nodesToUpdate)
+    private void UpdatePreviewPainters(IEnumerable<Guid> members, IEnumerable<Guid> masksToUpdate,
+        IEnumerable<Guid> nodesToUpdate)
     {
         Guid[] memberGuids = members as Guid[] ?? members.ToArray();
         Guid[] maskGuids = masksToUpdate as Guid[] ?? masksToUpdate.ToArray();
         Guid[] nodesGuids = nodesToUpdate as Guid[] ?? nodesToUpdate.ToArray();
-        
+
         RenderWholeCanvasPreview();
-        RenderMainPreviews(memberGuids);
+        RenderLayersPreview(memberGuids);
         RenderMaskPreviews(maskGuids);
+        RenderAnimationPreviews(memberGuids);
         RenderNodePreviews(nodesGuids);
     }
 
@@ -72,7 +78,7 @@ internal class MemberPreviewUpdater
         }
     }
 
-    private void RenderMainPreviews(Guid[] memberGuids)
+    private void RenderLayersPreview(Guid[] memberGuids)
     {
         foreach (var node in doc.NodeGraphHandler.AllNodes)
         {
@@ -99,6 +105,37 @@ internal class MemberPreviewUpdater
         }
     }
 
+    private void RenderAnimationPreviews(Guid[] memberGuids)
+    {
+        foreach (var keyFrame in doc.AnimationHandler.KeyFrames)
+        {
+            if (keyFrame is IKeyFrameGroupHandler groupHandler)
+            {
+                foreach (var childFrame in groupHandler.Children)
+                {
+                    if (!memberGuids.Contains(childFrame.LayerGuid))
+                        continue;
+
+                    RenderFramePreview(childFrame);
+                }
+
+                if (!memberGuids.Contains(groupHandler.LayerGuid))
+                    continue;
+
+                //RenderGroupPreview(groupHandler);
+            }
+        }
+    }
+
+    private void RenderFramePreview(IKeyFrameHandler keyFrame)
+    {
+        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(keyFrame.Id, out KeyFrame foundKeyFrame))
+        {
+            keyFrame.PreviewPainter ??= new PreviewPainter(AnimationKeyFramePreviewRenderer, keyFrame.Id.ToString());
+            keyFrame.PreviewPainter.Repaint();
+        }
+    }
+
     private void RenderGroupPreview(IKeyFrameHandler keyFrame, IStructureMemberHandler memberVM,
         IReadOnlyStructureNode member, [DisallowNull] AffectedArea? affArea, VecI position, float scaling)
     {
@@ -170,11 +207,11 @@ internal class MemberPreviewUpdater
 
     private void RenderAnimationFramePreview(IReadOnlyImageNode node, IKeyFrameHandler keyFrameVM, AffectedArea area)
     {
-        if (keyFrameVM.PreviewSurface is null)
+        /*if (keyFrameVM.PreviewPainter is null)
         {
-            keyFrameVM.PreviewSurface =
+            keyFrameVM.PreviewPainter =
                 new Texture(StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size));
-        }
+        }*/
 
         /*QueueRender(() =>
         {
@@ -239,7 +276,7 @@ internal class MemberPreviewUpdater
         {
             if (node is null)
                 continue;
-            
+
             if (!nodesGuids.Contains(node.Id))
                 continue;
 

+ 4 - 11
src/PixiEditor/Styles/Templates/KeyFrame.axaml

@@ -36,17 +36,10 @@
                                 </ImageBrush.Transform>
                             </ImageBrush>
                         </Border.Background>
-                        <visuals:TextureControl
-                            Texture="{Binding Item.PreviewSurface, RelativeSource={RelativeSource TemplatedParent}}"
-                            Stretch="Uniform" Width="60" Height="60">
-                            <ui:RenderOptionsBindable.BitmapInterpolationMode>
-                                <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
-                                    <Binding Path="Item.PreviewSurface.Size.X"
-                                             RelativeSource="{RelativeSource TemplatedParent}" />
-                                    <Binding RelativeSource="{RelativeSource Mode=Self}" Path="Bounds.Width" />
-                                </MultiBinding>
-                            </ui:RenderOptionsBindable.BitmapInterpolationMode>
-                        </visuals:TextureControl>
+                        <visuals:PreviewPainterControl
+                            PreviewPainter="{Binding Item.PreviewPainter, RelativeSource={RelativeSource TemplatedParent}}"
+                            Width="60" Height="60" RenderOptions.BitmapInterpolationMode="None">
+                            </visuals:PreviewPainterControl>
                     </Border>
                 </Grid>
             </ControlTemplate>

+ 5 - 4
src/PixiEditor/ViewModels/Document/KeyFrameViewModel.cs

@@ -4,12 +4,13 @@ using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Handlers;
+using PixiEditor.Models.Rendering;
 
 namespace PixiEditor.ViewModels.Document;
 
 internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
 {
-    private Texture? previewSurface;
+    private PreviewPainter? previewPainter;
     private int startFrameBindable;
     private int durationBindable;
     private bool isVisibleBindable = true;
@@ -27,10 +28,10 @@ internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
 
     IDocument IKeyFrameHandler.Document => Document;
 
-    public Texture? PreviewSurface
+    public PreviewPainter? PreviewPainter
     {
-        get => previewSurface;
-        set => SetProperty(ref previewSurface, value);
+        get => previewPainter;
+        set => SetProperty(ref previewPainter, value);
     }
 
     public virtual int StartFrameBindable

+ 5 - 10
src/PixiEditor/Views/Layers/FolderControl.axaml

@@ -67,15 +67,9 @@
                             </Border.Background>
                             <visuals:PreviewPainterControl 
                                 PreviewPainter="{Binding Folder.PreviewPainter, ElementName=folderControl}"
-                                FrameToRender="{Binding Path=Folder.Document.AnimationDataViewModel.ActiveFrameBindable, ElementName=folderControl}"
-                                Width="30" Height="30">
-                                <ui:RenderOptionsBindable.BitmapInterpolationMode>
-                                    <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
-                                        <Binding Path="Folder.PreviewPainter.Bounds.Size.X" ElementName="folderControl"/>
-                                        <Binding RelativeSource="{RelativeSource Mode=Self}" Path="Bounds.Width"/>
-                                    </MultiBinding>
-                                </ui:RenderOptionsBindable.BitmapInterpolationMode>
-                            </visuals:PreviewPainterControl>
+                                ClipToBounds="True"
+                                FrameToRender="{Binding Manager.ActiveDocument.AnimationDataViewModel.ActiveFrameBindable, ElementName=folderControl}"
+                                Width="30" Height="30" RenderOptions.BitmapInterpolationMode="None"/>
                         </Border>
                         <Border 
                             Width="32" Height="32" 
@@ -95,7 +89,8 @@
                                 <visuals:PreviewPainterControl 
                                     PreviewPainter="{Binding Folder.MaskPreviewPainter, ElementName=folderControl}" 
                                     Width="30" Height="30"
-                                    FrameToRender="{Binding Path=Folder.Document.AnimationDataViewModel.ActiveFrameBindable, ElementName=folderControl}"
+                                    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" 

+ 2 - 2
src/PixiEditor/Views/Layers/LayerControl.axaml

@@ -82,7 +82,7 @@
                         <visuals:PreviewPainterControl 
                             ClipToBounds="True" 
                             PreviewPainter="{Binding Layer.PreviewPainter, ElementName=uc}"
-                            FrameToRender="{Binding Layer.Document.AnimationHandler.ActiveFrameBindable, ElementName=uc}"
+                            FrameToRender="{Binding Manager.ActiveDocument.AnimationDataViewModel.ActiveFrameBindable, ElementName=uc}"
                             Width="30" Height="30"
                             RenderOptions.BitmapInterpolationMode="None" IsHitTestVisible="False" />
                     </Border>
@@ -110,7 +110,7 @@
                                     PreviewPainter="{Binding Layer.MaskPreviewPainter, ElementName=uc}" 
                                     Width="30" Height="30"
                                     ClipToBounds="True"
-                                    FrameToRender="{Binding Path=Layer.Document.AnimationHandler.ActiveFrameBindable, ElementName=uc}"
+                                    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"

+ 1 - 1
src/PixiEditor/Views/Visuals/PreviewPainterControl.cs

@@ -81,7 +81,7 @@ internal class DrawPreviewOperation : SkiaDrawOperation
 
     public override void Render(ISkiaSharpApiLease lease)
     {
-        RectD? previewBounds = PreviewPainter.PreviewRenderable.GetPreviewBounds(PreviewPainter.ElementToRenderName);
+        RectD? previewBounds = PreviewPainter.PreviewRenderable.GetPreviewBounds(frame, PreviewPainter.ElementToRenderName);
         if (PreviewPainter == null || previewBounds == null)
         {
             return;

+ 4 - 2
src/PixiEditor/Views/Visuals/PreviewPainterImage.cs

@@ -9,7 +9,9 @@ public class PreviewPainterImage : IImage
     public PreviewPainter PreviewPainter { get; set; }
     
     public int FrameToRender { get; set; }
-    public Size Size => new Size(PreviewPainter.PreviewRenderable.GetPreviewBounds()?.Size.X ?? 0, PreviewPainter.PreviewRenderable.GetPreviewBounds()?.Size.Y ?? 0); 
+    public Size Size => new Size(
+        PreviewPainter.PreviewRenderable.GetPreviewBounds(FrameToRender)?.Size.X ?? 0,
+        PreviewPainter.PreviewRenderable.GetPreviewBounds(FrameToRender)?.Size.Y ?? 0); 
     
     public PreviewPainterImage(PreviewPainter previewPainter, int frameToRender)
     {
@@ -19,7 +21,7 @@ public class PreviewPainterImage : IImage
     
     public void Draw(DrawingContext context, Rect sourceRect, Rect destRect)
     {
-        if (PreviewPainter.PreviewRenderable.GetPreviewBounds() == null) return;
+        if (PreviewPainter.PreviewRenderable.GetPreviewBounds(FrameToRender) == null) return;
         
         using DrawPreviewOperation drawPreviewOperation = new DrawPreviewOperation(destRect, PreviewPainter, FrameToRender); 
         context.Custom(drawPreviewOperation);