1 // Licensed to the .NET Foundation under one or more agreements. 2 // See the LICENSE file in the project root for more information. 3 // 4 // System.Drawing.ImageAnimator.cs 5 // 6 // Authors: 7 // Dennis Hayes (dennish@Raytek.com) 8 // Sanjay Gupta (gsanjay@novell.com) 9 // Sebastien Pouliot <sebastien@ximian.com> 10 // 11 // (C) 2002 Ximian, Inc 12 // Copyright (C) 2004,2006-2007 Novell, Inc (http://www.novell.com) 13 // 14 // Permission is hereby granted, free of charge, to any person obtaining 15 // a copy of this software and associated documentation files (the 16 // "Software"), to deal in the Software without restriction, including 17 // without limitation the rights to use, copy, modify, merge, publish, 18 // distribute, sublicense, and/or sell copies of the Software, and to 19 // permit persons to whom the Software is furnished to do so, subject to 20 // the following conditions: 21 // 22 // The above copyright notice and this permission notice shall be 23 // included in all copies or substantial portions of the Software. 24 // 25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 // 33 34 using System.Collections; 35 using System.Drawing.Imaging; 36 using System.Threading; 37 38 namespace System.Drawing 39 { 40 41 class AnimateEventArgs : EventArgs 42 { 43 44 private int frameCount; 45 private int activeFrame; 46 private Thread thread; 47 AnimateEventArgs(Image image)48 public AnimateEventArgs(Image image) 49 { 50 frameCount = image.GetFrameCount(FrameDimension.Time); 51 } 52 53 public Thread RunThread 54 { 55 get { return thread; } 56 set { thread = value; } 57 } 58 GetNextFrame()59 public int GetNextFrame() 60 { 61 if (activeFrame < frameCount - 1) 62 activeFrame++; 63 else 64 activeFrame = 0; 65 66 return activeFrame; 67 } 68 } 69 70 public sealed class ImageAnimator 71 { 72 73 static Hashtable ht = Hashtable.Synchronized(new Hashtable()); 74 ImageAnimator()75 private ImageAnimator() 76 { 77 } 78 Animate(Image image, EventHandler onFrameChangedHandler)79 public static void Animate(Image image, EventHandler onFrameChangedHandler) 80 { 81 // must be non-null and contain animation time frames 82 if (!CanAnimate(image)) 83 return; 84 85 // is animation already in progress ? 86 if (ht.ContainsKey(image)) 87 return; 88 89 PropertyItem item = image.GetPropertyItem(0x5100); // FrameDelay in libgdiplus 90 byte[] value = item.Value; 91 int[] delay = new int[(value.Length >> 2)]; 92 for (int i = 0, n = 0; i < value.Length; i += 4, n++) 93 { 94 int d = BitConverter.ToInt32(value, i) * 10; 95 // follow worse case (Opera) see http://news.deviantart.com/article/27613/ 96 delay[n] = d < 100 ? 100 : d; 97 } 98 99 AnimateEventArgs aea = new AnimateEventArgs(image); 100 WorkerThread wt = new WorkerThread(onFrameChangedHandler, aea, delay); 101 Thread thread = new Thread(new ThreadStart(wt.LoopHandler)); 102 thread.IsBackground = true; 103 aea.RunThread = thread; 104 ht.Add(image, aea); 105 thread.Start(); 106 } 107 CanAnimate(Image image)108 public static bool CanAnimate(Image image) 109 { 110 if (image == null) 111 return false; 112 113 int n = image.FrameDimensionsList.Length; 114 if (n < 1) 115 return false; 116 117 for (int i = 0; i < n; i++) 118 { 119 if (image.FrameDimensionsList[i].Equals(FrameDimension.Time.Guid)) 120 { 121 return (image.GetFrameCount(FrameDimension.Time) > 1); 122 } 123 } 124 return false; 125 } 126 StopAnimate(Image image, EventHandler onFrameChangedHandler)127 public static void StopAnimate(Image image, EventHandler onFrameChangedHandler) 128 { 129 if (image == null) 130 return; 131 132 if (ht.ContainsKey(image)) 133 { 134 AnimateEventArgs evtArgs = (AnimateEventArgs)ht[image]; 135 evtArgs.RunThread.Abort(); 136 ht.Remove(image); 137 } 138 } 139 UpdateFrames()140 public static void UpdateFrames() 141 { 142 foreach (Image image in ht.Keys) 143 UpdateImageFrame(image); 144 } 145 146 UpdateFrames(Image image)147 public static void UpdateFrames(Image image) 148 { 149 if (image == null) 150 return; 151 152 if (ht.ContainsKey(image)) 153 UpdateImageFrame(image); 154 } 155 156 // this method avoid checks that aren't requied for UpdateFrames() UpdateImageFrame(Image image)157 private static void UpdateImageFrame(Image image) 158 { 159 AnimateEventArgs aea = (AnimateEventArgs)ht[image]; 160 image.SelectActiveFrame(FrameDimension.Time, aea.GetNextFrame()); 161 } 162 } 163 164 class WorkerThread 165 { 166 167 private EventHandler frameChangeHandler; 168 private AnimateEventArgs animateEventArgs; 169 private int[] delay; 170 WorkerThread(EventHandler frmChgHandler, AnimateEventArgs aniEvtArgs, int[] delay)171 public WorkerThread(EventHandler frmChgHandler, AnimateEventArgs aniEvtArgs, int[] delay) 172 { 173 frameChangeHandler = frmChgHandler; 174 animateEventArgs = aniEvtArgs; 175 this.delay = delay; 176 } 177 LoopHandler()178 public void LoopHandler() 179 { 180 try 181 { 182 int n = 0; 183 while (true) 184 { 185 Thread.Sleep(delay[n++]); 186 frameChangeHandler(null, animateEventArgs); 187 if (n == delay.Length) 188 n = 0; 189 } 190 } 191 catch (ThreadAbortException) 192 { 193 Thread.ResetAbort(); // we're going to finish anyway 194 } 195 } 196 } 197 } 198