File: misc\GDI\MeasurementDCInfo.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
#if OPTIMIZED_MEASUREMENTDC
#if WGCM_TEST_SUITE // Enable tracking when built for the test suites.
#define TRACK_HDC
#define GDI_FONT_CACHE_TRACK
#endif
 
#if Microsoft_NAMESPACE
namespace System.Windows.Forms.Internal {
#elif DRAWING_NAMESPACE
namespace System.Drawing.Internal {
#else
namespace System.Experimental.Gdi {
#endif
#if Microsoft_PUBLIC_GRAPHICS_LIBRARY
    public
#else
    internal
#endif
    static class MeasurementDCInfo {
 
        /// MeasurementDCInfo
        /// This class optimizes the MeasurmentGraphics as it caches in the last used font and TextMargins used.
        /// This prevents unnecessary p/invoke calls to GetCurrentObject, etc 
        /// It has been found to give close to 2x performance when drawing lots of text in rapid succession 
        /// DataGridView with lots of text, etc.
        /// To turn it on for your DLL, use the OPTIMIZED_MEASUREMENTDC compiler switch and add this class to the sources.
 
       [ThreadStatic]        
       private static CachedInfo cachedMeasurementDCInfo;
 
       /// IsMeasurementDC
       ///  Returns whether the IDeviceContext passed in is our static MeasurementDC.
       ///  If it is, we know a bit more information about it.
       internal static bool IsMeasurementDC(DeviceContext dc) 
       {
            WindowsGraphics sharedGraphics = WindowsGraphicsCacheManager.GetCurrentMeasurementGraphics();           
            return sharedGraphics != null && sharedGraphics.DeviceContext != null && sharedGraphics.DeviceContext.Hdc == dc.Hdc;
       }
 
       /// LastUsedFont -
       ///  Returns the font we think was last selected into the MeasurementGraphics.
       ///
       internal static WindowsFont LastUsedFont 
       {
          get 
          {
             return (cachedMeasurementDCInfo == null) ? null : cachedMeasurementDCInfo.LastUsedFont;
          }
          set 
          {
             if (cachedMeasurementDCInfo == null) 
             {
                cachedMeasurementDCInfo = new CachedInfo();
             }
             cachedMeasurementDCInfo.UpdateFont(value);
          }
       }
 
 
       /// GetTextMargins - checks to see if we have cached information about the current font,
       /// returns info about it.
       /// An MRU of Font margins was considered, but seems like overhead.
       internal static IntNativeMethods.DRAWTEXTPARAMS GetTextMargins(WindowsGraphics wg, WindowsFont font) {
 
            // PERF: operate on a local reference rather than party directly on the thread static one.
            CachedInfo currentCachedInfo = cachedMeasurementDCInfo;
            
            if (currentCachedInfo != null && currentCachedInfo.LeftTextMargin >0 && currentCachedInfo.RightTextMargin >0 && font == currentCachedInfo.LastUsedFont) 
            {
                // we have to return clones as DrawTextEx will modify this struct
                return new IntNativeMethods.DRAWTEXTPARAMS(currentCachedInfo.LeftTextMargin,currentCachedInfo.RightTextMargin);                    
            }
            else if (currentCachedInfo == null) 
            {
                currentCachedInfo = new CachedInfo();
                cachedMeasurementDCInfo = currentCachedInfo;
            }
            IntNativeMethods.DRAWTEXTPARAMS drawTextParams = wg.GetTextMargins(font);
            currentCachedInfo.LeftTextMargin = drawTextParams.iLeftMargin;
            currentCachedInfo.RightTextMargin = drawTextParams.iRightMargin;
            
            // returning a copy here to be consistent with the return value from the cache.
            return new IntNativeMethods.DRAWTEXTPARAMS(currentCachedInfo.LeftTextMargin,currentCachedInfo.RightTextMargin);
            
       }
 
        internal static void ResetIfIsMeasurementDC(IntPtr hdc) {
           WindowsGraphics sharedGraphics = WindowsGraphicsCacheManager.GetCurrentMeasurementGraphics();           
           if (sharedGraphics != null && sharedGraphics.DeviceContext != null && sharedGraphics.DeviceContext.Hdc == hdc) {            
               CachedInfo currentCachedInfo = cachedMeasurementDCInfo;
               if (currentCachedInfo != null) 
               {
                    currentCachedInfo.UpdateFont(null);
               }                
           }
       }
       /// Reset
       ///  clear the current cached information about the measurement dc.
       internal static void Reset() {
           CachedInfo currentCachedInfo = cachedMeasurementDCInfo;
           if (currentCachedInfo != null) 
           {
                currentCachedInfo.UpdateFont(null);
           }                
       }
       /// CachedInfo
       ///  store all the thread statics together so we dont have to fetch individual fields out of TLS
       private sealed class CachedInfo 
       {
            public WindowsFont LastUsedFont;
            public int LeftTextMargin;            
            public int RightTextMargin;
            
            internal void UpdateFont(WindowsFont font) 
            {
                if (LastUsedFont != font) 
                {
                    LastUsedFont = font;
                    LeftTextMargin = -1;
                    RightTextMargin = -1;                    
                }
            }
       }
    }
}
#endif // OPTIMIZED_MEASUREMENTDC