mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request This PR improves icons for app items: - Refactors icon detection and selection from the AppX manifest out of `UWPApplication` - Prefer *unplated* UWP app logos so icons no longer appear smaller than expected - Adds an icon loader based on `IShellItemImageFactory` to correctly load large icons - Jumbo icons loaded from shortcuts are now crisp - Jumbo icons loaded from shortcuts are no longer scaled down - Refactors detail loading in `AppListItem` to prevent potential deadlocks - Makes PWA icons more crisp - Fixes fallback item (now it gets used not only when the icon is null, but also when it's empty). <table> <thead> <tr> <th></th> <th>Old</th> <th>New</th> </tr> </thead> <tr> <td>1</td> <td> <img width="830" height="495" alt="image" src="https://github.com/user-attachments/assets/bc9875bd-6a8b-4a3d-88e1-07a655a5a5cd" /> </td> <td> <img width="750" height="533" alt="image" src="https://github.com/user-attachments/assets/a82ed464-b925-4d0c-95c4-6c04859e886e" /> </td> </tr> <tr> <td>2</td> <td> <img width="814" height="233" alt="image" src="https://github.com/user-attachments/assets/d560d3c0-ffc5-4178-a610-4e3b3c7107c8" /> </td> <td> <img width="760" height="299" alt="image" src="https://github.com/user-attachments/assets/f29c825e-324f-46f1-b6bb-6edcf286fc9a" /> </td> </tr> <tr> <td>3</td> <td> <img width="813" height="262" alt="image" src="https://github.com/user-attachments/assets/d94f724d-ec26-48c8-bb8a-1b10f6a0f7eb" /> </td> <td> <img width="762" height="260" alt="image" src="https://github.com/user-attachments/assets/76c5debb-baac-417e-8aba-9cec198e742c" /> </td> </tr> <tr> <td>4</td> <td> <img width="819" height="250" alt="image" src="https://github.com/user-attachments/assets/5f16d714-56d8-42f2-ad8b-1c2be6570e5c" /> </td> <td> <img width="747" height="244" alt="image" src="https://github.com/user-attachments/assets/485c72cf-ef39-4c05-afdd-877f0a47f51a" /> </td> </tr> <tr> <td>5</td> <td> <img width="815" height="327" alt="image" src="https://github.com/user-attachments/assets/4108e36a-5950-43c9-bdff-6a9f58dadcf6" /> </td> <td> <img width="762" height="272" alt="image" src="https://github.com/user-attachments/assets/804a3159-a165-4a48-87f6-15849f5f4516" /> </td> </tr> <tr> <td>6</td> <td> <img width="809" height="257" alt="image" src="https://github.com/user-attachments/assets/93ad8241-1d75-415f-b08c-4161c0905e41" /> </td> <td> <img width="756" height="231" alt="image" src="https://github.com/user-attachments/assets/a0c9bb44-7151-438d-a811-82d5e2080f44" /> </td> </tr> <tr> <td></td> <td> </td> <td> </td> </tr> </table> <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #44970 - [x] Closes: #43320 <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed
133 lines
3.6 KiB
C#
133 lines
3.6 KiB
C#
// Copyright (c) Microsoft Corporation
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices.WindowsRuntime;
|
|
using System.Threading.Tasks;
|
|
using ManagedCommon;
|
|
using Windows.Graphics.Imaging;
|
|
using Windows.Storage.Streams;
|
|
using Windows.Win32;
|
|
using Windows.Win32.Foundation;
|
|
using Windows.Win32.Graphics.Gdi;
|
|
using Windows.Win32.UI.Shell;
|
|
|
|
namespace Microsoft.CmdPal.Ext.Apps.Helpers;
|
|
|
|
internal static class IconExtractor
|
|
{
|
|
public static async Task<IRandomAccessStream?> GetIconStreamAsync(string path, int size)
|
|
{
|
|
var bitmap = GetIcon(path, size);
|
|
if (bitmap == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var stream = new InMemoryRandomAccessStream();
|
|
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
|
|
encoder.SetSoftwareBitmap(bitmap);
|
|
await encoder.FlushAsync();
|
|
|
|
stream.Seek(0);
|
|
return stream;
|
|
}
|
|
|
|
public static unsafe SoftwareBitmap? GetIcon(string path, int size)
|
|
{
|
|
IShellItemImageFactory* factory = null;
|
|
HBITMAP hBitmap = default;
|
|
|
|
try
|
|
{
|
|
fixed (char* pPath = path)
|
|
{
|
|
var iid = IShellItemImageFactory.IID_Guid;
|
|
var hr = PInvoke.SHCreateItemFromParsingName(
|
|
pPath,
|
|
null,
|
|
&iid,
|
|
(void**)&factory);
|
|
|
|
if (hr.Failed || factory == null)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
var requestedSize = new SIZE { cx = size, cy = size };
|
|
var hr2 = factory->GetImage(
|
|
requestedSize,
|
|
SIIGBF.SIIGBF_ICONONLY | SIIGBF.SIIGBF_BIGGERSIZEOK | SIIGBF.SIIGBF_CROPTOSQUARE,
|
|
&hBitmap);
|
|
|
|
if (hr2.Failed || hBitmap.IsNull)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return CreateSoftwareBitmap(hBitmap, size);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"Failed to load icon from path='{path}',size={size}", ex);
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
if (!hBitmap.IsNull)
|
|
{
|
|
PInvoke.DeleteObject(hBitmap);
|
|
}
|
|
|
|
if (factory != null)
|
|
{
|
|
factory->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static unsafe SoftwareBitmap CreateSoftwareBitmap(HBITMAP hBitmap, int size)
|
|
{
|
|
var pixels = new byte[size * size * 4];
|
|
|
|
var bmi = new BITMAPINFO
|
|
{
|
|
bmiHeader = new BITMAPINFOHEADER
|
|
{
|
|
biSize = (uint)sizeof(BITMAPINFOHEADER),
|
|
biWidth = size,
|
|
biHeight = -size,
|
|
biPlanes = 1,
|
|
biBitCount = 32,
|
|
biCompression = 0,
|
|
},
|
|
};
|
|
|
|
var hdc = PInvoke.GetDC(default);
|
|
try
|
|
{
|
|
fixed (byte* pPixels = pixels)
|
|
{
|
|
_ = PInvoke.GetDIBits(
|
|
hdc,
|
|
hBitmap,
|
|
0,
|
|
(uint)size,
|
|
pPixels,
|
|
&bmi,
|
|
DIB_USAGE.DIB_RGB_COLORS);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ = PInvoke.ReleaseDC(default, hdc);
|
|
}
|
|
|
|
var bitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, size, size, BitmapAlphaMode.Premultiplied);
|
|
bitmap.CopyFromBuffer(pixels.AsBuffer());
|
|
return bitmap;
|
|
}
|
|
}
|