in src/Windows/Avalonia.Win32/Interop/Win32Icon.cs [187:360]
private static PixelSize ReplaceZeroesWithSystemMetrics(PixelSize pixelSize) => new(
pixelSize.Width == 0 ? UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXICON) : pixelSize.Width,
pixelSize.Height == 0 ? UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYICON) : pixelSize.Height
);
private static unsafe (IntPtr, PixelSize) LoadIconFromData(byte[] iconData, PixelSize size)
{
if (iconData.Length < sizeof(ICONDIR))
return default;
if (s_bitDepth == 0)
{
IntPtr dc = UnmanagedMethods.GetDC(IntPtr.Zero);
s_bitDepth = UnmanagedMethods.GetDeviceCaps(dc, UnmanagedMethods.DEVICECAP.BITSPIXEL);
s_bitDepth *= UnmanagedMethods.GetDeviceCaps(dc, UnmanagedMethods.DEVICECAP.PLANES);
UnmanagedMethods.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)
{
var dir = (ICONDIR*)b;
if (dir->idReserved != 0 || dir->idType != 1 || dir->idCount == 0)
{
return default;
}
byte bestWidth = 0;
byte bestHeight = 0;
if (sizeof(ICONDIRENTRY) * (dir->idCount - 1) + sizeof(ICONDIR) > iconData.Length)
return default;
var entries = new ReadOnlySpan<ICONDIRENTRY>(&dir->idEntries, dir->idCount);
var _bestBytesInRes = 0u;
var _bestBitDepth = 0u;
var _bestImageOffset = 0u;
foreach (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 specifying 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 - size.Width) + Math.Abs(bestHeight - size.Height);
int thisDelta = Math.Abs(entry.bWidth - size.Width) + Math.Abs(entry.bHeight - size.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 || _bestBytesInRes > int.MaxValue)
{
return default;
}
uint endOffset;
try
{
endOffset = checked(_bestImageOffset + _bestBytesInRes);
}
catch (OverflowException)
{
return default;
}
if (endOffset > iconData.Length)
{
return default;
}
var bestSize = new PixelSize(bestWidth, bestHeight);
IntPtr handle;
// 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);
try
{
fixed (byte* pbAlignedBuffer = alignedBuffer)
{
handle = UnmanagedMethods.CreateIconFromResourceEx(pbAlignedBuffer, _bestBytesInRes, 1,
0x00030000, 0, 0, 0);
}
}
finally
{
ArrayPool<byte>.Shared.Return(alignedBuffer);
}
}
else
{
try
{
handle = UnmanagedMethods.CreateIconFromResourceEx(checked(b + _bestImageOffset), _bestBytesInRes,
1, 0x00030000, 0, 0, 0);
}
catch (OverflowException)
{
return default;
}
}
if (handle == default)
{
var error = Marshal.GetLastWin32Error();
// If there we are out of GDI handles then our fallback won't work either, so throw now.
if (error == (int)Windows.Win32.Foundation.WIN32_ERROR.ERROR_NOT_ENOUGH_MEMORY)
{
throw new Win32Exception(error);
}
}
return (handle, bestSize);
}
}