Bladeren bron

File scoped namespace

Ruben 10 maanden geleden
bovenliggende
commit
7383fec8a0

+ 26 - 27
src/PicView.Avalonia/AnimatedImage/Decoding/GifRect.cs

@@ -1,34 +1,33 @@
-namespace PicView.Avalonia.AnimatedImage.Decoding
+namespace PicView.Avalonia.AnimatedImage.Decoding;
+
+public readonly struct GifRect(int x, int y, int width, int height)
 {
-    public readonly struct GifRect(int x, int y, int width, int height)
-    {
-        public int X { get; } = x;
-        public int Y { get; } = y;
-        public int Width { get; } = width;
-        public int Height { get; } = height;
-        public int TotalPixels { get; } = width * height;
+    public int X { get; } = x;
+    public int Y { get; } = y;
+    public int Width { get; } = width;
+    public int Height { get; } = height;
+    public int TotalPixels { get; } = width * height;
 
-        public static bool operator ==(GifRect a, GifRect b)
-        {
-            return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height;
-        }
+    public static bool operator ==(GifRect a, GifRect b)
+    {
+        return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height;
+    }
 
-        public static bool operator !=(GifRect a, GifRect b)
-        {
-            return !(a == b);
-        }
+    public static bool operator !=(GifRect a, GifRect b)
+    {
+        return !(a == b);
+    }
 
-        public override bool Equals(object? obj)
-        {
-            if (obj == null || GetType() != obj.GetType())
-                return false;
+    public override bool Equals(object? obj)
+    {
+        if (obj == null || GetType() != obj.GetType())
+            return false;
 
-            return this == (GifRect)obj;
-        }
+        return this == (GifRect)obj;
+    }
 
-        public override int GetHashCode()
-        {
-            return X.GetHashCode() ^ Y.GetHashCode() | Width.GetHashCode() ^ Height.GetHashCode();
-        }
+    public override int GetHashCode()
+    {
+        return X.GetHashCode() ^ Y.GetHashCode() | Width.GetHashCode() ^ Height.GetHashCode();
     }
-}
+}

+ 28 - 29
src/PicView.Avalonia/CustomControls/CopyButton.cs

@@ -2,44 +2,43 @@
 using Avalonia.Controls;
 using Avalonia.Interactivity;
 
-namespace PicView.Avalonia.CustomControls
+namespace PicView.Avalonia.CustomControls;
+
+public class CopyButton : Button
 {
-    public class CopyButton : Button
-    {
-        public static readonly AvaloniaProperty<string> CopyTextProperty =
-            AvaloniaProperty.Register<CopyButton, string>(nameof(CopyText));
+    public static readonly AvaloniaProperty<string> CopyTextProperty =
+        AvaloniaProperty.Register<CopyButton, string>(nameof(CopyText));
         
-        protected override Type StyleKeyOverride => typeof(Button);
+    protected override Type StyleKeyOverride => typeof(Button);
         
-        public string CopyText
-        {
-            get => (string)GetValue(CopyTextProperty);
-            set => SetValue(CopyTextProperty, value);
-        }
+    public string CopyText
+    {
+        get => (string)GetValue(CopyTextProperty);
+        set => SetValue(CopyTextProperty, value);
+    }
 
-        public CopyButton()
-        {
-            Click += CopyButton_OnClick;
-        }
+    public CopyButton()
+    {
+        Click += CopyButton_OnClick;
+    }
 
-        private async void CopyButton_OnClick(object? sender, RoutedEventArgs args)
+    private async void CopyButton_OnClick(object? sender, RoutedEventArgs args)
+    {
+        if (string.IsNullOrWhiteSpace(CopyText))
         {
-            if (string.IsNullOrWhiteSpace(CopyText))
-            {
-                return;
-            }
-
-            var topLevel = TopLevel.GetTopLevel(this);
-            if (topLevel?.Clipboard != null)
-            {
-                await topLevel.Clipboard.SetTextAsync(CopyText);
-            }
+            return;
         }
 
-        protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+        var topLevel = TopLevel.GetTopLevel(this);
+        if (topLevel?.Clipboard != null)
         {
-            base.OnDetachedFromVisualTree(e);
-            Click -= CopyButton_OnClick;  // Ensure unsubscription from event
+            await topLevel.Clipboard.SetTextAsync(CopyText);
         }
     }
+
+    protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+    {
+        base.OnDetachedFromVisualTree(e);
+        Click -= CopyButton_OnClick;  // Ensure unsubscription from event
+    }
 }

+ 367 - 368
src/PicView.Avalonia/Preloading/Preloader.cs

@@ -5,505 +5,504 @@ using PicView.Avalonia.ImageHandling;
 using PicView.Core.ImageDecoding;
 using static System.GC;
 
-namespace PicView.Avalonia.Preloading
+namespace PicView.Avalonia.Preloading;
+
+/// <summary>
+///     The PreLoader class is responsible for preloading images asynchronously and caching them.
+/// </summary>
+public sealed class PreLoader : IAsyncDisposable
 {
-    /// <summary>
-    ///     The PreLoader class is responsible for preloading images asynchronously and caching them.
-    /// </summary>
-    public sealed class PreLoader : IAsyncDisposable
-    {
 #if DEBUG
 
-        // ReSharper disable once ConvertToConstant.Local
-        private static readonly bool ShowAddRemove = true;
+    // ReSharper disable once ConvertToConstant.Local
+    private static readonly bool ShowAddRemove = true;
 #endif
 
-        private readonly PreLoaderConfig _config = new();
+    private readonly PreLoaderConfig _config = new();
 
-        private readonly ConcurrentDictionary<int, PreLoadValue> _preLoadList = new();
+    private readonly ConcurrentDictionary<int, PreLoadValue> _preLoadList = new();
 
-        private CancellationTokenSource? _cancellationTokenSource;
+    private CancellationTokenSource? _cancellationTokenSource;
 
-        /// <summary>
-        ///     Indicates whether the preloader is currently running.
-        /// </summary>
-        public static bool IsRunning { get; private set; }
+    /// <summary>
+    ///     Indicates whether the preloader is currently running.
+    /// </summary>
+    public static bool IsRunning { get; private set; }
 
-        /// <summary>
-        ///     Gets the maximum count of preloaded images.
-        /// </summary>
-        public static int MaxCount => PreLoaderConfig.MaxCount;
+    /// <summary>
+    ///     Gets the maximum count of preloaded images.
+    /// </summary>
+    public static int MaxCount => PreLoaderConfig.MaxCount;
 
-        /// <summary>
-        ///     Adds an image to the preload list asynchronously.
-        /// </summary>
-        /// <param name="index">The index of the image in the list.</param>
-        /// <param name="list">The list of image paths.</param>
-        /// <param name="imageModel">Optional image model to be added.</param>
-        /// <returns>True if the image was added successfully; otherwise, false.</returns>
-        public async Task<bool> AddAsync(int index, List<string> list, ImageModel? imageModel = null)
+    /// <summary>
+    ///     Adds an image to the preload list asynchronously.
+    /// </summary>
+    /// <param name="index">The index of the image in the list.</param>
+    /// <param name="list">The list of image paths.</param>
+    /// <param name="imageModel">Optional image model to be added.</param>
+    /// <returns>True if the image was added successfully; otherwise, false.</returns>
+    public async Task<bool> AddAsync(int index, List<string> list, ImageModel? imageModel = null)
+    {
+        if (list == null || index < 0 || index >= list.Count)
         {
-            if (list == null || index < 0 || index >= list.Count)
-            {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(AddAsync)} invalid parameters: \n{index}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(AddAsync)} invalid parameters: \n{index}");
 #endif
-                return false;
-            }
+            return false;
+        }
 
 
-            var preLoadValue = new PreLoadValue(imageModel);
-            try
+        var preLoadValue = new PreLoadValue(imageModel);
+        try
+        {
+            if (_preLoadList.TryAdd(index, preLoadValue))
             {
-                if (_preLoadList.TryAdd(index, preLoadValue))
+                preLoadValue.IsLoading = true;
+                if (imageModel == null)
+                {
+                    var fileInfo = new FileInfo(list[index]);
+                    imageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
+                }
+                else if (imageModel.Image == null)
                 {
-                    preLoadValue.IsLoading = true;
-                    if (imageModel == null)
-                    {
-                        var fileInfo = new FileInfo(list[index]);
-                        imageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
-                    }
-                    else if (imageModel.Image == null)
-                    {
-                        imageModel = await GetImageModel.GetImageModelAsync(imageModel.FileInfo).ConfigureAwait(false);
-                    }
-
-                    preLoadValue.ImageModel = imageModel;
-                    imageModel.EXIFOrientation ??= EXIFHelper.GetImageOrientation(imageModel.FileInfo);
+                    imageModel = await GetImageModel.GetImageModelAsync(imageModel.FileInfo).ConfigureAwait(false);
+                }
+
+                preLoadValue.ImageModel = imageModel;
+                imageModel.EXIFOrientation ??= EXIFHelper.GetImageOrientation(imageModel.FileInfo);
 
 #if DEBUG
-                    if (ShowAddRemove)
-                    {
-                        Trace.WriteLine($"{imageModel.FileInfo.Name} added at {index}");
-                    }
-#endif
-                    return true;
+                if (ShowAddRemove)
+                {
+                    Trace.WriteLine($"{imageModel.FileInfo.Name} added at {index}");
                 }
+#endif
+                return true;
             }
-            catch (Exception ex)
-            {
+        }
+        catch (Exception ex)
+        {
 #if DEBUG
-                Trace.WriteLine($"{nameof(AddAsync)} exception: \n{ex}");
+            Trace.WriteLine($"{nameof(AddAsync)} exception: \n{ex}");
 #endif
-                return false;
-            }
-            finally
-            {
-                preLoadValue.IsLoading = false;
-            }
-
             return false;
         }
+        finally
+        {
+            preLoadValue.IsLoading = false;
+        }
+
+        return false;
+    }
 
-        /// <summary>
-        ///     Refreshes the file information for a specific image in the preload list asynchronously.
-        /// </summary>
-        /// <param name="index">The index of the image in the list.</param>
-        /// <param name="list">The list of image paths.</param>
-        /// <returns>True if the image was refreshed successfully; otherwise, false.</returns>
-        public async Task<bool> RefreshFileInfo(int index, List<string> list)
+    /// <summary>
+    ///     Refreshes the file information for a specific image in the preload list asynchronously.
+    /// </summary>
+    /// <param name="index">The index of the image in the list.</param>
+    /// <param name="list">The list of image paths.</param>
+    /// <returns>True if the image was refreshed successfully; otherwise, false.</returns>
+    public async Task<bool> RefreshFileInfo(int index, List<string> list)
+    {
+        if (list == null)
         {
-            if (list == null)
-            {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(RefreshFileInfo)} list null \n{index}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(RefreshFileInfo)} list null \n{index}");
 #endif
-                return false;
-            }
+            return false;
+        }
 
-            if (index < 0 || index >= list.Count)
-            {
+        if (index < 0 || index >= list.Count)
+        {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(RefreshFileInfo)} invalid index: \n{index}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(RefreshFileInfo)} invalid index: \n{index}");
 #endif
-                return false;
-            }
-
-            var removed = _preLoadList.TryRemove(index, out var preLoadValue);
-            if (preLoadValue is null)
-            {
-                return removed;
-            }
-
-            if (preLoadValue.ImageModel != null)
-            {
-                preLoadValue.ImageModel.FileInfo = null;
-            }
+            return false;
+        }
 
-            await AddAsync(index, list, preLoadValue.ImageModel).ConfigureAwait(false);
+        var removed = _preLoadList.TryRemove(index, out var preLoadValue);
+        if (preLoadValue is null)
+        {
             return removed;
         }
 
-        /// <summary>
-        ///     Refreshes the file information for all images in the preload list.
-        /// </summary>
-        /// <param name="list">The list of image paths.</param>
-        /// <returns>True if any image information was successfully updated; otherwise, false.</returns>
-        /// <remarks>
-        ///     This method iterates over the preload list, updates the file information for each image,
-        ///     and attempts to remove and re-add them in the preload list using their updated file paths.
-        ///     If an exception occurs, it is caught and logged in debug mode.
-        /// </remarks>
-        public bool RefreshAllFileInfo(List<string> list)
-        {
-            if (list == null) return false;
-
-            try
-            {
-                foreach (var item in _preLoadList)
-                {
-                    // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
-                    if (item.Value?.ImageModel == null) continue;
+        if (preLoadValue.ImageModel != null)
+        {
+            preLoadValue.ImageModel.FileInfo = null;
+        }
 
-                    item.Value.ImageModel.FileInfo = new FileInfo(list[item.Key]);
-                    if (!_preLoadList.TryRemove(item.Key, out var newItem)) continue;
+        await AddAsync(index, list, preLoadValue.ImageModel).ConfigureAwait(false);
+        return removed;
+    }
 
-                    _preLoadList.TryAdd(list.IndexOf(newItem.ImageModel.FileInfo.FullName), newItem);
-                }
+    /// <summary>
+    ///     Refreshes the file information for all images in the preload list.
+    /// </summary>
+    /// <param name="list">The list of image paths.</param>
+    /// <returns>True if any image information was successfully updated; otherwise, false.</returns>
+    /// <remarks>
+    ///     This method iterates over the preload list, updates the file information for each image,
+    ///     and attempts to remove and re-add them in the preload list using their updated file paths.
+    ///     If an exception occurs, it is caught and logged in debug mode.
+    /// </remarks>
+    public bool RefreshAllFileInfo(List<string> list)
+    {
+        if (list == null) return false;
 
-                return true;
-            }
-            catch (Exception e)
+        try
+        {
+            foreach (var item in _preLoadList)
             {
+                // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
+                if (item.Value?.ImageModel == null) continue;
+
+                item.Value.ImageModel.FileInfo = new FileInfo(list[item.Key]);
+                if (!_preLoadList.TryRemove(item.Key, out var newItem)) continue;
+
+                _preLoadList.TryAdd(list.IndexOf(newItem.ImageModel.FileInfo.FullName), newItem);
+            }
+
+            return true;
+        }
+        catch (Exception e)
+        {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(RefreshAllFileInfo)} exception: \n{e.Message}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(RefreshAllFileInfo)} exception: \n{e.Message}");
 #endif
-                return false;
-            }
+            return false;
         }
+    }
 
 
-        /// <summary>
-        ///     Clears all preloaded images from the cache.
-        /// </summary>
-        public void Clear()
+    /// <summary>
+    ///     Clears all preloaded images from the cache.
+    /// </summary>
+    public void Clear()
+    {
+        _cancellationTokenSource?.Cancel();
+        foreach (var item in _preLoadList.Values)
         {
-            _cancellationTokenSource?.Cancel();
-            foreach (var item in _preLoadList.Values)
+            if (item.ImageModel?.Image is Bitmap img)
             {
-                if (item.ImageModel?.Image is Bitmap img)
-                {
-                    img.Dispose();
-                }
+                img.Dispose();
             }
-
-            _preLoadList.Clear();
         }
 
-        /// <summary>
-        ///     Clears all preloaded images from the cache asynchronously.
-        /// </summary>
-        /// <remarks>
-        ///     <para>
-        ///         This method is used to clear the preloaded images when the image list changes.
-        ///     </para>
-        ///     <para>
-        ///         It is an asynchronous version of the <see cref="Clear" /> method.
-        ///     </para>
-        /// </remarks>
-        public async Task ClearAsync()
-        {
-            await _cancellationTokenSource?.CancelAsync();
-            Clear();
-        }
+        _preLoadList.Clear();
+    }
 
-        /// <summary>
-        ///     Gets the preloaded value for a specific key.
-        /// </summary>
-        /// <param name="key">The key of the preloaded value.</param>
-        /// <param name="list">The list of image paths.</param>
-        /// <returns>The preloaded value if it exists; otherwise, null.</returns>
-        public PreLoadValue? Get(int key, List<string> list)
+    /// <summary>
+    ///     Clears all preloaded images from the cache asynchronously.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This method is used to clear the preloaded images when the image list changes.
+    ///     </para>
+    ///     <para>
+    ///         It is an asynchronous version of the <see cref="Clear" /> method.
+    ///     </para>
+    /// </remarks>
+    public async Task ClearAsync()
+    {
+        await _cancellationTokenSource?.CancelAsync();
+        Clear();
+    }
+
+    /// <summary>
+    ///     Gets the preloaded value for a specific key.
+    /// </summary>
+    /// <param name="key">The key of the preloaded value.</param>
+    /// <param name="list">The list of image paths.</param>
+    /// <returns>The preloaded value if it exists; otherwise, null.</returns>
+    public PreLoadValue? Get(int key, List<string> list)
+    {
+        if (list != null && key >= 0 && key < list.Count)
         {
-            if (list != null && key >= 0 && key < list.Count)
-            {
-                return Contains(key, list) ? _preLoadList[key] : null;
-            }
+            return Contains(key, list) ? _preLoadList[key] : null;
+        }
 #if DEBUG
-            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(Get)} invalid parameters: \n{key}");
+        Trace.WriteLine($"{nameof(PreLoader)}.{nameof(Get)} invalid parameters: \n{key}");
 #endif
-            return null;
+        return null;
 
-        }
+    }
 
 
-        /// <summary>
-        ///     Gets the preloaded value for a specific key asynchronously.
-        /// </summary>
-        /// <param name="key">The key of the preloaded value.</param>
-        /// <param name="list">The list of image paths.</param>
-        /// <returns>The preloaded value if it exists; otherwise, null.</returns>
-        public async Task<PreLoadValue?> GetAsync(int key, List<string> list)
+    /// <summary>
+    ///     Gets the preloaded value for a specific key asynchronously.
+    /// </summary>
+    /// <param name="key">The key of the preloaded value.</param>
+    /// <param name="list">The list of image paths.</param>
+    /// <returns>The preloaded value if it exists; otherwise, null.</returns>
+    public async Task<PreLoadValue?> GetAsync(int key, List<string> list)
+    {
+        if (list == null || key < 0 || key >= list.Count)
         {
-            if (list == null || key < 0 || key >= list.Count)
-            {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(GetAsync)} invalid parameters: \n{key}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(GetAsync)} invalid parameters: \n{key}");
 #endif
-                return null;
-            }
+            return null;
+        }
 
-            if (Contains(key, list)) return _preLoadList[key];
+        if (Contains(key, list)) return _preLoadList[key];
 
-            await AddAsync(key, list);
-            return Get(key, list);
-        }
+        await AddAsync(key, list);
+        return Get(key, list);
+    }
 
-        /// <summary>
-        ///     Checks if a specific key exists in the preload list.
-        /// </summary>
-        /// <param name="key">The key to check.</param>
-        /// <param name="list">The list of image paths.</param>
-        /// <returns>True if the key exists; otherwise, false.</returns>
-        public bool Contains(int key, List<string> list)
-        {
-            return list != null && key >= 0 && key < list.Count && _preLoadList.ContainsKey(key);
-        }
+    /// <summary>
+    ///     Checks if a specific key exists in the preload list.
+    /// </summary>
+    /// <param name="key">The key to check.</param>
+    /// <param name="list">The list of image paths.</param>
+    /// <returns>True if the key exists; otherwise, false.</returns>
+    public bool Contains(int key, List<string> list)
+    {
+        return list != null && key >= 0 && key < list.Count && _preLoadList.ContainsKey(key);
+    }
 
 
-        /// <summary>
-        ///     Removes a specific key from the preload list.
-        /// </summary>
-        /// <param name="key">The key to remove.</param>
-        /// <param name="list">The list of image paths.</param>
-        /// <returns>True if the key was removed; otherwise, false.</returns>
-        public bool Remove(int key, List<string> list)
+    /// <summary>
+    ///     Removes a specific key from the preload list.
+    /// </summary>
+    /// <param name="key">The key to remove.</param>
+    /// <param name="list">The list of image paths.</param>
+    /// <returns>True if the key was removed; otherwise, false.</returns>
+    public bool Remove(int key, List<string> list)
+    {
+        if (list == null || key < 0 || key >= list.Count || !Contains(key, list))
         {
-            if (list == null || key < 0 || key >= list.Count || !Contains(key, list))
-            {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(Remove)} invalid parameters: \n{key}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(Remove)} invalid parameters: \n{key}");
 #endif
-                return false;
-            }
+            return false;
+        }
 
-            try
+        try
+        {
+            if (_preLoadList.TryGetValue(key, out var item))
             {
-                if (_preLoadList.TryGetValue(key, out var item))
+                // Ensure proper Bitmap disposal
+                if (item.ImageModel?.Image is Bitmap bitmap)
                 {
-                    // Ensure proper Bitmap disposal
-                    if (item.ImageModel?.Image is Bitmap bitmap)
-                    {
-                        bitmap.Dispose();
-                    }
-
-                    if (item.ImageModel is not null)
-                    {
-                        item.ImageModel.Image = null;
-                        item.ImageModel.FileInfo = null;
-                    }
-
-                    var removed = _preLoadList.TryRemove(key, out _);
+                    bitmap.Dispose();
+                }
+
+                if (item.ImageModel is not null)
+                {
+                    item.ImageModel.Image = null;
+                    item.ImageModel.FileInfo = null;
+                }
+
+                var removed = _preLoadList.TryRemove(key, out _);
 #if DEBUG
-                    if (removed && ShowAddRemove)
-                    {
-                        Trace.WriteLine($"{list[key]} removed at {list.IndexOf(list[key])}");
-                    }
-#endif
-                    return removed;
+                if (removed && ShowAddRemove)
+                {
+                    Trace.WriteLine($"{list[key]} removed at {list.IndexOf(list[key])}");
                 }
+#endif
+                return removed;
             }
-            catch (Exception e)
-            {
+        }
+        catch (Exception e)
+        {
 #if DEBUG
-                Trace.WriteLine($"{nameof(Remove)} exception:\n{e.Message}");
+            Trace.WriteLine($"{nameof(Remove)} exception:\n{e.Message}");
 #endif
-            }
-
-            return false;
         }
 
-        /// <summary>
-        ///     Preloads images asynchronously.
-        /// </summary>
-        /// <param name="currentIndex">The current index of the image.</param>
-        /// <param name="reverse">Indicates whether to preload in reverse order.</param>
-        /// <param name="list">The list of image paths.</param>
-        public async Task PreLoadAsync(int currentIndex, bool reverse, List<string> list)
+        return false;
+    }
+
+    /// <summary>
+    ///     Preloads images asynchronously.
+    /// </summary>
+    /// <param name="currentIndex">The current index of the image.</param>
+    /// <param name="reverse">Indicates whether to preload in reverse order.</param>
+    /// <param name="list">The list of image paths.</param>
+    public async Task PreLoadAsync(int currentIndex, bool reverse, List<string> list)
+    {
+        if (list == null)
         {
-            if (list == null)
-            {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoader)}.{nameof(PreLoadAsync)} list null \n{currentIndex}");
+            Trace.WriteLine($"{nameof(PreLoader)}.{nameof(PreLoadAsync)} list null \n{currentIndex}");
 #endif
-                return;
-            }
+            return;
+        }
 
-            if (IsRunning)
-            {
-                return;
-            }
+        if (IsRunning)
+        {
+            return;
+        }
             
-            _cancellationTokenSource ??= new CancellationTokenSource();
+        _cancellationTokenSource ??= new CancellationTokenSource();
 
-            try
-            {
-                await PreLoadInternalAsync(currentIndex, reverse, list, _cancellationTokenSource.Token);
-            }
-            catch (OperationCanceledException)
-            {
-                // Handle cancellation gracefully
-            }
-            catch (Exception exception)
-            {
+        try
+        {
+            await PreLoadInternalAsync(currentIndex, reverse, list, _cancellationTokenSource.Token);
+        }
+        catch (OperationCanceledException)
+        {
+            // Handle cancellation gracefully
+        }
+        catch (Exception exception)
+        {
 #if DEBUG
-                Trace.WriteLine($"{nameof(PreLoadAsync)} exception:\n{exception.Message}");
+            Trace.WriteLine($"{nameof(PreLoadAsync)} exception:\n{exception.Message}");
 #endif
-            }
         }
+    }
+
+    private async Task PreLoadInternalAsync(int currentIndex, bool reverse, List<string> list,
+        CancellationToken token)
+    {
+        IsRunning = true;
 
-        private async Task PreLoadInternalAsync(int currentIndex, bool reverse, List<string> list,
-            CancellationToken token)
+        var count = list.Count;
+
+        int nextStartingIndex, prevStartingIndex;
+        if (reverse)
+        {
+            nextStartingIndex = (currentIndex - 1 + count) % count;
+            prevStartingIndex = currentIndex + 1;
+        }
+        else
         {
-            IsRunning = true;
+            nextStartingIndex = (currentIndex + 1) % count;
+            prevStartingIndex = currentIndex - 1;
+        }
 
-            var count = list.Count;
+        var array = new int[PreLoaderConfig.MaxCount];
 
-            int nextStartingIndex, prevStartingIndex;
+#if DEBUG
+        if (ShowAddRemove)
+        {
+            Trace.WriteLine($"\nPreLoading started at {currentIndex}\n");
+        }
+#endif
+
+        var options = new ParallelOptions
+        {
+            MaxDegreeOfParallelism = _config.MaxParallelism,
+            CancellationToken = token
+        };
+
+        try
+        {
             if (reverse)
             {
-                nextStartingIndex = (currentIndex - 1 + count) % count;
-                prevStartingIndex = currentIndex + 1;
+                await LoopAsync(options, false);
+                await LoopAsync(options, true);
             }
             else
             {
-                nextStartingIndex = (currentIndex + 1) % count;
-                prevStartingIndex = currentIndex - 1;
-            }
-
-            var array = new int[PreLoaderConfig.MaxCount];
-
-#if DEBUG
-            if (ShowAddRemove)
-            {
-                Trace.WriteLine($"\nPreLoading started at {currentIndex}\n");
+                await LoopAsync(options, true);
+                await LoopAsync(options, false);
             }
-#endif
 
-            var options = new ParallelOptions
-            {
-                MaxDegreeOfParallelism = _config.MaxParallelism,
-                CancellationToken = token
-            };
+            RemoveLoop();
+        }
+        finally
+        {
+            IsRunning = false;
+        }
 
-            try
+        return;
+            
+        async Task LoopAsync(ParallelOptions parallelOptions, bool positive)
+        {
+            await Parallel.ForAsync(0, PreLoaderConfig.PositiveIterations, parallelOptions, async (i, _) =>
             {
-                if (reverse)
+                var index = positive ? (nextStartingIndex + i) % count : (prevStartingIndex - i + count) % count;
+                var isAdded = await AddAsync(index, list);
+                if (isAdded)
                 {
-                    await LoopAsync(options, false);
-                    await LoopAsync(options, true);
-                }
-                else
-                {
-                    await LoopAsync(options, true);
-                    await LoopAsync(options, false);
+                    array[i] = index;
                 }
+            });
+        }
 
-                RemoveLoop();
-            }
-            finally
-            {
-                IsRunning = false;
-            }
+        void RemoveLoop()
+        {
+            // Iterate through the _preLoadList and remove items outside the preload range
 
-            return;
-            
-            async Task LoopAsync(ParallelOptions parallelOptions, bool positive)
+            if (list.Count <= PreLoaderConfig.MaxCount + PreLoaderConfig.NegativeIterations ||
+                _preLoadList.Count <= PreLoaderConfig.MaxCount)
             {
-                await Parallel.ForAsync(0, PreLoaderConfig.PositiveIterations, parallelOptions, async (i, _) =>
-                {
-                    var index = positive ? (nextStartingIndex + i) % count : (prevStartingIndex - i + count) % count;
-                    var isAdded = await AddAsync(index, list);
-                    if (isAdded)
-                    {
-                        array[i] = index;
-                    }
-                });
+                return;
             }
 
-            void RemoveLoop()
+            var deleteCount = _preLoadList.Count - PreLoaderConfig.MaxCount < PreLoaderConfig.MaxCount
+                ? PreLoaderConfig.MaxCount
+                : _preLoadList.Count - PreLoaderConfig.MaxCount;
+            for (var i = 0; i < deleteCount; i++)
             {
-                // Iterate through the _preLoadList and remove items outside the preload range
-
-                if (list.Count <= PreLoaderConfig.MaxCount + PreLoaderConfig.NegativeIterations ||
-                    _preLoadList.Count <= PreLoaderConfig.MaxCount)
+                var removeIndex = reverse ? _preLoadList.Keys.Max() : _preLoadList.Keys.Min();
+                if (i >= array.Length)
                 {
                     return;
                 }
 
-                var deleteCount = _preLoadList.Count - PreLoaderConfig.MaxCount < PreLoaderConfig.MaxCount
-                    ? PreLoaderConfig.MaxCount
-                    : _preLoadList.Count - PreLoaderConfig.MaxCount;
-                for (var i = 0; i < deleteCount; i++)
+                if (array[i] == removeIndex || removeIndex == currentIndex)
                 {
-                    var removeIndex = reverse ? _preLoadList.Keys.Max() : _preLoadList.Keys.Min();
-                    if (i >= array.Length)
-                    {
-                        return;
-                    }
-
-                    if (array[i] == removeIndex || removeIndex == currentIndex)
-                    {
-                        continue;
-                    }
-
-                    if (removeIndex > currentIndex + 2 || removeIndex < currentIndex - 2)
-                    {
-                        Remove(removeIndex, list);
-                    }
+                    continue;
                 }
 
-                if (deleteCount > 1)
+                if (removeIndex > currentIndex + 2 || removeIndex < currentIndex - 2)
                 {
-                    // Collect unmanaged memory, prevent memory leak
-                    Collect(0, GCCollectionMode.Optimized, false);
+                    Remove(removeIndex, list);
                 }
             }
+
+            if (deleteCount > 1)
+            {
+                // Collect unmanaged memory, prevent memory leak
+                Collect(0, GCCollectionMode.Optimized, false);
+            }
         }
+    }
 
-        #region IDisposable
+    #region IDisposable
 
-        private bool _disposed;
+    private bool _disposed;
 
-        public void Dispose()
-        {
-            Dispose(true);
-            SuppressFinalize(this);
-            Collect(MaxGeneration, GCCollectionMode.Optimized, false);
-        }
+    public void Dispose()
+    {
+        Dispose(true);
+        SuppressFinalize(this);
+        Collect(MaxGeneration, GCCollectionMode.Optimized, false);
+    }
 
-        private void Dispose(bool disposing)
+    private void Dispose(bool disposing)
+    {
+        if (_disposed)
         {
-            if (_disposed)
-            {
-                return;
-            }
-
-            if (disposing)
-            {
-                Clear();
-            }
-
-            _disposed = true;
+            return;
         }
 
-        public async ValueTask DisposeAsync()
+        if (disposing)
         {
-            if (_disposed)
-            {
-                return;
-            }
-
-            await ClearAsync().ConfigureAwait(false);
-
-            Dispose(false);
+            Clear();
         }
 
-        ~PreLoader()
+        _disposed = true;
+    }
+
+    public async ValueTask DisposeAsync()
+    {
+        if (_disposed)
         {
-            Dispose(false);
+            return;
         }
 
-        #endregion
+        await ClearAsync().ConfigureAwait(false);
+
+        Dispose(false);
     }
+
+    ~PreLoader()
+    {
+        Dispose(false);
+    }
+
+    #endregion
 }

+ 31 - 32
src/PicView.Avalonia/Views/UC/GalleryItem.axaml.cs

@@ -5,49 +5,48 @@ using Avalonia.Input;
 using Avalonia.Media;
 using Exception = System.Exception;
 
-namespace PicView.Avalonia.Views.UC
+namespace PicView.Avalonia.Views.UC;
+
+public partial class GalleryItem : UserControl
 {
-    public partial class GalleryItem : UserControl
+    public GalleryItem()
     {
-        public GalleryItem()
+        InitializeComponent();
+        GalleryContextMenu.Opened += delegate
         {
-            InitializeComponent();
-            GalleryContextMenu.Opened += delegate
+            if (!Application.Current.TryGetResource("SecondaryAccentColor", Application.Current.RequestedThemeVariant, out var color))
             {
-                if (!Application.Current.TryGetResource("SecondaryAccentColor", Application.Current.RequestedThemeVariant, out var color))
-                {
-                    return;
-                }
+                return;
+            }
 
-                try
-                {
-                    var secondaryAccentBrush = (SolidColorBrush)color;
-                    ImageBorder.BorderBrush = secondaryAccentBrush;
-                }
+            try
+            {
+                var secondaryAccentBrush = (SolidColorBrush)color;
+                ImageBorder.BorderBrush = secondaryAccentBrush;
+            }
 #if DEBUG
-                catch (Exception e)
-                {
-                    Console.WriteLine(e);
-                }
+            catch (Exception e)
+            {
+                Console.WriteLine(e);
+            }
 #else
         catch (Exception){}
 #endif
-            };
-            GalleryContextMenu.Closed += delegate
-            {
-                ImageBorder.BorderBrush = Brushes.Transparent;
-            };
-        }
+        };
+        GalleryContextMenu.Closed += delegate
+        {
+            ImageBorder.BorderBrush = Brushes.Transparent;
+        };
+    }
         
-        private void Flyout_OnPointerPressed(object? sender, PointerPressedEventArgs e)
+    private void Flyout_OnPointerPressed(object? sender, PointerPressedEventArgs e)
+    {
+        if (sender is not Control ctl)
         {
-            if (sender is not Control ctl)
-            {
-                return;
-            }
-
-            FlyoutBase.ShowAttachedFlyout(ctl);
-            GalleryItemSizeSlider.SetMaxAndMin();
+            return;
         }
+
+        FlyoutBase.ShowAttachedFlyout(ctl);
+        GalleryItemSizeSlider.SetMaxAndMin();
     }
 }

+ 111 - 112
src/PicView.Core/ArchiveHandling/ArchiveExtraction.cs

@@ -2,158 +2,157 @@
 using SharpCompress.Common;
 using SharpCompress.Readers;
 
-namespace PicView.Core.ArchiveHandling
+namespace PicView.Core.ArchiveHandling;
+
+/// <summary>
+///     Provides methods for extracting supported files from an archive.
+/// </summary>
+public static class ArchiveExtraction
 {
     /// <summary>
-    ///     Provides methods for extracting supported files from an archive.
+    ///     Gets the path of the temporary directory where the archive contents are extracted.
     /// </summary>
-    public static class ArchiveExtraction
-    {
-        /// <summary>
-        ///     Gets the path of the temporary directory where the archive contents are extracted.
-        /// </summary>
-        public static string? TempZipDirectory { get; private set; }
+    public static string? TempZipDirectory { get; private set; }
         
-        public static string? LastOpenedArchive { get; private set; }
+    public static string? LastOpenedArchive { get; private set; }
 
-        /// <summary>
-        ///     Asynchronously extracts supported files from a given archive to a temporary directory.
-        /// </summary>
-        /// <param name="archivePath">
-        ///     The path of the archive file to extract.
-        /// </param>
-        /// <param name="extractWithLocalSoftwareAsync">
-        ///     A delegate function that attempts to extract the archive using local software (e.g., 7-Zip, WinRAR).
-        ///     This function should return a boolean value indicating whether the extraction was successful.
-        ///     It takes two parameters: the path to the archive and the path to the temporary extraction directory.
-        /// </param>
-        /// <returns>
-        ///     A task representing the asynchronous operation. The task result is a boolean:
-        ///     <c>true</c> if any supported files were successfully extracted; otherwise, <c>false</c>.
-        /// </returns>
-        public static async Task<bool> ExtractArchiveAsync(string archivePath,
-            Func<string, string, Task<bool>> extractWithLocalSoftwareAsync)
+    /// <summary>
+    ///     Asynchronously extracts supported files from a given archive to a temporary directory.
+    /// </summary>
+    /// <param name="archivePath">
+    ///     The path of the archive file to extract.
+    /// </param>
+    /// <param name="extractWithLocalSoftwareAsync">
+    ///     A delegate function that attempts to extract the archive using local software (e.g., 7-Zip, WinRAR).
+    ///     This function should return a boolean value indicating whether the extraction was successful.
+    ///     It takes two parameters: the path to the archive and the path to the temporary extraction directory.
+    /// </param>
+    /// <returns>
+    ///     A task representing the asynchronous operation. The task result is a boolean:
+    ///     <c>true</c> if any supported files were successfully extracted; otherwise, <c>false</c>.
+    /// </returns>
+    public static async Task<bool> ExtractArchiveAsync(string archivePath,
+        Func<string, string, Task<bool>> extractWithLocalSoftwareAsync)
+    {
+        try
         {
-            try
+            if (string.IsNullOrEmpty(archivePath) || !File.Exists(archivePath))
             {
-                if (string.IsNullOrEmpty(archivePath) || !File.Exists(archivePath))
-                {
-                    throw new ArgumentException("The archive path is invalid or the file does not exist.");
-                }
+                throw new ArgumentException("The archive path is invalid or the file does not exist.");
+            }
 
-                var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
-                Directory.CreateDirectory(tempDirectory);
-                TempZipDirectory = tempDirectory;
+            var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+            Directory.CreateDirectory(tempDirectory);
+            TempZipDirectory = tempDirectory;
 
-                var ext = Path.GetExtension(archivePath);
-                if (ext.Equals(".7z", StringComparison.OrdinalIgnoreCase) ||
-                    ext.Equals(".cb7", StringComparison.OrdinalIgnoreCase))
+            var ext = Path.GetExtension(archivePath);
+            if (ext.Equals(".7z", StringComparison.OrdinalIgnoreCase) ||
+                ext.Equals(".cb7", StringComparison.OrdinalIgnoreCase))
+            {
+                if (!await extractWithLocalSoftwareAsync(archivePath, tempDirectory))
                 {
-                    if (!await extractWithLocalSoftwareAsync(archivePath, tempDirectory))
-                    {
-                        return false;
-                    }
-
-                    LastOpenedArchive = archivePath;
-                    return true;
+                    return false;
                 }
 
-                await using var stream = File.OpenRead(archivePath);
-                using var reader = ReaderFactory.Open(stream);
+                LastOpenedArchive = archivePath;
+                return true;
+            }
+
+            await using var stream = File.OpenRead(archivePath);
+            using var reader = ReaderFactory.Open(stream);
 
-                var count = 0;
+            var count = 0;
                 
-                await Task.Run(() =>
+            await Task.Run(() =>
+            {
+                // Process each entry asynchronously to avoid blocking the thread
+                while (reader.MoveToNextEntry())
                 {
-                    // Process each entry asynchronously to avoid blocking the thread
-                    while (reader.MoveToNextEntry())
+                    if (reader.Entry.IsDirectory)
                     {
-                        if (reader.Entry.IsDirectory)
-                        {
-                            continue;
-                        }
+                        continue;
+                    }
 
-                        // Extract only if the file is supported
-                        var entryFileName = reader.Entry.Key;
-                        if (entryFileName.IsSupported())
+                    // Extract only if the file is supported
+                    var entryFileName = reader.Entry.Key;
+                    if (entryFileName.IsSupported())
+                    {
+                        reader.WriteEntryToDirectory(tempDirectory, new ExtractionOptions
                         {
-                            reader.WriteEntryToDirectory(tempDirectory, new ExtractionOptions
-                            {
-                                ExtractFullPath = true,
-                                Overwrite = true
-                            });
+                            ExtractFullPath = true,
+                            Overwrite = true
+                        });
 #if DEBUG
-                            Console.WriteLine($"Extracted: {entryFileName}");
+                        Console.WriteLine($"Extracted: {entryFileName}");
 #endif
 
-                            count++;
-                        }
-                        else
-                        {
+                        count++;
+                    }
+                    else
+                    {
 #if DEBUG
-                            Console.WriteLine($"Skipped unsupported file: {entryFileName}");
+                        Console.WriteLine($"Skipped unsupported file: {entryFileName}");
 #endif
-                        }
                     }
-                });
-
-                if (count <= 0)
-                {
-                    return false;
                 }
+            });
 
-                LastOpenedArchive = archivePath;
-                return true;
-
-            }
-            catch (IOException ioEx)
+            if (count <= 0)
             {
-#if DEBUG
-                Console.WriteLine($"IO Exception during extraction: {ioEx.Message}");
-#endif
                 return false;
             }
-            catch (UnauthorizedAccessException authEx)
-            {
+
+            LastOpenedArchive = archivePath;
+            return true;
+
+        }
+        catch (IOException ioEx)
+        {
 #if DEBUG
-                Console.WriteLine($"Access denied during extraction: {authEx.Message}");
+            Console.WriteLine($"IO Exception during extraction: {ioEx.Message}");
 #endif
-                return false;
-            }
-            catch (Exception ex)
-            {
+            return false;
+        }
+        catch (UnauthorizedAccessException authEx)
+        {
 #if DEBUG
-                Console.WriteLine($"Extraction failed: {ex.Message}");
+            Console.WriteLine($"Access denied during extraction: {authEx.Message}");
 #endif
-                return false;
-            }
+            return false;
+        }
+        catch (Exception ex)
+        {
+#if DEBUG
+            Console.WriteLine($"Extraction failed: {ex.Message}");
+#endif
+            return false;
         }
+    }
 
-        /// <summary>
-        ///     Deletes the temporary directory created during extraction, if it exists.
-        /// </summary>
-        public static void Cleanup()
+    /// <summary>
+    ///     Deletes the temporary directory created during extraction, if it exists.
+    /// </summary>
+    public static void Cleanup()
+    {
+        try
         {
-            try
+            if (string.IsNullOrEmpty(TempZipDirectory) || !Directory.Exists(TempZipDirectory))
             {
-                if (string.IsNullOrEmpty(TempZipDirectory) || !Directory.Exists(TempZipDirectory))
-                {
-                    return;
-                }
-
-                Directory.Delete(TempZipDirectory, true);
+                return;
             }
-            catch (Exception ex)
-            {
+
+            Directory.Delete(TempZipDirectory, true);
+        }
+        catch (Exception ex)
+        {
 #if DEBUG
-                Console.WriteLine($"{nameof(ArchiveExtraction)}: Cleanup exception \n {ex.Message}");
+            Console.WriteLine($"{nameof(ArchiveExtraction)}: Cleanup exception \n {ex.Message}");
 #endif
-            }
-            finally
-            {
-                TempZipDirectory = null;
-                LastOpenedArchive = null;
-            }
+        }
+        finally
+        {
+            TempZipDirectory = null;
+            LastOpenedArchive = null;
         }
     }
 }

+ 286 - 287
src/PicView.Core/Calculations/ImageSizeCalculationHelper.cs

@@ -1,369 +1,368 @@
 using System.Runtime.InteropServices;
 
-namespace PicView.Core.Calculations
+namespace PicView.Core.Calculations;
+
+public static class ImageSizeCalculationHelper
 {
-    public static class ImageSizeCalculationHelper
+    /// <summary>
+    ///  Returns the interface size of the titlebar based on OS
+    /// </summary>
+    public static double GetInterfaceSize()
     {
-        /// <summary>
-        ///  Returns the interface size of the titlebar based on OS
-        /// </summary>
-        public static double GetInterfaceSize()
-        {
-            // TODO: find a more elegant solution
-            return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 165 : 230;
-        }
+        // TODO: find a more elegant solution
+        return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 165 : 230;
+    }
 
-        public static ImageSize GetImageSize(double width,
-            double height,
-            double monitorWidth,
-            double monitorHeight,
-            double monitorMinWidth,
-            double monitorMinHeight,
-            double interfaceSize,
-            double rotationAngle,
-            double padding,
-            double dpiScaling,
-            double uiTopSize,
-            double uiBottomSize,
-            double galleryHeight,
-            double containerWidth,
-            double containerHeight)
+    public static ImageSize GetImageSize(double width,
+        double height,
+        double monitorWidth,
+        double monitorHeight,
+        double monitorMinWidth,
+        double monitorMinHeight,
+        double interfaceSize,
+        double rotationAngle,
+        double padding,
+        double dpiScaling,
+        double uiTopSize,
+        double uiBottomSize,
+        double galleryHeight,
+        double containerWidth,
+        double containerHeight)
+    {
+        if (width <= 0 || height <= 0 || rotationAngle > 360 || rotationAngle < 0)
         {
-            if (width <= 0 || height <= 0 || rotationAngle > 360 || rotationAngle < 0)
-            {
-                return new ImageSize(0, 0, 0, 0, 0, 0, 0, 0);
-            }
+            return new ImageSize(0, 0, 0, 0, 0, 0, 0, 0);
+        }
 
-            double aspectRatio;
-            double maxWidth, maxHeight;
-            var margin = 0d;
+        double aspectRatio;
+        double maxWidth, maxHeight;
+        var margin = 0d;
 
-            var fullscreen = Settings.WindowProperties.Fullscreen ||
-                             Settings.WindowProperties.Maximized;
+        var fullscreen = Settings.WindowProperties.Fullscreen ||
+                         Settings.WindowProperties.Maximized;
             
-            var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
-            var borderSpaceWidth = fullscreen ? 0 : padding;
+        var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
+        var borderSpaceWidth = fullscreen ? 0 : padding;
 
-            var workAreaWidth = monitorWidth - borderSpaceWidth;
-            var workAreaHeight = monitorHeight - borderSpaceHeight;
+        var workAreaWidth = monitorWidth - borderSpaceWidth;
+        var workAreaHeight = monitorHeight - borderSpaceHeight;
 
-            if (Settings.Zoom.ScrollEnabled)
-            {
-                workAreaWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
-                containerWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
+        if (Settings.Zoom.ScrollEnabled)
+        {
+            workAreaWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
+            containerWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
 
-                maxWidth = workAreaWidth - padding;
-                maxHeight = height;
-            }
-            else if (Settings.WindowProperties.AutoFit)
-            {
-                maxWidth = Settings.ImageScaling.StretchImage
-                    ? workAreaWidth - padding
-                    : Math.Min(workAreaWidth - padding, width);
+            maxWidth = workAreaWidth - padding;
+            maxHeight = height;
+        }
+        else if (Settings.WindowProperties.AutoFit)
+        {
+            maxWidth = Settings.ImageScaling.StretchImage
+                ? workAreaWidth - padding
+                : Math.Min(workAreaWidth - padding, width);
                     
-                maxHeight = Settings.ImageScaling.StretchImage
-                    ? workAreaHeight - padding
-                    : Math.Min(workAreaHeight - padding, height);
-            }
-            else
-            {
-                maxWidth = Settings.ImageScaling.StretchImage
-                    ? containerWidth
-                    : Math.Min(containerWidth, width);
+            maxHeight = Settings.ImageScaling.StretchImage
+                ? workAreaHeight - padding
+                : Math.Min(workAreaHeight - padding, height);
+        }
+        else
+        {
+            maxWidth = Settings.ImageScaling.StretchImage
+                ? containerWidth
+                : Math.Min(containerWidth, width);
 
-                maxHeight = Settings.ImageScaling.StretchImage
-                    ? containerHeight - galleryHeight
-                    : Math.Min(containerHeight - galleryHeight, height);
-            }
+            maxHeight = Settings.ImageScaling.StretchImage
+                ? containerHeight - galleryHeight
+                : Math.Min(containerHeight - galleryHeight, height);
+        }
 
-            if (Settings.Gallery.IsBottomGalleryShown)
+        if (Settings.Gallery.IsBottomGalleryShown)
+        {
+            if (!Settings.UIProperties.ShowInterface)
             {
-                if (!Settings.UIProperties.ShowInterface)
+                if (Settings.Gallery.ShowBottomGalleryInHiddenUI)
                 {
-                    if (Settings.Gallery.ShowBottomGalleryInHiddenUI)
-                    {
-                        margin = galleryHeight > 0 ? galleryHeight : 0;
-                    }
-                    else
-                    {
-                        margin = 0;
-                    }
+                    margin = galleryHeight > 0 ? galleryHeight : 0;
                 }
                 else
                 {
-                    margin = galleryHeight > 0 ? galleryHeight : 0;
+                    margin = 0;
                 }
             }
-
-            // aspect ratio calculation
-            switch (rotationAngle)
+            else
             {
-                case 0:
-                case 180:
-                    aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
-                    break;
-
-                case 90:
-                case 270:
-                    aspectRatio = Math.Min(maxWidth / height, maxHeight / width);
-                    break;
-
-                default:
-                    var rotationRadians = rotationAngle * Math.PI / 180;
-                    var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
-                                   Math.Abs(height * Math.Sin(rotationRadians));
-                    var newHeight = Math.Abs(width * Math.Sin(rotationRadians)) +
-                                    Math.Abs(height * Math.Cos(rotationRadians));
-                    aspectRatio = Math.Min(maxWidth / newWidth, maxHeight / newHeight);
-                    break;
+                margin = galleryHeight > 0 ? galleryHeight : 0;
             }
+        }
 
-            // Fit image by aspect ratio calculation
-            // and update values
-            double scrollWidth, scrollHeight, xWidth, xHeight;
-            if (Settings.Zoom.ScrollEnabled)
-            {
-                if (Settings.WindowProperties.AutoFit)
-                {
-                    xWidth = maxWidth - SizeDefaults.ScrollbarSize - 10;
-                    xHeight = maxWidth * height / width;
+        // aspect ratio calculation
+        switch (rotationAngle)
+        {
+            case 0:
+            case 180:
+                aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
+                break;
+
+            case 90:
+            case 270:
+                aspectRatio = Math.Min(maxWidth / height, maxHeight / width);
+                break;
+
+            default:
+                var rotationRadians = rotationAngle * Math.PI / 180;
+                var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
+                               Math.Abs(height * Math.Sin(rotationRadians));
+                var newHeight = Math.Abs(width * Math.Sin(rotationRadians)) +
+                                Math.Abs(height * Math.Cos(rotationRadians));
+                aspectRatio = Math.Min(maxWidth / newWidth, maxHeight / newHeight);
+                break;
+        }
 
-                    scrollWidth = maxWidth;
-                    scrollHeight = containerHeight - padding - 8;
-                }
-                else
-                {
-                    scrollWidth = containerWidth + SizeDefaults.ScrollbarSize;
-                    scrollHeight = containerHeight;
+        // Fit image by aspect ratio calculation
+        // and update values
+        double scrollWidth, scrollHeight, xWidth, xHeight;
+        if (Settings.Zoom.ScrollEnabled)
+        {
+            if (Settings.WindowProperties.AutoFit)
+            {
+                xWidth = maxWidth - SizeDefaults.ScrollbarSize - 10;
+                xHeight = maxWidth * height / width;
 
-                    xWidth = containerWidth - SizeDefaults.ScrollbarSize + 10;
-                    xHeight = height / width * xWidth;
-                }
+                scrollWidth = maxWidth;
+                scrollHeight = containerHeight - padding - 8;
             }
             else
             {
-                scrollWidth = double.NaN;
-                scrollHeight = double.NaN;
+                scrollWidth = containerWidth + SizeDefaults.ScrollbarSize;
+                scrollHeight = containerHeight;
 
-                xWidth = width * aspectRatio;
-                xHeight = height * aspectRatio;
+                xWidth = containerWidth - SizeDefaults.ScrollbarSize + 10;
+                xHeight = height / width * xWidth;
             }
+        }
+        else
+        {
+            scrollWidth = double.NaN;
+            scrollHeight = double.NaN;
 
-            var titleMaxWidth = GetTitleMaxWidth(rotationAngle, xWidth, xHeight, monitorMinWidth, monitorMinHeight,
-                interfaceSize, containerWidth, dpiScaling);
-
-            return new ImageSize(xWidth, xHeight, 0, scrollWidth, scrollHeight, titleMaxWidth, margin, aspectRatio);
+            xWidth = width * aspectRatio;
+            xHeight = height * aspectRatio;
         }
 
-        public static ImageSize GetImageSize(double width,
-            double height,
-            double secondaryWidth,
-            double secondaryHeight,
-            double monitorWidth,
-            double monitorHeight,
-            double monitorMinWidth,
-            double monitorMinHeight,
-            double interfaceSize,
-            double rotationAngle,
-            double padding,
-            double dpiScaling,
-            double uiTopSize,
-            double uiBottomSize,
-            double galleryHeight,
-            double containerWidth,
-            double containerHeight)
+        var titleMaxWidth = GetTitleMaxWidth(rotationAngle, xWidth, xHeight, monitorMinWidth, monitorMinHeight,
+            interfaceSize, containerWidth, dpiScaling);
+
+        return new ImageSize(xWidth, xHeight, 0, scrollWidth, scrollHeight, titleMaxWidth, margin, aspectRatio);
+    }
+
+    public static ImageSize GetImageSize(double width,
+        double height,
+        double secondaryWidth,
+        double secondaryHeight,
+        double monitorWidth,
+        double monitorHeight,
+        double monitorMinWidth,
+        double monitorMinHeight,
+        double interfaceSize,
+        double rotationAngle,
+        double padding,
+        double dpiScaling,
+        double uiTopSize,
+        double uiBottomSize,
+        double galleryHeight,
+        double containerWidth,
+        double containerHeight)
+    {
+        if (width <= 0 || height <= 0 || secondaryWidth <= 0 || secondaryHeight <= 0 || rotationAngle > 360 ||
+            rotationAngle < 0)
         {
-            if (width <= 0 || height <= 0 || secondaryWidth <= 0 || secondaryHeight <= 0 || rotationAngle > 360 ||
-                rotationAngle < 0)
-            {
-                return new ImageSize(0, 0, 0, 0, 0, 0, 0, 0);
-            }
+            return new ImageSize(0, 0, 0, 0, 0, 0, 0, 0);
+        }
 
-            // Get sizes for both images
-            var firstSize = GetImageSize(width, height, monitorWidth, monitorHeight, monitorMinWidth, monitorMinHeight,
-                interfaceSize, rotationAngle, padding, dpiScaling, uiTopSize, uiBottomSize, galleryHeight,
-                containerWidth,
-                containerHeight);
-            var secondSize = GetImageSize(secondaryWidth, secondaryHeight, monitorWidth, monitorHeight, monitorMinWidth,
-                monitorMinHeight, interfaceSize, rotationAngle, padding, dpiScaling, uiTopSize, uiBottomSize,
-                galleryHeight,
-                containerWidth, containerHeight);
+        // Get sizes for both images
+        var firstSize = GetImageSize(width, height, monitorWidth, monitorHeight, monitorMinWidth, monitorMinHeight,
+            interfaceSize, rotationAngle, padding, dpiScaling, uiTopSize, uiBottomSize, galleryHeight,
+            containerWidth,
+            containerHeight);
+        var secondSize = GetImageSize(secondaryWidth, secondaryHeight, monitorWidth, monitorHeight, monitorMinWidth,
+            monitorMinHeight, interfaceSize, rotationAngle, padding, dpiScaling, uiTopSize, uiBottomSize,
+            galleryHeight,
+            containerWidth, containerHeight);
 
-            // Determine maximum height for both images
-            var xHeight = Math.Max(firstSize.Height, secondSize.Height);
+        // Determine maximum height for both images
+        var xHeight = Math.Max(firstSize.Height, secondSize.Height);
 
-            // Recalculate the widths to maintain the aspect ratio with the new maximum height
-            var xWidth1 = firstSize.Width / firstSize.Height * xHeight;
-            var xWidth2 = secondSize.Width / secondSize.Height * xHeight;
+        // Recalculate the widths to maintain the aspect ratio with the new maximum height
+        var xWidth1 = firstSize.Width / firstSize.Height * xHeight;
+        var xWidth2 = secondSize.Width / secondSize.Height * xHeight;
 
-            // Combined width of both images
-            var combinedWidth = xWidth1 + xWidth2;
+        // Combined width of both images
+        var combinedWidth = xWidth1 + xWidth2;
 
-            if (Settings.WindowProperties.AutoFit)
+        if (Settings.WindowProperties.AutoFit)
+        {
+            var widthPadding = Settings.ImageScaling.StretchImage ? 4 : padding;
+            var availableWidth = monitorWidth - widthPadding;
+            var availableHeight = monitorHeight - (widthPadding + uiBottomSize + uiTopSize);
+            if (rotationAngle is 0 or 180)
             {
-                var widthPadding = Settings.ImageScaling.StretchImage ? 4 : padding;
-                var availableWidth = monitorWidth - widthPadding;
-                var availableHeight = monitorHeight - (widthPadding + uiBottomSize + uiTopSize);
-                if (rotationAngle is 0 or 180)
+                // If combined width exceeds available width, scale both images down proportionally
+                if (combinedWidth > availableWidth)
                 {
-                    // If combined width exceeds available width, scale both images down proportionally
-                    if (combinedWidth > availableWidth)
-                    {
-                        var scaleFactor = availableWidth / combinedWidth;
-                        xWidth1 *= scaleFactor;
-                        xWidth2 *= scaleFactor;
-                        xHeight *= scaleFactor;
-
-                        combinedWidth = xWidth1 + xWidth2;
-                    }
+                    var scaleFactor = availableWidth / combinedWidth;
+                    xWidth1 *= scaleFactor;
+                    xWidth2 *= scaleFactor;
+                    xHeight *= scaleFactor;
+
+                    combinedWidth = xWidth1 + xWidth2;
                 }
-                else
+            }
+            else
+            {
+                if (combinedWidth > availableHeight)
                 {
-                    if (combinedWidth > availableHeight)
-                    {
-                        var scaleFactor = availableHeight / combinedWidth;
-                        xWidth1 *= scaleFactor;
-                        xWidth2 *= scaleFactor;
-                        xHeight *= scaleFactor;
+                    var scaleFactor = availableHeight / combinedWidth;
+                    xWidth1 *= scaleFactor;
+                    xWidth2 *= scaleFactor;
+                    xHeight *= scaleFactor;
                         
-                        combinedWidth = xWidth1 + xWidth2;
-                    }
+                    combinedWidth = xWidth1 + xWidth2;
                 }
             }
-            else
+        }
+        else
+        {
+            if (rotationAngle is 0 or 180)
             {
-                if (rotationAngle is 0 or 180)
+                if (combinedWidth > containerWidth)
                 {
-                    if (combinedWidth > containerWidth)
-                    {
-                        var scaleFactor = containerWidth / combinedWidth;
-                        xWidth1 *= scaleFactor;
-                        xWidth2 *= scaleFactor;
-                        xHeight *= scaleFactor;
-
-                        combinedWidth = xWidth1 + xWidth2;
-                    }
+                    var scaleFactor = containerWidth / combinedWidth;
+                    xWidth1 *= scaleFactor;
+                    xWidth2 *= scaleFactor;
+                    xHeight *= scaleFactor;
+
+                    combinedWidth = xWidth1 + xWidth2;
                 }
-                else
+            }
+            else
+            {
+                if (combinedWidth > containerHeight)
                 {
-                    if (combinedWidth > containerHeight)
-                    {
-                        var scaleFactor = containerHeight / combinedWidth;
-                        xWidth1 *= scaleFactor;
-                        xWidth2 *= scaleFactor;
-                        xHeight *= scaleFactor;
+                    var scaleFactor = containerHeight / combinedWidth;
+                    xWidth1 *= scaleFactor;
+                    xWidth2 *= scaleFactor;
+                    xHeight *= scaleFactor;
                         
-                        combinedWidth = xWidth1 + xWidth2;
-                    }
+                    combinedWidth = xWidth1 + xWidth2;
                 }
-
             }
 
-            double scrollWidth, scrollHeight;
-            if (Settings.Zoom.ScrollEnabled)
+        }
+
+        double scrollWidth, scrollHeight;
+        if (Settings.Zoom.ScrollEnabled)
+        {
+            if (Settings.WindowProperties.AutoFit)
             {
-                if (Settings.WindowProperties.AutoFit)
-                {
-                    combinedWidth -= SizeDefaults.ScrollbarSize;
-                    scrollWidth = combinedWidth + SizeDefaults.ScrollbarSize + 8;
-
-                    var fullscreen = Settings.WindowProperties.Fullscreen ||
-                                     Settings.WindowProperties.Maximized;
-                    var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
-                    var workAreaHeight = monitorHeight * dpiScaling - borderSpaceHeight;
-                    scrollHeight = Settings.ImageScaling.StretchImage
-                        ? workAreaHeight
-                        : workAreaHeight - padding;
-                }
-                else
-                {
-                    combinedWidth -= SizeDefaults.ScrollbarSize + 8;
-                    scrollWidth = double.NaN;
-                    scrollHeight = double.NaN;
-                }
+                combinedWidth -= SizeDefaults.ScrollbarSize;
+                scrollWidth = combinedWidth + SizeDefaults.ScrollbarSize + 8;
+
+                var fullscreen = Settings.WindowProperties.Fullscreen ||
+                                 Settings.WindowProperties.Maximized;
+                var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
+                var workAreaHeight = monitorHeight * dpiScaling - borderSpaceHeight;
+                scrollHeight = Settings.ImageScaling.StretchImage
+                    ? workAreaHeight
+                    : workAreaHeight - padding;
             }
             else
             {
+                combinedWidth -= SizeDefaults.ScrollbarSize + 8;
                 scrollWidth = double.NaN;
                 scrollHeight = double.NaN;
             }
+        }
+        else
+        {
+            scrollWidth = double.NaN;
+            scrollHeight = double.NaN;
+        }
 
-            var titleMaxWidth = GetTitleMaxWidth(rotationAngle, combinedWidth, xHeight, monitorMinWidth,
-                monitorMinHeight, interfaceSize, containerWidth, dpiScaling);
+        var titleMaxWidth = GetTitleMaxWidth(rotationAngle, combinedWidth, xHeight, monitorMinWidth,
+            monitorMinHeight, interfaceSize, containerWidth, dpiScaling);
 
-            var margin = firstSize.Height > secondSize.Height ? firstSize.Margin : secondSize.Margin;
-            return new ImageSize(combinedWidth, xHeight, xWidth2, scrollWidth, scrollHeight, titleMaxWidth, margin,
-                firstSize.AspectRatio);
-        }
+        var margin = firstSize.Height > secondSize.Height ? firstSize.Margin : secondSize.Margin;
+        return new ImageSize(combinedWidth, xHeight, xWidth2, scrollWidth, scrollHeight, titleMaxWidth, margin,
+            firstSize.AspectRatio);
+    }
 
 
-        public static double GetTitleMaxWidth(double rotationAngle, double width, double height, double monitorMinWidth,
-            double monitorMinHeight, double interfaceSize, double containerWidth, double dpiScaling)
-        {
-            double titleMaxWidth;
-            var maximized = Settings.WindowProperties.Fullscreen ||
-                            Settings.WindowProperties.Maximized;
+    public static double GetTitleMaxWidth(double rotationAngle, double width, double height, double monitorMinWidth,
+        double monitorMinHeight, double interfaceSize, double containerWidth, double dpiScaling)
+    {
+        double titleMaxWidth;
+        var maximized = Settings.WindowProperties.Fullscreen ||
+                        Settings.WindowProperties.Maximized;
 
-            if (Settings.WindowProperties.AutoFit && !maximized)
+        if (Settings.WindowProperties.AutoFit && !maximized)
+        {
+            switch (rotationAngle)
             {
-                switch (rotationAngle)
+                case 0 or 180:
+                    titleMaxWidth = Math.Max(width, monitorMinWidth);
+                    break;
+                case 90 or 270:
+                    titleMaxWidth = Math.Max(height, monitorMinHeight);
+                    break;
+                default:
                 {
-                    case 0 or 180:
-                        titleMaxWidth = Math.Max(width, monitorMinWidth);
-                        break;
-                    case 90 or 270:
-                        titleMaxWidth = Math.Max(height, monitorMinHeight);
-                        break;
-                    default:
-                    {
-                        var rotationRadians = rotationAngle * Math.PI / 180;
-                        var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
-                                       Math.Abs(height * Math.Sin(rotationRadians));
-
-                        titleMaxWidth = Math.Max(newWidth, monitorMinWidth);
-                        break;
-                    }
-                }
-
-                titleMaxWidth = titleMaxWidth - interfaceSize < interfaceSize
-                    ? interfaceSize
-                    : titleMaxWidth - interfaceSize;
+                    var rotationRadians = rotationAngle * Math.PI / 180;
+                    var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
+                                   Math.Abs(height * Math.Sin(rotationRadians));
 
-                if (Settings.Zoom.ScrollEnabled)
-                {
-                    titleMaxWidth += SizeDefaults.ScrollbarSize + 4;
+                    titleMaxWidth = Math.Max(newWidth, monitorMinWidth);
+                    break;
                 }
             }
-            else
+
+            titleMaxWidth = titleMaxWidth - interfaceSize < interfaceSize
+                ? interfaceSize
+                : titleMaxWidth - interfaceSize;
+
+            if (Settings.Zoom.ScrollEnabled)
             {
-                // Fix title width to window size
-                titleMaxWidth = containerWidth - interfaceSize <= 0 ? 0 : containerWidth - interfaceSize;
+                titleMaxWidth += SizeDefaults.ScrollbarSize + 4;
             }
-
-            return titleMaxWidth;
         }
-
-        public readonly struct ImageSize(
-            double width,
-            double height,
-            double secondaryWidth,
-            double scrollViewerWidth,
-            double scrollViewerHeight,
-            double titleMaxWidth,
-            double margin,
-            double aspectRatio)
+        else
         {
-            public double TitleMaxWidth { get; } = titleMaxWidth;
-            public double Width { get; } = width;
-            public double Height { get; } = height;
+            // Fix title width to window size
+            titleMaxWidth = containerWidth - interfaceSize <= 0 ? 0 : containerWidth - interfaceSize;
+        }
 
-            public double ScrollViewerWidth { get; } = scrollViewerWidth;
-            public double ScrollViewerHeight { get; } = scrollViewerHeight;
+        return titleMaxWidth;
+    }
 
-            public double SecondaryWidth { get; } = secondaryWidth;
-            public double Margin { get; } = margin;
+    public readonly struct ImageSize(
+        double width,
+        double height,
+        double secondaryWidth,
+        double scrollViewerWidth,
+        double scrollViewerHeight,
+        double titleMaxWidth,
+        double margin,
+        double aspectRatio)
+    {
+        public double TitleMaxWidth { get; } = titleMaxWidth;
+        public double Width { get; } = width;
+        public double Height { get; } = height;
 
-            public double AspectRatio { get; } = aspectRatio;
-        }
+        public double ScrollViewerWidth { get; } = scrollViewerWidth;
+        public double ScrollViewerHeight { get; } = scrollViewerHeight;
+
+        public double SecondaryWidth { get; } = secondaryWidth;
+        public double Margin { get; } = margin;
+
+        public double AspectRatio { get; } = aspectRatio;
     }
 }

+ 27 - 28
src/PicView.Core/ImageDecoding/ImageFunctionHelper.cs

@@ -1,41 +1,40 @@
 using System.Diagnostics;
 using ImageMagick;
 
-namespace PicView.Core.ImageDecoding
+namespace PicView.Core.ImageDecoding;
+
+public static class ImageFunctionHelper
 {
-    public static class ImageFunctionHelper
+    /// <summary>
+    ///     Gets the number of frames in an image.
+    /// </summary>
+    /// <param name="file">The path to the image file.</param>
+    /// <returns>The number of frames in the image. Returns 0 if an error occurs.</returns>
+    /// <remarks>
+    ///     This method uses the Magick.NET library to load the image and retrieve the frame count.
+    /// </remarks>
+    public static int GetImageFrames(string file)
     {
-        /// <summary>
-        ///     Gets the number of frames in an image.
-        /// </summary>
-        /// <param name="file">The path to the image file.</param>
-        /// <returns>The number of frames in the image. Returns 0 if an error occurs.</returns>
-        /// <remarks>
-        ///     This method uses the Magick.NET library to load the image and retrieve the frame count.
-        /// </remarks>
-        public static int GetImageFrames(string file)
+        try
+        {
+            using var magickImageCollection = new MagickImageCollection();
+            magickImageCollection.Ping(file);
+            return magickImageCollection.Count;
+        }
+        catch (MagickException ex)
         {
-            try
-            {
-                using var magickImageCollection = new MagickImageCollection();
-                magickImageCollection.Ping(file);
-                return magickImageCollection.Count;
-            }
-            catch (MagickException ex)
-            {
 #if DEBUG
-                Trace.WriteLine($"{nameof(GetImageFrames)} Exception \n{ex}");
+            Trace.WriteLine($"{nameof(GetImageFrames)} Exception \n{ex}");
 #endif
 
-                return 0;
-            }
+            return 0;
         }
+    }
         
-        public static uint GetCompressionQuality(string file)
-        {
-            using var magickImage = new MagickImage();
-            magickImage.Ping(file);
-            return magickImage.Quality;
-        }
+    public static uint GetCompressionQuality(string file)
+    {
+        using var magickImage = new MagickImage();
+        magickImage.Ping(file);
+        return magickImage.Quality;
     }
 }