From 2f12127cc9d6b8cde70d2e842f2b27852c1ec5e9 Mon Sep 17 00:00:00 2001 From: Paddy Xu Date: Mon, 31 Jul 2017 00:14:21 +0300 Subject: [PATCH] Use my home-made GIF and APNG animator. It's really save CPU compared to previous one! --- .../AnimatedImage/APNGAnimationProvider.cs | 108 +++++++++++ .../AnimatedImage/AnimatedImage.cs | 76 ++++++++ .../AnimatedImage/GIFAnimationProvider.cs | 182 ++++++++++++++++++ .../AnimatedImage/IAnimationProvider.cs | 26 +++ .../AnimatedImage/ImageMagickProvider.cs | 51 +++++ .../ImagePanel.xaml | 6 +- .../ImagePanel.xaml.cs | 22 ++- .../QuickLook.Plugin.ImageViewer/LibAPNG.dll | Bin 0 -> 20480 bytes .../QuickLook.Plugin.ImageViewer/LibAPNG.xml | 176 +++++++++++++++++ .../QuickLook.Plugin.ImageViewer/Plugin.cs | 31 +-- .../QuickLook.Plugin.ImageViewer.csproj | 11 +- .../packages.config | 2 +- 12 files changed, 658 insertions(+), 33 deletions(-) create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/APNGAnimationProvider.cs create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/GIFAnimationProvider.cs create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/IAnimationProvider.cs create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/ImageMagickProvider.cs create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.dll create mode 100644 QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.xml diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/APNGAnimationProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/APNGAnimationProvider.cs new file mode 100644 index 0000000..9e1b31f --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/APNGAnimationProvider.cs @@ -0,0 +1,108 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using LibAPNG; + +namespace QuickLook.Plugin.ImageViewer.AnimatedImage +{ + internal class APNGAnimationProvider : IAnimationProvider + { + public void GetAnimator(ObjectAnimationUsingKeyFrames animator, string path) + { + var decoder = new APNGBitmap(path); + + if (decoder.IsSimplePNG) + { + animator.KeyFrames.Add( + new DiscreteObjectKeyFrame(decoder.DefaultImage.GetBitmapSource(), TimeSpan.Zero)); + animator.Duration = Duration.Forever; + return; + } + + var clock = TimeSpan.Zero; + var header = decoder.IHDRChunk; + Frame prevFrame = null; + BitmapSource prevRenderedFrame = null; + foreach (var rawFrame in decoder.Frames) + { + var frame = MakeFrame(header, rawFrame, prevFrame, prevRenderedFrame); + prevFrame = rawFrame; + prevRenderedFrame = frame; + + var delay = TimeSpan.FromSeconds( + (double) rawFrame.fcTLChunk.DelayNum / + (rawFrame.fcTLChunk.DelayDen == 0 ? 100 : rawFrame.fcTLChunk.DelayDen)); + + animator.KeyFrames.Add(new DiscreteObjectKeyFrame(frame, clock)); + clock += delay; + } + + animator.Duration = clock; + animator.RepeatBehavior = RepeatBehavior.Forever; + } + + private static BitmapSource MakeFrame(IHDRChunk header, Frame rawFrame, Frame previousFrame, + BitmapSource previousRenderedFrame) + { + var visual = new DrawingVisual(); + using (var context = visual.RenderOpen()) + { + switch (rawFrame.fcTLChunk.DisposeOp) + { + case DisposeOps.APNGDisposeOpNone: + // restore previousRenderedFrame + //if (previousRenderedFrame != null) + //{ + // var fullRect = new Rect(0, 0, header.Width, header.Height); + // context.DrawImage(previousRenderedFrame, fullRect); + //} + break; + case DisposeOps.APNGDisposeOpPrevious: + // restore previousFrame + if (previousFrame != null) + { + var pFrameRect = new Rect(previousFrame.fcTLChunk.XOffset, + previousFrame.fcTLChunk.YOffset, + previousFrame.fcTLChunk.Width, previousFrame.fcTLChunk.Height); + context.DrawImage(previousFrame.GetBitmapSource(), pFrameRect); + } + break; + case DisposeOps.APNGDisposeOpBackground: + // do nothing + break; + } + + // draw current frame + var frameRect = new Rect(rawFrame.fcTLChunk.XOffset, rawFrame.fcTLChunk.YOffset, + rawFrame.fcTLChunk.Width, rawFrame.fcTLChunk.Height); + context.DrawImage(rawFrame.GetBitmapSource(), frameRect); + } + + var bitmap = new RenderTargetBitmap( + header.Width, header.Height, + 96, 96, + PixelFormats.Pbgra32); + bitmap.Render(visual); + return bitmap; + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs new file mode 100644 index 0000000..dd8ad35 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs @@ -0,0 +1,76 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.IO; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media.Animation; + +namespace QuickLook.Plugin.ImageViewer.AnimatedImage +{ + public class AnimatedImage : Image, IDisposable + { + public static readonly DependencyProperty AnimationUriProperty = + DependencyProperty.Register("AnimationUri", typeof(Uri), typeof(AnimatedImage), + new UIPropertyMetadata(null, LoadImage)); + + private readonly ObjectAnimationUsingKeyFrames _animator = new ObjectAnimationUsingKeyFrames(); + + public Uri AnimationUri + { + get => (Uri) GetValue(AnimationUriProperty); + set => SetValue(AnimationUriProperty, value); + } + + public void Dispose() + { + BeginAnimation(SourceProperty, null); + Source = null; + _animator.KeyFrames.Clear(); + } + + private static void LoadImage(DependencyObject obj, DependencyPropertyChangedEventArgs ev) + { + var instance = obj as AnimatedImage; + if (instance == null) + return; + + var path = ((Uri) ev.NewValue).LocalPath; + var ext = Path.GetExtension(path).ToLower(); + + IAnimationProvider provider; + + switch (ext) + { + case ".gif": + provider = new GIFAnimationProvider(); + break; + case ".png": + provider = new APNGAnimationProvider(); + break; + default: + provider = new ImageMagickProvider(); + break; + } + + provider.GetAnimator(instance._animator, path); + + instance.BeginAnimation(SourceProperty, instance._animator); + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/GIFAnimationProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/GIFAnimationProvider.cs new file mode 100644 index 0000000..6672b8f --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/GIFAnimationProvider.cs @@ -0,0 +1,182 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; + +namespace QuickLook.Plugin.ImageViewer.AnimatedImage +{ + internal class GIFAnimationProvider : IAnimationProvider + { + public void GetAnimator(ObjectAnimationUsingKeyFrames animator, string path) + { + var decoder = + new GifBitmapDecoder(new Uri(path), BitmapCreateOptions.PreservePixelFormat, + BitmapCacheOption.Default); + + var clock = TimeSpan.Zero; + BitmapSource prevFrame = null; + FrameInfo prevInfo = null; + foreach (var rawFrame in decoder.Frames) + { + var info = GetFrameInfo(rawFrame); + var frame = MakeFrame( + decoder.Frames[0], + rawFrame, info, + prevFrame, prevInfo); + prevFrame = frame; + prevInfo = info; + + animator.KeyFrames.Add(new DiscreteObjectKeyFrame(frame, clock)); + clock += info.Delay; + } + + animator.Duration = clock; + animator.RepeatBehavior = RepeatBehavior.Forever; + } + + #region private methods + + private static BitmapSource MakeFrame( + BitmapSource fullImage, + BitmapSource rawFrame, FrameInfo frameInfo, + BitmapSource previousFrame, FrameInfo previousFrameInfo) + { + var visual = new DrawingVisual(); + using (var context = visual.RenderOpen()) + { + if (previousFrameInfo != null && previousFrame != null && + previousFrameInfo.DisposalMethod == FrameDisposalMethod.Combine) + { + var fullRect = new Rect(0, 0, fullImage.PixelWidth, fullImage.PixelHeight); + context.DrawImage(previousFrame, fullRect); + } + + context.DrawImage(rawFrame, frameInfo.Rect); + } + var bitmap = new RenderTargetBitmap( + fullImage.PixelWidth, fullImage.PixelHeight, + fullImage.DpiX, fullImage.DpiY, + PixelFormats.Pbgra32); + bitmap.Render(visual); + return bitmap; + } + + private static FrameInfo GetFrameInfo(BitmapFrame frame) + { + var frameInfo = new FrameInfo + { + Delay = TimeSpan.FromMilliseconds(100), + DisposalMethod = FrameDisposalMethod.Replace, + Width = frame.PixelWidth, + Height = frame.PixelHeight, + Left = 0, + Top = 0 + }; + + try + { + if (frame.Metadata is BitmapMetadata metadata) + { + const string delayQuery = "/grctlext/Delay"; + const string disposalQuery = "/grctlext/Disposal"; + const string widthQuery = "/imgdesc/Width"; + const string heightQuery = "/imgdesc/Height"; + const string leftQuery = "/imgdesc/Left"; + const string topQuery = "/imgdesc/Top"; + + var delay = metadata.GetQueryOrNull(delayQuery); + if (delay.HasValue) + frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value); + + var disposal = metadata.GetQueryOrNull(disposalQuery); + if (disposal.HasValue) + frameInfo.DisposalMethod = (FrameDisposalMethod) disposal.Value; + + var width = metadata.GetQueryOrNull(widthQuery); + if (width.HasValue) + frameInfo.Width = width.Value; + + var height = metadata.GetQueryOrNull(heightQuery); + if (height.HasValue) + frameInfo.Height = height.Value; + + var left = metadata.GetQueryOrNull(leftQuery); + if (left.HasValue) + frameInfo.Left = left.Value; + + var top = metadata.GetQueryOrNull(topQuery); + if (top.HasValue) + frameInfo.Top = top.Value; + } + } + catch (NotSupportedException) + { + } + + return frameInfo; + } + + #endregion + + #region structs + + private class FrameInfo + { + public TimeSpan Delay { get; set; } + public FrameDisposalMethod DisposalMethod { get; set; } + public double Width { private get; set; } + public double Height { private get; set; } + public double Left { private get; set; } + public double Top { private get; set; } + + public Rect Rect => new Rect(Left, Top, Width, Height); + } + + private enum FrameDisposalMethod + { + Replace = 0, + Combine = 1, + RestoreBackground = 2, + RestorePrevious = 3 + } + + #endregion + } + + #region extensions + + public static class Extensions + { + public static T? GetQueryOrNull(this BitmapMetadata metadata, string query) + where T : struct + { + if (metadata.ContainsQuery(query)) + { + var value = metadata.GetQuery(query); + if (value != null) + return (T) value; + } + return null; + } + } + + #endregion +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/IAnimationProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/IAnimationProvider.cs new file mode 100644 index 0000000..a86f1cf --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/IAnimationProvider.cs @@ -0,0 +1,26 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Windows.Media.Animation; + +namespace QuickLook.Plugin.ImageViewer.AnimatedImage +{ + internal interface IAnimationProvider + { + void GetAnimator(ObjectAnimationUsingKeyFrames animator, string path); + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/ImageMagickProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/ImageMagickProvider.cs new file mode 100644 index 0000000..cb73e0c --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/ImageMagickProvider.cs @@ -0,0 +1,51 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.IO; +using System.Reflection; +using System.Windows; +using System.Windows.Media.Animation; +using ImageMagick; + +namespace QuickLook.Plugin.ImageViewer.AnimatedImage +{ + internal class ImageMagickProvider : IAnimationProvider + { + public void GetAnimator(ObjectAnimationUsingKeyFrames animator, string path) + { + // set dcraw.exe for Magick.NET + Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + + using (var image = new MagickImage(path)) + { + image.Rotate(image.Orientation == OrientationType.RightTop + ? 90 + : image.Orientation == OrientationType.BottomRight + ? 180 + : image.Orientation == OrientationType.LeftBotom + ? 270 + : 0); + + animator.KeyFrames.Add(new DiscreteObjectKeyFrame(image.ToBitmapSource(), TimeSpan.Zero)); + animator.Duration = Duration.Forever; + } + + Directory.SetCurrentDirectory(App.AppPath); + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml index b7e7ce1..240b2fc 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml @@ -4,14 +4,16 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:QuickLook.Plugin.ImageViewer" + xmlns:animatedImage="clr-namespace:QuickLook.Plugin.ImageViewer.AnimatedImage" mc:Ignorable="d" x:Name="imagePanel" d:DesignHeight="300" d:DesignWidth="300"> - + \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs index 6706cb7..0b92b86 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs @@ -34,9 +34,10 @@ namespace QuickLook.Plugin.ImageViewer /// /// Interaction logic for ImagePanel.xaml /// - public partial class ImagePanel : UserControl, INotifyPropertyChanged + public partial class ImagePanel : UserControl, INotifyPropertyChanged, IDisposable { private Point? _dragInitPos; + private Uri _imageSource; private DateTime _lastZoomTime = DateTime.MinValue; private double _maxZoomFactor = 3d; private double _minZoomFactor = 0.1d; @@ -111,6 +112,16 @@ namespace QuickLook.Plugin.ImageViewer } } + public Uri ImageUriSource + { + get => _imageSource; + set + { + _imageSource = value; + OnPropertyChanged(); + } + } + public BitmapSource Source { get => _source; @@ -118,9 +129,18 @@ namespace QuickLook.Plugin.ImageViewer { _source = value; OnPropertyChanged(); + + if (ImageUriSource == null) + viewPanelImage.Source = _source; } } + public void Dispose() + { + viewPanelImage?.Dispose(); + viewPanelImage = null; + } + public event PropertyChangedEventHandler PropertyChanged; public event EventHandler ImageScrolled; diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.dll b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.dll new file mode 100644 index 0000000000000000000000000000000000000000..94e0fa7794466219b72fe74cc5c0adb83ae710e1 GIT binary patch literal 20480 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 Xr2VfzrT)9%r}>Au|Nry + + + LibAPNG + + + + + Sequence number of the animation chunk, starting from 0 + + + + + Width of the following frame + + + + + Height of the following frame + + + + + X position at which to render the following frame + + + + + Y position at which to render the following frame + + + + + Frame delay fraction numerator + + + + + Frame delay fraction denominator + + + + + Type of frame area disposal to be done after rendering this frame + + + + + Type of frame area rendering for this frame + + + + + Indicate whether the file is a simple PNG. + + + + + Indicate whether the default image is part of the animation + + + + + Gets the base image. + If IsSimplePNG = True, returns the only image; + if False, returns the default image + + + + + Gets the frame array. + If IsSimplePNG = True, returns empty + + + + + Gets the IHDR Chunk + + + + + Gets the acTL Chunk + + + + + Get raw data of the chunk + + + + + Modify the ChunkData part. + + + + + Modify the ChunkData part. + + + + + Convert big-endian to little-endian or reserve + + + + + Convert big-endian to little-endian or reserve + + + + + Convert big-endian to little-endian or reserve + + + + + Convert big-endian to little-endian or reserve + + + + + Convert big-endian to little-endian or reserve + + + + + Compare two byte array + + + + + Describe a single frame. + + + + + Gets or Sets the acTL chunk + + + + + Gets or Sets the fcTL chunk + + + + + Gets or Sets the IEND chunk + + + + + Gets or Sets the other chunks + + + + + Gets or Sets the IDAT chunks + + + + + Add an Chunk to end end of existing list. + + + + + Add an IDAT Chunk to end end of existing list. + + + + + Gets the frame as PNG FileStream. + + + + diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs index 05b9da3..cb3acbb 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs @@ -18,10 +18,8 @@ using System; using System.IO; using System.Linq; -using System.Reflection; using System.Windows; using ImageMagick; -using XamlAnimatedGif; namespace QuickLook.Plugin.ImageViewer { @@ -35,7 +33,10 @@ namespace QuickLook.Plugin.ImageViewer ".orf", ".pef", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw", ".tif", ".x3f", // normal - ".bmp", ".ggg", ".ico", ".icon", ".jpg", ".jpeg", ".png", ".psd", ".svg", ".wdp", ".tiff", ".tga", ".webp" + ".bmp", ".ico", ".icon", ".jpg", ".jpeg", ".psd", ".svg", ".wdp", ".tif", ".tiff", ".tga", + ".webp", + // animated + ".png", ".apng", ".gif" }; private Size _imageSize; private ImagePanel _ip; @@ -58,9 +59,6 @@ namespace QuickLook.Plugin.ImageViewer public void Prepare(string path, ContextObject context) { - // set dcraw.exe for Magick.NET - Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); - _imageSize = ImageFileHelper.GetImageSize(path) ?? Size.Empty; if (!_imageSize.IsEmpty) @@ -73,9 +71,6 @@ namespace QuickLook.Plugin.ImageViewer public void View(string path, ContextObject context) { - // set dcraw.exe for Magick.NET - Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); - _ip = new ImagePanel(); context.ViewerContent = _ip; @@ -86,8 +81,6 @@ namespace QuickLook.Plugin.ImageViewer LoadImage(_ip, path); context.IsBusy = false; - - Directory.SetCurrentDirectory(App.AppPath); } public void Cleanup() @@ -97,21 +90,7 @@ namespace QuickLook.Plugin.ImageViewer private void LoadImage(ImagePanel ui, string path) { - if (Path.GetExtension(path).ToLower() == ".gif") - AnimationBehavior.SetSourceUri(ui.viewPanelImage, new Uri(path)); - - using (var image = new MagickImage(path)) - { - image.Rotate(image.Orientation == OrientationType.RightTop - ? 90 - : image.Orientation == OrientationType.BottomRight - ? 180 - : image.Orientation == OrientationType.LeftBotom - ? 270 - : 0); - - ui.Source = image.ToBitmapSource(); - } + ui.ImageUriSource = new Uri(path); } } } \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj index 26c7abf..797201a 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj @@ -55,6 +55,9 @@ ..\..\packages\ExifLib.1.7.0.0\lib\net45\ExifLib.dll + + .\LibAPNG.dll + ..\..\packages\Magick.NET-Q8-AnyCPU.7.0.6.102\lib\net40\Magick.NET-Q8-AnyCPU.dll @@ -65,14 +68,16 @@ - - ..\..\packages\XamlAnimatedGif.1.1.10\lib\net45\XamlAnimatedGif.dll - Properties\GitVersion.cs + + + + + ImagePanel.xaml diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config index 89825f7..47282b4 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config @@ -1,6 +1,6 @@  + - \ No newline at end of file