浏览代码

Refactor image handling and drag-and-drop logic.

Ruben 4 月之前
父节点
当前提交
82672098d7

+ 255 - 173
src/PicView.Avalonia/DragAndDrop/DragAndDropHelper.cs

@@ -19,13 +19,14 @@ namespace PicView.Avalonia.DragAndDrop;
 public static class DragAndDropHelper
 {
     private static DragDropView? _dragDropView;
-
     private static PreLoadValue? _preLoadValue;
 
+    #region Public Entry Points
+
     public static async Task Drop(DragEventArgs e, MainViewModel vm)
     {
         RemoveDragDropView();
-        
+
         var files = e.Data.GetFiles();
         if (files == null)
         {
@@ -35,77 +36,90 @@ public static class DragAndDropHelper
 
         var storageItems = files as IStorageItem[] ?? files.ToArray();
         var firstFile = storageItems.FirstOrDefault();
+        if (firstFile == null)
+        {
+            return;
+        }
+        
+        // Handle opening additional files in new windows if needed
+        _ = Task.Run(() => HandleAdditionalFiles(storageItems.Skip(1)));
+
         var path = firstFile.Path.LocalPath;
+
         if (e.Data.Contains("text/x-moz-url"))
         {
             await HandleDropFromUrl(e, vm);
-            if (vm.CurrentView != vm.ImageViewer)
-            {
-                await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
-            }
+            await EnsureImageViewerDisplayed(vm);
         }
         else if (path.IsSupported())
         {
-            if (vm.CurrentView != vm.ImageViewer)
-            {
-                await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
-            }
-            
-            if (_preLoadValue is not null)
-            {
-                
-                if (_preLoadValue.ImageModel.FileInfo.DirectoryName == Path.GetDirectoryName(path))
-                {
-                    // Check for edge case error
-                    var isAddedToPreloader = NavigationManager.AddToPreloader(path, _preLoadValue.ImageModel);
-                    if (isAddedToPreloader)
-                    {
-                        NavigationManager.ImageIterator.Resynchronize();
-                        await NavigationManager.LoadPicFromFile(path, vm, _preLoadValue.ImageModel.FileInfo);
-                    }
-                    else
-                    {
-                        await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
-                    }
-                }
-                else
-                {
-                    await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
-                }
-            }
-            else
-            {
-                await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
-            }
-            
+            await EnsureImageViewerDisplayed(vm);
+            await LoadSupportedFile(path, vm);
         }
         else if (Directory.Exists(path))
         {
-            if (vm.CurrentView != vm.ImageViewer)
-            {
-                await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
-            }
+            await EnsureImageViewerDisplayed(vm);
             await NavigationManager.LoadPicFromDirectoryAsync(path, vm).ConfigureAwait(false);
-
         }
         else if (path.IsArchive())
         {
-            if (vm.CurrentView != vm.ImageViewer)
+            await EnsureImageViewerDisplayed(vm);
+            await NavigationManager.LoadPicFromArchiveAsync(path, vm).ConfigureAwait(false);
+        }
+    }
+
+    public static async Task DragEnter(DragEventArgs e, MainViewModel vm, Control control)
+    {
+        var files = e.Data.GetFiles();
+        if (files != null)
+        {
+            await HandleDragEnterWithFiles(files, vm, control);
+        }
+        else
+        {
+            // Try handling as URL
+            var handled = await HandleDragEnterFromUrl(e.Data.Get("text/x-moz-url"), vm);
+            if (!handled)
             {
-                await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
+                RemoveDragDropView();
             }
-            await NavigationManager.LoadPicFromArchiveAsync(path, vm).ConfigureAwait(false);
         }
+    }
 
-        if (!Settings.UIProperties.OpenInSameWindow)
+    public static void DragLeave(DragEventArgs e, Control control)
+    {
+        if (control.IsPointerOver)
         {
-            foreach (var file in storageItems.Skip(1))
+            return;
+        }
+
+        RemoveDragDropView();
+        _preLoadValue = null;
+    }
+
+    public static void RemoveDragDropView()
+    {
+        UIHelper.GetMainView.MainGrid.Children.Remove(_dragDropView);
+        _dragDropView = null;
+    }
+
+    #endregion
+
+    #region Private Helpers
+
+    private static void HandleAdditionalFiles(IEnumerable<IStorageItem> additionalFiles)
+    {
+        if (Settings.UIProperties.OpenInSameWindow)
+        {
+            return;
+        }
+
+        foreach (var file in additionalFiles)
+        {
+            var filepath = file.Path.LocalPath;
+            if (filepath.IsSupported())
             {
-                var filepath = file.Path.LocalPath;
-                if (filepath.IsSupported())
-                {
-                    ProcessHelper.StartNewProcess(filepath);
-                }
+                ProcessHelper.StartNewProcess(filepath);
             }
         }
     }
@@ -119,157 +133,179 @@ public static class DragAndDropHelper
             var url = dataStr.Split((char)10).FirstOrDefault();
             if (url != null)
             {
-                if (url.StartsWith("file://"))
-                {
-                    var file = url[7..];
-                    if (file.StartsWith('/'))
-                    {
-                        file = file[1..];
-                    }
-                    if (file.IsArchive())
-                    {
-                        await NavigationManager.LoadPicFromArchiveAsync(file, vm).ConfigureAwait(false);
-                    }
-                    else
-                    {
-                        await NavigationManager.LoadPicFromFile(file, vm).ConfigureAwait(false);
-                    }
-                }
-                else
-                {
-                    await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
-                }
+                await LoadFromUrl(url, vm);
             }
         }
     }
 
-    public static async Task DragEnter(DragEventArgs e, MainViewModel vm, Control control) =>
-        await HandleDragEnter(e.Data.GetFiles(), e, vm, control);
-
-    private static async Task HandleDragEnter(IEnumerable<IStorageItem> files, DragEventArgs e, MainViewModel vm, Control control)
+    private static async Task LoadFromUrl(string url, MainViewModel vm)
     {
-        IStorageItem[]? fileArray = null;
-        if (files is not null)
+        // Remove preview first and show loading
+        RemoveDragDropView();
+        vm.IsLoading = true;
+        if (vm.ImageViewer?.MainImage != null)
         {
-           fileArray = files as IStorageItem[] ?? files.ToArray();
+            vm.ImageViewer.MainImage.Source = null;
         }
 
-        await Dispatcher.UIThread.InvokeAsync(() =>
+        if (url.StartsWith("file://"))
         {
-            if (_dragDropView == null)
+            var file = url[7..];
+            if (file.StartsWith('/'))
             {
-                _dragDropView = new DragDropView
-                {
-                    DataContext = vm
-                };
-                if (!control.IsPointerOver)
-                {
-                    UIHelper.GetMainView.MainGrid.Children.Add(_dragDropView);
-                }
+                file = file[1..];
             }
-            else
+
+            if (file.IsArchive())
             {
-                _dragDropView.RemoveThumbnail();
+                await NavigationManager.LoadPicFromArchiveAsync(file, vm).ConfigureAwait(false);
             }
-        });
-        if (fileArray is null)
-        {
-            var handledFromUrl = await HandleDragEnterFromUrl(e, vm);
-            if (!handledFromUrl)
+            else
             {
-                RemoveDragDropView();
+                await NavigationManager.LoadPicFromFile(file, vm).ConfigureAwait(false);
             }
+        }
+        else
+        {
+            await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
+        }
+    }
+
+    private static async Task HandleDragEnterWithFiles(IEnumerable<IStorageItem> files, MainViewModel vm,
+        Control control)
+    {
+        var fileArray = files as IStorageItem[] ?? files.ToArray();
+        if (fileArray.Length == 0)
+        {
             return;
         }
+
+        await EnsureDragDropViewCreated(vm, control);
+
         var firstFile = fileArray[0];
         var path = firstFile.Path.LocalPath;
+
         if (Directory.Exists(path))
         {
-            await Dispatcher.UIThread.InvokeAsync(() =>
-            {
-                if (!control.IsPointerOver)
-                {
-                    _dragDropView.AddDirectoryIcon();
-                }
-            });
+            await ShowDirectoryIcon(control);
         }
-        else
+        else if (path.IsArchive())
+        {
+            await ShowArchiveIcon(control);
+        }
+        else if (path.IsSupported())
+        {
+            await ShowFilePreview(path, control);
+        }
+    }
+
+    private static async Task ShowDirectoryIcon(Control control)
+    {
+        await Dispatcher.UIThread.InvokeAsync(() =>
         {
-            if (path.IsArchive())
+            if (!control.IsPointerOver)
             {
-                if (!control.IsPointerOver)
-                {
-                    _dragDropView.AddZipIcon();
-                }
+                _dragDropView?.AddDirectoryIcon();
+            }
+        });
+    }
+
+    private static async Task ShowArchiveIcon(Control control)
+    {
+        await Dispatcher.UIThread.InvokeAsync(() =>
+        {
+            if (!control.IsPointerOver)
+            {
+                _dragDropView?.AddZipIcon();
             }
-            else if (path.IsSupported())
+        });
+    }
+
+    private static async Task ShowFilePreview(string path, Control control)
+    {
+        var ext = Path.GetExtension(path);
+        if (ext.Equals(".svg", StringComparison.InvariantCultureIgnoreCase) ||
+            ext.Equals(".svgz", StringComparison.InvariantCultureIgnoreCase))
+        {
+            await Dispatcher.UIThread.InvokeAsync(() => _dragDropView?.UpdateSvgThumbnail(path));
+            return;
+        }
+
+        await LoadAndShowThumbnail(path);
+    }
+
+    private static async Task LoadAndShowThumbnail(string path)
+    {
+        Bitmap? thumb;
+        // Try to get preloaded image first
+        var preload = NavigationManager.GetPreLoadValue(path);
+        if (preload?.ImageModel?.Image is Bitmap bmp)
+        {
+            thumb = bmp;
+        }
+        else
+        {
+            // Generate thumbnail
+            thumb = await GetThumbnails.GetThumbAsync(path, SizeDefaults.WindowMinSize - 30)
+                .ConfigureAwait(false);
+        }
+
+        await UpdateThumbnailUI(thumb);
+
+        // Load full image in background
+        await PreloadFullImage(path, preload, thumb);
+    }
+
+    private static async Task PreloadFullImage(string path, PreLoadValue? preload, Bitmap? thumb)
+    {
+        await Task.Run(async () =>
+        {
+            var fileInfo = new FileInfo(path);
+            var sameDirectory = fileInfo.DirectoryName ==
+                                NavigationManager.ImageIterator.InitialFileInfo.DirectoryName;
+
+            if (sameDirectory)
             {
-                var ext = Path.GetExtension(path);
-                if (ext.Equals(".svg", StringComparison.InvariantCultureIgnoreCase) || ext.Equals(".svgz", StringComparison.InvariantCultureIgnoreCase))
+                if (preload is null)
                 {
-                    await Dispatcher.UIThread.InvokeAsync(() =>
+                    _preLoadValue = await NavigationManager.GetPreLoadValueAsync(path);
+                    thumb = _preLoadValue.ImageModel.Image as Bitmap;
+                    if (thumb is not null)
                     {
-                        _dragDropView?.UpdateSvgThumbnail(path);
-                    });
+                        await UpdateThumbnailUI(thumb);
+                    }
                 }
                 else
                 {
-                    Bitmap? thumb;
-                    // ReSharper disable once MethodHasAsyncOverload
-                    var preload = NavigationManager.GetPreLoadValue(path);
-                    if (preload?.ImageModel?.Image is Bitmap bmp)
-                    {
-                        thumb = bmp;
-                    }
-                    else
-                    {
-                        thumb = await GetThumbnails.GetThumbAsync(path, SizeDefaults.WindowMinSize - 30)
-                            .ConfigureAwait(false);
-                    }
-                    await Dispatcher.UIThread.InvokeAsync(() => { _dragDropView?.UpdateThumbnail(thumb); });
-                    await Task.Run(async () =>
-                    {
-                        var fileInfo = new FileInfo(path);
-                        if (fileInfo.DirectoryName == NavigationManager.ImageIterator.InitialFileInfo.DirectoryName)
-                        {
-                            if (preload is null)
-                            {
-                                _preLoadValue = await NavigationManager.GetPreLoadValueAsync(path);
-                                thumb = _preLoadValue.ImageModel.Image as Bitmap;
-                                if (thumb is not null)
-                                {
-                                    await Dispatcher.UIThread.InvokeAsync(() =>
-                                    {
-                                        _dragDropView?.UpdateThumbnail(thumb);
-                                    }, DispatcherPriority.Loaded);
-                                }
-                            }
-                            else
-                            {
-                                _preLoadValue = preload;
-                            }
-                        }
-                        else if (preload is null)
-                        {
-                            thumb = await GetImage.GetDefaultBitmapAsync(fileInfo);
-                            await Dispatcher.UIThread.InvokeAsync(() =>
-                            {
-                                _dragDropView?.UpdateThumbnail(thumb);
-                            }, DispatcherPriority.Render);
-                        }
-                    });
+                    _preLoadValue = preload;
+                    await UpdateThumbnailUI(thumb);
                 }
             }
-        }
+            else if (preload is null)
+            {
+                var model = await GetImageModel.GetImageModelAsync(fileInfo);
+                await UpdateThumbnailUI(thumb);
+                _preLoadValue = new PreLoadValue(model);
+            }
+            else
+            {
+                _preLoadValue = preload;
+                await UpdateThumbnailUI(thumb);
+            }
+        });
     }
-    
+
+    private static async Task UpdateThumbnailUI(Bitmap? thumb) =>
+        await Dispatcher.UIThread.InvokeAsync(() => _dragDropView?.UpdateThumbnail(thumb));
+
     private static async Task<bool> HandleDragEnterFromUrl(object? urlObject, MainViewModel vm)
     {
         if (urlObject is null)
         {
-            _dragDropView.RemoveThumbnail();
+            _dragDropView?.RemoveThumbnail();
             return false;
         }
+
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
             _dragDropView ??= new DragDropView { DataContext = vm };
@@ -277,28 +313,74 @@ public static class DragAndDropHelper
             {
                 _dragDropView.AddLinkChain();
             }
+
             if (!UIHelper.GetMainView.MainGrid.Children.Contains(_dragDropView))
             {
                 UIHelper.GetMainView.MainGrid.Children.Add(_dragDropView);
             }
         });
+
         return true;
     }
 
-    public static void DragLeave(DragEventArgs e, Control control)
+    private static async Task EnsureDragDropViewCreated(MainViewModel vm, Control control)
     {
-        if (control.IsPointerOver)
+        await Dispatcher.UIThread.InvokeAsync(() =>
         {
-            return;
-        }
+            if (_dragDropView == null)
+            {
+                _dragDropView = new DragDropView { DataContext = vm };
+                if (!control.IsPointerOver)
+                {
+                    UIHelper.GetMainView.MainGrid.Children.Add(_dragDropView);
+                }
+            }
+            else
+            {
+                _dragDropView.RemoveThumbnail();
+            }
+        });
+    }
 
-        RemoveDragDropView();
-        _preLoadValue = null;
+    private static async Task LoadSupportedFile(string path, MainViewModel vm)
+    {
+        if (_preLoadValue is not null)
+        {
+            var currentDirectory = Path.GetDirectoryName(path);
+            var preloadDirectory = _preLoadValue.ImageModel.FileInfo.DirectoryName;
+
+            if (currentDirectory == preloadDirectory)
+            {
+                // Check for edge case error
+                var isAddedToPreloader = NavigationManager.AddToPreloader(path, _preLoadValue.ImageModel);
+                if (isAddedToPreloader)
+                {
+                    NavigationManager.ImageIterator.Resynchronize();
+                    await NavigationManager.LoadPicFromFile(path, vm, _preLoadValue.ImageModel.FileInfo);
+                }
+                else
+                {
+                    await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
+                }
+            }
+            else
+            {
+                await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
+            }
+        }
+        else
+        {
+            await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
+        }
     }
 
-    public static void RemoveDragDropView()
+    private static async Task EnsureImageViewerDisplayed(MainViewModel vm)
     {
-        UIHelper.GetMainView.MainGrid.Children.Remove(_dragDropView);
-        _dragDropView = null;
+        if (vm.CurrentView != vm.ImageViewer)
+        {
+            await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
+        }
     }
+
+    #endregion
 }

+ 75 - 106
src/PicView.Avalonia/ImageHandling/GetImageModel.cs

@@ -1,5 +1,6 @@
 using Avalonia.Media.Imaging;
 using ImageMagick;
+using PicView.Core.DebugTools;
 using PicView.Core.ImageDecoding;
 
 namespace PicView.Avalonia.ImageHandling;
@@ -7,76 +8,82 @@ namespace PicView.Avalonia.ImageHandling;
 public static class GetImageModel
 {
     // Group extensions by how they should be handled
-    private static readonly Dictionary<string, ImageHandler> ExtensionHandlers = new()
+    private static readonly Dictionary<string, Func<FileInfo, ImageModel, Task>> ExtensionHandlers = new()
     {
-        { ".webp", new AnimatedWebpHandler() },
-        { ".gif", new AnimatedGifHandler() },
-        { ".png", new StandardBitmapHandler() },
-        { ".jpg", new StandardBitmapHandler() },
-        { ".jpeg", new StandardBitmapHandler() },
-        { ".jpe", new StandardBitmapHandler() },
-        { ".bmp", new StandardBitmapHandler() },
-        { ".jfif", new StandardBitmapHandler() },
-        { ".ico", new StandardBitmapHandler() },
-        { ".wbmp", new StandardBitmapHandler() },
-        { ".svg", new SvgHandler() },
-        { ".svgz", new SvgHandler() },
-        { ".b64", new Base64Handler() }
+        { ".webp", ProcessAnimatedWebpAsync },
+        { ".gif", ProcessAnimatedGifAsync },
+        { ".png", ProcessStandardBitmapAsync },
+        { ".jpg", ProcessStandardBitmapAsync },
+        { ".jpeg", ProcessStandardBitmapAsync },
+        { ".jpe", ProcessStandardBitmapAsync },
+        { ".bmp", ProcessStandardBitmapAsync },
+        { ".jfif", ProcessStandardBitmapAsync },
+        { ".ico", ProcessStandardBitmapAsync },
+        { ".wbmp", ProcessStandardBitmapAsync },
+        { ".svg", ProcessSvgAsync },
+        { ".svgz", ProcessSvgAsync },
+        { ".b64", ProcessBase64Async }
     };
 
     public static async Task<ImageModel> GetImageModelAsync(FileInfo fileInfo, MagickImage? magickImage = null)
     {
         if (fileInfo is null)
         {
-            LogError("fileInfo is null");
+            DebugHelper.LogDebug(nameof(GetImageModel), nameof(GetImageModelAsync), "fileInfo is null");
             return CreateErrorImageModel(null);
         }
 
         var imageModel = new ImageModel { FileInfo = fileInfo };
+        var shouldDisposeMagickImage = magickImage is null;
 
         try
         {
-            // Get extension and prepare MagickImage for metadata
-            var ext = fileInfo.Extension.ToLower();
-            if (magickImage is null)
-            {
-                magickImage = new MagickImage();
-                magickImage.Ping(fileInfo);
-            }
+            // Initialize MagickImage if not provided
+            magickImage ??= CreateAndPingMagickImage(fileInfo);
 
-            // Extract EXIF orientation early
-            imageModel.EXIFOrientation = EXIFHelper.GetImageOrientation(magickImage);
-            imageModel.Format = magickImage.Format;
+            // Extract metadata early
+            ExtractImageMetadata(magickImage, imageModel);
 
             // Process the image based on extension
-            if (ExtensionHandlers.TryGetValue(ext, out var handler))
-            {
-                await handler.ProcessImageAsync(fileInfo, imageModel).ConfigureAwait(false);
-            }
-            else
-            {
-                // Unknown format - try default handler
-                await new DefaultImageHandler().ProcessImageAsync(fileInfo, imageModel).ConfigureAwait(false);
-            }
+            var ext = fileInfo.Extension.ToLowerInvariant();
+            var processor = ExtensionHandlers.GetValueOrDefault(ext, ProcessDefaultImageAsync);
+            await processor(fileInfo, imageModel).ConfigureAwait(false);
 
             return imageModel;
         }
         catch (Exception e)
         {
-            LogError($"Error processing {fileInfo.Name}: {e.Message}");
+            DebugHelper.LogDebug(nameof(GetImageModel), nameof(GetImageModelAsync), e);
             return CreateErrorImageModel(fileInfo);
         }
         finally
         {
-            magickImage.Dispose();
+            if (shouldDisposeMagickImage)
+            {
+                magickImage?.Dispose();
+            }
         }
     }
 
-    private static void LogError(string message)
+    private static MagickImage CreateAndPingMagickImage(FileInfo fileInfo)
     {
-#if DEBUG
-        Console.WriteLine($"Error: {nameof(GetImageModel)}:{nameof(GetImageModelAsync)}: {message}");
-#endif
+        var magickImage = new MagickImage();
+        magickImage.Ping(fileInfo);
+        return magickImage;
+    }
+
+    private static void ExtractImageMetadata(MagickImage magickImage, ImageModel imageModel)
+    {
+        imageModel.EXIFOrientation = EXIFHelper.GetImageOrientation(magickImage);
+        imageModel.Format = magickImage.Format;
+    }
+
+    private static void SetBitmapProperties(Bitmap? bitmap, ImageModel imageModel, ImageType imageType = ImageType.Bitmap)
+    {
+        imageModel.Image = bitmap;
+        imageModel.PixelWidth = bitmap?.PixelSize.Width ?? 0;
+        imageModel.PixelHeight = bitmap?.PixelSize.Height ?? 0;
+        imageModel.ImageType = imageType;
     }
 
     private static ImageModel CreateErrorImageModel(FileInfo? fileInfo)
@@ -92,89 +99,51 @@ public static class GetImageModel
         };
     }
 
-    #region Image Handlers
-
-    // Base abstract class for all image handlers
-    private abstract class ImageHandler
-    {
-        public abstract Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel);
-
-        protected static void SetBitmapModel(Bitmap bitmap, ImageModel imageModel, ImageType imageType = ImageType.Bitmap)
-        {
-            imageModel.Image = bitmap;
-            imageModel.PixelWidth = bitmap?.PixelSize.Width ?? 0;
-            imageModel.PixelHeight = bitmap?.PixelSize.Height ?? 0;
-            imageModel.ImageType = imageType;
-        }
-    }
+    #region Image Processing Methods
 
-    // Handler for standard bitmap formats
-    private class StandardBitmapHandler : ImageHandler
+    private static async Task ProcessStandardBitmapAsync(FileInfo fileInfo, ImageModel imageModel)
     {
-        public override async Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel)
-        {
-            var bitmap = await GetImage.GetStandardBitmapAsync(fileInfo).ConfigureAwait(false);
-            SetBitmapModel(bitmap, imageModel);
-        }
+        var bitmap = await GetImage.GetStandardBitmapAsync(fileInfo).ConfigureAwait(false);
+        SetBitmapProperties(bitmap, imageModel);
     }
 
-    // Handler for animated WebP
-    private class AnimatedWebpHandler : ImageHandler
+    private static async Task ProcessAnimatedWebpAsync(FileInfo fileInfo, ImageModel imageModel)
     {
-        public override async Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel)
-        {
-            var bitmap = await GetImage.GetStandardBitmapAsync(fileInfo).ConfigureAwait(false);
-            SetBitmapModel(bitmap, imageModel, 
-                ImageAnalyzer.IsAnimated(fileInfo) ? ImageType.AnimatedWebp : ImageType.Bitmap);
-        }
+        var bitmap = await GetImage.GetStandardBitmapAsync(fileInfo).ConfigureAwait(false);
+        var imageType = ImageAnalyzer.IsAnimated(fileInfo) ? ImageType.AnimatedWebp : ImageType.Bitmap;
+        SetBitmapProperties(bitmap, imageModel, imageType);
     }
 
-    // Handler for animated GIF
-    private class AnimatedGifHandler : ImageHandler
+    private static async Task ProcessAnimatedGifAsync(FileInfo fileInfo, ImageModel imageModel)
     {
-        public override async Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel)
-        {
-            var bitmap = await GetImage.GetStandardBitmapAsync(fileInfo).ConfigureAwait(false);
-            SetBitmapModel(bitmap, imageModel, 
-                ImageAnalyzer.IsAnimated(fileInfo) ? ImageType.AnimatedGif : ImageType.Bitmap);
-        }
+        var bitmap = await GetImage.GetStandardBitmapAsync(fileInfo).ConfigureAwait(false);
+        var imageType = ImageAnalyzer.IsAnimated(fileInfo) ? ImageType.AnimatedGif : ImageType.Bitmap;
+        SetBitmapProperties(bitmap, imageModel, imageType);
     }
 
-    // Handler for SVG images
-    private class SvgHandler : ImageHandler
+    private static Task ProcessSvgAsync(FileInfo fileInfo, ImageModel imageModel)
     {
-        public override Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel)
-        {
-            using var svg = new MagickImage();
-            svg.Ping(fileInfo.FullName);
-            
-            imageModel.PixelWidth = (int)svg.Width;
-            imageModel.PixelHeight = (int)svg.Height;
-            imageModel.ImageType = ImageType.Svg;
-            imageModel.Image = fileInfo.FullName;
-            
-            return Task.CompletedTask;
-        }
+        using var svg = new MagickImage();
+        svg.Ping(fileInfo.FullName);
+        
+        imageModel.PixelWidth = (int)svg.Width;
+        imageModel.PixelHeight = (int)svg.Height;
+        imageModel.ImageType = ImageType.Svg;
+        imageModel.Image = fileInfo.FullName;
+        
+        return Task.CompletedTask;
     }
 
-    // Handler for Base64 encoded images
-    private class Base64Handler : ImageHandler
+    private static async Task ProcessBase64Async(FileInfo fileInfo, ImageModel imageModel)
     {
-        public override async Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel)
-        {
-            var bitmap = await GetImage.GetBase64ImageAsync(fileInfo).ConfigureAwait(false);
-            SetBitmapModel(bitmap, imageModel);
-        }
+        var bitmap = await GetImage.GetBase64ImageAsync(fileInfo).ConfigureAwait(false);
+        SetBitmapProperties(bitmap, imageModel);
     }
 
-    // Default handler for unknown formats
-    private class DefaultImageHandler : ImageHandler
+    private static async Task ProcessDefaultImageAsync(FileInfo fileInfo, ImageModel imageModel)
     {
-        public override async Task ProcessImageAsync(FileInfo fileInfo, ImageModel imageModel)
-        {
-            var bitmap = await GetImage.GetDefaultBitmapAsync(fileInfo).ConfigureAwait(false);
-            SetBitmapModel(bitmap, imageModel);
-        }
+        var bitmap = await GetImage.GetDefaultBitmapAsync(fileInfo).ConfigureAwait(false);
+        SetBitmapProperties(bitmap, imageModel);
     }
 
     #endregion

+ 2 - 20
src/PicView.Avalonia/ImageHandling/GetThumbnails.cs

@@ -62,6 +62,7 @@ public static class GetThumbnails
 
         var profile = magick.GetExifProfile();
         var thumbnail = profile?.CreateThumbnail();
+        thumbnail?.AutoOrient();
         return thumbnail?.ToWriteableBitmap();
     }
 
@@ -76,25 +77,6 @@ public static class GetThumbnails
         fileInfo ??= new FileInfo(path);
         await using var fileStream = FileHelper.GetOptimizedFileStream(fileInfo);
 
-        switch (Path.GetExtension(path).ToLowerInvariant())
-        {
-            case ".webp":
-            case ".png":
-            case ".jpg":
-            case ".jpeg":
-            case ".jpe":
-            case ".bmp":
-            case ".jfif":
-            case ".ico":
-            case ".wbmp":
-                return Bitmap.DecodeToHeight(fileStream, (int)height);
-
-            case ".svg":
-            case ".svgz":
-                return null;
-        }
-        
-
         if (fileInfo.Length >= 2147483648)
         {
             // Fixes "The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size."
@@ -107,8 +89,8 @@ public static class GetThumbnails
         }
 
         var geometry = new MagickGeometry(0, height);
-        magick.Thumbnail(geometry);
         magick.AutoOrient();
+        magick.Thumbnail(geometry);
         return magick.ToWriteableBitmap();
     }
 }

+ 1 - 1
src/PicView.Avalonia/ImageHandling/ImageModel.cs

@@ -14,7 +14,7 @@ public class ImageModel
     
     public MagickFormat? Format { get; set; }
 
-    public double Rotation
+    public int Rotation
     {
         get
         {