// 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(); } } }
private unsafe void Initialize(int width, int height) { if ((this.iconData == null) || (this.handle != IntPtr.Zero)) { throw new InvalidOperationException(System.Drawing.SR.GetString("IllegalState", new object[] { base.GetType().Name })); } if (this.iconData.Length < Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIR))) { throw new ArgumentException(System.Drawing.SR.GetString("InvalidPictureType", new object[] { "picture", "Icon" })); } if (width == 0) { width = System.Drawing.UnsafeNativeMethods.GetSystemMetrics(11); } if (height == 0) { height = System.Drawing.UnsafeNativeMethods.GetSystemMetrics(12); } if (bitDepth == 0) { IntPtr dC = System.Drawing.UnsafeNativeMethods.GetDC(System.Drawing.NativeMethods.NullHandleRef); bitDepth = System.Drawing.UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dC), 12); bitDepth *= System.Drawing.UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dC), 14); System.Drawing.UnsafeNativeMethods.ReleaseDC(System.Drawing.NativeMethods.NullHandleRef, new HandleRef(null, dC)); if (bitDepth == 8) { bitDepth = 4; } } fixed(byte *numRef = this.iconData) { short @short = this.GetShort(numRef); short num2 = this.GetShort(numRef + 2); short num3 = this.GetShort(numRef + 4); if (((@short != 0) || (num2 != 1)) || (num3 == 0)) { throw new ArgumentException(System.Drawing.SR.GetString("InvalidPictureType", new object[] { "picture", "Icon" })); } byte bWidth = 0; byte bHeight = 0; int length = 0; byte *numPtr = numRef + 6; int num7 = Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIRENTRY)); if ((num7 * num3) >= this.iconData.Length) { throw new ArgumentException(System.Drawing.SR.GetString("InvalidPictureType", new object[] { "picture", "Icon" })); } for (int i = 0; i < num3; i++) { SafeNativeMethods.ICONDIRENTRY icondirentry; icondirentry.bWidth = numPtr[0]; icondirentry.bHeight = numPtr[1]; icondirentry.bColorCount = numPtr[2]; icondirentry.bReserved = numPtr[3]; icondirentry.wPlanes = this.GetShort(numPtr + 4); icondirentry.wBitCount = this.GetShort(numPtr + 6); icondirentry.dwBytesInRes = this.GetInt(numPtr + 8); icondirentry.dwImageOffset = this.GetInt(numPtr + 12); bool flag = false; int wBitCount = 0; if (icondirentry.bColorCount != 0) { wBitCount = 4; if (icondirentry.bColorCount < 0x10) { wBitCount = 1; } } else { wBitCount = icondirentry.wBitCount; } if (wBitCount == 0) { wBitCount = 8; } if (length == 0) { flag = true; } else { int num10 = Math.Abs((int)(bWidth - width)) + Math.Abs((int)(bHeight - height)); int introduced25 = Math.Abs((int)(icondirentry.bWidth - width)); int num11 = introduced25 + Math.Abs((int)(icondirentry.bHeight - height)); if ((num11 < num10) || ((num11 == num10) && (((wBitCount <= bitDepth) && (wBitCount > this.bestBitDepth)) || ((this.bestBitDepth > bitDepth) && (wBitCount < this.bestBitDepth))))) { flag = true; } } if (flag) { bWidth = icondirentry.bWidth; bHeight = icondirentry.bHeight; this.bestImageOffset = icondirentry.dwImageOffset; length = icondirentry.dwBytesInRes; this.bestBitDepth = wBitCount; } numPtr += num7; } if ((this.bestImageOffset < 0) || ((this.bestImageOffset + length) > this.iconData.Length)) { throw new ArgumentException(System.Drawing.SR.GetString("InvalidPictureType", new object[] { "picture", "Icon" })); } if ((this.bestImageOffset % IntPtr.Size) != 0) { byte[] destinationArray = new byte[length]; Array.Copy(this.iconData, this.bestImageOffset, destinationArray, 0, length); fixed(byte *numRef2 = destinationArray) { this.handle = SafeNativeMethods.CreateIconFromResourceEx(numRef2, length, true, 0x30000, 0, 0, 0); } } else { this.handle = SafeNativeMethods.CreateIconFromResourceEx(numRef + this.bestImageOffset, length, true, 0x30000, 0, 0, 0); } if (this.handle == IntPtr.Zero) { throw new Win32Exception(); } } }
/// <include file='doc\Icon.uex' path='docs/doc[@for="Icon.Initialize"]/*' /> /// <devdoc> /// 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. /// </devdoc> private unsafe void Initialize(int width, int height) { if (_iconData == null || _handle != IntPtr.Zero) { throw new InvalidOperationException(SR.Format(SR.IllegalState, GetType().Name)); } int icondirSize = Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIR)); if (_iconData.Length < icondirSize) { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", "Icon")); } // Get the correct width / 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 = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); s_bitDepth = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), SafeNativeMethods.BITSPIXEL); s_bitDepth *= UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), SafeNativeMethods.PLANES); UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); // If the bitdepth 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 (s_bitDepth == 8) { s_bitDepth = 4; } } fixed(byte *pbIconData = _iconData) { short idReserved = GetShort(pbIconData); short idType = GetShort(pbIconData + 2); short idCount = GetShort(pbIconData + 4); if (idReserved != 0 || idType != 1 || idCount == 0) { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", "Icon")); } SafeNativeMethods.ICONDIRENTRY EntryTemp; byte bestWidth = 0; byte bestHeight = 0; //int bestBitDepth = 0; byte *pbIconDirEntry = unchecked (pbIconData + 6); int icondirEntrySize = Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIRENTRY)); Debug.Assert((icondirEntrySize * (idCount - 1) + icondirSize) <= _iconData.Length, "Illegal number of ICONDIRENTRIES"); if ((icondirEntrySize * (idCount - 1) + icondirSize) > _iconData.Length) { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", "Icon")); } for (int i = 0; i < idCount; i++) { // // Fill in EntryTemp // EntryTemp.bWidth = pbIconDirEntry[0]; EntryTemp.bHeight = pbIconDirEntry[1]; EntryTemp.bColorCount = pbIconDirEntry[2]; EntryTemp.bReserved = pbIconDirEntry[3]; EntryTemp.wPlanes = GetShort(pbIconDirEntry + 4); EntryTemp.wBitCount = GetShort(pbIconDirEntry + 6); EntryTemp.dwBytesInRes = GetInt(pbIconDirEntry + 8); EntryTemp.dwImageOffset = GetInt(pbIconDirEntry + 12); // // // bool fUpdateBestFit = false; int iconBitDepth = 0; if (EntryTemp.bColorCount != 0) { iconBitDepth = 4; if (EntryTemp.bColorCount < 0x10) { iconBitDepth = 1; } } else { iconBitDepth = EntryTemp.wBitCount; } // it looks like if nothing is specified at this point, bpp is 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 (0 == _bestBytesInRes) { fUpdateBestFit = true; } else { int bestDelta = Math.Abs(bestWidth - width) + Math.Abs(bestHeight - height); int thisDelta = Math.Abs(EntryTemp.bWidth - width) + Math.Abs(EntryTemp.bHeight - height); if ((thisDelta < bestDelta) || (thisDelta == bestDelta && (iconBitDepth <= s_bitDepth && iconBitDepth > _bestBitDepth || _bestBitDepth > s_bitDepth && iconBitDepth < _bestBitDepth))) { fUpdateBestFit = true; } } if (fUpdateBestFit) { bestWidth = EntryTemp.bWidth; bestHeight = EntryTemp.bHeight; _bestImageOffset = EntryTemp.dwImageOffset; _bestBytesInRes = EntryTemp.dwBytesInRes; _bestBitDepth = iconBitDepth; } pbIconDirEntry += icondirEntrySize; } Debug.Assert(_bestImageOffset >= 0 && _bestBytesInRes >= 0 && (_bestImageOffset + _bestBytesInRes) <= _iconData.Length, "Illegal offset/length for the Icon data"); if (_bestImageOffset < 0) { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", "Icon")); } if (_bestBytesInRes < 0) { throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER); } int 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", "Icon")); } // See DevDivBugs 17509. Copying bytes into an aligned buffer if needed if ((_bestImageOffset % IntPtr.Size) != 0) { // Beginning of icon's content is misaligned byte[] alignedBuffer = new byte[_bestBytesInRes]; Array.Copy(_iconData, _bestImageOffset, alignedBuffer, 0, _bestBytesInRes); fixed(byte *pbAlignedBuffer = alignedBuffer) { _handle = SafeNativeMethods.CreateIconFromResourceEx(pbAlignedBuffer, _bestBytesInRes, true, 0x00030000, 0, 0, 0); } } else { try { _handle = SafeNativeMethods.CreateIconFromResourceEx(checked (pbIconData + _bestImageOffset), _bestBytesInRes, true, 0x00030000, 0, 0, 0); } catch (OverflowException) { throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER); } } if (_handle == IntPtr.Zero) { throw new Win32Exception(); } } }
/// <include file='doc\Icon.uex' path='docs/doc[@for="Icon.Initialize"]/*' /> /// <devdoc> /// 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. /// </devdoc> private unsafe void Initialize(int width, int height) { if (iconData == null || handle != IntPtr.Zero) { throw new InvalidOperationException(SR.GetString(SR.IllegalState, GetType().Name)); } if (iconData.Length < Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIR))) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } // Get the correct width / height // if (width == 0) { width = UnsafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CXICON); } if (height == 0) { height = UnsafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CYICON); } if (bitDepth == 0) { IntPtr dc = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); bitDepth = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), SafeNativeMethods.BITSPIXEL); bitDepth *= UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), SafeNativeMethods.PLANES); UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); // If the bitdepth 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 (bitDepth == 8) { bitDepth = 4; } } fixed(byte *pbIconData = iconData) { SafeNativeMethods.ICONDIR *pIconDir = (SafeNativeMethods.ICONDIR *)pbIconData; if (pIconDir->idReserved != 0 || pIconDir->idType != 1 || pIconDir->idCount == 0) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } SafeNativeMethods.ICONDIRENTRY *pIconDirEntry = &pIconDir->idEntries; SafeNativeMethods.ICONDIRENTRY *pBestFit = null; int bestBitDepth = 0; int icondirEntrySize = Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIRENTRY)); Debug.Assert((icondirEntrySize * pIconDir->idCount) < iconData.Length, "Illegal number of ICONDIRENTRIES"); if ((icondirEntrySize * pIconDir->idCount) >= iconData.Length) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } for (int i = 0; i < pIconDir->idCount; i++) { int iconBitDepth = pIconDirEntry->wPlanes * pIconDirEntry->wBitCount; if (iconBitDepth == 0) { if (pIconDirEntry->bColorCount == 0) { iconBitDepth = 16; } else { iconBitDepth = 8; if (pIconDirEntry->bColorCount < 0xFF) { iconBitDepth = 4; } if (pIconDirEntry->bColorCount < 0x10) { iconBitDepth = 2; } } } // 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 (pBestFit == null) { pBestFit = pIconDirEntry; bestBitDepth = iconBitDepth; } else { int bestDelta = Math.Abs(pBestFit->bWidth - width) + Math.Abs(pBestFit->bHeight - height); int thisDelta = Math.Abs(pIconDirEntry->bWidth - width) + Math.Abs(pIconDirEntry->bHeight - height); if (thisDelta < bestDelta) { pBestFit = pIconDirEntry; bestBitDepth = iconBitDepth; } else if (thisDelta == bestDelta && (iconBitDepth <= bitDepth && iconBitDepth > bestBitDepth || bestBitDepth > bitDepth && iconBitDepth < bestBitDepth)) { pBestFit = pIconDirEntry; bestBitDepth = iconBitDepth; } } pIconDirEntry++; } Debug.Assert(pBestFit->dwImageOffset >= 0 && (pBestFit->dwImageOffset + pBestFit->dwBytesInRes) <= iconData.Length, "Illegal offset/length for the Icon data"); if (pBestFit->dwImageOffset < 0 || (pBestFit->dwImageOffset + pBestFit->dwBytesInRes) > iconData.Length) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } handle = SafeNativeMethods.CreateIconFromResourceEx(pbIconData + pBestFit->dwImageOffset, pBestFit->dwBytesInRes, true, 0x00030000, 0, 0, 0); if (handle == IntPtr.Zero) { throw new Win32Exception(); } } }