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 } }