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(); } } }