using modified exiv2-ql (QL-Win/exiv2@cf560437bb) to detect Exif; switch from NConvert to Magick.NET

This commit is contained in:
Paddy Xu
2020-05-01 19:02:15 +03:00
parent 208c5d5391
commit b98f8e5ec6
36 changed files with 400 additions and 5612 deletions

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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>(() =>
{

View File

@@ -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;

View File

@@ -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));
}