File: winforms\Managed\System\WinForms\GDI\WindowsGraphicsWrapper.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="TextRenderer.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
namespace System.Windows.Forms 
{
    using System.Internal;
    using System;
    using System.Drawing;
    using System.Windows.Forms.Internal;
    using System.Diagnostics;
    using System.Runtime.Versioning;
 
    /// <devdoc>
    ///     This class wrapps a WindowsGraphics and is provided to be able to manipulate WindowsGraphics objects 
    ///     created from a Graphics object in the same way as one created from any other IDeviceContext object,
    ///     which could be a custom one.
    ///     This class was designed to help TextRenderer determine how to create the underlying WindowsGraphics.
    /// </devdoc>
    internal sealed class WindowsGraphicsWrapper : IDisposable
    {
        IDeviceContext idc;
        WindowsGraphics wg;
 
        /// <devdoc>
        ///     Constructor that determines how to create the WindowsGraphics, there are three posible cases
        ///     for the IDeviceContext object type:
        ///     1. It is a Graphics object: In this case we need to check the TextFormatFlags to determine whether
        ///        we need to re-apply some of the Graphics properties to the WindowsGraphics, if so we call
        ///        WindowsGraphics.FromGraphics passing the corresponding flags. If not, we treat it as a custom
        ///        IDeviceContext (see below).
        ///     2. It is a WindowsGraphics object: 
        ///        In this case we just need to use the wg directly and be careful not to dispose of it since
        ///        it is owned by the caller.
        ///     3. It is a custom IDeviceContext object:
        ///        In this case we create the WindowsGraphics from the native DC by calling IDeviceContext.GetHdc,
        ///        on dispose we need to call IDeviceContext.ReleaseHdc.
        /// </devdoc>
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public WindowsGraphicsWrapper( IDeviceContext idc, TextFormatFlags flags)
        {
            if( idc is Graphics )
            {
                ApplyGraphicsProperties properties = ApplyGraphicsProperties.None;
 
                if( (flags & TextFormatFlags.PreserveGraphicsClipping) != 0)
                {
                    properties |= ApplyGraphicsProperties.Clipping;
                }
 
                if( (flags & TextFormatFlags.PreserveGraphicsTranslateTransform) != 0)
                {
                    properties |= ApplyGraphicsProperties.TranslateTransform;
                }
 
                // Create the WindowsGraphics from the Grahpics object only if Graphics properties need
                // to be reapplied to the DC wrapped by the WindowsGraphics.
                if( properties != ApplyGraphicsProperties.None )
                {
                    this.wg = WindowsGraphics.FromGraphics( idc as Graphics, properties);
                }
            }
            else
            {
                // If passed-in IDeviceContext object is a WindowsGraphics we can use it directly.
                this.wg = idc as WindowsGraphics;
 
                if( this.wg != null )
                {
                    // In this case we cache the idc to compare it against the wg in the Dispose method to avoid
                    // disposing of the wg.
                    this.idc = idc;
                }
            }
 
            if( this.wg == null )
            {
                // The IDeviceContext object is not a WindowsGraphics, or it is a custom IDeviceContext, or
                // it is a Graphics object but we did not need to re-apply Graphics propertiesto the hdc.  
                // So create the WindowsGraphics from the hdc directly. 
                // Cache the IDC so the hdc can be released on dispose.
                this.idc = idc;
                this.wg = WindowsGraphics.FromHdc( idc.GetHdc() );
            }
 
            // Set text padding on the WindowsGraphics (if any).
            if( (flags & TextFormatFlags.LeftAndRightPadding) != 0 )
            {
                wg.TextPadding = TextPaddingOptions.LeftAndRightPadding;
            }
            else if ((flags & TextFormatFlags.NoPadding) != 0 )
            {
                wg.TextPadding = TextPaddingOptions.NoPadding;
            }
            // else wg.TextPadding = TextPaddingOptions.GlyphOverhangPadding - the default value.
        }
 
        public WindowsGraphics WindowsGraphics
        {
            get
            {
                Debug.Assert( this.wg != null, "WindowsGraphics is null." );
                return this.wg;
            }
        }
 
        ~WindowsGraphicsWrapper()
        {
            Dispose(false);
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        public void Dispose( bool disposing )
        {
            Debug.Assert( disposing, "We should always dispose of this guy and not let GC do it for us!" );
        
            if( this.wg != null )
            {
                // We need to dispose of the WindowsGraphics if it is created by this class only, if the IDeviceContext is 
                // a WindowsGraphics object we must not dispose of it since it is owned by the caller.
                if( this.wg != this.idc )
                {
                    // resets the hdc and disposes of the internal Graphics (if inititialized from one) which releases the hdc.
                    this.wg.Dispose();
 
                    if( this.idc != null ) // not initialized from a Graphics idc.   
                    {
                        this.idc.ReleaseHdc();
                    }
                }
 
                this.idc = null;
                this.wg = null;
            }
        }
    }
}