Browse Source

Fixed stuff in rendering

flabbet 11 months ago
parent
commit
d76823a221

+ 12 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs

@@ -1,4 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Context;
@@ -10,6 +11,7 @@ public class FuncContext
     public VecD Position { get; private set; }
     public VecI Size { get; private set; }
     public bool HasContext { get; private set; }
+    public RenderingContext RenderingContext { get; set; }
 
     public void ThrowOnMissingContext()
     {
@@ -19,6 +21,16 @@ public class FuncContext
         }
     }
 
+    public FuncContext()
+    {
+        
+    }
+    
+    public FuncContext(RenderingContext renderingContext)
+    {
+        RenderingContext = renderingContext;
+    }
+
     public void UpdateContext(VecD position, VecI size)
     {
         Position = position;

+ 18 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs

@@ -1,4 +1,5 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using System.Collections.Concurrent;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
@@ -13,8 +14,6 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 [PairNode(typeof(ModifyImageRightNode), "ModifyImageZone", true)]
 public class ModifyImageLeftNode : Node
 {
-    private Pixmap? pixmap;
-
     public InputProperty<Surface?> Image { get; }
     
     public FuncOutputProperty<VecD> Coordinate { get; }
@@ -22,6 +21,8 @@ public class ModifyImageLeftNode : Node
     public FuncOutputProperty<Color> Color { get; }
 
     public override string DisplayName { get; set; } = "MODIFY_IMAGE_LEFT_NODE";
+    
+    private ConcurrentDictionary<RenderingContext, Pixmap> pixmapCache = new();
 
     public ModifyImageLeftNode()
     {
@@ -33,19 +34,29 @@ public class ModifyImageLeftNode : Node
     private Color GetColor(FuncContext context)
     {
         context.ThrowOnMissingContext();
+
+        var targetPixmap = pixmapCache[context.RenderingContext];
         
-        if (pixmap == null)
+        if (targetPixmap == null)
             return new Color();
         
         var x = context.Position.X * context.Size.X;
         var y = context.Position.Y * context.Size.Y;
         
-        return pixmap.GetPixelColor((int)x, (int)y);
+        return targetPixmap.GetPixelColor((int)x, (int)y);
     }
 
-    internal void PreparePixmap()
+    internal void PreparePixmap(RenderingContext forContext)
     {
-        pixmap = Image.Value?.PeekPixels();
+        pixmapCache[forContext] = Image.Value?.DrawingSurface.Snapshot().PeekPixels();
+    }
+    
+    internal void DisposePixmap(RenderingContext forContext)
+    {
+        if (pixmapCache.TryRemove(forContext, out var targetPixmap))
+        {
+            targetPixmap?.Dispose();
+        }
     }
 
     protected override Surface? OnExecute(RenderingContext context)
@@ -53,6 +64,5 @@ public class ModifyImageLeftNode : Node
         return Image.Value;
     }
 
-
     public override Node CreateCopy() => new ModifyImageLeftNode();
 }

+ 10 - 12
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -51,35 +51,33 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
         {
             return null;
         }
-
-        startNode.PreparePixmap();
-
+        
         var width = size.X;
         var height = size.Y;
+        
+        surface = new Surface(size);
 
-        if (surface == null || surface.Size != size)
-        {
-            surface?.Dispose();
-            surface = new Surface(size);
-        }
-
+        startNode.PreparePixmap(renderingContext);
+        
         using Pixmap targetPixmap = surface.PeekPixels();
 
-        ModifyImageInParallel(targetPixmap, width, height);
+        ModifyImageInParallel(renderingContext, targetPixmap, width, height);
+        
+        startNode.DisposePixmap(renderingContext);
 
         Output.Value = surface;
 
         return Output.Value;
     }
 
-    private unsafe void ModifyImageInParallel(Pixmap targetPixmap, int width, int height)
+    private unsafe void ModifyImageInParallel(RenderingContext renderingContext, Pixmap targetPixmap, int width, int height)
     {
         int threads = Environment.ProcessorCount;
         int chunkHeight = height / threads;
 
         Parallel.For(0, threads, i =>
         {
-            FuncContext context = new();
+            FuncContext context = new(renderingContext);
             
             int startY = i * chunkHeight;
             int endY = (i + 1) * chunkHeight;

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

@@ -60,6 +60,8 @@ public class DocumentRenderer
             }
 
             using var chunkSnapshot = evaluated.DrawingSurface.Snapshot((RectI)sourceRect);
+            
+            if(context.IsDisposed) return new EmptyChunk();
 
             chunk.Surface.DrawingSurface.Canvas.DrawImage(chunkSnapshot, 0, 0, context.ReplacingPaintWithOpacity);
 

+ 8 - 0
src/PixiEditor.ChangeableDocument/Rendering/RenderingContext.cs

@@ -20,6 +20,8 @@ public class RenderingContext : IDisposable
     public ChunkResolution ChunkResolution { get; }
     public VecI DocumentSize { get; set; }
 
+    public bool IsDisposed { get; private set; }
+    
     public RenderingContext(KeyFrameTime frameTime, VecI chunkToUpdate, ChunkResolution chunkResolution, VecI docSize)
     {
         FrameTime = frameTime;
@@ -55,6 +57,12 @@ public class RenderingContext : IDisposable
 
     public void Dispose()
     {
+        if (IsDisposed)
+        {
+            return;
+        }
+        
+        IsDisposed = true;
         BlendModePaint.Dispose();
         BlendModeOpacityPaint.Dispose();
         ReplacingPaintWithOpacity.Dispose();

+ 1 - 1
src/PixiEditor.DrawingApi.Skia/Implementations/SKObjectImplementation.cs

@@ -17,7 +17,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         
         public T this[IntPtr objPtr]
         {
-            get => ManagedInstances[objPtr];
+            get => ManagedInstances.TryGetValue(objPtr, out var instance) ? instance : throw new ObjectDisposedException(nameof(objPtr));
             set => ManagedInstances[objPtr] = value;
         }
     }

+ 16 - 2
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaCanvasImplementation.cs

@@ -57,8 +57,22 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
 
         public void DrawImage(IntPtr objPtr, Image image, int x, int y, Paint paint)
         {
-            var canvas = ManagedInstances[objPtr];
-            canvas.DrawImage(_imageImpl.ManagedInstances[image.ObjectPointer], x, y, _paintImpl.ManagedInstances[paint.ObjectPointer]);
+            if(!ManagedInstances.TryGetValue(objPtr, out var canvas))
+            {
+                throw new ObjectDisposedException(nameof(canvas));
+            }
+            
+            if (!_paintImpl.ManagedInstances.TryGetValue(paint.ObjectPointer, out var skPaint))
+            {
+                throw new ObjectDisposedException(nameof(paint));
+            }
+            
+            if(!_imageImpl.ManagedInstances.TryGetValue(image.ObjectPointer, out var img))
+            {
+                throw new ObjectDisposedException(nameof(image));
+            }
+            
+            canvas.DrawImage(img, x, y, skPaint);
         }
 
         public int Save(IntPtr objPtr)

+ 2 - 2
src/PixiEditor.UI.Common/Controls/ProgressBar.axaml

@@ -71,14 +71,14 @@
             <Setter Property="MinWidth" Value="200" />
             <Setter Property="MinHeight" Value="16" />
         </Style>
-        <Style Selector="^ /template/ Border#PART_Indicator">
+        <!--<Style Selector="^ /template/ Border#PART_Indicator">
             <Setter Property="Transitions">
                 <Transitions>
                     <DoubleTransition Duration="0:0:0.3" Property="Width" />
                     <DoubleTransition Duration="0:0:0.3" Property="Height" />
                 </Transitions>
             </Setter>
-        </Style>
+        </Style>-->
         <Style Selector="^:vertical">
             <Setter Property="MinWidth" Value="16" />
             <Setter Property="MinHeight" Value="200" />

+ 4 - 3
src/PixiEditor/Models/Files/ImageFileType.cs

@@ -74,7 +74,8 @@ internal abstract class ImageFileType : IoFileType
         
         job?.Report(0, new LocalizedString("RENDERING_FRAME", 0, document.AnimationDataViewModel.FramesCount));
 
-        document.RenderFramesProgressive((frame, index) =>
+        document.RenderFramesProgressive(
+            (frame, index) =>
         {
             job?.CancellationTokenSource.Token.ThrowIfCancellationRequested();
             
@@ -84,13 +85,13 @@ internal abstract class ImageFileType : IoFileType
             Surface target = frame;
             if (config.ExportSize != frame.Size)
             {
-               target =
+                target =
                     frame.ResizeNearestNeighbor(new VecI(config.ExportSize.X, config.ExportSize.Y));
             }
             
             surface!.DrawingSurface.Canvas.DrawSurface(target.DrawingSurface, x * config.ExportSize.X, y * config.ExportSize.Y);
             target.Dispose();
-        });
+        }, job?.CancellationTokenSource.Token ?? CancellationToken.None);
 
         return surface;
     }

+ 17 - 3
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -827,18 +827,26 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         }
     }
 
-    public Image[] RenderFrames(Func<Surface, Surface> processFrameAction = null)
+    public Image[] RenderFrames(Func<Surface, Surface> processFrameAction = null, CancellationToken token = default)
     {
         if (AnimationDataViewModel.KeyFrames.Count == 0)
             return [];
+        
+        if(token.IsCancellationRequested)
+            return [];
 
         int firstFrame = AnimationDataViewModel.FirstFrame;
         int framesCount = AnimationDataViewModel.FramesCount;
         int lastFrame = firstFrame + framesCount;
 
         Image[] images = new Image[framesCount];
+        
+        // TODO: Multi-threading
         for (int i = firstFrame; i < lastFrame; i++)
         {
+            if (token.IsCancellationRequested)
+                return [];
+            
             double normalizedTime = (double)(i - firstFrame) / framesCount;
             KeyFrameTime frameTime = new KeyFrameTime(i, normalizedTime);
             var surface = TryRenderWholeImage(frameTime);
@@ -863,7 +871,8 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
     ///     Render frames progressively and disposes the surface after processing.
     /// </summary>
     /// <param name="processFrameAction">Action to perform on rendered frame</param>
-    public void RenderFramesProgressive(Action<Surface, int> processFrameAction)
+    /// <param name="token"></param>
+    public void RenderFramesProgressive(Action<Surface, int> processFrameAction, CancellationToken token)
     {
         if (AnimationDataViewModel.KeyFrames.Count == 0)
             return;
@@ -876,7 +885,12 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
         for (int i = firstFrame; i < lastFrame; i++)
         {
-            var surface = TryRenderWholeImage(i);
+            if (token.IsCancellationRequested)
+                return;
+            
+            KeyFrameTime frameTime = new KeyFrameTime(i, (double)(i - firstFrame) / framesCount);
+            
+            var surface = TryRenderWholeImage(frameTime);
             if (surface.IsT0)
             {
                 continue;

+ 0 - 3
src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs

@@ -403,9 +403,6 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
                             info.ExportConfig,
                             job);
                     
-                    if(job?.CancellationTokenSource.IsCancellationRequested == true)
-                        return;
-                    
                     if (result.result == SaveResult.Success)
                         IOperatingSystem.Current.OpenFolder(result.finalPath);
                     else

+ 27 - 23
src/PixiEditor/Views/Dialogs/ExportFilePopup.axaml.cs

@@ -193,7 +193,6 @@ internal partial class ExportFilePopup : PixiEditorPopup
         videoPreviewTimer.Tick -= OnVideoPreviewTimerOnTick;
         videoPreviewTimer = null;
         cancellationTokenSource.Cancel();
-        cancellationTokenSource.Dispose();
 
         ExportPreview?.Dispose();
 
@@ -211,6 +210,11 @@ internal partial class ExportFilePopup : PixiEditorPopup
         if (videoPreviewFrames.Length > 0)
         {
             ExportPreview.DrawingSurface.Canvas.Clear();
+            if (videoPreviewFrames[activeFrame] == null)
+            {
+                return;
+            }
+            
             ExportPreview.DrawingSurface.Canvas.DrawImage(videoPreviewFrames[activeFrame], 0, 0);
             activeFrame = (activeFrame + 1) % videoPreviewFrames.Length;
         }
@@ -288,23 +292,28 @@ internal partial class ExportFilePopup : PixiEditorPopup
 
             Task.Run(() =>
             {
-                document.RenderFramesProgressive((frame, index) =>
+                try
                 {
-                    if (cancellationTokenSource.IsCancellationRequested)
-                    {
-                        throw new TaskCanceledException();
-                    }
-                    
-                    int x = index % clampedColumns;
-                    int y = index / clampedColumns;
-                    var resized = frame.ResizeNearestNeighbor(new VecI(singleFrameSize.X, singleFrameSize.Y));
-                    Dispatcher.UIThread.Post(() =>
-                    {
-                        ExportPreview!.DrawingSurface.Canvas.DrawSurface(resized.DrawingSurface, x * singleFrameSize.X,
-                            y * singleFrameSize.Y);
-                        resized.Dispose();
-                    });
-                });
+                    document.RenderFramesProgressive(
+                        (frame, index) =>
+                        {
+                            int x = index % clampedColumns;
+                            int y = index / clampedColumns;
+                            var resized = frame.ResizeNearestNeighbor(new VecI(singleFrameSize.X, singleFrameSize.Y));
+                            Dispatcher.UIThread.Post(() =>
+                            {
+                                if (ExportPreview.IsDisposed) return;
+                                ExportPreview!.DrawingSurface.Canvas.DrawSurface(resized.DrawingSurface,
+                                    x * singleFrameSize.X,
+                                    y * singleFrameSize.Y);
+                                resized.Dispose();
+                            });
+                        }, cancellationTokenSource.Token);
+                }
+                catch 
+                {
+                    // Ignore
+                }
             });
         }
     }
@@ -321,7 +330,7 @@ internal partial class ExportFilePopup : PixiEditorPopup
         Task.Run(
             () =>
             {
-                videoPreviewFrames = document.RenderFrames(ProcessFrame);
+                videoPreviewFrames = document.RenderFrames(ProcessFrame, cancellationTokenSource.Token);
             }, cancellationTokenSource.Token).ContinueWith(_ =>
         {
             Dispatcher.UIThread.Invoke(() =>
@@ -344,11 +353,6 @@ internal partial class ExportFilePopup : PixiEditorPopup
     {
         return Dispatcher.UIThread.Invoke(() =>
         {
-            if (cancellationTokenSource.IsCancellationRequested)
-            {
-                return surface;
-            }
-            
             Surface original = surface;
             if (SaveWidth != surface.Size.X || SaveHeight != surface.Size.Y)
             {