diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs index 1a14bdc..8a107ed 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimatedImage.cs @@ -1,4 +1,4 @@ -// Copyright © 2017 Paddy Xu +// Copyright © 2018 Paddy Xu // // This file is part of QuickLook program. // @@ -17,12 +17,9 @@ using System; using System.IO; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Media; using System.Windows.Threading; -using QuickLook.Common.Helpers; using QuickLook.Plugin.ImageViewer.Exiv2; namespace QuickLook.Plugin.ImageViewer.AnimatedImage @@ -30,17 +27,14 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage public class AnimatedImage : Image, IDisposable { private AnimationProvider _animation; - private bool _disposed; public void Dispose() { BeginAnimation(AnimationFrameIndexProperty, null); Source = null; + + _animation?.Dispose(); _animation = null; - - _disposed = true; - - Task.Delay(500).ContinueWith(t => ProcessHelper.PerformAggressiveGC()); } private static void LoadFullImage(DependencyObject obj, DependencyPropertyChangedEventArgs ev) @@ -48,11 +42,11 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage if (!(obj is AnimatedImage instance)) return; - instance._animation = LoadFullImageCore((Uri) ev.NewValue); + instance._animation = LoadFullImageCore((Uri) ev.NewValue, instance.Dispatcher); instance.BeginAnimation(AnimationFrameIndexProperty, instance._animation.Animator); } - private static AnimationProvider LoadFullImageCore(Uri path) + private static AnimationProvider LoadFullImageCore(Uri path, Dispatcher uiDispatcher) { byte[] sign; using (var reader = @@ -64,7 +58,7 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage AnimationProvider provider = null; if (sign[0] == 'G' && sign[1] == 'I' && sign[2] == 'F' && sign[3] == '8') - provider = new GIFAnimationProvider(path.LocalPath); + provider = new GifAnimationProvider(path.LocalPath, uiDispatcher); //else if (sign[0] == 0x89 && sign[1] == 'P' && sign[2] == 'N' && sign[3] == 'G') // provider = new APNGAnimationProvider(); //else @@ -122,13 +116,9 @@ namespace QuickLook.Plugin.ImageViewer.AnimatedImage if (!(obj is AnimatedImage instance)) return; - new Task(() => - { - var image = instance._animation.GetRenderedFrame((int) ev.NewValue); - - instance.Dispatcher.BeginInvoke( - new Action(() => { instance.Source = image; }), DispatcherPriority.Loaded); - }).Start(); + var image = instance._animation.GetRenderedFrame((int) ev.NewValue); + //if (!ReferenceEquals(instance.Source, image)) + instance.Source = image; } #endregion DependencyProperty diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimationProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimationProvider.cs index 5b7514b..dee9104 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimationProvider.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/AnimationProvider.cs @@ -15,23 +15,29 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using System; using System.Windows.Media; using System.Windows.Media.Animation; -using System.Windows.Media.Imaging; +using System.Windows.Threading; namespace QuickLook.Plugin.ImageViewer.AnimatedImage { - internal abstract class AnimationProvider + internal abstract class AnimationProvider : IDisposable { - public AnimationProvider(string path) + protected AnimationProvider(string path, Dispatcher uiDispatcher) { Path = path; + Dispatcher = uiDispatcher; } + public Dispatcher Dispatcher { get; } + public string Path { get; } public Int32Animation Animator { get; protected set; } - public abstract DrawingImage GetRenderedFrame(int index); + public abstract void Dispose(); + + public abstract ImageSource GetRenderedFrame(int index); } } \ 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 index 3b10aa2..d54b90f 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/GIFAnimationProvider.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/GIFAnimationProvider.cs @@ -1,4 +1,4 @@ -// Copyright © 2017 Paddy Xu +// Copyright © 2018 Paddy Xu // // This file is part of QuickLook program. // @@ -16,187 +16,60 @@ // along with this program. If not, see . using System; -using System.Collections.Generic; -using System.Linq; +using System.Drawing; using System.Windows; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; +using System.Windows.Threading; using QuickLook.Common.ExtensionMethods; namespace QuickLook.Plugin.ImageViewer.AnimatedImage { - internal class GIFAnimationProvider : AnimationProvider + internal class GifAnimationProvider : AnimationProvider { - private readonly List _decodedFrames; - private readonly int _lastRenderedFrameIndex; - private readonly DrawingGroup renderedFrame; + private Bitmap _frame; + private BitmapSource _frameSource; + private bool _isPlaying; - public GIFAnimationProvider(string path) : base(path) + public GifAnimationProvider(string path, Dispatcher uiDispatcher) : base(path, uiDispatcher) { - var decoder = new GifBitmapDecoder(new Uri(path), BitmapCreateOptions.PreservePixelFormat, - BitmapCacheOption.OnLoad); + _frame = (Bitmap) Image.FromFile(path); + _frameSource = _frame.ToBitmapSource(); - _decodedFrames = new List(decoder.Frames.Count); - decoder.Frames.ForEach(f => _decodedFrames.Add(GetFrameInfo(f))); - - renderedFrame = new DrawingGroup(); - _lastRenderedFrameIndex = -1; - - var delay = _decodedFrames[0].Delay.TotalMilliseconds; - - Animator = new Int32Animation(0, decoder.Frames.Count - 1, - new Duration(TimeSpan.FromMilliseconds(delay * (decoder.Frames.Count - 1)))) + Animator = new Int32Animation(0, 1, new Duration(TimeSpan.FromMilliseconds(50))) { RepeatBehavior = RepeatBehavior.Forever }; } - public override DrawingImage GetRenderedFrame(int index) + public override void Dispose() { - for (var i = _lastRenderedFrameIndex + 1; i < index; i++) - MakeFrame(renderedFrame, _decodedFrames[i], i > 0 ? _decodedFrames[i - 1] : null); + if (_frame == null) + return; - MakeFrame( - renderedFrame, - _decodedFrames[index], - index > 0 ? _decodedFrames[index - 1] : null); - - var di=new DrawingImage(renderedFrame); - di.Freeze(); + ImageAnimator.StopAnimate(_frame, OnFrameChanged); + _frame.Dispose(); - return di; + _frame = null; + _frameSource = null; } - #region private methods - - private static void MakeFrame( - DrawingGroup renderedFrame, - FrameInfo currentFrame, - FrameInfo previousFrame) + public override ImageSource GetRenderedFrame(int index) { - if (previousFrame == null) - renderedFrame.Children.Clear(); - else - switch (previousFrame.DisposalMethod) - { - case FrameDisposalMethod.Unspecified: - case FrameDisposalMethod.Combine: - break; - case FrameDisposalMethod.RestorePrevious: - renderedFrame.Children.RemoveAt(renderedFrame.Children.Count - 1); - break; - case FrameDisposalMethod.RestoreBackground: - var bg = renderedFrame.Children.First(); - renderedFrame.Children.Clear(); - renderedFrame.Children.Add(bg); - break; - } - - renderedFrame.Children.Add(new ImageDrawing(currentFrame.Frame, currentFrame.Rect)); - } - - private static FrameInfo GetFrameInfo(BitmapFrame frame) - { - var frameInfo = new FrameInfo - { - Frame = frame, - Delay = TimeSpan.FromMilliseconds(100), - DisposalMethod = FrameDisposalMethod.Unspecified, - 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) + if (!_isPlaying) { + _isPlaying = true; + ImageAnimator.Animate(_frame, OnFrameChanged); } - return frameInfo; + return _frameSource; } - #endregion - - #region structs - - private class FrameInfo + private void OnFrameChanged(object sender, EventArgs e) { - public BitmapSource Frame { get; set; } - public FrameDisposalMethod DisposalMethod { get; set; } - public TimeSpan Delay { get; set; } - public Rect Rect => new Rect(Left, Top, Width, Height); - - public double Width { private get; set; } - public double Height { private get; set; } - public double Left { private get; set; } - public double Top { private get; set; } - } - - private enum FrameDisposalMethod - { - Unspecified = 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; + ImageAnimator.UpdateFrames(); + _frameSource = _frame.ToBitmapSource(); } } - - #endregion } \ 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 34bc7fd..9ce7fed 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/ImagePanel.xaml.cs @@ -1,4 +1,4 @@ -// Copyright © 2017 Paddy Xu +// Copyright © 2018 Paddy Xu // // This file is part of QuickLook program. // diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs index 10b324b..434a3a5 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs @@ -1,4 +1,4 @@ -// Copyright © 2017 Paddy Xu +// Copyright © 2018 Paddy Xu // // This file is part of QuickLook program. // @@ -84,6 +84,7 @@ namespace QuickLook.Plugin.ImageViewer public void Cleanup() { + _ip?.Dispose(); _ip = null; } diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj index 2f34f70..eccba99 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj @@ -79,7 +79,7 @@ - + diff --git a/QuickLook.sln.DotSettings b/QuickLook.sln.DotSettings index 385a2aa..734638b 100644 --- a/QuickLook.sln.DotSettings +++ b/QuickLook.sln.DotSettings @@ -19,7 +19,7 @@ 0 NEVER - Copyright © $CURRENT_YEAR$ $USER_NAME$ + Copyright © $CURRENT_YEAR$ Paddy Xu This file is part of $SOLUTION$ program.