Skip to content

Feature: Switched away from GetThumbnailAsync API #14423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 10 additions & 15 deletions src/Files.App/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,29 +124,26 @@ public static class GridViewBrowser
{
public const int GridViewIncrement = 20;

// Max achievable ctrl + scroll, not a default layout size
public const int GridViewSizeMax = 300;

public const int GridViewSizeLarge = 220;

public const int GridViewSizeMedium = 160;

public const int GridViewSizeSmall = 100;

public const int TilesView = 260;
public const int TilesView = 100;
}
}

public static class DetailsLayoutBrowser
{
public const int DetailsViewSize = 32;
}
// Default icon sizes that are available for files and folders
public static class DefaultIconSizes
{
public const int Small = 16;

public static class ColumnViewBrowser
{
public const int ColumnViewSize = 32;
public const int Large = 32;

public const int ColumnViewSizeSmall = 24;
}
public const int ExtraLarge = 48;

public const int Jumbo = 256;
}

public static class Widgets
Expand All @@ -155,8 +152,6 @@ public static class Drives
{
public const float LowStorageSpacePercentageThreshold = 90.0f;
}

public const int WidgetIconSize = 256;
}

public static class LocalSettings
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Data/Items/DriveItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ public async Task LoadThumbnailAsync(bool isSidebar = false)
else
{
if (!string.IsNullOrEmpty(DeviceID) && !string.Equals(DeviceID, "network-folder"))
IconData ??= await FileThumbnailHelper.LoadIconWithoutOverlayAsync(DeviceID, 16);
IconData ??= await FileThumbnailHelper.LoadIconWithoutOverlayAsync(DeviceID, Constants.DefaultIconSizes.Large, false, true);

if (Root is not null)
{
Expand Down
144 changes: 31 additions & 113 deletions src/Files.App/Data/Models/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public GitProperties EnabledGitProperties
(!gitItem.StatusPropertiesInitialized && value is GitProperties.All or GitProperties.Status
|| !gitItem.CommitPropertiesInitialized && value is GitProperties.All or GitProperties.Commit))
{
await LoadGitPropertiesAsync(gitItem);
await LoadGitPropertiesAsync(gitItem);
}
});
}
Expand Down Expand Up @@ -944,136 +944,54 @@ private async Task<BitmapImage> GetShieldIcon()
private async Task LoadItemThumbnailAsync(ListedItem item, uint thumbnailSize = 96, IStorageItem? matchingStorageItem = null)
{
var wasIconLoaded = false;


if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive)
{
if (UserSettingsService.FoldersSettingsService.ShowThumbnails &&
!item.IsShortcut && !item.IsHiddenItem && !FtpHelpers.IsFtpPath(item.ItemPath))
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false;
var iconInfo = await FileThumbnailHelper.LoadIconAndOverlayAsync(item.ItemPath, thumbnailSize, false, getIconOnly);
if (iconInfo.IconData is not null)
{
var matchingStorageFile = matchingStorageItem?.AsBaseStorageFile() ?? await GetFileFromPathAsync(item.ItemPath);

if (matchingStorageFile is not null)
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// SingleItem returns image thumbnails in the correct aspect ratio for the grid layouts
// ListView is used for the details and columns layout
var thumbnailMode = thumbnailSize < 96 ? ThumbnailMode.ListView : ThumbnailMode.SingleItem;

using StorageItemThumbnail Thumbnail = await FilesystemTasks.Wrap(() => matchingStorageFile.GetThumbnailAsync(thumbnailMode, thumbnailSize, ThumbnailOptions.ResizeThumbnail).AsTask());

if (!(Thumbnail is null || Thumbnail.Size == 0 || Thumbnail.OriginalHeight == 0 || Thumbnail.OriginalWidth == 0))
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
if (!string.IsNullOrEmpty(item.FileExtension) &&
!item.IsShortcut && !item.IsExecutable &&
!ImagePreviewViewModel.ContainsExtension(item.FileExtension.ToLowerInvariant()))
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
var img = new BitmapImage();
img.DecodePixelType = DecodePixelType.Logical;
img.DecodePixelWidth = (int)thumbnailSize;
await img.SetSourceAsync(Thumbnail);
item.FileImage = img;
if (!string.IsNullOrEmpty(item.FileExtension) &&
!item.IsShortcut && !item.IsExecutable &&
!ImagePreviewViewModel.ContainsExtension(item.FileExtension.ToLowerInvariant()))
{
DefaultIcons.AddIfNotPresent(item.FileExtension.ToLowerInvariant(), item.FileImage);
}
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal);
wasIconLoaded = true;
DefaultIcons.AddIfNotPresent(item.FileExtension.ToLowerInvariant(), item.FileImage);
}

var overlayInfo = await FileThumbnailHelper.LoadOverlayAsync(item.ItemPath, thumbnailSize);
if (overlayInfo is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await overlayInfo.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

if (!wasIconLoaded)
if (iconInfo.OverlayData is not null)
{
var iconInfo = await FileThumbnailHelper.LoadIconAndOverlayAsync(item.ItemPath, thumbnailSize, false);
if (iconInfo.IconData is not null)
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
if (!string.IsNullOrEmpty(item.FileExtension) &&
!item.IsShortcut && !item.IsExecutable &&
!ImagePreviewViewModel.ContainsExtension(item.FileExtension.ToLowerInvariant()))
{
DefaultIcons.AddIfNotPresent(item.FileExtension.ToLowerInvariant(), item.FileImage);
}
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

if (iconInfo.OverlayData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconInfo.OverlayData.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
item.IconOverlay = await iconInfo.OverlayData.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
else
{
if (!item.IsShortcut && !item.IsHiddenItem && !FtpHelpers.IsFtpPath(item.ItemPath))
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 80;
var iconInfo = await FileThumbnailHelper.LoadIconAndOverlayAsync(item.ItemPath, thumbnailSize, true, getIconOnly);
if (iconInfo.IconData is not null)
{
var matchingStorageFolder = matchingStorageItem?.AsBaseStorageFolder() ?? await GetFolderFromPathAsync(item.ItemPath);
if (matchingStorageFolder is not null)
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// SingleItem returns image thumbnails in the correct aspect ratio for the grid layouts
// ListView is used for the details and columns layout
var thumbnailMode = thumbnailSize < 96 ? ThumbnailMode.ListView : ThumbnailMode.SingleItem;

// We use ReturnOnlyIfCached because otherwise folders thumbnails have a black background, this has the downside the folder previews don't work
using StorageItemThumbnail Thumbnail = await FilesystemTasks.Wrap(() => matchingStorageFolder.GetThumbnailAsync(thumbnailMode, thumbnailSize, ThumbnailOptions.ReturnOnlyIfCached).AsTask());
if (!(Thumbnail is null || Thumbnail.Size == 0 || Thumbnail.OriginalHeight == 0 || Thumbnail.OriginalWidth == 0))
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
var img = new BitmapImage();
img.DecodePixelType = DecodePixelType.Logical;
img.DecodePixelWidth = (int)thumbnailSize;
await img.SetSourceAsync(Thumbnail);
item.FileImage = img;
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal);
wasIconLoaded = true;
}

var overlayInfo = await FileThumbnailHelper.LoadOverlayAsync(item.ItemPath, thumbnailSize);
if (overlayInfo is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await overlayInfo.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

if (!wasIconLoaded)
if (iconInfo.OverlayData is not null)
{
var iconInfo = await FileThumbnailHelper.LoadIconAndOverlayAsync(item.ItemPath, thumbnailSize, true);
if (iconInfo.IconData is not null)
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

if (iconInfo.OverlayData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconInfo.OverlayData.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
item.IconOverlay = await iconInfo.OverlayData.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
}
Expand Down Expand Up @@ -1263,7 +1181,7 @@ public async Task LoadGitPropertiesAsync(GitItem gitItem)
{
var getStatus = EnabledGitProperties is GitProperties.All or GitProperties.Status && !gitItem.StatusPropertiesInitialized;
var getCommit = EnabledGitProperties is GitProperties.All or GitProperties.Commit && !gitItem.CommitPropertiesInitialized;

if (!getStatus && !getCommit)
return;

Expand Down Expand Up @@ -1331,7 +1249,7 @@ await SafetyExtensions.IgnoreExceptions(() =>
ImageSource? groupImage = null;
if (item.PrimaryItemAttribute != StorageItemTypes.Folder || item.IsArchive)
{
var headerIconInfo = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(item.ItemPath, 64u, false);
var headerIconInfo = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(item.ItemPath, Constants.DefaultIconSizes.ExtraLarge, false, true);

if (headerIconInfo is not null && !item.IsShortcut)
groupImage = await dispatcherQueue.EnqueueOrInvokeAsync(() => headerIconInfo.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Data/Models/SidebarPinnedModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public async Task<LocationItem> CreateLocationItemFromPathAsync(string path)

if (locationItem.IconData is null)
{
locationItem.IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(path, 48u);
locationItem.IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(path, 48u, false, true);

if (locationItem.IconData is not null)
locationItem.Icon = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync());
Expand Down
12 changes: 6 additions & 6 deletions src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public int GridViewSize
else // Size up from tiles to grid
{
// Set grid size to allow immediate UI update
var newValue = (LayoutMode == FolderLayoutModes.TilesView) ? Constants.Browser.GridViewBrowser.GridViewSizeSmall : (value <= Constants.Browser.GridViewBrowser.GridViewSizeMax) ? value : Constants.Browser.GridViewBrowser.GridViewSizeMax;
var newValue = (LayoutMode == FolderLayoutModes.TilesView) ? Constants.Browser.GridViewBrowser.GridViewSizeSmall : (value <= Constants.Browser.GridViewBrowser.GridViewSizeLarge) ? value : Constants.Browser.GridViewBrowser.GridViewSizeLarge;
SetProperty(ref LayoutPreferencesItem.GridViewSize, newValue, nameof(GridViewSize));

// Only update layout mode if it isn't already in grid view
Expand All @@ -113,7 +113,7 @@ public int GridViewSize
}

// Don't request a grid resize if it is already at the max size
if (value < Constants.Browser.GridViewBrowser.GridViewSizeMax)
if (value < Constants.Browser.GridViewBrowser.GridViewSizeLarge)
GridViewSizeChangeRequested?.Invoke(this, EventArgs.Empty);
}
}
Expand Down Expand Up @@ -308,18 +308,18 @@ public uint GetIconSize()
return LayoutMode switch
{
FolderLayoutModes.DetailsView
=> Constants.Browser.DetailsLayoutBrowser.DetailsViewSize,
=> Constants.DefaultIconSizes.Large,
FolderLayoutModes.ColumnView
=> Constants.Browser.ColumnViewBrowser.ColumnViewSize,
=> Constants.DefaultIconSizes.Large,
FolderLayoutModes.TilesView
=> Constants.Browser.GridViewBrowser.GridViewSizeSmall,
=> Constants.Browser.GridViewBrowser.TilesView,
_ when GridViewSize <= Constants.Browser.GridViewBrowser.GridViewSizeSmall
=> Constants.Browser.GridViewBrowser.GridViewSizeSmall,
_ when GridViewSize <= Constants.Browser.GridViewBrowser.GridViewSizeMedium
=> Constants.Browser.GridViewBrowser.GridViewSizeMedium,
_ when GridViewSize <= Constants.Browser.GridViewBrowser.GridViewSizeLarge
=> Constants.Browser.GridViewBrowser.GridViewSizeLarge,
_ => Constants.Browser.GridViewBrowser.GridViewSizeMax,
_ => Constants.Browser.GridViewBrowser.GridViewSizeLarge,
};
}

Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task LoadCardThumbnailAsync()
{
// Try load thumbnail using ListView mode
if (thumbnailData is null || thumbnailData.Length == 0)
thumbnailData = await FileThumbnailHelper.LoadIconFromPathAsync(Item.Path, Convert.ToUInt32(Constants.Widgets.WidgetIconSize), Windows.Storage.FileProperties.ThumbnailMode.SingleItem, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail);
thumbnailData = await FileThumbnailHelper.LoadIconFromPathAsync(Item.Path, Convert.ToUInt32(Constants.DefaultIconSizes.Jumbo), Windows.Storage.FileProperties.ThumbnailMode.SingleItem, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail);

// Thumbnail is still null, use DriveItem icon (loaded using SingleItem mode)
if (thumbnailData is null || thumbnailData.Length == 0)
Expand All @@ -52,7 +52,7 @@ public async Task LoadCardThumbnailAsync()

// Thumbnail data is valid, set the item icon
if (thumbnailData is not null && thumbnailData.Length > 0)
Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize));
Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.DefaultIconSizes.Jumbo));
}

public int CompareTo(DriveCardItem? other) => Item.Path.CompareTo(other?.Item?.Path);
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ public async Task LoadCardThumbnailAsync()
{
if (thumbnailData is null || thumbnailData.Length == 0)
{
thumbnailData = await FileThumbnailHelper.LoadIconFromPathAsync(Path, Convert.ToUInt32(Constants.Widgets.WidgetIconSize), Windows.Storage.FileProperties.ThumbnailMode.SingleItem, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail);
thumbnailData = await FileThumbnailHelper.LoadIconFromPathAsync(Path, Convert.ToUInt32(Constants.DefaultIconSizes.Jumbo), Windows.Storage.FileProperties.ThumbnailMode.SingleItem, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail);
}
if (thumbnailData is not null && thumbnailData.Length > 0)
{
Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize));
Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.DefaultIconSizes.Jumbo));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Utils/Cloud/CloudDrivesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static async Task UpdateDrivesAsync()
ShowProperties = true,
};

var iconData = provider.IconData ?? await FileThumbnailHelper.LoadIconWithoutOverlayAsync(provider.SyncFolder, 24);
var iconData = provider.IconData ?? await FileThumbnailHelper.LoadIconWithoutOverlayAsync(provider.SyncFolder, Constants.DefaultIconSizes.Large, false, true);
if (iconData is not null)
{
cloudProviderItem.IconData = iconData;
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Utils/Library/LibraryLocationItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public async Task<bool> CheckDefaultSaveFolderAccess()

public async Task LoadLibraryIconAsync()
{
IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(Path, 24u);
IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(Path, Constants.DefaultIconSizes.Large, false, true);

if (IconData is not null)
Icon = await IconData.ToBitmapAsync();
Expand Down
8 changes: 2 additions & 6 deletions src/Files.App/Utils/RecentItem/RecentItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,8 @@ public RecentItem(ShellFileItem fileItem) : base()

public async Task LoadRecentItemIconAsync()
{
var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(RecentPath, 96u, ThumbnailMode.SingleItem, ThumbnailOptions.ResizeThumbnail);
if (iconData is null)
{
EmptyImgVis = true;
}
else
var iconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(RecentPath, Constants.DefaultIconSizes.Large, false, false);
if (iconData is not null)
{
EmptyImgVis = false;
FileImg = await iconData.ToBitmapAsync();
Expand Down
6 changes: 4 additions & 2 deletions src/Files.App/Utils/Shell/Win32API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ private class IconAndOverlayCacheEntry

private static readonly object _lock = new object();

public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path, int thumbnailSize, bool isFolder, bool getOverlay = true, bool onlyGetOverlay = false)
public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path, int thumbnailSize, bool isFolder, bool getIconOnly, bool getOverlay = true, bool onlyGetOverlay = false)
{
byte[]? iconData = null, overlayData = null;
var entry = _iconAndOverlayCache.GetOrAdd(path, _ => new());
Expand Down Expand Up @@ -256,7 +256,9 @@ public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path,
if (shellItem is not null && shellItem.IShellItem is Shell32.IShellItemImageFactory fctry)
{
var flags = Shell32.SIIGBF.SIIGBF_BIGGERSIZEOK;
if (thumbnailSize < 80) flags |= Shell32.SIIGBF.SIIGBF_ICONONLY;

if (getIconOnly)
flags |= Shell32.SIIGBF.SIIGBF_ICONONLY;

var hres = fctry.GetImage(new SIZE(thumbnailSize, thumbnailSize), flags, out var hbitmap);
if (hres == HRESULT.S_OK)
Expand Down
Loading