using System;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Developpez.Dotnet.Windows.Behaviors.Gif;
namespace Developpez.Dotnet.Windows.Behaviors
{
///
/// Fournit des propriétés attachées pour ajouter des fonctionnalités au contrôle Image
///
public static class ImageBehavior
{
#region AnimatedSource
///
/// Obtient la valeur de la propriété attachée AnimatedSource pour l'objet spécifié
///
/// Objet dont on veut obtenir la valeur de la propriété
/// L'image animée affichée
[AttachedPropertyBrowsableForType(typeof(Image))]
public static ImageSource GetAnimatedSource(Image obj)
{
return (ImageSource)obj.GetValue(AnimatedSourceProperty);
}
///
/// Définit la valeur de la propriété attachée AnimatedSource pour l'objet spécifié
///
/// Objet dont on veut définir la valeur de la propriété
/// L'image animée à afficher
public static void SetAnimatedSource(Image obj, ImageSource value)
{
obj.SetValue(AnimatedSourceProperty, value);
}
///
/// Identifie la propriété attachée AnimatedSource
///
public static readonly DependencyProperty AnimatedSourceProperty =
DependencyProperty.RegisterAttached(
"AnimatedSource",
typeof(ImageSource),
typeof(ImageBehavior),
new UIPropertyMetadata(
null,
AnimatedSourceChanged));
///
/// Obtient la valeur de la propriété attachée RepeatBehavior pour l'objet spécifié
///
/// Objet dont on veut obtenir la valeur de la propriété
/// Le comportement de répétition de l'image animée
[AttachedPropertyBrowsableForType(typeof(Image))]
public static RepeatBehavior GetRepeatBehavior(Image obj)
{
return (RepeatBehavior)obj.GetValue(RepeatBehaviorProperty);
}
///
/// Définit la valeur de la propriété attachée RepeatBehavior pour l'objet spécifié
///
/// Objet dont on veut définir la valeur de la propriété
/// Le comportement de répétition de l'image animée
public static void SetRepeatBehavior(Image obj, RepeatBehavior value)
{
obj.SetValue(RepeatBehaviorProperty, value);
}
///
/// Identifie la propriété attachée RepeatBehavior
///
public static readonly DependencyProperty RepeatBehaviorProperty =
DependencyProperty.RegisterAttached(
"RepeatBehavior",
typeof(RepeatBehavior),
typeof(ImageBehavior),
new UIPropertyMetadata(RepeatBehavior.Forever));
private static void AnimatedSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
Image imageControl = o as Image;
if (imageControl == null)
return;
var oldValue = e.OldValue as ImageSource;
var newValue = e.NewValue as ImageSource;
if (oldValue != null)
{
imageControl.BeginAnimation(Image.SourceProperty, null);
}
if (newValue != null)
{
imageControl.DoWhenLoaded(InitAnimationOrImage);
}
}
private static void InitAnimationOrImage(Image imageControl)
{
BitmapSource source = GetAnimatedSource(imageControl) as BitmapSource;
if (source != null)
{
GifFile gifFile;
var decoder = GetDecoder(source, out gifFile) as GifBitmapDecoder;
if (decoder != null && decoder.Frames.Count > 1)
{
int index = 0;
var animation = new ObjectAnimationUsingKeyFrames();
var totalDuration = TimeSpan.Zero;
BitmapSource prevFrame = null;
FrameInfo prevInfo = null;
foreach (var rawFrame in decoder.Frames)
{
GifFrame gifFrame = null;
if (gifFile != null && index < gifFile.Frames.Count)
gifFrame = gifFile.Frames[index];
var info = GetFrameInfo(rawFrame, gifFrame);
var frame = MakeFrame(
source,
rawFrame, info,
prevFrame, prevInfo);
var keyFrame = new DiscreteObjectKeyFrame(frame, totalDuration);
animation.KeyFrames.Add(keyFrame);
totalDuration += info.Delay;
prevFrame = frame;
prevInfo = info;
index++;
}
animation.Duration = totalDuration;
animation.RepeatBehavior = GetRepeatBehavior(imageControl);
if (animation.KeyFrames.Count > 0)
imageControl.Source = (ImageSource)animation.KeyFrames[0].Value;
else
imageControl.Source = decoder.Frames[0];
imageControl.BeginAnimation(Image.SourceProperty, animation);
return;
}
}
imageControl.Source = source;
return;
}
private static BitmapDecoder GetDecoder(BitmapSource image, out GifFile gifFile)
{
gifFile = null;
BitmapDecoder decoder = null;
Stream stream = null;
Uri uri = null;
BitmapCreateOptions createOptions = BitmapCreateOptions.None;
var bmp = image as BitmapImage;
if (bmp != null)
{
createOptions = bmp.CreateOptions;
if (bmp.StreamSource != null)
{
stream = bmp.StreamSource;
}
else if (bmp.UriSource != null)
{
uri = bmp.UriSource;
if (bmp.BaseUri != null && !uri.IsAbsoluteUri)
uri = new Uri(bmp.BaseUri, uri);
}
}
else
{
BitmapFrame frame = image as BitmapFrame;
if (frame != null)
{
decoder = frame.Decoder;
Uri.TryCreate(frame.BaseUri, frame.ToString(), out uri);
}
}
if (stream != null)
{
stream.Position = 0;
decoder = BitmapDecoder.Create(stream, createOptions, BitmapCacheOption.OnLoad);
stream.Position = 0;
gifFile = GifDecoder.DecodeGif(stream, true);
}
else if (uri != null)
{
decoder = BitmapDecoder.Create(uri, createOptions, BitmapCacheOption.OnLoad);
gifFile = DecodeGifFile(uri);
}
return decoder;
}
private static GifFile DecodeGifFile(Uri uri)
{
Stream stream;
if (uri.Scheme == PackUriHelper.UriSchemePack)
{
var sri = Application.GetResourceStream(uri);
stream = sri.Stream;
}
else
{
WebClient wc = new WebClient();
stream = wc.OpenRead(uri);
}
if (stream != null)
{
using (stream)
{
return GifDecoder.DecodeGif(stream, true);
}
}
return null;
}
private static BitmapSource MakeFrame(
BitmapSource fullImage,
BitmapSource rawFrame, FrameInfo frameInfo,
BitmapSource previousFrame, FrameInfo previousFrameInfo)
{
DrawingVisual 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 class FrameInfo
{
public TimeSpan Delay { get; set; }
public FrameDisposalMethod DisposalMethod { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Left { get; set; }
public double Top { get; set; }
public Rect Rect
{
get { return new Rect(Left, Top, Width, Height); }
}
}
private enum FrameDisposalMethod
{
Replace = 0,
Combine = 1,
RestoreBackground = 2,
RestorePrevious = 3
}
private static FrameInfo GetFrameInfo(BitmapFrame frame, GifFrame gifFrame)
{
var frameInfo = new FrameInfo
{
Delay = TimeSpan.FromMilliseconds(100),
DisposalMethod = FrameDisposalMethod.Replace,
Width = frame.PixelWidth,
Height = frame.PixelHeight,
Left = 0,
Top = 0
};
BitmapMetadata metadata;
try
{
metadata = frame.Metadata as BitmapMetadata;
if (metadata != null)
{
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 && delay.Value > 0)
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 && width.Value > 0)
frameInfo.Width = width.Value;
var height = metadata.GetQueryOrNull(heightQuery);
if (height.HasValue && height.Value > 0)
frameInfo.Height = height.Value;
var left = metadata.GetQueryOrNull(leftQuery);
if (left.HasValue && left.Value > 0)
frameInfo.Left = left.Value;
var top = metadata.GetQueryOrNull(topQuery);
if (top.HasValue && top.Value > 0)
frameInfo.Top = top.Value;
}
else if (gifFrame != null)
{
var gce = gifFrame.Extensions.OfType().FirstOrDefault();
if (gce != null)
{
frameInfo.DisposalMethod = (FrameDisposalMethod)gce.DisposalMethod;
if (gce.Delay > 0)
frameInfo.Delay = TimeSpan.FromMilliseconds(gce.Delay);
}
if (gifFrame.Descriptor.Left > 0)
frameInfo.Left = gifFrame.Descriptor.Left;
if (gifFrame.Descriptor.Top > 0)
frameInfo.Top = gifFrame.Descriptor.Top;
if (gifFrame.Descriptor.Width > 0)
frameInfo.Width = gifFrame.Descriptor.Width;
if (gifFrame.Descriptor.Height > 0)
frameInfo.Height = gifFrame.Descriptor.Height;
}
}
catch (NotSupportedException)
{
}
return frameInfo;
}
private static T? GetQueryOrNull(this BitmapMetadata metadata, string query)
where T : struct
{
if (metadata.ContainsQuery(query))
{
object value = metadata.GetQuery(query);
if (value != null)
return (T) value;
}
return null;
}
#endregion
}
}