Browse Source

Image loading improvements, side by side incorrect image rendering fix, Collect unmanaged memory, prevent memory leaks

Ruben 1 year ago
parent
commit
48282878c1

+ 5 - 4
src/PicView.Avalonia/Keybindings/MainKeyboardShortcuts.cs

@@ -1,7 +1,5 @@
-using Avalonia.Input;
-using System.Diagnostics;
-using Avalonia.Controls.ApplicationLifetimes;
-using PicView.Avalonia.Navigation;
+using System.Diagnostics;
+using Avalonia.Input;
 using PicView.Avalonia.UI;
 
 namespace PicView.Avalonia.Keybindings;
@@ -51,6 +49,9 @@ public static class MainKeyboardShortcuts
 
                 // Show Avalonia DevTools in DEBUG mode
                 return;
+            case Key.F8:
+                await FunctionsHelper.Invalidate();
+                return;
             case Key.F9:
                 await FunctionsHelper.ShowStartUpMenu();
                 return;

+ 8 - 4
src/PicView.Avalonia/Navigation/ImageIterator.cs

@@ -383,7 +383,7 @@ public sealed class ImageIterator : IDisposable
 
     public PreLoader.PreLoadValue? GetNextPreLoadValue()
     {
-        var nextIndex = GetIteration(CurrentIndex, NavigateTo.Next);
+        var nextIndex = GetIteration(CurrentIndex, IsReversed ? NavigateTo.Previous : NavigateTo.Next);
         return PreLoader.Get(nextIndex, ImagePaths);
     }
 
@@ -545,7 +545,8 @@ public sealed class ImageIterator : IDisposable
 
                 if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide)
                 {
-                    var nextPreloadValue = await GetNextPreLoadValueAsync().ConfigureAwait(false);
+                    var nextIndex = GetIteration(index, IsReversed ? NavigateTo.Previous : NavigateTo.Next);
+                    var nextPreloadValue = await PreLoader.GetAsync(nextIndex, ImagePaths);
                     lock (_lock)
                     {
                         if (CurrentIndex != index)
@@ -555,7 +556,10 @@ public sealed class ImageIterator : IDisposable
                         }
                     }
 
-                    _vm.SecondaryImageSource = nextPreloadValue.ImageModel.Image;
+                    if (nextPreloadValue is not null)
+                    {
+                        _vm.SecondaryImageSource = nextPreloadValue.ImageModel?.Image;
+                    }
                     await UpdateImage.UpdateSource(_vm, index, ImagePaths, IsReversed, preloadValue, nextPreloadValue)
                         .ConfigureAwait(false);
                 }
@@ -590,7 +594,7 @@ public sealed class ImageIterator : IDisposable
             catch (Exception e)
             {
 #if DEBUG
-                Console.WriteLine($"{nameof(IterateToIndex)} exception: \n{e.Message}");
+                Console.WriteLine($"{nameof(IterateToIndex)} exception: \n{e.Message}\n{e.StackTrace}");
                 await TooltipHelper.ShowTooltipMessageAsync(e.Message);
 #endif
             }

+ 8 - 0
src/PicView.Avalonia/Navigation/Preloader.cs

@@ -366,6 +366,8 @@ public sealed class PreLoader : IDisposable
 
         void RemoveLoop()
         {
+            // Iterate through the _preLoadList and remove items outside the preload range
+            
             if (list.Count <= MaxCount + NegativeIterations || _preLoadList.Count <= MaxCount)
             {
                 return;
@@ -389,6 +391,12 @@ public sealed class PreLoader : IDisposable
                     Remove(removeIndex, list);
                 }
             }
+
+            if (deleteCount > 1)
+            {
+                // Collect unmanaged memory, prevent memory leak
+                GC.Collect(0, GCCollectionMode.Optimized, false);
+            }
         }
     }
 

+ 16 - 2
src/PicView.Avalonia/Navigation/UpdateImage.cs

@@ -22,7 +22,7 @@ public static class UpdateImage
         PreLoader.PreLoadValue? nextPreloadValue = null)
     {
         preLoadValue ??= await vm.ImageIterator.GetPreLoadValueAsync(index).ConfigureAwait(false);
-        if (preLoadValue.ImageModel?.Image is null)
+        if (preLoadValue.ImageModel?.Image is null && index == vm.ImageIterator.CurrentIndex)
         {
             var fileInfo = preLoadValue.ImageModel?.FileInfo ?? new FileInfo(imagePaths[index]);
             preLoadValue.ImageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
@@ -31,7 +31,7 @@ public static class UpdateImage
         if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide)
         {
             nextPreloadValue ??= await vm.ImageIterator.GetNextPreLoadValueAsync().ConfigureAwait(false);
-            if (nextPreloadValue.ImageModel?.Image is null)
+            if (nextPreloadValue.ImageModel?.Image is null && index == vm.ImageIterator.CurrentIndex)
             {
                 var fileInfo = nextPreloadValue.ImageModel?.FileInfo ?? new FileInfo(
                     imagePaths[
@@ -43,6 +43,10 @@ public static class UpdateImage
 
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
+            if (index != vm.ImageIterator.CurrentIndex)
+            {
+                return;
+            }
             vm.ImageViewer.SetTransform(preLoadValue.ImageModel.EXIFOrientation);
             if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide)
             {
@@ -87,6 +91,16 @@ public static class UpdateImage
         vm.PixelWidth = preLoadValue.ImageModel.PixelWidth;
         vm.PixelHeight = preLoadValue.ImageModel.PixelHeight;
         ExifHandling.UpdateExifValues(preLoadValue.ImageModel, vm);
+        
+        if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide)
+        {
+            // Fixes incorrect rendering in the side by side view
+            // TODO: Improve and fix side by side and remove this hack 
+            Dispatcher.UIThread.Post(() =>
+            {
+                vm.ImageViewer?.MainImage?.InvalidateVisual();
+            });
+        }
     }
 
     /// <summary>

+ 8 - 0
src/PicView.Avalonia/UI/FunctionsHelper.cs

@@ -1103,4 +1103,12 @@ public static class FunctionsHelper
     #endregion
     
     #endregion
+
+    #if DEBUG
+    public static async Task Invalidate()
+    {
+        Vm?.ImageViewer?.MainImage?.InvalidateVisual();
+        //Vm?.ImageViewer?.InvalidateVisual();
+    }
+    #endif
 }

+ 32 - 28
src/PicView.Avalonia/UI/TooltipHelper.cs

@@ -22,47 +22,49 @@ public static class TooltipHelper
     {
         try
         {
-            var toolTip = UIHelper.GetToolTipMessage;
-            var startAnimation = AnimationsHelper.OpacityAnimation(0, 1, .6);
-            var endAnimation = AnimationsHelper.OpacityAnimation(1, 0, .5);
-        
-            if (_isRunning)
+            await Dispatcher.UIThread.InvokeAsync(async () =>
             {
-                await Dispatcher.UIThread.InvokeAsync(() =>
+                var toolTip = UIHelper.GetToolTipMessage;
+                var startAnimation = AnimationsHelper.OpacityAnimation(0, 1, .6);
+                var endAnimation = AnimationsHelper.OpacityAnimation(1, 0, .5);
+
+                if (_isRunning)
                 {
                     UIHelper.GetToolTipMessage.Opacity = 1;
                     toolTip.ToolTipMessageText.Text = message.ToString();
                     toolTip.Margin = center ? new Thickness(0) : new Thickness(0, 0, 0, 15);
                     toolTip.VerticalAlignment = center ? VerticalAlignment.Center : VerticalAlignment.Bottom;
+
+                    await Task.Delay(interval);
+                    await endAnimation.RunAsync(UIHelper.GetToolTipMessage);
+                    _isRunning = false;
+                    return;
+                }
+
+                await Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    _isRunning = true;
+                    UIHelper.GetToolTipMessage.IsVisible = true;
+                    UIHelper.GetToolTipMessage.ToolTipMessageText.Text = message.ToString();
+                    UIHelper.GetToolTipMessage.Margin = center ? new Thickness(0) : new Thickness(0, 0, 0, 15);
+                    UIHelper.GetToolTipMessage.VerticalAlignment =
+                        center ? VerticalAlignment.Center : VerticalAlignment.Bottom;
+                    UIHelper.GetToolTipMessage.Opacity = 0;
                 });
+                await startAnimation.RunAsync(UIHelper.GetToolTipMessage);
                 await Task.Delay(interval);
                 await endAnimation.RunAsync(UIHelper.GetToolTipMessage);
                 _isRunning = false;
-                return;
-            }
-
-            await Dispatcher.UIThread.InvokeAsync(() =>
-            {
-                _isRunning = true;
-                UIHelper.GetToolTipMessage.IsVisible = true;
-                UIHelper.GetToolTipMessage.ToolTipMessageText.Text = message.ToString();
-                UIHelper.GetToolTipMessage.Margin = center ? new Thickness(0) : new Thickness(0, 0, 0, 15);
-                UIHelper.GetToolTipMessage.VerticalAlignment = center ? VerticalAlignment.Center : VerticalAlignment.Bottom;
-                UIHelper.GetToolTipMessage.Opacity = 0;
             });
-            await startAnimation.RunAsync(UIHelper.GetToolTipMessage);
-            await Task.Delay(interval);
-            await endAnimation.RunAsync(UIHelper.GetToolTipMessage);
-            _isRunning = false;
         }
         catch (Exception e)
         {
 #if DEBUG
-            Console.WriteLine(e);
+            Console.WriteLine(e.Message);
 #endif
         }
     }
-    
+
     /// <summary>
     /// Shows the tooltip message on the UI.
     /// </summary>
@@ -85,11 +87,12 @@ public static class TooltipHelper
                     }
                 });
             }
+
             _isRunning = false;
             timer.Stop();
         };
         timer.Start();
-        
+
         try
         {
             Dispatcher.UIThread.Invoke(() =>
@@ -99,6 +102,7 @@ public static class TooltipHelper
                 {
                     return;
                 }
+
                 _isRunning = true;
                 toolTip.IsVisible = true;
                 toolTip.ToolTipMessageText.Text = message.ToString();
@@ -107,10 +111,10 @@ public static class TooltipHelper
                 toolTip.Opacity = 1;
             });
         }
-        catch (Exception e)
+        catch (Exception exception)
         {
 #if DEBUG
-            Console.WriteLine(e);
+            Console.WriteLine($"{nameof(ShowTooltipMessage)} exception, \n{exception.Message}");
 #endif
         }
     }
@@ -124,7 +128,7 @@ public static class TooltipHelper
     {
         ShowTooltipMessage(message, center, TimeSpan.FromSeconds(2));
     }
-    
+
     /// <summary>
     /// Shows the tooltip message on the UI with a default duration of 2 seconds.
     /// </summary>
@@ -134,4 +138,4 @@ public static class TooltipHelper
     {
         await ShowTooltipMessageAsync(message, center, TimeSpan.FromSeconds(2));
     }
-}
+}