Skip to content

Commit

Permalink
Feature: Switched away from GetThumbnailAsync API (#14423)
Browse files Browse the repository at this point in the history
  • Loading branch information
yaira2 committed Jan 12, 2024
1 parent 246ddac commit c19f829
Show file tree
Hide file tree
Showing 20 changed files with 78 additions and 173 deletions.
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 @@ public async Task LoadGitPropertiesAsync(GitItem gitItem)
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

0 comments on commit c19f829

Please sign in to comment.