/// <devdoc> /// Removes an image from the image manager so it is no longer animated. /// </devdoc> public static void StopAnimate(Image image, EventHandler onFrameChangedHandler) { // Make sure we have a list of images if (image == null || imageInfoList == null) { return; } // Acquire a writer lock to modify the image info list - See comments on Animate() about this locking. bool readerLockHeld = rwImgListLock.IsReaderLockHeld; LockCookie lockDowngradeCookie = new LockCookie(); threadWriterLockWaitCount++; try { if (readerLockHeld) { lockDowngradeCookie = rwImgListLock.UpgradeToWriterLock(Timeout.Infinite); } else { rwImgListLock.AcquireWriterLock(Timeout.Infinite); } } finally { threadWriterLockWaitCount--; Debug.Assert(threadWriterLockWaitCount >= 0, "threadWriterLockWaitCount less than zero."); } try { // Find the corresponding reference and remove it for (int i = 0; i < imageInfoList.Count; i++) { ImageInfoExpender imageInfo = imageInfoList[i]; if (image == imageInfo.Image) { if ((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler))) { imageInfoList.Remove(imageInfo); } break; } } } finally { if (readerLockHeld) { rwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie); } else { rwImgListLock.ReleaseWriterLock(); } } }
static void AnimateImages50ms() { Debug.Assert(imageInfoList != null, "Null images list"); while (Dispose) { // Acquire reader-lock to access imageInfoList, elemens in the list can be modified w/o needing a writer-lock. // Observe that we don't need to check if the thread is waiting or a writer lock here since the thread this // method runs in never acquires a writer lock. rwImgListLock.AcquireReaderLock(Timeout.Infinite); try { for (int i = 0; i < imageInfoList.Count; i++) { ImageInfoExpender imageInfo = imageInfoList[i]; imageInfo.FrameTimer += 5; if (imageInfo.FrameTimer >= imageInfo.FrameDelay(imageInfo.Frame)) { imageInfo.FrameTimer = 0; if (imageInfo.Frame + 1 < imageInfo.FrameCount) { imageInfo.Frame++; } else { imageInfo.Frame = 0; } if (imageInfo.FrameDirty) { anyFrameDirty = true; } } } } finally { rwImgListLock.ReleaseReaderLock(); } Thread.Sleep(50); } animationThread = null; }
///// <devdoc> ///// Advances the frame in all images currently being animated. The new frame is drawn the next time the image is rendered. ///// </devdoc> //public static void UpdateFrames() { // if (!anyFrameDirty || imageInfoList == null) { // return; // } // if (threadWriterLockWaitCount > 0) { // // Cannot acquire reader lock at this time, frames update will be missed. // return; // } // rwImgListLock.AcquireReaderLock(Timeout.Infinite); // try { // foreach (GifImageInfo imageInfo in imageInfoList) { // // See comment in the class header about locking the image ref. // lock (imageInfo.Image) { // imageInfo.UpdateFrame(); // } // } // anyFrameDirty = false; // } // finally { // rwImgListLock.ReleaseReaderLock(); // } //} /// <devdoc> /// Adds an image to the image manager. If the image does not support animation this method does nothing. /// This method creates the image list and spawns the animation thread the first time it is called. /// </devdoc> public static void Animate(Image image, EventHandler onFrameChangedHandler) { if (image == null) { return; } ImageInfoExpender imageInfo = null; // See comment in the class header about locking the image ref. lock (image) { // could we avoid creating an ImageInfo object if FrameCount == 1 ? imageInfo = new ImageInfoExpender(image); } // If the image is already animating, stop animating it StopAnimate(image, onFrameChangedHandler); // Acquire a writer lock to modify the image info list. If the thread has a reader lock we need to upgrade // it to a writer lock; acquiring a reader lock in this case would block the thread on itself. // If the thread already has a writer lock its ref count will be incremented w/o placing the request in the // writer queue. See ReaderWriterLock.AcquireWriterLock method in the MSDN. bool readerLockHeld = rwImgListLock.IsReaderLockHeld; LockCookie lockDowngradeCookie = new LockCookie(); threadWriterLockWaitCount++; try { if (readerLockHeld) { lockDowngradeCookie = rwImgListLock.UpgradeToWriterLock(Timeout.Infinite); } else { rwImgListLock.AcquireWriterLock(Timeout.Infinite); } } finally { threadWriterLockWaitCount--; Debug.Assert(threadWriterLockWaitCount >= 0, "threadWriterLockWaitCount less than zero."); } try { if (imageInfo.Animated) { // Construct the image array // if (imageInfoList == null) { imageInfoList = new List <ImageInfoExpender>(); } // Add the new image // imageInfo.FrameChangedHandler = onFrameChangedHandler; imageInfoList.Add(imageInfo); // Construct a new timer thread if we haven't already // if (animationThread == null) { animationThread = new Thread(new ThreadStart(AnimateImages50ms)); animationThread.Name = typeof(ImageAnimator).Name; animationThread.IsBackground = true; animationThread.Start(); } } } finally { if (readerLockHeld) { rwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie); } else { rwImgListLock.ReleaseWriterLock(); } } }