IconHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / AppModel / IconHelper.cs / 1305600 / IconHelper.cs

                            //---------------------------------------------------------------------------- 
//
// File: IconHelper.cs
//
// Description: Static internal class implements utility functions for icon 
//              implementation for the Window class.
// 
// Created: 07/27/05 
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
//---------------------------------------------------------------------------

using System; 
using System.Security;
using System.Security.Permissions; 
using System.Diagnostics; 
using System.Collections.ObjectModel;
using System.Runtime.InteropServices; 
using System.ComponentModel;

using System.Windows;
using System.Windows.Interop; 
using System.Windows.Media.Imaging;
using System.Windows.Media; 
 
using MS.Internal;
using MS.Internal.Interop; 
using MS.Internal.PresentationFramework;                   // SecurityHelper
using MS.Win32;

namespace MS.Internal.AppModel 
{
    internal static class IconHelper 
    { 
        private static Size s_smallIconSize;
        private static Size s_iconSize; 
        private static int s_systemBitDepth;

        /// Lazy init of static fields.  Call this at the beginning of any external entrypoint.
        ///  
        ///     Critical:       Calls GetDC, ReleaseDC and GetDeviceCaps that are marked SecurityCritical
        ///     TreatAsSafe:    These set local static fields that aren't directly exposed outside this class. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private static void EnsureSystemMetrics() 
        {
            if (s_systemBitDepth == 0)
            {
                // The values here *may* change, but it's not worthwhile to requery. 

                // We need to release the DC to correctly track our native handles. 
                var hdcDesktop = new HandleRef(null, UnsafeNativeMethods.GetDC(new HandleRef())); 
                try
                { 
                    int sysBitDepth = UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.BITSPIXEL);
                    sysBitDepth *= UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.PLANES);

                    // If the s_systemBitDepth is 8, make it 4.  Why?  Because windows does not 
                    // choose a 256 color icon if the display is running in 256 color mode
                    // because of palette flicker. 
                    if (sysBitDepth == 8) 
                    {
                        sysBitDepth = 4; 
                    }

                    // We really want to be pixel aware here.  Don't use the SystemParameters class.
                    int cxSmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXSMICON); 
                    int cySmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYSMICON);
                    int cxIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXICON); 
                    int cyIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYICON); 

                    s_smallIconSize = new Size(cxSmallIcon, cySmallIcon); 
                    s_iconSize = new Size(cxIcon, cyIcon);
                    s_systemBitDepth = sysBitDepth;
                }
                finally 
                {
                    UnsafeNativeMethods.ReleaseDC(new HandleRef(), hdcDesktop); 
                } 
            }
        } 

        /// 
        ///     Critical: This code elevates to unmanaged Code permission
        /// TreatAsSafe: There is a demand here 
        /// 
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        public static void GetDefaultIconHandles(out NativeMethods.IconHandle largeIconHandle, out NativeMethods.IconHandle smallIconHandle)
        { 
            largeIconHandle = null;
            smallIconHandle = null;

            SecurityHelper.DemandUIWindowPermission(); 

            // Get the handle of the module that created the running process. 
            string iconModuleFile = UnsafeNativeMethods.GetModuleFileName(new HandleRef()); 

            // We don't really care about the return value.  Handles will be invalid on error. 
            int extractedCount = UnsafeNativeMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
        }

        ///  
        ///     Critical: Since it calls CreateIconHandleFromImageSource
        ///     TAS:      Since it creates icons with known h/w i.e. IconWidth/Height or SmallIconWidth/Height 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        public static void GetIconHandlesFromImageSource(ImageSource image, out NativeMethods.IconHandle largeIconHandle, out NativeMethods.IconHandle smallIconHandle) 
        {
            EnsureSystemMetrics();
            largeIconHandle = CreateIconHandleFromImageSource(image, s_iconSize);
            smallIconHandle = CreateIconHandleFromImageSource(image, s_smallIconSize); 
        }
 
        ///  
        ///     Critical: Since it calls CreateIconHandleFromBitmapFrame
        ///  
        /// A new HICON based on the image source
        [SecurityCritical]
        public static NativeMethods.IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
        { 
            EnsureSystemMetrics();
 
            bool asGoodAsItGets = false; 

            var bf = image as BitmapFrame; 
            if (bf != null)
            {
                Invariant.Assert(bf.Decoder != null, "The BitmapFrame's Decoder should never be null");
                Invariant.Assert(bf.Decoder.Frames != null, "The BitmapFrame's Decoder.Frames should never be null"); 

                bf = GetBestMatch(bf.Decoder.Frames, size); 
 
                // If this was actually a multi-framed icon then we don't want to do any corrections.
                //   Let Windows do its thing.  We don't want to unnecessarily deviate from the system. 
                // If this was a jpeg or png, then we're doing something Windows doesn't do,
                //   and we can be better. (unless it was a perfect match :)
                asGoodAsItGets = bf.Decoder is IconBitmapDecoder // i.e. was this a .ico?
                    || bf.PixelWidth == size.Width && bf.PixelHeight == size.Height; 

                image = bf; 
            } 

            if (!asGoodAsItGets) 
            {
                // Unless this was a .ico, render it into a new BitmapFrame with the appropriate dimensions
                // to preserve the aspect ratio in the HICON and do the appropriate padding.
                bf = BitmapFrame.Create(GenerateBitmapSource(image, size)); 
            }
 
            return CreateIconHandleFromBitmapFrame(bf); 
        }
 
        /// 
        /// Creates a BitmapSource from an arbitrary ImageSource.
        /// 
        private static BitmapSource GenerateBitmapSource(ImageSource img, Size renderSize) 
        {
            // By now we should just assume it's a vector image that we need to rasterize. 
            // We want to keep the aspect ratio, but one of the dimensions will go the full length. 
            var drawingDimensions = new Rect(0, 0, renderSize.Width, renderSize.Height);
 
            // There's no reason to assume that the requested image dimensions are square.
            double renderRatio = renderSize.Width / renderSize.Height;
            double aspectRatio = img.Width / img.Height;
 
            // If it's smaller than the requested size, then place it in the middle and pad the image.
            if (img.Width <= renderSize.Width && img.Height <= renderSize.Height) 
            { 
                drawingDimensions = new Rect((renderSize.Width - img.Width) / 2, (renderSize.Height - img.Height) / 2, img.Width, img.Height);
            } 
            else if (renderRatio > aspectRatio)
            {
                double scaledRenderWidth = (img.Width / img.Height) * renderSize.Width;
                drawingDimensions = new Rect((renderSize.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, renderSize.Height); 
            }
            else if (renderRatio < aspectRatio) 
            { 
                double scaledRenderHeight = (img.Height / img.Width) * renderSize.Height;
                drawingDimensions = new Rect(0, (renderSize.Height - scaledRenderHeight) / 2, renderSize.Width, scaledRenderHeight); 
            }

            var dv = new DrawingVisual();
            DrawingContext dc = dv.RenderOpen(); 
            dc.DrawImage(img, drawingDimensions);
            dc.Close(); 
 
            // Need to use Pbgra32 because that's all that RenderTargetBitmap currently supports.
            // 96 is the right DPI to use here because we're being very pixel aware. 
            var bmp = new RenderTargetBitmap((int)renderSize.Width, (int)renderSize.Height, 96, 96, PixelFormats.Pbgra32);
            bmp.Render(dv);

            return bmp; 
        }
 
        ///  
        ///     Critical: Since it calls CreateIconCursor which is SecurityCritical.  CreateIconCursor is SecurityCritical b/c
        ///               it creates bitmaps with the input w/h etc.  That width/height is passed by this method using 
        ///               sourceBitmapFrame.Pixel[Width/Height] and is not guarded.
        /// 
        /// 
        // 
        //  Creates and HICON from a bitmap frame
        [SecurityCritical] 
        private static NativeMethods.IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame) 
        {
            Invariant.Assert(sourceBitmapFrame != null, "sourceBitmapFrame cannot be null here"); 

            BitmapSource bitmapSource = sourceBitmapFrame;

            if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32) 
            {
                bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0); 
            } 

            // data used by CopyPixels 
            int w = bitmapSource.PixelWidth;
            int h = bitmapSource.PixelHeight;
            int bpp = bitmapSource.Format.BitsPerPixel;
            // ensuring it is in 4 byte increments since we're dealing 
            // with ARGB fromat
            int stride = (bpp * w + 31) / 32 * 4; 
            int sizeCopyPixels = stride * h; 
            byte[] xor = new byte[sizeCopyPixels];
            bitmapSource.CopyPixels(xor, stride, 0); 

            return CreateIconCursor(xor, w, h, 0, 0, true);
        }
 
        // Also used by PenCursorManager
        // Creates a 32 bit per pixel Icon or cursor.  This code is moved from framework\ms\internal\ink\pencursormanager.cs 
        ///  
        ///     Critical: Critical as this code create a DIB section and writes data to it
        ///  
        [SecurityCritical]
        internal static NativeMethods.IconHandle CreateIconCursor(
            byte[] colorArray,
            int width, 
            int height,
            int xHotspot, 
            int yHotspot, 
            bool isIcon)
        { 
            //   1. We are going to generate a WIN32 color bitmap which represents the color cursor.
            //   2. Then we need to create a monochrome bitmap which is used as the cursor mask.
            //   3. At last we create a WIN32 HICON from the above two bitmaps
            NativeMethods.BitmapHandle colorBitmap = null; 
            NativeMethods.BitmapHandle maskBitmap = null;
 
            try 
            {
                // 1) Create the color bitmap using colorArray 
                // Fill in the header information
                NativeMethods.BITMAPINFO bi = new NativeMethods.BITMAPINFO(
                                                    width,      // width
                                                    -height,    // A negative value indicates the bitmap is top-down DIB 
                                                    32          // biBitCount
                                                    ); 
                bi.bmiHeader_biCompression = NativeMethods.BI_RGB; 

                IntPtr bits = IntPtr.Zero; 
                colorBitmap = MS.Win32.UnsafeNativeMethods.CreateDIBSection(
                                        new HandleRef(null, IntPtr.Zero),   // A device context. Pass null in if no DIB_PAL_COLORS is used.
                                        ref bi,                             // A BITMAPINFO structure which specifies the dimensions and colors.
                                        NativeMethods.DIB_RGB_COLORS,       // Specifies the type of data contained in the bmiColors array member of the BITMAPINFO structure 
                                        ref bits,                           // An out Pointer to a variable that receives a pointer to the location of the DIB bit values
                                        IntPtr.Zero,                        // Handle to a file-mapping object that the function will use to create the DIB. This parameter can be null. 
                                        0                                   // dwOffset. This value is ignored if hSection is NULL 
                                        );
 
                if ( colorBitmap.IsInvalid || bits == IntPtr.Zero)
                {
                    // Note we will release the GDI resources in the finally block.
                    return NativeMethods.IconHandle.GetInvalidIcon(); 
                }
 
                // Copy the color bits to the win32 bitmap 
                Marshal.Copy(colorArray, 0, bits, colorArray.Length);
 

                // 2) Now create the mask bitmap which is monochrome
                byte[] maskArray = GenerateMaskArray(width, height, colorArray);
                Invariant.Assert(maskArray != null); 

                maskBitmap = UnsafeNativeMethods.CreateBitmap(width, height, 1, 1, maskArray); 
                if ( maskBitmap.IsInvalid ) 
                {
                    // Note we will release the GDI resources in the finally block. 
                    return NativeMethods.IconHandle.GetInvalidIcon();
                }

                // Now create HICON from two bitmaps. 
                NativeMethods.ICONINFO iconInfo = new NativeMethods.ICONINFO();
                iconInfo.fIcon = isIcon;            // fIcon == ture means creating an Icon, otherwise Cursor 
                iconInfo.xHotspot = xHotspot; 
                iconInfo.yHotspot = yHotspot;
                iconInfo.hbmMask = maskBitmap; 
                iconInfo.hbmColor = colorBitmap;

                return UnsafeNativeMethods.CreateIconIndirect(iconInfo);
            } 
            finally
            { 
                if (colorBitmap != null) 
                {
                    colorBitmap.Dispose(); 
                    colorBitmap = null;
                }

                if (maskBitmap != null) 
                {
                    maskBitmap.Dispose(); 
                    maskBitmap = null; 
                }
            } 
        }

        // generates the mask array for the input colorArray.
        // The mask array is 1 bpp 
        private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
        { 
            int nCount = width * height; 

            // NOTICE-2005/04/26-WAYNEZEN, 
            // Check out the notes in CreateBitmap in MSDN. The scan line has to be aliged to WORD.
            int bytesPerScanLine = AlignToBytes(width, 2) / 8;

            byte[] bitsMask = new byte[bytesPerScanLine * height]; 

            // We are scaning all pixels in color bitmap. 
            // If the alpha value is 0, we should set the corresponding mask bit to 1. So the pixel will show 
            // the screen pixel. Otherwise, we should set the mask bit to 0 which causes the cursor to display
            // the color bitmap pixel. 
            for ( int i = 0; i < nCount; i++ )
            {
                // Get the i-th pixel position (hPos, vPos)
                int hPos = i % width; 
                int vPos = i / width;
 
                // For each byte in 2-bit color bitmap, the lowest the bit represents the right-most display pixel. 
                // For example the bollow mask -
                //    1 1 1 0 0 0 0 1 
                //    ^             ^
                //  offsetBit = 0x80   offsetBit = 0x01
                int byteIndex = hPos / 8;
                byte offsetBit = (byte)( 0x80 >> ( hPos % 8 ) ); 

                // Now we turn the mask on or off accordingly. 
                if ( colorArray[i * 4 + 3] /* Alpha value since it's in Argb32 Format */ == 0x00 ) 
                {
                    // Set the mask bit to 1. 
                    bitsMask[byteIndex + bytesPerScanLine * vPos] |= (byte)offsetBit;
                }
                else
                { 
                    // Reset the mask bit to 0
                    bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte)( ~offsetBit ); 
                } 

                // Since the scan line of the mask bitmap has to be aligned to word. We have set all padding bits to 1. 
                // So the extra pixel can be seen through.
                if ( hPos == width - 1 && width == 8 )
                {
                    bitsMask[1 + bytesPerScanLine * vPos] = 0xff; 
                }
            } 
 
            return bitsMask;
        } 

        // Also used by PenCursorManager
        /// 
        /// Calculate the bits count aligned to N-Byte based on the input count 
        /// 
        /// The original value 
        /// N-Byte 
        /// the nearest bit count which is aligned to N-Byte
        internal static int AlignToBytes(double original, int nBytesCount) 
        {
            Debug.Assert(nBytesCount > 0, "The N-Byte has to be greater than 0!");

            int nBitsCount = 8 << (nBytesCount - 1); 
            return (((int)Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount) * nBitsCount;
        } 
 
        ///
        /// We're copying the algorithm Windows uses to pick icons. 
        /// The comments and implementation are based on core\ntuser\client\clres.c
        ///
        /// MatchImage
        /// 
        /// This function takes LPINTs for width & height in case of "real size".
        /// For this option, we use dimensions of 1st icon in resdir as size to 
        /// load, instead of system metrics. 
        /// Returns a number that measures how "far away" the given image is
        /// from a desired one.  The value is 0 for an exact match.  Note that our 
        /// formula has the following properties:
        ///     (1) Differences in width/height count much more than differences in
        ///             color format.
        ///     (2) Bigger images are better than smaller, since shrinking produces 
        ///             better results than stretching.
        ///     (3) Color matching is done by the difference in bit depth.  No 
        ///             preference is given to having a candidate equally different 
        ///             above and below the target.
        /// 
        /// The formula is the sum of the following terms:
        ///     abs(bppCandidate - bppTarget)
        ///     abs(cxCandidate - cxTarget), times 2 if the image is
        ///        narrower than what we'd like.  This is because we will get a 
        ///        better result when consolidating more information into a smaller
        ///        space, than when extrapolating from less information to more. 
        ///     abs(cxCandidate - cxTarget), times 2 if the image is 
        ///        shorter than what we'd like.  This is for the same reason as
        ///        the width. 
        ///
        /// Let's step through an example.  Suppose we want a 4bpp (16 color),
        /// 32x32 image.  We would choose the various candidates in the following order:
        /// 
        /// Candidate     Score   Formula
        /// 
        /// 32x32x4bpp  = 0       abs(32-32)*1 + abs(32-32)*1 + 2*abs(4-4)*1 
        /// 32x32x2bpp  = 4
        /// 32x32x8bpp  = 8 
        /// 32x32x16bpp = 24
        /// 48x48x4bpp  = 32
        /// 48x48x2bpp  = 36
        /// 48x48x8bpp  = 40 
        /// 32x32x32bpp = 56
        /// 48x48x16bpp = 56      abs(48-32)*1 + abs(48-32)*1 + 2*abs(16-4)*1 
        /// 16x16x4bpp  = 64 
        /// 16x16x2bpp  = 68      abs(16-32)*2 + abs(16-32)*2 + 2*abs(2-4)*1
        /// 16x16x8bpp  = 72 
        /// 48x48x32bpp = 88      abs(48-32)*1 + abs(48-32)*1 + 2*abs(32-4)*1
        /// 16x16x16bpp = 88
        /// 16x16x32bpp = 104
        private static int MatchImage(BitmapFrame frame, Size size, int bpp) 
        {
            /* 
             * Here are the rules for our "match" formula: 
             *      (1) A close size match is much preferable to a color match
             *      (2) Bigger icons are better than smaller 
             *      (3) The smaller the difference in bit depths the better
             */
            int score = 2 * MyAbs(bpp, s_systemBitDepth, false) +
                    MyAbs(frame.PixelWidth, (int)size.Width, true) + 
                    MyAbs(frame.PixelHeight, (int)size.Height, true);
 
            return score; 
        }
 
        ///
        /// MyAbs (also from core\ntuser\client\clres.c)
        ///
        /// Calcules my weighted absolute value of the difference between 2 nums. 
        /// This of course normalizes values to >= zero.  But it can also "punish" the
        /// returned value by a factor of two if valueHave < valueWant.  This is 
        /// because you get worse results trying to extrapolate from less info up then 
        /// interpolating from more info down.
        /// 
        private static int MyAbs(int valueHave, int valueWant, bool fPunish)
        {
            int diff = (valueHave - valueWant);
 
            if (diff < 0)
            { 
                diff = (fPunish ? -2 : -1) * diff; 
            }
 
            return diff;
        }

        /// From a list of BitmapFrames find the one that best matches the requested dimensions. 
        /// The methods used here are copied from Win32 sources.  We want to be consistent with
        /// system behaviors. 
        private static BitmapFrame GetBestMatch(ReadOnlyCollection frames, Size size) 
        {
            Invariant.Assert(size.Width != 0, "input param width should not be zero"); 
            Invariant.Assert(size.Height != 0, "input param height should not be zero");

            int bestScore = int.MaxValue;
            int bestBpp = 0; 
            int bestIndex = 0;
 
            bool isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder; 

            for (int i = 0; i < frames.Count && bestScore != 0; ++i) 
            {
                // determine the bit-depth (# of colors) in the
                // current frame
                // 
                // if the icon is palettized, Format.BitsPerPixel gives
                // the # of bits required to index into the palette (thus, 
                // the # of colors in the palette).  If it is a true 
                // color icon, it gives the # of bits required to support
                // true colors. 
                // For icons, get the Format from the Thumbnail rather than from the
                // BitmapFrame directly because the unmanaged icon decoder
                // converts every icon to 32-bit. Thumbnail.Format.BitsPerPixel
                // will give us the original bit depth. 
                int currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
 
                // If it looks like nothing is specified at this point, assume a bpp of 8. 
                if (currentIconBitDepth == 0)
                { 
                    currentIconBitDepth = 8;
                }

                int score = MatchImage(frames[i], size, currentIconBitDepth); 
                if (score < bestScore)
                { 
                    bestIndex = i; 
                    bestBpp = currentIconBitDepth;
                    bestScore = score; 
                }
                else if (score == bestScore)
                {
                    // Tie breaker: choose the higher color depth.  If that fails, choose first one. 
                    if (bestBpp < currentIconBitDepth)
                    { 
                        bestIndex = i; 
                        bestBpp = currentIconBitDepth;
                    } 
                }
            }

            return frames[bestIndex]; 
        }
    } 
} 


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

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