mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-11 09:49:07 +00:00
using modified exiv2-ql (QL-Win/exiv2@cf560437bb) to detect Exif; switch from NConvert to Magick.NET
This commit is contained in:
@@ -37,7 +37,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
private int _lastEffectivePreviousPreviousFrameIndex;
|
||||
private NativeImageProvider _nativeImageProvider;
|
||||
|
||||
public APngAnimationProvider(string path, NConvert meta) : base(path, meta)
|
||||
public APngAnimationProvider(string path, MetaProvider meta) : base(path, meta)
|
||||
{
|
||||
if (!IsAnimatedPng(path))
|
||||
{
|
||||
@@ -68,12 +68,18 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<BitmapSource> GetThumbnail(Size size, Size fullSize)
|
||||
public override Task<BitmapSource> GetThumbnail(Size renderSize)
|
||||
{
|
||||
if (_nativeImageProvider != null)
|
||||
return _nativeImageProvider.GetThumbnail(size, fullSize);
|
||||
return _nativeImageProvider.GetThumbnail(renderSize);
|
||||
|
||||
return new Task<BitmapSource>(() => _baseFrame.GetBitmapSource());
|
||||
return new Task<BitmapSource>(() =>
|
||||
{
|
||||
var bs = _baseFrame.GetBitmapSource();
|
||||
|
||||
bs.Freeze();
|
||||
return bs;
|
||||
});
|
||||
}
|
||||
|
||||
public override Task<BitmapSource> GetRenderedFrame(int index)
|
||||
|
@@ -48,7 +48,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
public event EventHandler ImageLoaded;
|
||||
public event EventHandler DoZoomToFit;
|
||||
|
||||
private static AnimationProvider LoadFullImageCore(Uri path, NConvert meta)
|
||||
private static AnimationProvider InitAnimationProvider(Uri path, MetaProvider meta)
|
||||
{
|
||||
var ext = Path.GetExtension(path.LocalPath).ToLower();
|
||||
var type = Providers.First(p => p.Key.Contains(ext) || p.Key.Contains("*")).Value;
|
||||
@@ -69,7 +69,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
new UIPropertyMetadata(null, AnimationUriChanged));
|
||||
|
||||
public static readonly DependencyProperty MetaProperty =
|
||||
DependencyProperty.Register("Meta", typeof(NConvert), typeof(AnimatedImage));
|
||||
DependencyProperty.Register("Meta", typeof(MetaProvider), typeof(AnimatedImage));
|
||||
|
||||
public static readonly DependencyProperty ContextObjectProperty =
|
||||
DependencyProperty.Register("ContextObject", typeof(ContextObject), typeof(AnimatedImage));
|
||||
@@ -86,9 +86,9 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
set => SetValue(AnimationUriProperty, value);
|
||||
}
|
||||
|
||||
public NConvert Meta
|
||||
public MetaProvider Meta
|
||||
{
|
||||
private get => (NConvert) GetValue(MetaProperty);
|
||||
private get => (MetaProvider) GetValue(MetaProperty);
|
||||
set => SetValue(MetaProperty, value);
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
//var thumbnail = instance.Meta?.GetThumbnail(true);
|
||||
//instance.Source = thumbnail;
|
||||
|
||||
instance._animation = LoadFullImageCore((Uri) ev.NewValue, instance.Meta);
|
||||
instance._animation = InitAnimationProvider((Uri) ev.NewValue, instance.Meta);
|
||||
ShowThumbnailAndStartAnimation(instance);
|
||||
}
|
||||
|
||||
private static void ShowThumbnailAndStartAnimation(AnimatedImage instance)
|
||||
{
|
||||
var task = instance._animation.GetThumbnail(instance.ContextObject.PreferredSize, instance.Meta.GetSize());
|
||||
var task = instance._animation.GetThumbnail(instance.ContextObject.PreferredSize);
|
||||
if (task == null) return;
|
||||
|
||||
task.ContinueWith(_ => instance.Dispatcher.Invoke(() =>
|
||||
@@ -121,8 +121,12 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
return;
|
||||
|
||||
instance.Source = _.Result;
|
||||
instance.DoZoomToFit?.Invoke(instance, new EventArgs());
|
||||
instance.ImageLoaded?.Invoke(instance, new EventArgs());
|
||||
|
||||
if (_.Result != null)
|
||||
{
|
||||
instance.DoZoomToFit?.Invoke(instance, new EventArgs());
|
||||
instance.ImageLoaded?.Invoke(instance, new EventArgs());
|
||||
}
|
||||
|
||||
instance.BeginAnimation(AnimationFrameIndexProperty, instance._animation?.Animator);
|
||||
}));
|
||||
@@ -141,8 +145,18 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
|
||||
task.ContinueWith(_ => instance.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (!instance._disposing)
|
||||
instance.Source = _.Result;
|
||||
if (instance._disposing)
|
||||
return;
|
||||
|
||||
var firstLoad = instance.Source == null;
|
||||
|
||||
instance.Source = _.Result;
|
||||
|
||||
if (firstLoad)
|
||||
{
|
||||
instance.DoZoomToFit?.Invoke(instance, new EventArgs());
|
||||
instance.ImageLoaded?.Invoke(instance, new EventArgs());
|
||||
}
|
||||
}));
|
||||
task.Start();
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
{
|
||||
internal abstract class AnimationProvider : IDisposable
|
||||
{
|
||||
protected AnimationProvider(string path, NConvert meta)
|
||||
protected AnimationProvider(string path, MetaProvider meta)
|
||||
{
|
||||
Path = path;
|
||||
Meta = meta;
|
||||
@@ -33,13 +33,13 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public NConvert Meta { get; }
|
||||
public MetaProvider Meta { get; }
|
||||
|
||||
public Int32AnimationUsingKeyFrames Animator { get; protected set; }
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract Task<BitmapSource> GetThumbnail(Size size, Size fullSize);
|
||||
public abstract Task<BitmapSource> GetThumbnail(Size renderSize);
|
||||
|
||||
public abstract Task<BitmapSource> GetRenderedFrame(int index);
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using QuickLook.Common.ExtensionMethods;
|
||||
using QuickLook.Common.Helpers;
|
||||
using Size = System.Windows.Size;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
@@ -31,10 +32,13 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
private BitmapSource _frame;
|
||||
private bool _isPlaying;
|
||||
|
||||
public GifAnimationProvider(string path, NConvert meta) : base(path, meta)
|
||||
public GifAnimationProvider(string path, MetaProvider meta) : base(path, meta)
|
||||
{
|
||||
_fileHandle = (Bitmap) Image.FromFile(path);
|
||||
|
||||
_fileHandle.SetResolution(DpiHelper.DefaultDpi * DpiHelper.GetCurrentScaleFactor().Horizontal,
|
||||
DpiHelper.DefaultDpi * DpiHelper.GetCurrentScaleFactor().Vertical);
|
||||
|
||||
Animator = new Int32AnimationUsingKeyFrames {RepeatBehavior = RepeatBehavior.Forever};
|
||||
Animator.KeyFrames.Add(new DiscreteInt32KeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));
|
||||
Animator.KeyFrames.Add(new DiscreteInt32KeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(10))));
|
||||
@@ -53,7 +57,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
_frame = null;
|
||||
}
|
||||
|
||||
public override Task<BitmapSource> GetThumbnail(Size size, Size fullSize)
|
||||
public override Task<BitmapSource> GetThumbnail(Size renderSize)
|
||||
{
|
||||
return new Task<BitmapSource>(() =>
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2018 Paddy Xu
|
||||
// Copyright © 2020 Paddy Xu
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
@@ -16,49 +16,53 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ImageMagick;
|
||||
using QuickLook.Common.Helpers;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
{
|
||||
internal class NConvertImageProvider : AnimationProvider
|
||||
internal class ImageMagickProvider : AnimationProvider
|
||||
{
|
||||
public NConvertImageProvider(string path, NConvert meta) : base(path, meta)
|
||||
public ImageMagickProvider(string path, MetaProvider meta) : base(path, meta)
|
||||
{
|
||||
Animator = new Int32AnimationUsingKeyFrames();
|
||||
Animator.KeyFrames.Add(new DiscreteInt32KeyFrame(0,
|
||||
KeyTime.FromTimeSpan(TimeSpan.Zero)));
|
||||
}
|
||||
|
||||
public override Task<BitmapSource> GetThumbnail(Size size, Size fullSize)
|
||||
public override Task<BitmapSource> GetThumbnail(Size renderSize)
|
||||
{
|
||||
var decodeWidth = (int) Math.Round(fullSize.Width *
|
||||
Math.Min(size.Width / 2 / fullSize.Width,
|
||||
size.Height / 2 / fullSize.Height));
|
||||
var decodeHeight = (int) Math.Round(fullSize.Height / fullSize.Width * decodeWidth);
|
||||
var fullSize = Meta.GetSize();
|
||||
|
||||
return new Task<BitmapSource>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ms = Meta.GetTiffStream(true))
|
||||
using (var buffer = new MemoryStream(Meta.GetThumbnail()))
|
||||
{
|
||||
if (buffer.Length == 0)
|
||||
return null;
|
||||
|
||||
var img = new BitmapImage();
|
||||
img.BeginInit();
|
||||
img.StreamSource = ms;
|
||||
img.StreamSource = buffer;
|
||||
img.CacheOption = BitmapCacheOption.OnLoad;
|
||||
img.DecodePixelWidth = decodeWidth;
|
||||
img.DecodePixelHeight = decodeHeight; // specific size to avoid .net's double to int conversion
|
||||
//// specific renderSize to avoid .net's double to int conversion
|
||||
//img.DecodePixelWidth = Math.Max(1, (int) Math.Floor(renderSize.Width));
|
||||
//img.DecodePixelHeight = Math.Max(1, (int) Math.Floor(renderSize.Height));
|
||||
img.EndInit();
|
||||
|
||||
var scaled = new TransformedBitmap(img,
|
||||
new ScaleTransform(fullSize.Width / img.PixelWidth, fullSize.Height / img.PixelHeight));
|
||||
scaled.Freeze();
|
||||
|
||||
Helper.DpiHack(scaled);
|
||||
scaled.Freeze();
|
||||
return scaled;
|
||||
}
|
||||
}
|
||||
@@ -72,17 +76,27 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
|
||||
public override Task<BitmapSource> GetRenderedFrame(int index)
|
||||
{
|
||||
var fullSize = Meta.GetSize();
|
||||
|
||||
return new Task<BitmapSource>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ms = Meta.GetTiffStream(false))
|
||||
using (var mi = new MagickImage(Path))
|
||||
{
|
||||
var img = new BitmapImage();
|
||||
img.BeginInit();
|
||||
img.StreamSource = ms;
|
||||
img.CacheOption = BitmapCacheOption.OnLoad;
|
||||
img.EndInit();
|
||||
var profile = mi.GetColorProfile();
|
||||
if (profile?.Description != null && !profile.Description.Contains("sRGB"))
|
||||
mi.SetProfile(ColorProfile.SRGB);
|
||||
|
||||
mi.AutoOrient();
|
||||
|
||||
if (mi.Width != (int) fullSize.Width || mi.Height != (int) fullSize.Height)
|
||||
mi.Resize((int) fullSize.Width, (int) fullSize.Height);
|
||||
|
||||
mi.Density = new Density(DpiHelper.DefaultDpi * DpiHelper.GetCurrentScaleFactor().Horizontal,
|
||||
DpiHelper.DefaultDpi * DpiHelper.GetCurrentScaleFactor().Vertical);
|
||||
|
||||
var img = mi.ToBitmapSource(BitmapDensity.Use);
|
||||
|
||||
img.Freeze();
|
||||
return img;
|
@@ -27,19 +27,25 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
{
|
||||
internal class NativeImageProvider : AnimationProvider
|
||||
{
|
||||
public NativeImageProvider(string path, NConvert meta) : base(path, meta)
|
||||
public NativeImageProvider(string path, MetaProvider meta) : base(path, meta)
|
||||
{
|
||||
Animator = new Int32AnimationUsingKeyFrames();
|
||||
Animator.KeyFrames.Add(new DiscreteInt32KeyFrame(0,
|
||||
KeyTime.FromTimeSpan(TimeSpan.Zero)));
|
||||
}
|
||||
|
||||
public override Task<BitmapSource> GetThumbnail(Size size, Size fullSize)
|
||||
public override Task<BitmapSource> GetThumbnail(Size renderSize)
|
||||
{
|
||||
var decodeWidth = (int) Math.Round(fullSize.Width *
|
||||
Math.Min(size.Width / 2 / fullSize.Width,
|
||||
size.Height / 2 / fullSize.Height));
|
||||
var decodeHeight = (int) Math.Round(fullSize.Height / fullSize.Width * decodeWidth);
|
||||
var fullSize = Meta.GetSize();
|
||||
|
||||
//var decodeWidth = (int) Math.Round(fullSize.Width *
|
||||
// Math.Min(renderSize.Width / 2 / fullSize.Width,
|
||||
// renderSize.Height / 2 / fullSize.Height));
|
||||
//var decodeHeight = (int) Math.Round(fullSize.Height / fullSize.Width * decodeWidth);
|
||||
var decodeWidth =
|
||||
(int) Math.Round(Math.Min(Meta.GetSize().Width, Math.Max(1d, Math.Floor(renderSize.Width))));
|
||||
var decodeHeight =
|
||||
(int) Math.Round(Math.Min(Meta.GetSize().Height, Math.Max(1d, Math.Floor(renderSize.Height))));
|
||||
var orientation = Meta.GetOrientation();
|
||||
var rotate = ShouldRotate(orientation);
|
||||
|
||||
@@ -51,18 +57,22 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
img.BeginInit();
|
||||
img.UriSource = new Uri(Path);
|
||||
img.CacheOption = BitmapCacheOption.OnLoad;
|
||||
// specific renderSize to avoid .net's double to int conversion
|
||||
img.DecodePixelWidth = rotate ? decodeHeight : decodeWidth;
|
||||
img.DecodePixelHeight = rotate ? decodeWidth : decodeHeight; // specific size to avoid .net's double to int conversion
|
||||
img.DecodePixelHeight = rotate ? decodeWidth : decodeHeight;
|
||||
img.EndInit();
|
||||
|
||||
var scaled = rotate ?
|
||||
new TransformedBitmap(img,
|
||||
var scaled = rotate
|
||||
? new TransformedBitmap(img,
|
||||
new ScaleTransform(fullSize.Height / img.PixelWidth, fullSize.Width / img.PixelHeight))
|
||||
: new TransformedBitmap(img,
|
||||
new ScaleTransform(fullSize.Width / img.PixelWidth, fullSize.Height / img.PixelHeight));
|
||||
|
||||
var rotated = ApplyTransformFromExif(scaled, orientation);
|
||||
|
||||
Helper.DpiHack(rotated);
|
||||
rotated.Freeze();
|
||||
|
||||
return rotated;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -75,6 +85,9 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
|
||||
public override Task<BitmapSource> GetRenderedFrame(int index)
|
||||
{
|
||||
var fullSize = Meta.GetSize();
|
||||
var rotate = ShouldRotate(Meta.GetOrientation());
|
||||
|
||||
return new Task<BitmapSource>(() =>
|
||||
{
|
||||
try
|
||||
@@ -83,9 +96,13 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
img.BeginInit();
|
||||
img.UriSource = new Uri(Path);
|
||||
img.CacheOption = BitmapCacheOption.OnLoad;
|
||||
img.DecodePixelWidth = (int) (rotate ? fullSize.Height : fullSize.Width);
|
||||
img.DecodePixelHeight = (int) (rotate ? fullSize.Width : fullSize.Height);
|
||||
img.EndInit();
|
||||
|
||||
var img2 = ApplyTransformFromExif(img, Meta.GetOrientation());
|
||||
|
||||
Helper.DpiHack(img2);
|
||||
img2.Freeze();
|
||||
|
||||
return img2;
|
||||
@@ -100,13 +117,13 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
|
||||
private static bool ShouldRotate(Orientation orientation)
|
||||
{
|
||||
bool rotate = false;
|
||||
var rotate = false;
|
||||
switch (orientation)
|
||||
{
|
||||
case Orientation.LeftTop:
|
||||
case Orientation.RightTop:
|
||||
case Orientation.RightBottom:
|
||||
case Orientation.Leftbottom:
|
||||
case Orientation.LeftBottom:
|
||||
rotate = true;
|
||||
break;
|
||||
}
|
||||
@@ -137,7 +154,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage
|
||||
return new TransformedBitmap(
|
||||
new TransformedBitmap(image, new RotateTransform(270)),
|
||||
new ScaleTransform(-1, 1, 0, 0));
|
||||
case Orientation.Leftbottom:
|
||||
case Orientation.LeftBottom:
|
||||
return new TransformedBitmap(image, new RotateTransform(270));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user