File: misc\GDI\DeviceContext.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="IDeviceContext.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
// #define TRACK_HDC
// #define GDI_FINALIZATION_WATCH
 
#if Microsoft_NAMESPACE
namespace System.Windows.Forms.Internal
#elif DRAWING_NAMESPACE
namespace System.Drawing.Internal
#else
namespace System.Experimental.Gdi
#endif
{
    using System;
    using System.Collections;
    using System.Internal;
    using System.Security;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.ComponentModel;
    using System.Drawing;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime.Versioning;
 
    /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext"]/*' />
    /// <devdoc>
    ///     Represents a Win32 device context.  Provides operations for setting some of the properties
    ///     of a device context.  It's the managed wrapper for an HDC.
    ///     
    ///     This class is divided into two files separating the code that needs to be compiled into
    ///     reatail builds and debugging code.
    /// </devdoc>
#if Microsoft_PUBLIC_GRAPHICS_LIBRARY
    public
#else
    internal
#endif
    sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext, IDisposable
    {
        /// <devdoc>
        ///     This class is a wrapper to a Win32 device context, and the Hdc property is the way to get a 
        ///     handle to it.
        ///     
        ///     The hDc is released/deleted only when owned by the object, meaning it was created internally; 
        ///     in this case, the object is responsible for releasing/deleting it. 
        ///     In the case the object is created from an exisiting hdc, it is not released; this is consistent 
        ///     with the Win32 guideline that says if you call GetDC/CreateDC/CreatIC/CreateEnhMetafile, you are 
        ///     responsible for calling ReleaseDC/DeleteDC/DeleteEnhMetafile respectivelly.
        ///     
        ///     This class implements some of the operations commonly performed on the properties of a dc  in Microsoft, 
        ///     specially for interacting with GDI+, like clipping and coordinate transformation.  
        ///     Several properties are not persisted in the dc but instead they are set/reset during a more comprehensive
        ///     operation like text rendering or painting; for instance text alignment is set and reset during DrawText (GDI), 
        ///     DrawString (GDI+).  
        ///     
        ///     Other properties are persisted from operation to operation until they are reset, like clipping, 
        ///     one can make several calls to Graphics or WindowsGraphics obect after setting the dc clip area and 
        ///     before resetting it; these kinds of properties are the ones implemented in this class.
        ///     This kind of properties place an extra chanllenge in the scenario where a DeviceContext is obtained 
        ///     from a Graphics object that has been used with GDI+, because GDI+ saves the hdc internally, rendering the 
        ///     DeviceContext underlying hdc out of sync.  DeviceContext needs to support these kind of properties to 
        ///     be able to keep the GDI+ and GDI HDCs in sync.
        ///     
        ///     A few other persisting properties have been implemented in DeviceContext2, among them:
        ///     1. Window origin.
        ///     2. Bounding rectangle.
        ///     3. DC origin.
        ///     4. View port extent.
        ///     5. View port origin.
        ///     6. Window extent
        ///     
        ///     Other non-persisted properties just for information: Background/Forground color, Palette, Color adjustment,
        ///     Color space, ICM mode and profile, Current pen position, Binary raster op (not supported by GDI+), 
        ///     Background mode, Logical Pen, DC pen color, ARc direction, Miter limit, Logical brush, DC brush color,
        ///     Brush origin, Polygon filling mode, Bitmap stretching mode, Logical font, Intercharacter spacing, 
        ///     Font mapper flags, Text alignment, Test justification, Layout, Path, Meta region.
        ///     See book "Windows Graphics Programming - Feng Yuang", P315 - Device Context Attributes.
        /// </devdoc>
 
        [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]    
        IntPtr hDC;
        DeviceContextType dcType;
 
        public event EventHandler Disposing;
 
        bool disposed;
 
        // We cache the hWnd when creating the dc from one, to provide support forIDeviceContext.GetHdc/ReleaseHdc.  
        // This hWnd could be null, in such case it is referring to the screen.
        [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
        IntPtr hWnd = (IntPtr) (-1); // Unlikely to be a valid hWnd.  
 
        IntPtr hInitialPen;
        IntPtr hInitialBrush;
        IntPtr hInitialBmp;
        IntPtr hInitialFont;
 
        IntPtr hCurrentPen;
        IntPtr hCurrentBrush;
        IntPtr hCurrentBmp;
        IntPtr hCurrentFont;
 
        Stack  contextStack;
 
#if GDI_FINALIZATION_WATCH
        private string AllocationSite = DbgUtil.StackTrace;
        private string DeAllocationSite = "";
#endif
 
        ///
        /// Class properties...
        ///
 
        /// <devdoc>
        ///     Specifies whether a modification has been applied to the dc, like setting the clipping area or a coordinate transform.
        /// </devdoc>
 
        /// <devdoc>
        ///     The device type the context refers to.
        /// </devdoc>
        public DeviceContextType DeviceContextType
        {
            get
            {
                return this.dcType;
            }
        }
 
        /// <devdoc>
        ///     This object's hdc.  If this property is called, then the object will be used as an HDC wrapper,
        ///     so the hdc is cached and calls to GetHdc/ReleaseHdc won't PInvoke into GDI.
        ///     Call Dispose to properly release the hdc.
        /// </devdoc>
        public IntPtr Hdc
        {
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            get
            {
                if( this.hDC == IntPtr.Zero )
                {
                    if( this.dcType == DeviceContextType.Display )
                    {
                        Debug.Assert(!this.disposed, "Accessing a disposed DC, forcing recreation of HDC - this will generate a Handle leak!");
 
                        // Note: ReleaseDC must be called from the same thread. This applies only to HDC obtained
                        // from calling GetDC. This means Display DeviceContext objects should never be finalized.
                        this.hDC = ((IDeviceContext)this).GetHdc();  // this.hDC will be released on call to Dispose.
                        CacheInitialState();
                    }
#if GDI_FINALIZATION_WATCH
                    else
                    {
                        try { Debug.WriteLine(string.Format("Allocation stack:\r\n{0}\r\nDeallocation stack:\r\n{1}", AllocationSite, DeAllocationSite)); } catch  {}
                    }
#endif
                }
 
                Debug.Assert( this.hDC != IntPtr.Zero, "Attempt to use deleted HDC - DC type: " + this.dcType );
 
                return this.hDC;
            }
        }
 
        // VSWhidbey 536325
        // Due to a problem with calling DeleteObject() on currently selected GDI objects,
        // we now track the initial set of objects when a DeviceContext is created.  Then,
        // we also track which objects are currently selected in the DeviceContext.  When 
        // a currently selected object is disposed, it is first replaced in the DC and then
        // deleted.
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        private void CacheInitialState()
        {
            Debug.Assert(this.hDC != IntPtr.Zero, "Cannot get initial state without a valid HDC");
            hCurrentPen   = hInitialPen   = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_PEN);
            hCurrentBrush = hInitialBrush = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_BRUSH);
            hCurrentBmp   = hInitialBmp   = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_BITMAP);
            hCurrentFont  = hInitialFont  = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_FONT);
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public void DeleteObject(IntPtr handle, GdiObjectType type) {
            IntPtr handleToDelete = IntPtr.Zero;
            switch (type) {
                case GdiObjectType.Pen:
                    if (handle == hCurrentPen) {
                        IntPtr currentPen = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialPen));
                        Debug.Assert(currentPen == hCurrentPen, "DeviceContext thinks a different pen is selected than the HDC");
                        hCurrentPen = IntPtr.Zero;
                    }
                    handleToDelete = handle;
                    break;
                case GdiObjectType.Brush:
                    if (handle == hCurrentBrush) {
                        IntPtr currentBrush = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialBrush));
                        Debug.Assert(currentBrush == hCurrentBrush, "DeviceContext thinks a different brush is selected than the HDC");
                        hCurrentBrush = IntPtr.Zero;
                    }
                    handleToDelete = handle;
                    break;
                case GdiObjectType.Bitmap:
                    if (handle == hCurrentBmp) {
                        IntPtr currentBmp = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialBmp));
                        Debug.Assert(currentBmp == hCurrentBmp, "DeviceContext thinks a different brush is selected than the HDC");
                        hCurrentBmp = IntPtr.Zero;
                    }
                    handleToDelete = handle;
                    break;
            }
 
            IntUnsafeNativeMethods.DeleteObject(new HandleRef(this, handleToDelete));
        }
 
        //
        // object construction API.  Publicly constructable from static methods only.
        //
 
        /// <devdoc>
        ///     Constructor to contruct a DeviceContext object from an window handle.
        /// </devdoc>
        private DeviceContext(IntPtr hWnd)
        { 
            this.hWnd   = hWnd;
            this.dcType = DeviceContextType.Display;
 
            DeviceContexts.AddDeviceContext(this);
 
            // the hDc will be created on demand.
 
#if TRACK_HDC
            Debug.WriteLine( DbgUtil.StackTraceToStr(String.Format( "DeviceContext( hWnd=0x{0:x8} )", unchecked((int) hWnd))));
#endif
        }
 
        /// <devdoc>
        ///     Constructor to contruct a DeviceContext object from an existing Win32 device context handle.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        private DeviceContext(IntPtr hDC, DeviceContextType dcType)
        { 
            this.hDC    = hDC;
            this.dcType = dcType;
 
            CacheInitialState();
            DeviceContexts.AddDeviceContext(this);
 
            if( dcType == DeviceContextType.Display )
            {
                this.hWnd = IntUnsafeNativeMethods.WindowFromDC( new HandleRef( this, this.hDC) );
            }
#if TRACK_HDC
            Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DeviceContext( hDC=0x{0:X8}, Type={1} )", unchecked((int) hDC), dcType) ));
#endif
        }
        
        // 
 
 
        // 
 
 
        /// <devdoc>
        ///     CreateDC creates a DeviceContext object wrapping an hdc created with the Win32 CreateDC function.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public static DeviceContext CreateDC(string driverName, string deviceName, string fileName, HandleRef devMode)
        {
            // Note: All input params can be null but not at the same time.  See MSDN for information.
 
            IntPtr hdc = IntUnsafeNativeMethods.CreateDC(driverName, deviceName, fileName, devMode);
            return new DeviceContext( hdc, DeviceContextType.NamedDevice );
        }
 
        /// <devdoc>
        ///     CreateIC creates a DeviceContext object wrapping an hdc created with the Win32 CreateIC function.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public static DeviceContext CreateIC(string driverName, string deviceName, string fileName, HandleRef devMode)
        {
            // Note: All input params can be null but not at the same time.  See MSDN for information.
            
            IntPtr hdc = IntUnsafeNativeMethods.CreateIC(driverName, deviceName, fileName, devMode);
            return new DeviceContext( hdc, DeviceContextType.Information );
        }
 
        /// <devdoc>
        ///     Creates a DeviceContext object wrapping a memory DC compatible with the specified device.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public static DeviceContext FromCompatibleDC(IntPtr hdc)
        { 
   
            
            // If hdc is null, the function creates a memory DC compatible with the application's current screen.
            // Win2K+: (See CreateCompatibleDC in the MSDN).
            // In this case the thread that calls CreateCompatibleDC owns the HDC that is created. When this thread is destroyed, 
            // the HDC is no longer valid.
 
            IntPtr compatibleDc = IntUnsafeNativeMethods.CreateCompatibleDC( new HandleRef(null, hdc) );
            return new DeviceContext(compatibleDc, DeviceContextType.Memory);
        }
        
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.FromHdc"]/*' />
        /// <devdoc>
        ///     Used for wrapping an existing hdc.  In this case, this object doesn't own the hdc
        ///     so calls to GetHdc/ReleaseHdc don't PInvoke into GDI.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public static DeviceContext FromHdc(IntPtr hdc)
        { 
            Debug.Assert( hdc != IntPtr.Zero, "hdc == 0" );
            return new DeviceContext(hdc, DeviceContextType.Unknown);
        }
 
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.FromHwnd"]/*' />
        /// <devdoc>
        ///     When hwnd is null, we are getting the screen DC.
        /// </devdoc>
        public static DeviceContext FromHwnd( IntPtr hwnd )
        { 
            return new DeviceContext(hwnd);
        }
 
        
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.Finalize"]/*' />
        ~DeviceContext()
        {
            Dispose(false);
        }
 
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.Dispose"]/*' />
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.Dispose1"]/*' />
        internal void Dispose(bool disposing)
        {
            if (disposed)
            {
                return;
            }
 
            if (this.Disposing != null)
            {
                this.Disposing(this, EventArgs.Empty);
            }
 
            this.disposed = true;
            
#if !DRAWING_NAMESPACE
            DisposeFont(disposing);
#endif            
           
            switch( this.dcType )
            {
                case DeviceContextType.Display:
                    Debug.Assert( disposing, "WARNING: Finalizing a Display DeviceContext.\r\nReleaseDC may fail when not called from the same thread GetDC was called from." );
 
                    ((IDeviceContext)this).ReleaseHdc();
                    break;
 
                case DeviceContextType.Information:
                case DeviceContextType.NamedDevice:
 
                    // CreateDC and CreateIC add an HDC handle to the HandleCollector; to remove it properly we need 
                    // to call DeleteHDC.
#if TRACK_HDC
                    Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DC.DeleteHDC(hdc=0x{0:x8})", unchecked((int) this.hDC))));
#endif
 
                    IntUnsafeNativeMethods.DeleteHDC(new HandleRef(this, this.hDC));
 
                    this.hDC = IntPtr.Zero;
                    break;
 
                case DeviceContextType.Memory:
 
                    // CreatCompatibleDC adds a GDI handle to HandleCollector, to remove it properly we need to call 
                    // DeleteDC.
#if TRACK_HDC
                    Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DC.DeleteDC(hdc=0x{0:x8})", unchecked((int) this.hDC))));
#endif
                    IntUnsafeNativeMethods.DeleteDC(new HandleRef(this, this.hDC));
 
                    this.hDC = IntPtr.Zero;
                    break;
 
                // case DeviceContextType.Metafile: - not yet supported.
#if Microsoft_PUBLIC_GRAPHICS_LIBRARY
                case DeviceContextType.Metafile:
                    IntUnsafeNativeMethods.CloseEnhMetaFile(new HandleRef(this, this.Hdc));
                    
                    this.hDC = IntPtr.Zero;
                    break;
#endif
                case DeviceContextType.Unknown:
                default:
                    return;
                    // do nothing, the hdc is not owned by this object.
                    // in this case it is ok if disposed throught finalization.
            }
 
            DbgUtil.AssertFinalization(this, disposing);
        }
 
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.GetHdc"]/*' />
        /// <devdoc>
        ///     Explicit interface method implementation to hide them a bit for usability reasons so the object is seen 
        ///     as a wrapper around an hdc that is always available, and for performance reasons since it caches the hdc 
        ///     if used in this way.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        IntPtr IDeviceContext.GetHdc()
        {
            if (this.hDC == IntPtr.Zero)
            {
                Debug.Assert( this.dcType == DeviceContextType.Display, "Calling GetDC from a non display/window device." );
 
                // Note: for common DCs, GetDC assigns default attributes to the DC each time it is retrieved. 
                // For example, the default font is System.
                this.hDC = IntUnsafeNativeMethods.GetDC(new HandleRef(this, this.hWnd));
#if TRACK_HDC
                Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("hdc[0x{0:x8}]=DC.GetHdc(hWnd=0x{1:x8})", unchecked((int) this.hDC), unchecked((int) this.hWnd))));
#endif            
            }
 
            return this.hDC;
        }
 
 
        /// <include file='doc\IDeviceContext.uex' path='docs/doc[@for="DeviceContext.ReleaseHdc"]/*' />
        ///<devdoc>
        ///     If the object was created from a DC, this object doesn't 'own' the dc so we just ignore 
        ///     this call.
        ///</devdoc>
        void IDeviceContext.ReleaseHdc()
        {
            if (this.hDC != IntPtr.Zero && this.dcType == DeviceContextType.Display)
            {
#if TRACK_HDC
                int retVal = 
#endif
                IntUnsafeNativeMethods.ReleaseDC(new HandleRef(this, this.hWnd), new HandleRef(this, this.hDC));
                // Note: retVal == 0 means it was not released but doesn't necessarily means an error; class or private DCs are never released.
#if TRACK_HDC
                Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("[ret={0}]=DC.ReleaseDC(hDc=0x{1:x8}, hWnd=0x{2:x8})", retVal, unchecked((int) this.hDC), unchecked((int) this.hWnd))));
#endif                 
                this.hDC = IntPtr.Zero;
            }
        }
 
 
        /// <devdoc>
        ///     Specifies whether the DC is in GM_ADVANCE mode (supported only in NT platforms).  
        ///     If false, it is in GM_COMPATIBLE mode.
        /// </devdoc>
        public DeviceContextGraphicsMode GraphicsMode
        {
            [ResourceExposure(ResourceScope.None)]
            [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
            get
            {
                return (DeviceContextGraphicsMode) IntUnsafeNativeMethods.GetGraphicsMode( new HandleRef( this, this.Hdc ) );
            }
#if Microsoft_PUBLIC_GRAPHICS_LIBRARY
            set
            {
                SetGraphicsMode(value);
            }
#endif
        }
 
        /// <devdoc>
        ///     Sets the dc graphics mode and returns the old value.
        /// </devdoc>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public DeviceContextGraphicsMode SetGraphicsMode(DeviceContextGraphicsMode newMode)
        {
            return (DeviceContextGraphicsMode) IntUnsafeNativeMethods.SetGraphicsMode( new HandleRef( this, this.Hdc ), unchecked((int) newMode));
        }
 
        /// <devdoc>
        ///     Restores the device context to the specified state. The DC is restored by popping state information off a 
        ///     stack created by earlier calls to the SaveHdc function. 
        ///     The stack can contain the state information for several instances of the DC. If the state specified by the 
        ///     specified parameter is not at the top of the stack, RestoreDC deletes all state information between the top 
        ///     of the stack and the specified instance. 
        ///     Specifies the saved state to be restored. If this parameter is positive, nSavedDC represents a specific 
        ///     instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative 
        ///     to the current state. For example, -1 restores the most recently saved state. 
        ///     See MSDN for more info.
        /// </devdoc>
        public void RestoreHdc() 
        {
#if TRACK_HDC
            bool result = 
#endif
            // Note: Don't use the Hdc property here, it would force handle creation.
            IntUnsafeNativeMethods.RestoreDC(new HandleRef(this, this.hDC), -1);
#if TRACK_HDC
            // Note: Microsoft may call this method during app exit at which point the DC may have been finalized already causing this assert to popup.
            Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("ret[0]=DC.RestoreHdc(hDc=0x{1:x8})", result, unchecked((int) this.hDC)) ));
#endif 
            Debug.Assert(contextStack != null, "Someone is calling RestoreHdc() before SaveHdc()");
 
            if (contextStack != null) {
                GraphicsState g = (GraphicsState) contextStack.Pop();
                
                hCurrentBmp     = g.hBitmap;
                hCurrentBrush   = g.hBrush;
                hCurrentPen     = g.hPen;
                hCurrentFont    = g.hFont;
 
#if !DRAWING_NAMESPACE
                if (g.font != null && g.font.IsAlive) {
                    selectedFont    = g.font.Target as WindowsFont;
                }
                else {
                    WindowsFont previousFont = selectedFont;
                    selectedFont = null;
                    if (previousFont != null && MeasurementDCInfo.IsMeasurementDC(this)) {
                        previousFont.Dispose();
                    }
                }
#endif
                
            }
 
#if OPTIMIZED_MEASUREMENTDC
            // in this case, GDI will copy back the previously saved font into the DC.
            // we dont actually know what the font is in our measurement DC so 
            // we need to clear it off.
            MeasurementDCInfo.ResetIfIsMeasurementDC(this.hDC);
#endif        
 
        }
 
        /// <devdoc>
        ///     Saves the current state of the device context by copying data describing selected objects and graphic 
        ///     modes (such as the bitmap, brush, palette, font, pen, region, drawing mode, and mapping mode) to a 
        ///     context stack. 
        ///     The SaveDC function can be used any number of times to save any number of instances of the DC state. 
        ///     A saved state can be restored by using the RestoreHdc method.
        ///     See MSDN for more details. 
        /// </devdoc>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public int SaveHdc() 
        {
            HandleRef hdc = new HandleRef( this, this.Hdc);
            int state = IntUnsafeNativeMethods.SaveDC(hdc);
 
            if (contextStack == null) {
                contextStack = new Stack();
            }
 
            GraphicsState g = new GraphicsState();
            g.hBitmap = hCurrentBmp;
            g.hBrush  = hCurrentBrush;
            g.hPen    = hCurrentPen;            
            g.hFont   = hCurrentFont;
 
#if !DRAWING_NAMESPACE
            g.font    = new WeakReference(selectedFont);
#endif
 
 
            contextStack.Push(g);
                                   
#if TRACK_HDC
            Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("state[0]=DC.SaveHdc(hDc=0x{1:x8})", state, unchecked((int) this.hDC)) ));
#endif 
 
            return state;
        }
 
        /// <devdoc>
        ///     Selects a region as the current clipping region for the device context.
        ///     Remarks (From MSDN):
        ///         - Only a copy of the selected region is used. The region itself can be selected for any number of other device contexts or it can be deleted. 
        ///         - The SelectClipRgn function assumes that the coordinates for a region are specified in device units. 
        ///         - To remove a device-context's clipping region, specify a NULL region handle. 
        /// </devdoc>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public void SetClip(WindowsRegion region)
        {
            HandleRef hdc = new HandleRef(this, this.Hdc);
            HandleRef hRegion = new HandleRef(region, region.HRegion);
 
            IntUnsafeNativeMethods.SelectClipRgn(hdc, hRegion);
        }    
 
        ///<devdoc>
        ///     Creates a new clipping region from the intersection of the current clipping region and 
        ///     the specified rectangle. 
        ///</devdoc>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public void IntersectClip(WindowsRegion wr)
        {
            //if the incoming windowsregion is infinite, there is no need to do any intersecting.
            if (wr.HRegion == IntPtr.Zero) {
                return;
            }
 
            WindowsRegion clip = new WindowsRegion(0,0,0,0);
            try {
                 int result = IntUnsafeNativeMethods.GetClipRgn(new HandleRef( this, this.Hdc), new HandleRef(clip, clip.HRegion));
 
                 // If the function succeeds and there is a clipping region for the given device context, the return value is 1. 
                 if (result == 1) {
                     Debug.Assert(clip.HRegion != IntPtr.Zero);
                     wr.CombineRegion(clip, wr, RegionCombineMode.AND); //1 = AND (or Intersect)
                 }
 
                 SetClip(wr);
            }
            finally {
                 clip.Dispose();
            }
        }
 
        /// <devdoc>
        ///     Modifies the viewport origin for a device context using the specified horizontal and vertical offsets in logical units.
        /// </devdoc>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public void TranslateTransform(int dx, int dy) 
        {
            IntNativeMethods.POINT orgn = new IntNativeMethods.POINT();
            IntUnsafeNativeMethods.OffsetViewportOrgEx( new HandleRef( this, this.Hdc ), dx, dy, orgn );
        }
 
        /// <summary>
        /// </summary>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public override bool Equals(object obj)
        {
            DeviceContext other = obj as DeviceContext;
 
            if (other == this)
            {
                return true;
            }
 
            if (other == null)
            {
                return false;
            }
 
            // Note: Use property instead of field so the HDC is initialized.  Also, this avoid serialization issues (the obj could be a proxy that does not have access to private fields).
            return other.Hdc == this.Hdc;
        }
 
        /// <summary>
        ///     This allows collections to treat DeviceContext objects wrapping the same HDC as the same objects.
        /// </summary>
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
        public override int GetHashCode()
        {
            return this.Hdc.GetHashCode();
        }
 
 
        internal class GraphicsState {
            internal IntPtr hBrush;
            internal IntPtr hFont;
            internal IntPtr hPen;
            internal IntPtr hBitmap;
#if !DRAWING_NAMESPACE
            internal WeakReference font;
#endif
        }
     }
}