diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs index dd8ad35..3a527a4 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs @@ -16,10 +16,16 @@ // along with this program. If not, see . using System; +using System.Diagnostics; using System.IO; +using System.Threading; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Animation; +using System.Windows.Threading; +using QuickLook.Helpers; +using QuickLook.Plugin.ImageViewer.Exiv2; namespace QuickLook.Plugin.ImageViewer.AnimatedImage { @@ -29,7 +35,11 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage DependencyProperty.Register("AnimationUri", typeof(Uri), typeof(AnimatedImage), new UIPropertyMetadata(null, LoadImage)); - private readonly ObjectAnimationUsingKeyFrames _animator = new ObjectAnimationUsingKeyFrames(); + public static readonly DependencyProperty MetaProperty = + DependencyProperty.Register("Meta", typeof(Meta), typeof(AnimatedImage)); + + private ObjectAnimationUsingKeyFrames _animator = new ObjectAnimationUsingKeyFrames(); + private bool _disposed; public Uri AnimationUri { @@ -37,11 +47,19 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage set => SetValue(AnimationUriProperty, value); } + public Meta Meta + { + private get => (Meta) GetValue(MetaProperty); + set => SetValue(MetaProperty, value); + } + public void Dispose() { BeginAnimation(SourceProperty, null); Source = null; _animator.KeyFrames.Clear(); + + _disposed = true; } private static void LoadImage(DependencyObject obj, DependencyPropertyChangedEventArgs ev) @@ -50,8 +68,55 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage if (instance == null) return; - var path = ((Uri) ev.NewValue).LocalPath; - var ext = Path.GetExtension(path).ToLower(); + var thumbnail = instance.Meta?.GetThumbnail(true); + instance.Source = thumbnail; + + if (thumbnail != null) + LoadFullImageAsync(obj, ev); + else + LoadFullImage(obj, ev); + } + + private static void LoadFullImage(DependencyObject obj, DependencyPropertyChangedEventArgs ev) + { + var instance = obj as AnimatedImage; + if (instance == null) + return; + + instance._animator = LoadFullImageCore((Uri) ev.NewValue); + instance.BeginAnimation(SourceProperty, instance._animator); + } + + private static void LoadFullImageAsync(DependencyObject obj, DependencyPropertyChangedEventArgs ev) + { + Task.Run(() => + { + var instance = obj as AnimatedImage; + if (instance == null) + return; + + var animator = LoadFullImageCore((Uri) ev.NewValue); + + instance.Dispatcher.Invoke(DispatcherPriority.Render, + new Action(() => + { + if (instance._disposed) + { + ProcessHelper.PerformAggressiveGC(); + return; + } + + instance._animator = animator; + instance.BeginAnimation(SourceProperty, instance._animator); + + Debug.WriteLine($"LoadFullImageAsync {Thread.CurrentThread.ManagedThreadId}"); + })); + }); + } + + private static ObjectAnimationUsingKeyFrames LoadFullImageCore(Uri path) + { + var ext = Path.GetExtension(path.LocalPath).ToLower(); IAnimationProvider provider; @@ -67,10 +132,11 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage provider = new ImageMagickProvider(); break; } + var animator = new ObjectAnimationUsingKeyFrames(); + provider.GetAnimator(animator, path.LocalPath); + animator.Freeze(); - provider.GetAnimator(instance._animator, path); - - instance.BeginAnimation(SourceProperty, instance._animator); + return animator; } } } \ 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 index ace4971..620ecb1 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/ImageMagickProvider.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/ImageMagickProvider.cs @@ -28,9 +28,6 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage { 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.Density = new Density(Math.Floor(image.Density.X), Math.Floor(image.Density.Y)); @@ -39,8 +36,6 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage 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/ImageFileHelper.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImageFileHelper.cs index 7c3d0d2..245eeaa 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImageFileHelper.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImageFileHelper.cs @@ -15,50 +15,33 @@ // 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 ExifLib; using ImageMagick; +using QuickLook.Plugin.ImageViewer.Exiv2; namespace QuickLook.Plugin.ImageViewer { internal static class ImageFileHelper { - internal static Size? GetImageSize(string path) + internal static Size GetImageSize(string path, Meta meta) { - var ori = GetOrientationFromExif(path); + var size = meta.GetSize(); + + if (!size.IsEmpty) + return size; try { var info = new MagickImageInfo(path); - if (ori == OrientationType.RightTop || ori == OrientationType.LeftBotom) + if (meta.GetOrientation() == OrientationType.RightTop || + meta.GetOrientation() == OrientationType.LeftBotom) return new Size {Width = info.Height, Height = info.Width}; return new Size {Width = info.Width, Height = info.Height}; } catch (MagickException) { - return null; - } - } - - private static OrientationType GetOrientationFromExif(string path) - { - try - { - using (var re = new ExifReader(path)) - { - re.GetTagValue(ExifTags.Orientation, out ushort orientation); - - if (orientation == 0) - return OrientationType.Undefined; - - return (OrientationType) orientation; - } - } - catch (Exception) - { - return OrientationType.Undefined; + return Size.Empty; } } } diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml index 0b968f9..6d6e4e8 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml @@ -18,7 +18,7 @@ - diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs index 27a5c65..4c5a9a1 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs @@ -29,6 +29,7 @@ using System.Windows.Media.Imaging; using System.Windows.Threading; using QuickLook.Annotations; using QuickLook.Helpers; +using QuickLook.Plugin.ImageViewer.Exiv2; namespace QuickLook.Plugin.ImageViewer { @@ -42,6 +43,7 @@ namespace QuickLook.Plugin.ImageViewer private Uri _imageSource; private DateTime _lastZoomTime = DateTime.MinValue; private double _maxZoomFactor = 3d; + private Meta _meta; private double _minZoomFactor = 0.1d; private BitmapScalingMode _renderMode = BitmapScalingMode.HighQuality; private BitmapSource _source; @@ -70,6 +72,11 @@ namespace QuickLook.Plugin.ImageViewer viewPanel.ManipulationDelta += ViewPanel_ManipulationDelta; } + internal ImagePanel(Meta meta) : this() + { + Meta = meta; + } + public BitmapScalingMode RenderMode { get => _renderMode; @@ -163,6 +170,17 @@ namespace QuickLook.Plugin.ImageViewer } } + public Meta Meta + { + get => _meta; + set + { + if (Equals(value, _meta)) return; + _meta = value; + OnPropertyChanged(); + } + } + public void Dispose() { viewPanelImage?.Dispose(); diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs index 93ed08b..3636801 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs @@ -20,6 +20,7 @@ using System.IO; using System.Linq; using System.Windows; using ImageMagick; +using QuickLook.Plugin.ImageViewer.Exiv2; namespace QuickLook.Plugin.ImageViewer { @@ -28,18 +29,18 @@ namespace QuickLook.Plugin.ImageViewer private static readonly string[] Formats = { // camera raw - ".3fr", ".ari", ".arw", ".bay", ".crw", ".cr2", ".cap", ".data", ".dcs", ".dcr", ".dng", ".drf", ".eip", - ".erf", ".fff", ".gpr", ".iiq", ".k25", ".kdc", ".mdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm", - ".orf", ".pef", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw", - ".tif", ".x3f", + ".ari", ".arw", ".bay", ".crw", ".cr2", ".cap", ".dcs", ".dcr", ".dng", ".drf", ".eip", ".erf", ".fff", + ".iiq", ".k25", ".kdc", ".mdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm", ".orf", ".pef", ".ptx", + ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw", ".x3f", // normal - ".bmp", ".ico", ".icon", ".jpg", ".jpeg", ".psd", ".svg", ".wdp", ".tif", ".tiff", ".tga", - ".webp", ".pbm", ".pgm", ".ppm", ".pnm", + ".bmp", ".ico", ".icon", ".jpg", ".jpeg", ".psd", ".svg", ".wdp", ".tif", ".tiff", ".tga", ".webp", ".pbm", + ".pgm", ".ppm", ".pnm", // animated ".png", ".apng", ".gif" }; - private Size _imageSize; private ImagePanel _ip; + private Meta _meta; + private Size _imageSize; public int Priority => int.MaxValue; @@ -55,26 +56,19 @@ namespace QuickLook.Plugin.ImageViewer public void Prepare(string path, ContextObject context) { - _imageSize = ImageFileHelper.GetImageSize(path) ?? Size.Empty; + _imageSize = ImageFileHelper.GetImageSize(path, _meta = new Meta(path)); if (!_imageSize.IsEmpty) - context.SetPreferredSizeFit(_imageSize, 0.6); + context.SetPreferredSizeFit(_imageSize, 0.8); else context.PreferredSize = new Size(800, 600); - - context.PreferredSize = new Size(context.PreferredSize.Width, context.PreferredSize.Height + 32); - - Directory.SetCurrentDirectory(App.AppPath); - - context.TitlebarBlurVisibility = true; - context.TitlebarOverlap = true; - context.TitlebarAutoHide = false; + context.UseDarkTheme = true; } public void View(string path, ContextObject context) { - _ip = new ImagePanel(); + _ip = new ImagePanel(_meta); context.ViewerContent = _ip; context.Title = _imageSize.IsEmpty diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj index 33fa89f..b16d099 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj @@ -58,9 +58,6 @@ key.snk - - ..\..\packages\ExifLib.1.7.0.0\lib\net45\ExifLib.dll - .\LibAPNG.dll @@ -84,6 +81,7 @@ + ImagePanel.xaml @@ -109,11 +107,22 @@ - - Always - + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/dcraw.exe b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/dcraw.exe deleted file mode 100644 index 0ea3ba9..0000000 Binary files a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/dcraw.exe and /dev/null differ diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/Meta.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/Meta.cs new file mode 100644 index 0000000..f03e006 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/Meta.cs @@ -0,0 +1,176 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using ImageMagick; +using QuickLook.ExtensionMethods; + +namespace QuickLook.Plugin.ImageViewer.Exiv2 +{ + public class Meta + { + private static readonly string ExivPath = + Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "exiv2\\exiv2.exe"); + private readonly string _path; + + private OrientationType _orientation = OrientationType.Undefined; + private Dictionary _summary; + + public Meta(string path) + { + _path = path; + } + + private Dictionary GetSummary() + { + if (_summary != null) + return _summary; + + return _summary = Run($"\"{_path}\"", ":"); + } + + public BitmapSource GetThumbnail(bool autoZoom = false) + { + GetOrientation(); + + var count = Run($"-pp \"{_path}\"", ",").Count; + + if (count == 0) + return null; + + var suc = Run($"-f -ep{count} -l \"{Path.GetTempPath().TrimEnd('\\')}\" \"{_path}\"", ","); + if (suc.Count != 0) + return null; + + try + { + using (var image = new MagickImage(Path.Combine(Path.GetTempPath(), + $"{Path.GetFileNameWithoutExtension(_path)}-preview{count}.jpg"))) + { + File.Delete(image.FileName); + + if (_orientation == OrientationType.RightTop) + image.Rotate(90); + else if (_orientation == OrientationType.BottomRight) + image.Rotate(180); + else if (_orientation == OrientationType.LeftBotom) + image.Rotate(270); + if (!autoZoom) + return image.ToBitmapSource(); + + var size = GetSize(); + return new TransformedBitmap(image.ToBitmapSource(), + new ScaleTransform(size.Width / image.Width, size.Height / image.Height)); + } + } + catch (Exception) + { + return null; + } + } + + public Size GetSize() + { + if (_summary == null) + GetSummary(); + + if (!_summary.ContainsKey("Image size")) + return Size.Empty; + + var width = int.Parse(_summary["Image size"].Split('x')[0].Trim()); + var height = int.Parse(_summary["Image size"].Split('x')[1].Trim()); + + switch (GetOrientation()) + { + case OrientationType.RightTop: + case OrientationType.LeftBotom: + return new Size(height, width); + default: + return new Size(width, height); + } + } + + public OrientationType GetOrientation() + { + if (_orientation != OrientationType.Undefined) + return _orientation; + + try + { + var ori = Run($"-g Exif.Image.Orientation -Pkv \"{_path}\"", "\\s"); + + if (ori?.ContainsKey("Exif.Image.Orientation") == true) + _orientation = (OrientationType) int.Parse(ori["Exif.Image.Orientation"]); + else + _orientation = OrientationType.TopLeft; + } + catch (Exception) + { + _orientation = OrientationType.TopLeft; + } + + return _orientation; + } + + private Dictionary Run(string arg, string regexSplit) + { + string result; + + using (var p = new Process()) + { + p.StartInfo.UseShellExecute = false; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.FileName = ExivPath; + p.StartInfo.Arguments = arg; + p.StartInfo.StandardOutputEncoding = Encoding.UTF8; + p.Start(); + p.WaitForExit(); + + result = p.StandardOutput.ReadToEnd(); + } + + return string.IsNullOrWhiteSpace(result) + ? new Dictionary() + : ParseResult(result, regexSplit); + } + + private Dictionary ParseResult(string result, string regexSplit) + { + var res = new Dictionary(); + + result.Replace("\r\n", "\n").Split('\n').ForEach(l => + { + if (string.IsNullOrWhiteSpace(l)) + return; + var eles = Regex.Split(l, regexSplit + "{1,}"); + res.Add(eles[0].Trim(), eles[1].Trim()); + }); + + return res; + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/exiv2.dll b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/exiv2.dll new file mode 100644 index 0000000..3d7eb62 Binary files /dev/null and b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/exiv2.dll differ diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/exiv2.exe b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/exiv2.exe new file mode 100644 index 0000000..e48eaff Binary files /dev/null and b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/exiv2.exe differ diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/expat.dll b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/expat.dll new file mode 100644 index 0000000..df2c63c Binary files /dev/null and b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/expat.dll differ diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/zlib.dll b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/zlib.dll new file mode 100644 index 0000000..099a6dd Binary files /dev/null and b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/exiv2/zlib.dll differ diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config index 1f128a7..ca7b6b9 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config @@ -1,5 +1,4 @@  - \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml index 2b5e8e1..6d6dce9 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml @@ -27,7 +27,6 @@ VirtualizingPanel.IsVirtualizing="True" Width="150" SelectedIndex="0" Focusable="False" - Margin="0,32,0,0" Background="#00FFFFFF" ItemsSource="{Binding PageIds, ElementName=thisPdfViewer}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0,0,1,0" diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs index 3a0d2ed..88b3acf 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs @@ -60,9 +60,6 @@ namespace QuickLook.Plugin.PDFViewer context.SetPreferredSizeFit(desiredSize, 0.6); context.PreferredSize = new Size(context.PreferredSize.Width, context.PreferredSize.Height + 32); - - context.TitlebarBlurVisibility = true; - context.TitlebarOverlap = true; } public void View(string path, ContextObject context) diff --git a/QuickLook/Helpers/ProcessHelper.cs b/QuickLook/Helpers/ProcessHelper.cs index 5e2c344..20f4a04 100644 --- a/QuickLook/Helpers/ProcessHelper.cs +++ b/QuickLook/Helpers/ProcessHelper.cs @@ -22,7 +22,7 @@ using QuickLook.NativeMethods; namespace QuickLook.Helpers { - internal class ProcessHelper + public class ProcessHelper { private const int ErrorInsufficientBuffer = 0x7A;