Use my home-made GIF and APNG animator. It's really save CPU compared to previous one!

This commit is contained in:
Paddy Xu
2017-07-31 00:14:21 +03:00
parent 071a3b4f22
commit 2f12127cc9
12 changed files with 658 additions and 33 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
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;
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
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);
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
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<ushort>(delayQuery);
if (delay.HasValue)
frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value);
var disposal = metadata.GetQueryOrNull<byte>(disposalQuery);
if (disposal.HasValue)
frameInfo.DisposalMethod = (FrameDisposalMethod) disposal.Value;
var width = metadata.GetQueryOrNull<ushort>(widthQuery);
if (width.HasValue)
frameInfo.Width = width.Value;
var height = metadata.GetQueryOrNull<ushort>(heightQuery);
if (height.HasValue)
frameInfo.Height = height.Value;
var left = metadata.GetQueryOrNull<ushort>(leftQuery);
if (left.HasValue)
frameInfo.Left = left.Value;
var top = metadata.GetQueryOrNull<ushort>(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<T>(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
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
using System.Windows.Media.Animation;
namespace QuickLook.Plugin.ImageViewer.AnimatedImage
{
internal interface IAnimationProvider
{
void GetAnimator(ObjectAnimationUsingKeyFrames animator, string path);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
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);
}
}
}

View File

@@ -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">
<Grid>
<ScrollViewer x:Name="viewPanel" BorderThickness="0" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" Focusable="False" IsManipulationEnabled="True">
<Image x:Name="viewPanelImage" Stretch="None" Source="{Binding Source, ElementName=imagePanel}"
RenderOptions.BitmapScalingMode="{Binding RenderMode, ElementName=imagePanel}" />
<animatedImage:AnimatedImage x:Name="viewPanelImage" Stretch="None"
RenderOptions.BitmapScalingMode="{Binding RenderMode, ElementName=imagePanel}"
AnimationUri="{Binding ImageUriSource, ElementName=imagePanel}" />
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -34,9 +34,10 @@ namespace QuickLook.Plugin.ImageViewer
/// <summary>
/// Interaction logic for ImagePanel.xaml
/// </summary>
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<int> ImageScrolled;

View File

@@ -0,0 +1,176 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>LibAPNG</name>
</assembly>
<members>
<member name="P:LibAPNG.fcTLChunk.SequenceNumber">
<summary>
Sequence number of the animation chunk, starting from 0
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.Width">
<summary>
Width of the following frame
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.Height">
<summary>
Height of the following frame
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.XOffset">
<summary>
X position at which to render the following frame
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.YOffset">
<summary>
Y position at which to render the following frame
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.DelayNum">
<summary>
Frame delay fraction numerator
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.DelayDen">
<summary>
Frame delay fraction denominator
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.DisposeOp">
<summary>
Type of frame area disposal to be done after rendering this frame
</summary>
</member>
<member name="P:LibAPNG.fcTLChunk.BlendOp">
<summary>
Type of frame area rendering for this frame
</summary>
</member>
<member name="P:LibAPNG.APNGBitmap.IsSimplePNG">
<summary>
Indicate whether the file is a simple PNG.
</summary>
</member>
<member name="P:LibAPNG.APNGBitmap.DefaultImageIsAnimated">
<summary>
Indicate whether the default image is part of the animation
</summary>
</member>
<member name="P:LibAPNG.APNGBitmap.DefaultImage">
<summary>
Gets the base image.
If IsSimplePNG = True, returns the only image;
if False, returns the default image
</summary>
</member>
<member name="P:LibAPNG.APNGBitmap.Frames">
<summary>
Gets the frame array.
If IsSimplePNG = True, returns empty
</summary>
</member>
<member name="P:LibAPNG.APNGBitmap.IHDRChunk">
<summary>
Gets the IHDR Chunk
</summary>
</member>
<member name="P:LibAPNG.APNGBitmap.acTLChunk">
<summary>
Gets the acTL Chunk
</summary>
</member>
<member name="P:LibAPNG.Chunk.RawData">
<summary>
Get raw data of the chunk
</summary>
</member>
<member name="M:LibAPNG.Chunk.ModifyChunkData(System.Int32,System.Byte[])">
<summary>
Modify the ChunkData part.
</summary>
</member>
<member name="M:LibAPNG.Chunk.ModifyChunkData(System.Int32,System.UInt32)">
<summary>
Modify the ChunkData part.
</summary>
</member>
<member name="M:LibAPNG.Helper.ConvertEndian(System.Byte[])">
<summary>
Convert big-endian to little-endian or reserve
</summary>
</member>
<member name="M:LibAPNG.Helper.ConvertEndian(System.Int32)">
<summary>
Convert big-endian to little-endian or reserve
</summary>
</member>
<member name="M:LibAPNG.Helper.ConvertEndian(System.UInt32)">
<summary>
Convert big-endian to little-endian or reserve
</summary>
</member>
<member name="M:LibAPNG.Helper.ConvertEndian(System.Int16)">
<summary>
Convert big-endian to little-endian or reserve
</summary>
</member>
<member name="M:LibAPNG.Helper.ConvertEndian(System.UInt16)">
<summary>
Convert big-endian to little-endian or reserve
</summary>
</member>
<member name="M:LibAPNG.Helper.IsBytesEqual(System.Byte[],System.Byte[])">
<summary>
Compare two byte array
</summary>
</member>
<member name="T:LibAPNG.Frame">
<summary>
Describe a single frame.
</summary>
</member>
<member name="P:LibAPNG.Frame.IHDRChunk">
<summary>
Gets or Sets the acTL chunk
</summary>
</member>
<member name="P:LibAPNG.Frame.fcTLChunk">
<summary>
Gets or Sets the fcTL chunk
</summary>
</member>
<member name="P:LibAPNG.Frame.IENDChunk">
<summary>
Gets or Sets the IEND chunk
</summary>
</member>
<member name="P:LibAPNG.Frame.OtherChunks">
<summary>
Gets or Sets the other chunks
</summary>
</member>
<member name="P:LibAPNG.Frame.IDATChunks">
<summary>
Gets or Sets the IDAT chunks
</summary>
</member>
<member name="M:LibAPNG.Frame.AddOtherChunk(LibAPNG.OtherChunk)">
<summary>
Add an Chunk to end end of existing list.
</summary>
</member>
<member name="M:LibAPNG.Frame.AddIDATChunk(LibAPNG.IDATChunk)">
<summary>
Add an IDAT Chunk to end end of existing list.
</summary>
</member>
<member name="M:LibAPNG.Frame.GetStream">
<summary>
Gets the frame as PNG FileStream.
</summary>
</member>
</members>
</doc>

View File

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

View File

@@ -55,6 +55,9 @@
<Reference Include="ExifLib, Version=1.7.0.0, Culture=neutral, PublicKeyToken=30284005913968db, processorArchitecture=MSIL">
<HintPath>..\..\packages\ExifLib.1.7.0.0\lib\net45\ExifLib.dll</HintPath>
</Reference>
<Reference Include="LibAPNG">
<HintPath>.\LibAPNG.dll</HintPath>
</Reference>
<Reference Include="Magick.NET-Q8-AnyCPU, Version=7.0.0.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET-Q8-AnyCPU.7.0.6.102\lib\net40\Magick.NET-Q8-AnyCPU.dll</HintPath>
</Reference>
@@ -65,14 +68,16 @@
<Reference Include="System.Drawing" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
<Reference Include="XamlAnimatedGif, Version=1.1.10.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\XamlAnimatedGif.1.1.10\lib\net45\XamlAnimatedGif.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\GitVersion.cs">
<Link>Properties\GitVersion.cs</Link>
</Compile>
<Compile Include="AnimatedImage\AnimatedImage.cs" />
<Compile Include="AnimatedImage\APNGAnimationProvider.cs" />
<Compile Include="AnimatedImage\GIFAnimationProvider.cs" />
<Compile Include="AnimatedImage\IAnimationProvider.cs" />
<Compile Include="AnimatedImage\ImageMagickProvider.cs" />
<Compile Include="ImageFileHelper.cs" />
<Compile Include="ImagePanel.xaml.cs">
<DependentUpon>ImagePanel.xaml</DependentUpon>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ExifLib" version="1.7.0.0" targetFramework="net462" />
<package id="Magick.NET-Q8-AnyCPU" version="7.0.6.102" targetFramework="net462" />
<package id="XamlAnimatedGif" version="1.1.10" targetFramework="net462" />
</packages>