WindowsGraphicsCacheManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Misc / GDI / WindowsGraphicsCacheManager.cs / 1305376 / WindowsGraphicsCacheManager.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

#if WGCM_TEST_SUITE // Enable tracking when built for the test suites. 
#define TRACK_HDC 
#define GDI_FONT_CACHE_TRACK
#endif 

#if [....]_NAMESPACE
namespace System.Windows.Forms.Internal
#elif DRAWING_NAMESPACE 
namespace System.Drawing.Internal
#else 
namespace System.Experimental.Gdi 
#endif
{ 
    using System;
    using System.Collections.Generic;
    using System.Internal;
    using System.Diagnostics; 
    using System.Drawing;
    using System.Threading; 
    using Microsoft.Win32; 
    using System.Runtime.Versioning;
 
    /// 
    ///     Keeps a cache of some graphics primitives.
    ///     Created to improve performance of TextRenderer.MeasureText methods that don't receive a WindowsGraphics.
    ///     This class mantains a cache of MRU WindowsFont objects in the process. (See VSWhidbey#301492). 
    /// 
#if [....]_PUBLIC_GRAPHICS_LIBRARY 
    public 
#else
    internal 
#endif
    class WindowsGraphicsCacheManager
    {
        // From MSDN: Do not specify initial values for fields marked with ThreadStaticAttribute, because such initialization occurs only once, 
        // when the class constructor executes, and therefore affects only one thread.
 
        // WindowsGraphics object used for measuring text based on the screen DC.  TLS to avoid synchronization issues. 
        [ThreadStatic]
        private static WindowsGraphics measurementGraphics; 

        // Circular list implementing the WindowsFont per-process cache.
        private const int CacheSize = 10;
        [ThreadStatic] 
        private static int currentIndex;
        [ThreadStatic] 
        private static List> windowsFontCache; 

        ///  
        ///     Static constructor since this is a utility class.
        /// 
        static WindowsGraphicsCacheManager()
        { 
            //
 
 

 


        }
 
        /// 
        ///     Class is never instantiated, private constructor prevents the compiler from generating a default constructor. 
        ///  
        private WindowsGraphicsCacheManager()
        { 
        }

        /// 
        ///     Initializes the WindowsFontCache object. 
        /// 
        private static List> WindowsFontCache 
        { 
            get
            { 
                if (windowsFontCache == null)
                {
                    currentIndex = -1;
                    windowsFontCache = new List>(CacheSize); 
                }
 
                return windowsFontCache; 
            }
        } 

        /// 
        ///     Get the cached screen (primary monitor) memory dc.
        ///     Users of this class should always use this property to get the WindowsGraphics and never cache it, it could be mistakenly 
        ///     disposed and we would recreate it if needed.
        ///     Users should not dispose of the WindowsGraphics so it can be reused for the lifetime of the thread. 
        ///  
        public static WindowsGraphics MeasurementGraphics
        { 
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            get
            { 
                if (measurementGraphics == null || measurementGraphics.DeviceContext == null /*object disposed*/)
                { 
                    Debug.Assert( measurementGraphics == null || measurementGraphics.DeviceContext != null, "TLS MeasurementGraphics was disposed somewhere, enable TRACK_HDC macro to determine who did it, recreating it for now ..." ); 
#if TRACK_HDC
                    //Debug.WriteLine( DbgUtil.StackTraceToStr(string.Format("Initializing MeasurementGraphics for thread: [0x{0:x8}]", Thread.CurrentThread.ManagedThreadId))); 
                    Debug.WriteLine( DbgUtil.StackTraceToStr("Initializing MeasurementGraphics"));
#endif
                    measurementGraphics = WindowsGraphics.CreateMeasurementWindowsGraphics();
                } 

                return measurementGraphics; 
            } 
        }
#if OPTIMIZED_MEASUREMENTDC 
        // in some cases, we dont want to demand create MeasurementGraphics, as creating it has
        // re-entrant side effects.
        internal static WindowsGraphics GetCurrentMeasurementGraphics()
        { 
            return measurementGraphics;
        } 
#endif 

 
        /// 
        ///     Get the cached WindowsFont associated with the specified font if one exists, otherwise create one and
        ///     add it to the cache.
        ///  
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)] 
        public static WindowsFont GetWindowsFont(Font font) 
        {
            return GetWindowsFont(font, WindowsFontQuality.Default); 
        }


        [ResourceExposure(ResourceScope.Process)] 
        [ResourceConsumption(ResourceScope.Process)]
        public static WindowsFont GetWindowsFont(Font font, WindowsFontQuality fontQuality) 
        { 
            if( font == null )
            { 
                return null;
            }

            // First check if font is in the cache. 

            int count = 0; 
            int index = currentIndex; 

            // Search by index of most recently added object. 
            while( count < WindowsFontCache.Count )
            {
                if (WindowsFontCache[index].Key.Equals(font))  // don't do shallow comparison, we could miss cloned fonts.
                { 
                    // We got a Font in the cache, let's see if we have a WindowsFont with the same quality as required by the caller.
 
                    // WARNING: It is not expected that the WindowsFont is disposed externally since it is created by this class. 
                    Debug.Assert(WindowsFontCache[index].Value.Hfont != IntPtr.Zero, "Cached WindowsFont was disposed, enable GDI_FINALIZATION_WATCH to track who did it!");
 
                    WindowsFont wf = WindowsFontCache[index].Value;
                    if(wf.Quality == fontQuality)
                    {
                        return wf; 
                    }
                } 
 
                index--;
                count++; 

                if( index < 0 )
                {
                    index = CacheSize - 1; 
                }
            } 
 
            // Font is not in the cache, let's add it.
 
            WindowsFont winFont = WindowsFont.FromFont(font, fontQuality);
            KeyValuePair newEntry = new KeyValuePair(font, winFont);

            currentIndex++; 

            if (currentIndex == CacheSize) 
            { 
                currentIndex = 0;
            } 

            if (WindowsFontCache.Count == CacheSize)  // No more room, update current index.
            {
                WindowsFont wfont = null; 

                // Go through the existing fonts in the cache, and see if any 
                // are not in use by a DC.  If one isn't, replace that.  If 
                // all are in use, new up a new font and do not cache it.
 
                bool finished = false;
                int startIndex = currentIndex;
                int loopIndex = startIndex + 1;
                while (!finished) { 
                    if (loopIndex >= CacheSize) {
                        loopIndex = 0; 
                    } 

                    if (loopIndex == startIndex) { 
                        finished = true;
                    }

                    wfont = WindowsFontCache[loopIndex].Value; 
                    if (!DeviceContexts.IsFontInUse(wfont)) {
                        currentIndex = loopIndex; 
                        finished = true; 
                        break;
                    } 
                    else {
                        loopIndex++;
                        wfont = null;
                    } 
                }
 
                if (wfont != null) { 
                    WindowsFontCache[currentIndex] = newEntry;
                    winFont.OwnedByCacheManager = true; 


#if GDI_FONT_CACHE_TRACK
                    Debug.WriteLine("Removing from cache: " + wfont); 
                    Debug.WriteLine( "Adding to cache: " + winFont );
#endif 
 

                    wfont.OwnedByCacheManager = false; 
                    wfont.Dispose();
                }
                else {
                    // do not cache font - caller is ALWAYS responsible for 
                    // disposing now.  If it is owned  by the CM, it will not
                    // disposed. 
 
                    winFont.OwnedByCacheManager = false;
 

#if GDI_FONT_CACHE_TRACK
                    Debug.WriteLine("Creating uncached font: " + winFont);
#endif 
                }
            } 
            else 
            {
                winFont.OwnedByCacheManager = true; 
                WindowsFontCache.Add(newEntry);


#if GDI_FONT_CACHE_TRACK 
                Debug.WriteLine( "Adding to cache: " + winFont );
#endif 
            } 
            return winFont;
        } 


#if [....]_PUBLIC_GRAPHICS_LIBRARY
        /// The following methods are not needed in production code since the cached objects are meant to be reused and should not be explicitly disposed, 
        /// left here for testing purposes.
 
        ///  
        /// 
        public static void Reset() 
        {
            ResetFontCache();
            ResetMeasurementGraphics();
        } 

        ///  
        ///     Dispose of all cached WindowsFont objects and reset the collection. 
        /// 
        public static void ResetFontCache() 
        {
            if( WindowsFontCache.Count > 0 )
            {
                for(int index = 0; index < WindowsFontCache.Count; index++ ) 
                {
                    WindowsFontCache[index].Value.Dispose(); 
                } 

                WindowsFontCache.Clear(); 
                currentIndex = -1;

#if GDI_FONT_CACHE_TRACK
                Debug.WriteLine( "Font cache reset.  Count: " + WindowsFontCache.Count ); 
#endif
            } 
        } 

        ///  
        ///     Dispose of cached memory dc.
        /// 
        public static void ResetMeasurementGraphics()
        { 
            if( measurementGraphics != null )
            { 
#if TRACK_HDC 
                //Debug.WriteLine( DbgUtil.StackTraceToStr(string.Format("Disposing measurement DC and WG for thread: [0x{0:x8}]", Thread.CurrentThread.ManagedThreadId)));
                Debug.WriteLine( DbgUtil.StackTraceToStr("Disposing measurement DC and WG")); 
#endif
                measurementGraphics.Dispose();
                measurementGraphics = null;
            } 
        }
#endif 
    } 

} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

#if WGCM_TEST_SUITE // Enable tracking when built for the test suites. 
#define TRACK_HDC 
#define GDI_FONT_CACHE_TRACK
#endif 

#if [....]_NAMESPACE
namespace System.Windows.Forms.Internal
#elif DRAWING_NAMESPACE 
namespace System.Drawing.Internal
#else 
namespace System.Experimental.Gdi 
#endif
{ 
    using System;
    using System.Collections.Generic;
    using System.Internal;
    using System.Diagnostics; 
    using System.Drawing;
    using System.Threading; 
    using Microsoft.Win32; 
    using System.Runtime.Versioning;
 
    /// 
    ///     Keeps a cache of some graphics primitives.
    ///     Created to improve performance of TextRenderer.MeasureText methods that don't receive a WindowsGraphics.
    ///     This class mantains a cache of MRU WindowsFont objects in the process. (See VSWhidbey#301492). 
    /// 
#if [....]_PUBLIC_GRAPHICS_LIBRARY 
    public 
#else
    internal 
#endif
    class WindowsGraphicsCacheManager
    {
        // From MSDN: Do not specify initial values for fields marked with ThreadStaticAttribute, because such initialization occurs only once, 
        // when the class constructor executes, and therefore affects only one thread.
 
        // WindowsGraphics object used for measuring text based on the screen DC.  TLS to avoid synchronization issues. 
        [ThreadStatic]
        private static WindowsGraphics measurementGraphics; 

        // Circular list implementing the WindowsFont per-process cache.
        private const int CacheSize = 10;
        [ThreadStatic] 
        private static int currentIndex;
        [ThreadStatic] 
        private static List> windowsFontCache; 

        ///  
        ///     Static constructor since this is a utility class.
        /// 
        static WindowsGraphicsCacheManager()
        { 
            //
 
 

 


        }
 
        /// 
        ///     Class is never instantiated, private constructor prevents the compiler from generating a default constructor. 
        ///  
        private WindowsGraphicsCacheManager()
        { 
        }

        /// 
        ///     Initializes the WindowsFontCache object. 
        /// 
        private static List> WindowsFontCache 
        { 
            get
            { 
                if (windowsFontCache == null)
                {
                    currentIndex = -1;
                    windowsFontCache = new List>(CacheSize); 
                }
 
                return windowsFontCache; 
            }
        } 

        /// 
        ///     Get the cached screen (primary monitor) memory dc.
        ///     Users of this class should always use this property to get the WindowsGraphics and never cache it, it could be mistakenly 
        ///     disposed and we would recreate it if needed.
        ///     Users should not dispose of the WindowsGraphics so it can be reused for the lifetime of the thread. 
        ///  
        public static WindowsGraphics MeasurementGraphics
        { 
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            get
            { 
                if (measurementGraphics == null || measurementGraphics.DeviceContext == null /*object disposed*/)
                { 
                    Debug.Assert( measurementGraphics == null || measurementGraphics.DeviceContext != null, "TLS MeasurementGraphics was disposed somewhere, enable TRACK_HDC macro to determine who did it, recreating it for now ..." ); 
#if TRACK_HDC
                    //Debug.WriteLine( DbgUtil.StackTraceToStr(string.Format("Initializing MeasurementGraphics for thread: [0x{0:x8}]", Thread.CurrentThread.ManagedThreadId))); 
                    Debug.WriteLine( DbgUtil.StackTraceToStr("Initializing MeasurementGraphics"));
#endif
                    measurementGraphics = WindowsGraphics.CreateMeasurementWindowsGraphics();
                } 

                return measurementGraphics; 
            } 
        }
#if OPTIMIZED_MEASUREMENTDC 
        // in some cases, we dont want to demand create MeasurementGraphics, as creating it has
        // re-entrant side effects.
        internal static WindowsGraphics GetCurrentMeasurementGraphics()
        { 
            return measurementGraphics;
        } 
#endif 

 
        /// 
        ///     Get the cached WindowsFont associated with the specified font if one exists, otherwise create one and
        ///     add it to the cache.
        ///  
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)] 
        public static WindowsFont GetWindowsFont(Font font) 
        {
            return GetWindowsFont(font, WindowsFontQuality.Default); 
        }


        [ResourceExposure(ResourceScope.Process)] 
        [ResourceConsumption(ResourceScope.Process)]
        public static WindowsFont GetWindowsFont(Font font, WindowsFontQuality fontQuality) 
        { 
            if( font == null )
            { 
                return null;
            }

            // First check if font is in the cache. 

            int count = 0; 
            int index = currentIndex; 

            // Search by index of most recently added object. 
            while( count < WindowsFontCache.Count )
            {
                if (WindowsFontCache[index].Key.Equals(font))  // don't do shallow comparison, we could miss cloned fonts.
                { 
                    // We got a Font in the cache, let's see if we have a WindowsFont with the same quality as required by the caller.
 
                    // WARNING: It is not expected that the WindowsFont is disposed externally since it is created by this class. 
                    Debug.Assert(WindowsFontCache[index].Value.Hfont != IntPtr.Zero, "Cached WindowsFont was disposed, enable GDI_FINALIZATION_WATCH to track who did it!");
 
                    WindowsFont wf = WindowsFontCache[index].Value;
                    if(wf.Quality == fontQuality)
                    {
                        return wf; 
                    }
                } 
 
                index--;
                count++; 

                if( index < 0 )
                {
                    index = CacheSize - 1; 
                }
            } 
 
            // Font is not in the cache, let's add it.
 
            WindowsFont winFont = WindowsFont.FromFont(font, fontQuality);
            KeyValuePair newEntry = new KeyValuePair(font, winFont);

            currentIndex++; 

            if (currentIndex == CacheSize) 
            { 
                currentIndex = 0;
            } 

            if (WindowsFontCache.Count == CacheSize)  // No more room, update current index.
            {
                WindowsFont wfont = null; 

                // Go through the existing fonts in the cache, and see if any 
                // are not in use by a DC.  If one isn't, replace that.  If 
                // all are in use, new up a new font and do not cache it.
 
                bool finished = false;
                int startIndex = currentIndex;
                int loopIndex = startIndex + 1;
                while (!finished) { 
                    if (loopIndex >= CacheSize) {
                        loopIndex = 0; 
                    } 

                    if (loopIndex == startIndex) { 
                        finished = true;
                    }

                    wfont = WindowsFontCache[loopIndex].Value; 
                    if (!DeviceContexts.IsFontInUse(wfont)) {
                        currentIndex = loopIndex; 
                        finished = true; 
                        break;
                    } 
                    else {
                        loopIndex++;
                        wfont = null;
                    } 
                }
 
                if (wfont != null) { 
                    WindowsFontCache[currentIndex] = newEntry;
                    winFont.OwnedByCacheManager = true; 


#if GDI_FONT_CACHE_TRACK
                    Debug.WriteLine("Removing from cache: " + wfont); 
                    Debug.WriteLine( "Adding to cache: " + winFont );
#endif 
 

                    wfont.OwnedByCacheManager = false; 
                    wfont.Dispose();
                }
                else {
                    // do not cache font - caller is ALWAYS responsible for 
                    // disposing now.  If it is owned  by the CM, it will not
                    // disposed. 
 
                    winFont.OwnedByCacheManager = false;
 

#if GDI_FONT_CACHE_TRACK
                    Debug.WriteLine("Creating uncached font: " + winFont);
#endif 
                }
            } 
            else 
            {
                winFont.OwnedByCacheManager = true; 
                WindowsFontCache.Add(newEntry);


#if GDI_FONT_CACHE_TRACK 
                Debug.WriteLine( "Adding to cache: " + winFont );
#endif 
            } 
            return winFont;
        } 


#if [....]_PUBLIC_GRAPHICS_LIBRARY
        /// The following methods are not needed in production code since the cached objects are meant to be reused and should not be explicitly disposed, 
        /// left here for testing purposes.
 
        ///  
        /// 
        public static void Reset() 
        {
            ResetFontCache();
            ResetMeasurementGraphics();
        } 

        ///  
        ///     Dispose of all cached WindowsFont objects and reset the collection. 
        /// 
        public static void ResetFontCache() 
        {
            if( WindowsFontCache.Count > 0 )
            {
                for(int index = 0; index < WindowsFontCache.Count; index++ ) 
                {
                    WindowsFontCache[index].Value.Dispose(); 
                } 

                WindowsFontCache.Clear(); 
                currentIndex = -1;

#if GDI_FONT_CACHE_TRACK
                Debug.WriteLine( "Font cache reset.  Count: " + WindowsFontCache.Count ); 
#endif
            } 
        } 

        ///  
        ///     Dispose of cached memory dc.
        /// 
        public static void ResetMeasurementGraphics()
        { 
            if( measurementGraphics != null )
            { 
#if TRACK_HDC 
                //Debug.WriteLine( DbgUtil.StackTraceToStr(string.Format("Disposing measurement DC and WG for thread: [0x{0:x8}]", Thread.CurrentThread.ManagedThreadId)));
                Debug.WriteLine( DbgUtil.StackTraceToStr("Disposing measurement DC and WG")); 
#endif
                measurementGraphics.Dispose();
                measurementGraphics = null;
            } 
        }
#endif 
    } 

} 

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