ImageAnimator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / CommonUI / System / Drawing / ImageAnimator.cs / 1 / ImageAnimator.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 
namespace System.Drawing {
    using System.Threading; 
    using System; 
    using System.ComponentModel;
    using System.Collections; 
    using Microsoft.Win32;
    using System.Diagnostics;
    using System.Drawing.Imaging;
    using System.Diagnostics.CodeAnalysis; 
    using System.Collections.Generic;
 
    ///  
    ///     Animates one or more images that have time-based frames.
    ///     See the ImageInfo.cs file for the helper nested ImageInfo class. 
    ///
    ///     A common pattern for using this class is as follows (See PictureBox control):
    ///     1. The winform app (user's code) calls ImageAnimator.Animate() from the main thread.
    ///     2. Animate() spawns the animating (worker) thread in the background, which will update the image 
    ///        frames and raise the OnFrameChanged event, which handler will be executed in the main thred.
    ///     3. The main thread triggers a paint event (Invalidate()) from the OnFrameChanged handler. 
    ///     4. From the OnPaint event, the main thread calls ImageAnimator.UpdateFrames() and then paints the 
    ///        image (updated frame).
    ///     5. The main thread calls ImageAnimator.StopAnimate() when needed. This does not kill the worker thread. 
    ///
    ///     Comment on locking the image ref:
    ///     We need to synchronize access to sections of code that modify the image(s), but we don't want to block
    ///     animation of one image when modifying a different one; for this, we use the image ref for locking the 
    ///     critical section (lock(image)).
    /// 
    ///     This class is safe for multi-threading but Image is not; multithreaded applications must use a critical 
    ///     section lock using the image ref the image access is not from the same thread that executes ImageAnimator
    ///     code.  If the user code locks on the image ref forever a deadlock will happen preventing the animiation 
    ///     from occurring.
    /// 
    public sealed partial class ImageAnimator {
        ///  
        ///     A list of images to be animated.
        ///  
        static List imageInfoList; 

        ///  
        ///     A variable to flag when an image or images need to be updated due to the selection of a new frame
        ///     in an image.  We don't need to synchronize access to this variable, in the case it is true we don't
        ///     do anything, otherwise the worse case is where a thread attempts to update the image's frame after
        ///     another one did which is harmless. 
        /// 
        static bool anyFrameDirty; 
 
        /// 
        ///     The thread used for animating the images. 
        /// 
        static Thread animationThread;

        ///  
        ///     Lock that allows either concurrent read-access to the images list for multiple threads, or write-
        ///     access to it for a single thread.  Observe that synchronization access to image objects are done 
        ///     with critical sections (lock). 
        /// 
        static ReaderWriterLock rwImgListLock = new ReaderWriterLock(); 

        /// 
        ///     Flag to avoid a deadlock when waiting on a write-lock and a an attemp to acquire a read-lock is
        ///     made in the same thread. The following comments are from WeiWen Liu (CLR team): 
        ///     <[....]: If RWLock is currently owned by another thread, the current thread is going to wait on an
        ///     event using CoWaitForMultipleHandles while pumps message/>. 
        ///     The comment above refers to the COM STA message pump, not to be confused with the UI message pump. 
        ///     However, the effect is the same, the COM message pump will pump messages and dispatch them to the
        ///     window while waiting on the writer lock; this has the potential of creating a re-entrancy situation 
        //      that if during the message processing a wait on a reader lock is originated the thread will be block
        //      on itself - see call stack attached to VSW#465562.
        ///     <[....]: While processing STA message, the thread may call back into managed code. We do this because
        ///     we can not block finalizer thread.  Finalizer thread may need to release STA objects on this thread. If 
        ///     the current thread does not pump message, finalizer thread is blocked, and AD  unload is blocked while
        ///     waiting for finalizer thread. RWLock is a fair lock. If a thread waits for a writer lock, then it needs 
        ///     a reader lock while pumping message, the thread is blocked forever/>. 
        ///     See Bug VSW#364535 which was due to the above situation.
        /// 
        ///     This TLS variable is used to flag the above situation and avoid the deadlock, it is ThreadStatic so each
        ///     thread calling into ImageAnimator is garded against this problem.
        /// 
        [ThreadStatic] 
        static int threadWriterLockWaitCount;
 
        ///  
        ///     Prevent instantiation of this class.
        ///  
        private ImageAnimator() {
        }

        ///  
        ///     Advances the frame in the specified image. The new frame is drawn the next time the image is rendered.
        ///  
        public static void UpdateFrames(Image image) { 
            if (!anyFrameDirty || image == null || imageInfoList == null) {
                return; 
            }

            if (threadWriterLockWaitCount > 0) {
                // Cannot acquire reader lock - frame update will be missed. 
                return;
            } 
 
            // If the current thread already has the writer lock, no reader lock is acquired. Instead, the lock count on
            // the writer lock is incremented. It it already has a reader lock, the locks ref count will be incremented 
            // w/o placing the request at the end of the reader queue.

            rwImgListLock.AcquireReaderLock(Timeout.Infinite);
 
            try {
                bool foundDirty = false; 
                bool foundImage = false; 

                foreach (ImageInfo imageInfo in imageInfoList) { 
                    if (imageInfo.Image == image) {
                        if( imageInfo.FrameDirty ){
                            // See comment in the class header about locking the image ref.
                            lock( imageInfo.Image ){ 
                                imageInfo.UpdateFrame();
                            } 
                        } 
                        foundImage = true;
                    } 

                    if (imageInfo.FrameDirty) {
                        foundDirty = true;
                    } 

                    if (foundDirty && foundImage) { 
                        break; 
                    }
                } 

                anyFrameDirty = foundDirty;
            }
            finally { 
                rwImgListLock.ReleaseReaderLock();
            } 
        } 

        ///  
        ///     Advances the frame in all images currently being animated. The new frame is drawn the next time the image is rendered.
        /// 
        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 (ImageInfo imageInfo in imageInfoList) { 
                    // See comment in the class header about locking the image ref. 
                    lock (imageInfo.Image) {
                        imageInfo.UpdateFrame(); 
                    }
                }
                anyFrameDirty = false;
            } 
            finally {
                rwImgListLock.ReleaseReaderLock(); 
            } 
        }
 
        /// 
        ///     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.
        ///  
        public static void Animate(Image image, EventHandler onFrameChangedHandler) {
            if( image == null ) { 
                return; 
            }
 
            ImageInfo 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 ImageInfo(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(); 
                    }
 
                    // 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(); 
                }
            }
        }
 
        /// 
        ///    Whether or not the image has multiple time-based frames. 
        ///  
        public static bool CanAnimate(Image image) {
            if( image == null ) { 
                return false;
            }

            // See comment in the class header about locking the image ref. 
            lock( image ) {
                Guid[] dimensions = image.FrameDimensionsList; 
 
                foreach (Guid guid in dimensions) {
                    FrameDimension dimension = new FrameDimension(guid); 
                    if (dimension.Equals(FrameDimension.Time)) {
                        return image.GetFrameCount(FrameDimension.Time) > 1;
                    }
                } 
            }
 
            return false; 
        }
 
        /// 
        ///     Removes an image from the image manager so it is no longer animated.
        /// 
        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++) {
                    ImageInfo 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();
                } 
            }
        } 
 

        ///  
        ///     Worker thread procedure which implements the main animation loop.
        ///     NOTE: This is the ONLY code the worker thread executes, keeping it in one method helps better understand
        ///     any synchronization issues.
        ///     WARNING: Also, this is the only place where ImageInfo objects (not the contained image object) are modified, 
        ///     so no access synchronization is required to modify them.
        ///  
        [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")] 
        static void AnimateImages50ms() {
            Debug.Assert(imageInfoList != null, "Null images list"); 

            while( true ) {
                // 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++) {
                        ImageInfo imageInfo = imageInfoList[i]; 

                        // Frame delay is measured in 1/100ths of a second. This thread
                        // sleeps for 50 ms = 5/100ths of a second between frame updates,
                        // so we increase the frame delay count 5/100ths of a second 
                        // at a time.
                        // 
                        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);
            }
        }
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 
namespace System.Drawing {
    using System.Threading; 
    using System; 
    using System.ComponentModel;
    using System.Collections; 
    using Microsoft.Win32;
    using System.Diagnostics;
    using System.Drawing.Imaging;
    using System.Diagnostics.CodeAnalysis; 
    using System.Collections.Generic;
 
    ///  
    ///     Animates one or more images that have time-based frames.
    ///     See the ImageInfo.cs file for the helper nested ImageInfo class. 
    ///
    ///     A common pattern for using this class is as follows (See PictureBox control):
    ///     1. The winform app (user's code) calls ImageAnimator.Animate() from the main thread.
    ///     2. Animate() spawns the animating (worker) thread in the background, which will update the image 
    ///        frames and raise the OnFrameChanged event, which handler will be executed in the main thred.
    ///     3. The main thread triggers a paint event (Invalidate()) from the OnFrameChanged handler. 
    ///     4. From the OnPaint event, the main thread calls ImageAnimator.UpdateFrames() and then paints the 
    ///        image (updated frame).
    ///     5. The main thread calls ImageAnimator.StopAnimate() when needed. This does not kill the worker thread. 
    ///
    ///     Comment on locking the image ref:
    ///     We need to synchronize access to sections of code that modify the image(s), but we don't want to block
    ///     animation of one image when modifying a different one; for this, we use the image ref for locking the 
    ///     critical section (lock(image)).
    /// 
    ///     This class is safe for multi-threading but Image is not; multithreaded applications must use a critical 
    ///     section lock using the image ref the image access is not from the same thread that executes ImageAnimator
    ///     code.  If the user code locks on the image ref forever a deadlock will happen preventing the animiation 
    ///     from occurring.
    /// 
    public sealed partial class ImageAnimator {
        ///  
        ///     A list of images to be animated.
        ///  
        static List imageInfoList; 

        ///  
        ///     A variable to flag when an image or images need to be updated due to the selection of a new frame
        ///     in an image.  We don't need to synchronize access to this variable, in the case it is true we don't
        ///     do anything, otherwise the worse case is where a thread attempts to update the image's frame after
        ///     another one did which is harmless. 
        /// 
        static bool anyFrameDirty; 
 
        /// 
        ///     The thread used for animating the images. 
        /// 
        static Thread animationThread;

        ///  
        ///     Lock that allows either concurrent read-access to the images list for multiple threads, or write-
        ///     access to it for a single thread.  Observe that synchronization access to image objects are done 
        ///     with critical sections (lock). 
        /// 
        static ReaderWriterLock rwImgListLock = new ReaderWriterLock(); 

        /// 
        ///     Flag to avoid a deadlock when waiting on a write-lock and a an attemp to acquire a read-lock is
        ///     made in the same thread. The following comments are from WeiWen Liu (CLR team): 
        ///     <[....]: If RWLock is currently owned by another thread, the current thread is going to wait on an
        ///     event using CoWaitForMultipleHandles while pumps message/>. 
        ///     The comment above refers to the COM STA message pump, not to be confused with the UI message pump. 
        ///     However, the effect is the same, the COM message pump will pump messages and dispatch them to the
        ///     window while waiting on the writer lock; this has the potential of creating a re-entrancy situation 
        //      that if during the message processing a wait on a reader lock is originated the thread will be block
        //      on itself - see call stack attached to VSW#465562.
        ///     <[....]: While processing STA message, the thread may call back into managed code. We do this because
        ///     we can not block finalizer thread.  Finalizer thread may need to release STA objects on this thread. If 
        ///     the current thread does not pump message, finalizer thread is blocked, and AD  unload is blocked while
        ///     waiting for finalizer thread. RWLock is a fair lock. If a thread waits for a writer lock, then it needs 
        ///     a reader lock while pumping message, the thread is blocked forever/>. 
        ///     See Bug VSW#364535 which was due to the above situation.
        /// 
        ///     This TLS variable is used to flag the above situation and avoid the deadlock, it is ThreadStatic so each
        ///     thread calling into ImageAnimator is garded against this problem.
        /// 
        [ThreadStatic] 
        static int threadWriterLockWaitCount;
 
        ///  
        ///     Prevent instantiation of this class.
        ///  
        private ImageAnimator() {
        }

        ///  
        ///     Advances the frame in the specified image. The new frame is drawn the next time the image is rendered.
        ///  
        public static void UpdateFrames(Image image) { 
            if (!anyFrameDirty || image == null || imageInfoList == null) {
                return; 
            }

            if (threadWriterLockWaitCount > 0) {
                // Cannot acquire reader lock - frame update will be missed. 
                return;
            } 
 
            // If the current thread already has the writer lock, no reader lock is acquired. Instead, the lock count on
            // the writer lock is incremented. It it already has a reader lock, the locks ref count will be incremented 
            // w/o placing the request at the end of the reader queue.

            rwImgListLock.AcquireReaderLock(Timeout.Infinite);
 
            try {
                bool foundDirty = false; 
                bool foundImage = false; 

                foreach (ImageInfo imageInfo in imageInfoList) { 
                    if (imageInfo.Image == image) {
                        if( imageInfo.FrameDirty ){
                            // See comment in the class header about locking the image ref.
                            lock( imageInfo.Image ){ 
                                imageInfo.UpdateFrame();
                            } 
                        } 
                        foundImage = true;
                    } 

                    if (imageInfo.FrameDirty) {
                        foundDirty = true;
                    } 

                    if (foundDirty && foundImage) { 
                        break; 
                    }
                } 

                anyFrameDirty = foundDirty;
            }
            finally { 
                rwImgListLock.ReleaseReaderLock();
            } 
        } 

        ///  
        ///     Advances the frame in all images currently being animated. The new frame is drawn the next time the image is rendered.
        /// 
        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 (ImageInfo imageInfo in imageInfoList) { 
                    // See comment in the class header about locking the image ref. 
                    lock (imageInfo.Image) {
                        imageInfo.UpdateFrame(); 
                    }
                }
                anyFrameDirty = false;
            } 
            finally {
                rwImgListLock.ReleaseReaderLock(); 
            } 
        }
 
        /// 
        ///     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.
        ///  
        public static void Animate(Image image, EventHandler onFrameChangedHandler) {
            if( image == null ) { 
                return; 
            }
 
            ImageInfo 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 ImageInfo(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(); 
                    }
 
                    // 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(); 
                }
            }
        }
 
        /// 
        ///    Whether or not the image has multiple time-based frames. 
        ///  
        public static bool CanAnimate(Image image) {
            if( image == null ) { 
                return false;
            }

            // See comment in the class header about locking the image ref. 
            lock( image ) {
                Guid[] dimensions = image.FrameDimensionsList; 
 
                foreach (Guid guid in dimensions) {
                    FrameDimension dimension = new FrameDimension(guid); 
                    if (dimension.Equals(FrameDimension.Time)) {
                        return image.GetFrameCount(FrameDimension.Time) > 1;
                    }
                } 
            }
 
            return false; 
        }
 
        /// 
        ///     Removes an image from the image manager so it is no longer animated.
        /// 
        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++) {
                    ImageInfo 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();
                } 
            }
        } 
 

        ///  
        ///     Worker thread procedure which implements the main animation loop.
        ///     NOTE: This is the ONLY code the worker thread executes, keeping it in one method helps better understand
        ///     any synchronization issues.
        ///     WARNING: Also, this is the only place where ImageInfo objects (not the contained image object) are modified, 
        ///     so no access synchronization is required to modify them.
        ///  
        [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")] 
        static void AnimateImages50ms() {
            Debug.Assert(imageInfoList != null, "Null images list"); 

            while( true ) {
                // 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++) {
                        ImageInfo imageInfo = imageInfoList[i]; 

                        // Frame delay is measured in 1/100ths of a second. This thread
                        // sleeps for 50 ms = 5/100ths of a second between frame updates,
                        // so we increase the frame delay count 5/100ths of a second 
                        // at a time.
                        // 
                        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);
            }
        }
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK