Browse Source

Hackfix disposed preview render

flabbet 1 year ago
parent
commit
16e09b53d8

+ 2 - 0
src/ChunkyImageLib/Surface.cs

@@ -17,6 +17,8 @@ public class Surface : IDisposable
     public DrawingSurface DrawingSurface { get; }
     public DrawingSurface DrawingSurface { get; }
     public int BytesPerPixel { get; }
     public int BytesPerPixel { get; }
     public VecI Size { get; }
     public VecI Size { get; }
+    
+    public bool IsDisposed => disposed;
 
 
     public event SurfaceChangedEventHandler? Changed;
     public event SurfaceChangedEventHandler? Changed;
 
 

+ 4 - 1
src/PixiEditor.AvaloniaUI/Views/Dialogs/ExportFilePopup.axaml

@@ -4,6 +4,7 @@
                          xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
                          xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
                          xmlns:ui1="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
                          xmlns:ui1="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
                          xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
                          xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
+                         xmlns:indicators="clr-namespace:PixiEditor.AvaloniaUI.Views.Indicators"
                          CanResize="False"
                          CanResize="False"
                          CanMinimize="False"
                          CanMinimize="False"
                          SizeToContent="WidthAndHeight"
                          SizeToContent="WidthAndHeight"
@@ -65,7 +66,9 @@
                         </Grid.RowDefinitions>
                         </Grid.RowDefinitions>
                         
                         
                         <TextBlock Text="Export Preview"/>
                         <TextBlock Text="Export Preview"/>
-                        <Border Grid.Row="1" BorderThickness="1" Height="200" Width="150">
+                        <indicators:LoadingIndicator Grid.Row="1" IsVisible="{Binding IsPreviewGenerating, ElementName=saveFilePopup}"
+                                          Margin="0, 10, 0, 0"/>
+                        <Border Grid.Row="1" BorderThickness="1" Height="200" Width="150" IsVisible="{Binding !IsPreviewGenerating, ElementName=saveFilePopup}">
                             <Border RenderOptions.BitmapInterpolationMode="None">
                             <Border RenderOptions.BitmapInterpolationMode="None">
                                 <visuals:SurfaceControl x:Name="surfaceControl"
                                 <visuals:SurfaceControl x:Name="surfaceControl"
                                                         Surface="{Binding ExportPreview, ElementName=saveFilePopup}"
                                                         Surface="{Binding ExportPreview, ElementName=saveFilePopup}"

+ 21 - 1
src/PixiEditor.AvaloniaUI/Views/Dialogs/ExportFilePopup.axaml.cs

@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System.ComponentModel;
+using System.Threading.Tasks;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Platform.Storage;
 using Avalonia.Platform.Storage;
@@ -105,12 +106,27 @@ internal partial class ExportFilePopup : PixiEditorPopup
 
 
     public bool IsVideoExport => SelectedExportIndex == 1;
     public bool IsVideoExport => SelectedExportIndex == 1;
     public string SizeHint => new LocalizedString("EXPORT_SIZE_HINT", GetBestPercentage());
     public string SizeHint => new LocalizedString("EXPORT_SIZE_HINT", GetBestPercentage());
+    public bool IsPreviewGenerating
+    {
+        get
+        {
+            return isPreviewGenerating;
+        }
+        private set
+        {
+            isPreviewGenerating = value;
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsPreviewGenerating)));
+        }
+    }
 
 
     private DocumentViewModel document;
     private DocumentViewModel document;
     private Image[] videoPreviewFrames = [];
     private Image[] videoPreviewFrames = [];
     private DispatcherTimer videoPreviewTimer = new DispatcherTimer();
     private DispatcherTimer videoPreviewTimer = new DispatcherTimer();
     private int activeFrame = 0;
     private int activeFrame = 0;
     private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
     private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
+    private bool isPreviewGenerating = false;
+    
+    public event PropertyChangedEventHandler? PropertyChanged;
 
 
     static ExportFilePopup()
     static ExportFilePopup()
     {
     {
@@ -185,6 +201,8 @@ internal partial class ExportFilePopup : PixiEditorPopup
         {
         {
             return;
             return;
         }
         }
+        
+        IsPreviewGenerating = true;
 
 
         videoPreviewTimer.Stop();
         videoPreviewTimer.Stop();
         if (IsVideoExport)
         if (IsVideoExport)
@@ -200,6 +218,7 @@ internal partial class ExportFilePopup : PixiEditorPopup
                 VecI previewSize = CalculatePreviewSize(rendered.AsT1.Size);
                 VecI previewSize = CalculatePreviewSize(rendered.AsT1.Size);
                 ExportPreview = rendered.AsT1.ResizeNearestNeighbor(previewSize);
                 ExportPreview = rendered.AsT1.ResizeNearestNeighbor(previewSize);
                 rendered.AsT1.Dispose();
                 rendered.AsT1.Dispose();
+                IsPreviewGenerating = false;
             }
             }
         }
         }
     }
     }
@@ -250,6 +269,7 @@ internal partial class ExportFilePopup : PixiEditorPopup
                 }
                 }
             });
             });
 
 
+            IsPreviewGenerating = false;
             videoPreviewTimer.Start();
             videoPreviewTimer.Start();
         });
         });
     }
     }

+ 15 - 8
src/PixiEditor.AvaloniaUI/Views/Visuals/SurfaceControl.cs

@@ -21,8 +21,9 @@ internal class SurfaceControl : Control
     public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<SurfaceControl, Stretch>(
     public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<SurfaceControl, Stretch>(
         nameof(Stretch), Stretch.Uniform);
         nameof(Stretch), Stretch.Uniform);
 
 
-    public static readonly StyledProperty<IBrush> BackgroundProperty = AvaloniaProperty.Register<SurfaceControl, IBrush>(
-        nameof(Background));
+    public static readonly StyledProperty<IBrush> BackgroundProperty =
+        AvaloniaProperty.Register<SurfaceControl, IBrush>(
+            nameof(Background));
 
 
     public IBrush Background
     public IBrush Background
     {
     {
@@ -46,7 +47,6 @@ internal class SurfaceControl : Control
 
 
     static SurfaceControl()
     static SurfaceControl()
     {
     {
-        AffectsRender<SurfaceControl>(StretchProperty, SurfaceProperty);
         AffectsMeasure<SurfaceControl>(StretchProperty, SurfaceProperty);
         AffectsMeasure<SurfaceControl>(StretchProperty, SurfaceProperty);
         BoundsProperty.Changed.AddClassHandler<SurfaceControl>(BoundsChanged);
         BoundsProperty.Changed.AddClassHandler<SurfaceControl>(BoundsChanged);
         SurfaceProperty.Changed.AddClassHandler<SurfaceControl>(Rerender);
         SurfaceProperty.Changed.AddClassHandler<SurfaceControl>(Rerender);
@@ -93,14 +93,14 @@ internal class SurfaceControl : Control
 
 
     public override void Render(DrawingContext context)
     public override void Render(DrawingContext context)
     {
     {
-        if (Surface == null)
+        if (Surface == null || Surface.IsDisposed)
         {
         {
             return;
             return;
         }
         }
-        
+
         if (Background != null)
         if (Background != null)
         {
         {
-            context.FillRectangle(Background, new Rect(0,0, Bounds.Width, Bounds.Height));
+            context.FillRectangle(Background, new Rect(0, 0, Bounds.Width, Bounds.Height));
         }
         }
 
 
         var bounds = new Rect(Bounds.Size);
         var bounds = new Rect(Bounds.Size);
@@ -135,6 +135,7 @@ internal class SurfaceControl : Control
         {
         {
             oldSurface.Changed -= sender.SurfaceChanged;
             oldSurface.Changed -= sender.SurfaceChanged;
         }
         }
+
         if (e.NewValue is Surface newSurface)
         if (e.NewValue is Surface newSurface)
         {
         {
             newSurface.Changed += sender.SurfaceChanged;
             newSurface.Changed += sender.SurfaceChanged;
@@ -153,7 +154,8 @@ internal class DrawSurfaceOperation : SkiaDrawOperation
 
 
     private SKPaint _paint = new SKPaint();
     private SKPaint _paint = new SKPaint();
 
 
-    public DrawSurfaceOperation(Rect dirtyBounds, Surface surface, Stretch stretch, double opacity = 1) : base(dirtyBounds)
+    public DrawSurfaceOperation(Rect dirtyBounds, Surface surface, Stretch stretch, double opacity = 1) :
+        base(dirtyBounds)
     {
     {
         Surface = surface;
         Surface = surface;
         Stretch = stretch;
         Stretch = stretch;
@@ -163,7 +165,12 @@ internal class DrawSurfaceOperation : SkiaDrawOperation
     public override void Render(ISkiaSharpApiLease lease)
     public override void Render(ISkiaSharpApiLease lease)
     {
     {
         SKCanvas canvas = lease.SkCanvas;
         SKCanvas canvas = lease.SkCanvas;
-        if (Surface == null)
+        // TODO: When changing frames, for some reason disposed surface is trying to render
+        // I couldn't trace what is causing the rerender and why disposed surface is passed,
+        // preview updater is disposing the surface and creating a new one, but it should also
+        // update the control to use the new surface, debugging at SurfaceControl Render() never returns disposed surface.
+        // Probably some kind of race condition between dispose and render queue.
+        if (Surface == null || Surface.IsDisposed) 
         {
         {
             return;
             return;
         }
         }