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 0000000..94e0fa7 Binary files /dev/null and b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.dll differ diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.xml b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.xml new file mode 100644 index 0000000..04c1aa8 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/LibAPNG.xml @@ -0,0 +1,176 @@ + + + + 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