using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Markup; using System.Windows.Threading; using System.Diagnostics; using System.ComponentModel; using System.Windows.Media.Animation; using System.IO; namespace NerdPlusArt.Labs { #region ImageSequencer public class ImageSequencer : FrameworkElement, ISupportInitialize { #region Fields DispatcherTimer timer; static Random R = new Random(); #endregion #region Constructor public ImageSequencer() { timer = new DispatcherTimer(DispatcherPriority.Render); timer.Interval = TimeSpan.FromMilliseconds((int)Math.Abs((1000.0 / (double)FrameRate))); timer.Tick += new EventHandler(timer_Tick); timer.Start(); } #endregion #region Private Methods private int CalculateFrameOffset(int from, int offset) { if (ImageSequence != null && ImageSequence.Count > 0) { int newFrom = from; int newOffset = offset; int newIndex = newFrom + newOffset; while (newIndex < 0) { newFrom = ImageSequence.Count - 1; newOffset = newIndex; newIndex = newFrom + newOffset; } while (newIndex > (ImageSequence.Count - 1)) { newFrom = 0; newOffset = (newIndex - (ImageSequence.Count - 1)); newIndex = newFrom + newOffset; } return newIndex; } else { return 0; } } #endregion #region Overridden Methods protected override void OnRender(DrawingContext drawingContext) { if (ImageSequence != null && ImageSequence.Count > 0) { int frameDirection = -1; if ((SequenceDirection == SequenceDirection.Forward && FrameRate > 0) || (SequenceDirection == SequenceDirection.Backward && FrameRate < 0)) { frameDirection = 1; } for (int i = 0; i < FrameTrailCount; i++) { double opacity = (double)(i + 1) / (double)FrameTrailCount; int index = CalculateFrameOffset(CurrentFrame, (frameDirection * FrameTrailCount - (i + 1))); drawingContext.PushOpacity(opacity); drawingContext.DrawImage(ImageSequence[index], new Rect(0, 0, this.ActualWidth, this.ActualHeight)); drawingContext.Pop(); } CurrentFrame = CalculateFrameOffset(CurrentFrame, frameDirection); if (_waitingForPause && (CurrentFrame == _pauseFrame)) this.Pause(); base.OnRender(drawingContext); } } #endregion #region Event Handlers void timer_Tick(object sender, EventArgs e) { this.InvalidateVisual(); } #endregion #region Properties #region SequenceDirection public static readonly DependencyProperty SequenceDirectionProperty = DependencyProperty.Register("SequenceDirection", typeof(SequenceDirection), typeof(ImageSequencer), new FrameworkPropertyMetadata((SequenceDirection)SequenceDirection.Forward, FrameworkPropertyMetadataOptions.None)); public SequenceDirection SequenceDirection { get { return (SequenceDirection)GetValue(SequenceDirectionProperty); } set { SetValue(SequenceDirectionProperty, value); } } #endregion #region CurrentFrame public static readonly DependencyProperty CurrentFrameProperty = DependencyProperty.Register("CurrentFrame", typeof(int), typeof(ImageSequencer), new FrameworkPropertyMetadata((int)0, new PropertyChangedCallback(OnCurrentFrameChanged))); public int CurrentFrame { get { return (int)GetValue(CurrentFrameProperty); } set { SetValue(CurrentFrameProperty, value); } } private static void OnCurrentFrameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ImageSequencer)d).OnCurrentFrameChanged(e); } protected virtual void OnCurrentFrameChanged(DependencyPropertyChangedEventArgs e) { } #endregion #region FrameRate public static readonly DependencyProperty FrameRateProperty = DependencyProperty.Register("FrameRate", typeof(int), typeof(ImageSequencer), new FrameworkPropertyMetadata((int)60, new PropertyChangedCallback(OnFrameRateChanged))); public int FrameRate { get { return (int)GetValue(FrameRateProperty); } set { SetValue(FrameRateProperty, value); } } private static void OnFrameRateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ImageSequencer)d).OnFrameRateChanged(e); } protected virtual void OnFrameRateChanged(DependencyPropertyChangedEventArgs e) { if ((int)e.NewValue == 0) { timer.Stop(); } else { if (!_isPaused) timer.Start(); timer.Interval = TimeSpan.FromMilliseconds((int)Math.Abs((1000 / (double)((int)e.NewValue)))); } } #endregion #region FrameTrailCount public static readonly DependencyProperty FrameTrailCountProperty = DependencyProperty.Register("FrameTrailCount", typeof(int), typeof(ImageSequencer), new FrameworkPropertyMetadata((int)1)); public int FrameTrailCount { get { return (int)GetValue(FrameTrailCountProperty); } set { SetValue(FrameTrailCountProperty, value); } } #endregion #region ImageSequence public static readonly DependencyProperty ImageSequenceProperty = DependencyProperty.Register("ImageSequence", typeof(ImageSequence), typeof(ImageSequencer), new FrameworkPropertyMetadata((ImageSequence)null)); public ImageSequence ImageSequence { get { return (ImageSequence)GetValue(ImageSequenceProperty); } set { SetValue(ImageSequenceProperty, value); } } #endregion #region SourceFolder public static readonly DependencyProperty SourceFolderProperty = DependencyProperty.Register("SourceFolder", typeof(string), typeof(ImageSequencer), new FrameworkPropertyMetadata((string)null, new PropertyChangedCallback(OnSourceFolderChanged))); public string SourceFolder { get { return (string)GetValue(SourceFolderProperty); } set { SetValue(SourceFolderProperty, value); } } private static void OnSourceFolderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ImageSequencer)d).OnSourceFolderChanged(e); } protected virtual void OnSourceFolderChanged(DependencyPropertyChangedEventArgs e) { if (this.ImageSequence == null) { ImageSequence seq = new ImageSequence(); string folder = e.NewValue as string; if (Directory.Exists(folder)) { foreach (string filename in Directory.GetFiles(folder, "*.png")) { ImageSource img = new BitmapImage(new Uri(filename, UriKind.RelativeOrAbsolute)); seq.Add(img); } } this.ImageSequence = seq; } } #endregion #endregion #region Public Methods bool _isPaused = false; public void Pause() { timer.Stop(); _isPaused = true; _waitingForPause = false; } bool _waitingForPause = false; int _pauseFrame = 0; public void PauseOnFrame(int frame) { _waitingForPause = true; _pauseFrame = frame; } public void Play() { timer.Start(); _isPaused = false; } #endregion } #endregion #region ImageSequence public class ImageSequence : List, INotifyPropertyChanged, ISupportInitialize { #region Private Fields bool isInitializing = false; #endregion #region Properties private string _source = null; public string Source { get { return _source; } set { _source = value; if (!isInitializing) this.LoadImages(); RaisePropertyChanged("Source"); } } private int _totalFrames = 0; public int TotalFrames { get { return _totalFrames; } set { _totalFrames = value; RaisePropertyChanged("TotalFrames"); } } #endregion #region Private Methods private void LoadImages() { for (int i = 0; i < _totalFrames; i++) { string src = String.Format(_source, i); ImageSource img = new BitmapImage(new Uri(src, UriKind.RelativeOrAbsolute)); this.Add(img); } } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); } #endregion #region ISupportInitialize Members public void BeginInit() { isInitializing = true; } public void EndInit() { isInitializing = false; LoadImages(); } #endregion } #endregion #region SequenceDirection public enum SequenceDirection { Forward, Backward } #endregion }