Beispiel #1
0
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            string strValue = value as string;

            if (strValue != null)
            {
                object obj  = null;
                string text = strValue.Trim();

                if (text.Length == 0)
                {
                    obj = Color.Empty;
                }
                else
                {
                    // First, check to see if this is a standard name.
                    //
                    obj = GetNamedColor(text);

                    if (obj == null)
                    {
                        if (culture == null)
                        {
                            culture = CultureInfo.CurrentCulture;
                        }

                        char sep = culture.TextInfo.ListSeparator[0];
                        bool tryMappingToKnownColor = true;

                        TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));

                        // If the value is a 6 digit hex number only, then
                        // we want to treat the Alpha as 255, not 0
                        //
                        if (text.IndexOf(sep) == -1)
                        {
                            // text can be '' (empty quoted string)
                            if (text.Length >= 2 && (text[0] == '\'' || text[0] == '"') && text[0] == text[text.Length - 1])
                            {
                                // In quotes means a named value
                                string colorName = text.Substring(1, text.Length - 2);
                                obj = Color.FromName(colorName);
                                tryMappingToKnownColor = false;
                            }
                            else if ((text.Length == 7 && text[0] == '#') ||
                                     (text.Length == 8 && (text.StartsWith("0x") || text.StartsWith("0X"))) ||
                                     (text.Length == 8 && (text.StartsWith("&h") || text.StartsWith("&H"))))
                            {
                                // Note: ConvertFromString will raise exception if value cannot be converted.
                                obj = Color.FromArgb(unchecked ((int)(0xFF000000 | (uint)(int)intConverter.ConvertFromString(context, culture, text))));
                            }
                        }

                        // Nope.  Parse the RGBA from the text.
                        //
                        if (obj == null)
                        {
                            string[] tokens = text.Split(new char[] { sep });
                            int[]    values = new int[tokens.Length];
                            for (int i = 0; i < values.Length; i++)
                            {
                                values[i] = unchecked ((int)intConverter.ConvertFromString(context, culture, tokens[i]));
                            }

                            // We should now have a number of parsed integer values.
                            // We support 1, 3, or 4 arguments:
                            //
                            // 1 -- full ARGB encoded
                            // 3 -- RGB
                            // 4 -- ARGB
                            //
                            switch (values.Length)
                            {
                            case 1:
                                obj = Color.FromArgb(values[0]);
                                break;

                            case 3:
                                obj = Color.FromArgb(values[0], values[1], values[2]);
                                break;

                            case 4:
                                obj = Color.FromArgb(values[0], values[1], values[2], values[3]);
                                break;
                            }
                            tryMappingToKnownColor = true;
                        }

                        if ((obj != null) && tryMappingToKnownColor)
                        {
                            // Now check to see if this color matches one of our known colors.
                            // If it does, then substitute it.  We can only do this for "Colors"
                            // because system colors morph with user settings.
                            //
                            int targetARGB = ((Color)obj).ToArgb();

                            foreach (Color c in Colors.Values)
                            {
                                if (c.ToArgb() == targetARGB)
                                {
                                    obj = c;
                                    break;
                                }
                            }
                        }
                    }

                    if (obj == null)
                    {
                        throw new ArgumentException(SR.Format(SR.InvalidColor, text));
                    }
                }
                return(obj);
            }
            return(base.ConvertFrom(context, culture, value));
        }
        // Initializes this Image object.  This is identical to calling the image's
        // constructor with picture, but this allows non-constructor initialization,
        // which may be necessary in some instances.
        private unsafe void Initialize(int width, int height)
        {
            if (_iconData == null || _handle != IntPtr.Zero)
            {
                throw new InvalidOperationException(SR.Format(SR.IllegalState, GetType().Name));
            }

            if (_iconData.Length < sizeof(SafeNativeMethods.ICONDIR))
            {
                throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon)));
            }

            // Get the correct width and height.
            if (width == 0)
            {
                width = UnsafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CXICON);
            }

            if (height == 0)
            {
                height = UnsafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CYICON);
            }

            if (s_bitDepth == 0)
            {
                IntPtr dc = Interop.User32.GetDC(IntPtr.Zero);
                s_bitDepth  = Interop.Gdi32.GetDeviceCaps(dc, Interop.Gdi32.DeviceCapability.BITSPIXEL);
                s_bitDepth *= Interop.Gdi32.GetDeviceCaps(dc, Interop.Gdi32.DeviceCapability.PLANES);
                Interop.User32.ReleaseDC(IntPtr.Zero, dc);

                // If the bitdepth is 8, make it 4 because windows does not
                // choose a 256 color icon if the display is running in 256 color mode
                // due to palette flicker.
                if (s_bitDepth == 8)
                {
                    s_bitDepth = 4;
                }
            }

            fixed(byte *b = _iconData)
            {
                SafeNativeMethods.ICONDIR *dir = (SafeNativeMethods.ICONDIR *)b;

                if (dir->idReserved != 0 || dir->idType != 1 || dir->idCount == 0)
                {
                    throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon)));
                }

                byte bestWidth  = 0;
                byte bestHeight = 0;

                if (sizeof(SafeNativeMethods.ICONDIRENTRY) * (dir->idCount - 1) + sizeof(SafeNativeMethods.ICONDIR)
                    > _iconData.Length)
                {
                    throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon)));
                }

                var entries = new ReadOnlySpan <SafeNativeMethods.ICONDIRENTRY>(&dir->idEntries, dir->idCount);

                foreach (SafeNativeMethods.ICONDIRENTRY entry in entries)
                {
                    bool fUpdateBestFit = false;
                    uint iconBitDepth;
                    if (entry.bColorCount != 0)
                    {
                        iconBitDepth = 4;
                        if (entry.bColorCount < 0x10)
                        {
                            iconBitDepth = 1;
                        }
                    }
                    else
                    {
                        iconBitDepth = entry.wBitCount;
                    }

                    // If it looks like if nothing is specified at this point then set the bits per pixel to 8.
                    if (iconBitDepth == 0)
                    {
                        iconBitDepth = 8;
                    }

                    //  Windows rules for specifing an icon:
                    //
                    //  1.  The icon with the closest size match.
                    //  2.  For matching sizes, the image with the closest bit depth.
                    //  3.  If there is no color depth match, the icon with the closest color depth that does not exceed the display.
                    //  4.  If all icon color depth > display, lowest color depth is chosen.
                    //  5.  color depth of > 8bpp are all equal.
                    //  6.  Never choose an 8bpp icon on an 8bpp system.

                    if (_bestBytesInRes == 0)
                    {
                        fUpdateBestFit = true;
                    }
                    else
                    {
                        int bestDelta = Math.Abs(bestWidth - width) + Math.Abs(bestHeight - height);
                        int thisDelta = Math.Abs(entry.bWidth - width) + Math.Abs(entry.bHeight - height);

                        if ((thisDelta < bestDelta) ||
                            (thisDelta == bestDelta && (iconBitDepth <= s_bitDepth && iconBitDepth > _bestBitDepth || _bestBitDepth > s_bitDepth && iconBitDepth < _bestBitDepth)))
                        {
                            fUpdateBestFit = true;
                        }
                    }

                    if (fUpdateBestFit)
                    {
                        bestWidth        = entry.bWidth;
                        bestHeight       = entry.bHeight;
                        _bestImageOffset = entry.dwImageOffset;
                        _bestBytesInRes  = entry.dwBytesInRes;
                        _bestBitDepth    = iconBitDepth;
                    }
                }

                if (_bestImageOffset > int.MaxValue)
                {
                    throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon)));
                }

                if (_bestBytesInRes > int.MaxValue)
                {
                    throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER);
                }

                uint endOffset;

                try
                {
                    endOffset = checked (_bestImageOffset + _bestBytesInRes);
                }
                catch (OverflowException)
                {
                    throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER);
                }

                if (endOffset > _iconData.Length)
                {
                    throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon)));
                }

                // Copy the bytes into an aligned buffer if needed.
                if ((_bestImageOffset % IntPtr.Size) != 0)
                {
                    // Beginning of icon's content is misaligned.
                    byte[] alignedBuffer = ArrayPool <byte> .Shared.Rent((int)_bestBytesInRes);

                    Array.Copy(_iconData, _bestImageOffset, alignedBuffer, 0, _bestBytesInRes);

                    fixed(byte *pbAlignedBuffer = alignedBuffer)
                    {
                        _handle = SafeNativeMethods.CreateIconFromResourceEx(pbAlignedBuffer, _bestBytesInRes, true, 0x00030000, 0, 0, 0);
                    }

                    ArrayPool <byte> .Shared.Return(alignedBuffer);
                }
                else
                {
                    try
                    {
                        _handle = SafeNativeMethods.CreateIconFromResourceEx(checked (b + _bestImageOffset), _bestBytesInRes, true, 0x00030000, 0, 0, 0);
                    }
                    catch (OverflowException)
                    {
                        throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER);
                    }
                }

                if (_handle == IntPtr.Zero)
                {
                    throw new Win32Exception();
                }
            }
        }