[Peek] Fix thumbnails being created and not used. Fix icon bitmaps leaking memory. Simplify ImagePreviewer. (#34544)
Consolidated IconHelper and ThumbnailHelper. Fixed icon memory leak. Fixed ImagePreviewer thumbnails being created and then not used. Refactored ImagePreviewer.
This commit is contained in:
Родитель
a70aafb3b8
Коммит
360b6d0ccf
|
@ -20,20 +20,11 @@ namespace Peek.Common.Models
|
|||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NativeSize
|
||||
public struct NativeSize(int width, int height)
|
||||
{
|
||||
private int width;
|
||||
private int height;
|
||||
public int Width { get; set; } = width;
|
||||
|
||||
public int Width
|
||||
{
|
||||
set { width = value; }
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
set { height = value; }
|
||||
}
|
||||
public int Height { get; set; } = height;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace Peek.FilePreviewer.Previewers.Drive
|
|||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var iconBitmap = await IconHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
var iconBitmap = await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg"));
|
||||
|
||||
Preview = preview;
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
// 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;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
{
|
||||
public static class IconHelper
|
||||
{
|
||||
// Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
|
||||
private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
|
||||
|
||||
public static async Task<ImageSource?> GetIconAsync(string fileName, CancellationToken cancellationToken)
|
||||
{
|
||||
return await GetImageAsync(fileName, ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.IconOnly, true, cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task<ImageSource?> GetThumbnailAsync(string fileName, CancellationToken cancellationToken)
|
||||
{
|
||||
return await GetImageAsync(fileName, ThumbnailOptions.ThumbnailOnly, true, cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task<ImageSource?> GetImageAsync(string fileName, ThumbnailOptions options, bool isSupportingTransparency, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageSource? imageSource = null;
|
||||
IShellItem? nativeShellItem = null;
|
||||
IntPtr hbitmap = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
Guid shellItem2Guid = new(IShellItem2Guid);
|
||||
int retCode = NativeMethods.SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
|
||||
|
||||
if (retCode != 0)
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(retCode)!;
|
||||
}
|
||||
|
||||
NativeSize large = new NativeSize { Width = 256, Height = 256 };
|
||||
|
||||
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(large, options, out hbitmap);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
imageSource = hr == HResult.Ok ? await BitmapHelper.GetBitmapFromHBitmapAsync(hbitmap, isSupportingTransparency, cancellationToken) : null;
|
||||
|
||||
hbitmap = IntPtr.Zero;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hbitmap != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.DeleteObject(hbitmap);
|
||||
}
|
||||
|
||||
if (nativeShellItem != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
}
|
||||
}
|
||||
|
||||
return imageSource;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// 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;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Create thumbnail or icon images for a file, or retrieve them from the Windows Explorer thumbnail and icon caches.
|
||||
/// </summary>
|
||||
/// <remarks>Inspired by https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows</remarks>
|
||||
public static class ThumbnailHelper
|
||||
{
|
||||
// The maximum size Explorer's thumbnail cache currently supports. Used for cache retrieval.
|
||||
private static readonly NativeSize MaxThumbnailSize = new(2560, 2560);
|
||||
|
||||
// Default size for all previewers except ImagePreviewer.
|
||||
private static readonly NativeSize DefaultThumbnailSize = new(256, 256);
|
||||
|
||||
// Used to retrieve the Shell Item for a given Windows Explorer resource, so its thumbnail/icon can be retrieved.
|
||||
private static Guid _shellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93");
|
||||
|
||||
/// <summary>
|
||||
/// Get a file's icon bitmap.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the file.</param>
|
||||
/// <param name="cancellationToken">The async task cancellation token.</param>
|
||||
/// <returns>An <see cref="ImageSource"/> corresponding to the file's icon bitmap, or null if retrieval failed.</returns>
|
||||
/// <remarks>If a cached icon cannot be found, a new icon will be created and added to the Explorer icon cache.</remarks>
|
||||
public static async Task<ImageSource?> GetIconAsync(string path, CancellationToken cancellationToken)
|
||||
{
|
||||
return await GetImageAsync(path, ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.IconOnly, true, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a file's thumbnail bitmap.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the file.</param>
|
||||
/// <param name="cancellationToken">The async task cancellation token.</param>
|
||||
/// <returns>An <see cref="ImageSource"/> corresponding to the file's thumbnail bitmap, or null if retrieval failed.</returns>
|
||||
/// <remarks>If a cached thumbnail cannot be found, a new thumbnail will be created and added to the Explorer thumbnail cache.</remarks>
|
||||
public static async Task<ImageSource?> GetThumbnailAsync(string path, CancellationToken cancellationToken)
|
||||
{
|
||||
return await GetImageAsync(path, ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.ThumbnailOnly, true, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the highest-resolution image available for a file from the Explorer thumbnail and icon caches.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the file.</param>
|
||||
/// <param name="supportsTransparency">Whether the file's type supports transparency. Set to true for PNG image files.</param>
|
||||
/// <param name="cancellationToken">The async task cancellation token.</param>
|
||||
/// <returns>An <see cref="ImageSource"/> corresponding to the thumbnail or icon, or null if there is no icon or thumbnail cached for the file.</returns>
|
||||
public static async Task<ImageSource?> GetCachedThumbnailAsync(string path, bool supportsTransparency, CancellationToken cancellationToken)
|
||||
{
|
||||
return await GetImageAsync(path, ThumbnailOptions.InCacheOnly, supportsTransparency, cancellationToken);
|
||||
}
|
||||
|
||||
private static async Task<ImageSource?> GetImageAsync(string path, ThumbnailOptions options, bool supportsTransparency, CancellationToken cancellationToken)
|
||||
{
|
||||
IntPtr hBitmap = IntPtr.Zero;
|
||||
IShellItem? nativeShellItem = null;
|
||||
bool checkCacheOnly = options.HasFlag(ThumbnailOptions.InCacheOnly);
|
||||
|
||||
try
|
||||
{
|
||||
int retCode = NativeMethods.SHCreateItemFromParsingName(path, IntPtr.Zero, ref _shellItem2Guid, out nativeShellItem);
|
||||
if (retCode != 0)
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(retCode)!;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(checkCacheOnly ? MaxThumbnailSize : DefaultThumbnailSize, options, out hBitmap);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
return hr == HResult.Ok ? await BitmapHelper.GetBitmapFromHBitmapAsync(hBitmap, supportsTransparency, cancellationToken) : null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Delete the unmanaged bitmap resource.
|
||||
if (hBitmap != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.DeleteObject(hBitmap);
|
||||
}
|
||||
|
||||
if (nativeShellItem != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -88,8 +88,8 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var thumbnail = await IconHelper.GetThumbnailAsync(Item.Path, cancellationToken)
|
||||
?? await IconHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
var thumbnail = await ThumbnailHelper.GetThumbnailAsync(Item.Path, cancellationToken)
|
||||
?? await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Models;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public static class ThumbnailHelper
|
||||
{
|
||||
// Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
|
||||
private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
|
||||
|
||||
public static readonly NativeSize HighQualityThumbnailSize = new NativeSize { Width = 720, Height = 720, };
|
||||
public static readonly NativeSize LowQualityThumbnailSize = new NativeSize { Width = 256, Height = 256, };
|
||||
|
||||
private static readonly NativeSize FallBackThumbnailSize = new NativeSize { Width = 96, Height = 96, };
|
||||
private static readonly NativeSize LastFallBackThumbnailSize = new NativeSize { Width = 32, Height = 32, };
|
||||
|
||||
private static readonly List<NativeSize> ThumbnailFallBackSizes = new List<NativeSize>
|
||||
{
|
||||
HighQualityThumbnailSize,
|
||||
LowQualityThumbnailSize,
|
||||
FallBackThumbnailSize,
|
||||
LastFallBackThumbnailSize,
|
||||
};
|
||||
|
||||
// TODO: Add a re-try system if there is no thumbnail of requested size.
|
||||
public static HResult GetThumbnail(string filename, out IntPtr hbitmap, NativeSize thumbnailSize)
|
||||
{
|
||||
Guid shellItem2Guid = new Guid(IShellItem2Guid);
|
||||
int retCode = NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, ref shellItem2Guid, out IShellItem nativeShellItem);
|
||||
|
||||
if (retCode != 0)
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(retCode)!;
|
||||
}
|
||||
|
||||
var options = ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.ThumbnailOnly | ThumbnailOptions.ScaleUp;
|
||||
|
||||
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(thumbnailSize, options, out hbitmap);
|
||||
|
||||
// Try to get thumbnail using the fallback sizes order
|
||||
if (hr != HResult.Ok)
|
||||
{
|
||||
var currentThumbnailFallBackIndex = ThumbnailFallBackSizes.IndexOf(thumbnailSize);
|
||||
var nextThumbnailFallBackIndex = currentThumbnailFallBackIndex + 1;
|
||||
if (nextThumbnailFallBackIndex < ThumbnailFallBackSizes.Count - 1)
|
||||
{
|
||||
hr = GetThumbnail(filename, out hbitmap, ThumbnailFallBackSizes[nextThumbnailFallBackIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
public static async Task<BitmapImage?> GetThumbnailAsync(StorageFile? storageFile, uint size)
|
||||
{
|
||||
BitmapImage? bitmapImage = null;
|
||||
|
||||
var imageStream = await storageFile?.GetThumbnailAsync(
|
||||
Windows.Storage.FileProperties.ThumbnailMode.SingleItem,
|
||||
size,
|
||||
Windows.Storage.FileProperties.ThumbnailOptions.None);
|
||||
|
||||
if (imageStream == null)
|
||||
{
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
bitmapImage = new BitmapImage();
|
||||
bitmapImage.SetSource(imageStream);
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ using Microsoft.PowerToys.FilePreviewCommon;
|
|||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Extensions;
|
||||
using Peek.Common.Helpers;
|
||||
using Peek.Common.Models;
|
||||
|
@ -26,7 +25,7 @@ using Windows.Foundation;
|
|||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public partial class ImagePreviewer : ObservableObject, IImagePreviewer, IDisposable
|
||||
public partial class ImagePreviewer : ObservableObject, IImagePreviewer
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ImageSource? preview;
|
||||
|
@ -51,41 +50,24 @@ namespace Peek.FilePreviewer.Previewers
|
|||
|
||||
private IFileSystemItem Item { get; }
|
||||
|
||||
private bool IsPng() => Item.Extension == ".png";
|
||||
|
||||
private bool IsSvg() => Item.Extension == ".svg";
|
||||
|
||||
private bool IsQoi() => Item.Extension == ".qoi";
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
private Task<bool>? LowQualityThumbnailTask { get; set; }
|
||||
|
||||
private Task<bool>? HighQualityThumbnailTask { get; set; }
|
||||
|
||||
private Task<bool>? FullQualityImageTask { get; set; }
|
||||
|
||||
private bool IsHighQualityThumbnailLoaded => HighQualityThumbnailTask?.Status == TaskStatus.RanToCompletion;
|
||||
|
||||
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
||||
|
||||
private IntPtr lowQualityThumbnail;
|
||||
|
||||
private ImageSource? lowQualityThumbnailPreview;
|
||||
|
||||
private IntPtr highQualityThumbnail;
|
||||
|
||||
private ImageSource? highQualityThumbnailPreview;
|
||||
|
||||
public static bool IsItemSupported(IFileSystemItem item)
|
||||
{
|
||||
return _supportedFileTypes.Contains(item.Extension);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task<PreviewSize> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (IsSvg(Item))
|
||||
|
||||
if (IsSvg())
|
||||
{
|
||||
var size = await Task.Run(Item.GetSvgSize);
|
||||
if (size != null)
|
||||
|
@ -93,7 +75,7 @@ namespace Peek.FilePreviewer.Previewers
|
|||
ImageSize = size.Value;
|
||||
}
|
||||
}
|
||||
else if (IsQoi(Item))
|
||||
else if (IsQoi())
|
||||
{
|
||||
var size = await Task.Run(Item.GetQoiSize);
|
||||
if (size != null)
|
||||
|
@ -115,30 +97,12 @@ namespace Peek.FilePreviewer.Previewers
|
|||
|
||||
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Clear();
|
||||
State = PreviewState.Loading;
|
||||
|
||||
LowQualityThumbnailTask = LoadLowQualityThumbnailAsync(cancellationToken);
|
||||
HighQualityThumbnailTask = LoadHighQualityThumbnailAsync(cancellationToken);
|
||||
FullQualityImageTask = LoadFullQualityImageAsync(cancellationToken);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Task.WhenAll(LowQualityThumbnailTask, HighQualityThumbnailTask, FullQualityImageTask);
|
||||
State = PreviewState.Loading;
|
||||
|
||||
// If Preview is still null, FullQualityImage was not available. Preview the thumbnail instead.
|
||||
if (Preview == null)
|
||||
{
|
||||
if (highQualityThumbnailPreview != null)
|
||||
{
|
||||
Preview = highQualityThumbnailPreview;
|
||||
}
|
||||
else
|
||||
{
|
||||
Preview = lowQualityThumbnailPreview;
|
||||
}
|
||||
}
|
||||
|
||||
if (Preview == null && HasFailedLoadingPreview())
|
||||
if (!await LoadFullQualityImageAsync(cancellationToken) &&
|
||||
!await LoadThumbnailAsync(cancellationToken))
|
||||
{
|
||||
State = PreviewState.Error;
|
||||
}
|
||||
|
@ -173,69 +137,23 @@ namespace Peek.FilePreviewer.Previewers
|
|||
|
||||
private void UpdateMaxImageSize()
|
||||
{
|
||||
var imageWidth = ImageSize?.Width ?? 0;
|
||||
var imageHeight = ImageSize?.Height ?? 0;
|
||||
double imageWidth = ImageSize?.Width ?? 0;
|
||||
double imageHeight = ImageSize?.Height ?? 0;
|
||||
|
||||
if (ScalingFactor != 0)
|
||||
{
|
||||
MaxImageSize = new Size(imageWidth / ScalingFactor, imageHeight / ScalingFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxImageSize = new Size(imageWidth, imageHeight);
|
||||
}
|
||||
MaxImageSize = ScalingFactor != 0 ?
|
||||
new Size(imageWidth / ScalingFactor, imageHeight / ScalingFactor) :
|
||||
new Size(imageWidth, imageHeight);
|
||||
}
|
||||
|
||||
private Task<bool> LoadLowQualityThumbnailAsync(CancellationToken cancellationToken)
|
||||
private Task<bool> LoadThumbnailAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var hr = ThumbnailHelper.GetThumbnail(Item.Path, out lowQualityThumbnail, ThumbnailHelper.LowQualityThumbnailSize);
|
||||
if (hr != HResult.Ok)
|
||||
{
|
||||
Logger.LogError("Error loading low quality thumbnail - hresult: " + hr);
|
||||
throw new ImageLoadingException(nameof(lowQualityThumbnail));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!IsFullImageLoaded && !IsHighQualityThumbnailLoaded)
|
||||
{
|
||||
var thumbnailBitmap = await BitmapHelper.GetBitmapFromHBitmapAsync(lowQualityThumbnail, IsPng(Item), cancellationToken);
|
||||
lowQualityThumbnailPreview = thumbnailBitmap;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Task<bool> LoadHighQualityThumbnailAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var hr = ThumbnailHelper.GetThumbnail(Item.Path, out highQualityThumbnail, ThumbnailHelper.HighQualityThumbnailSize);
|
||||
if (hr != HResult.Ok)
|
||||
{
|
||||
Logger.LogError("Error loading high quality thumbnail - hresult: " + hr);
|
||||
throw new ImageLoadingException(nameof(highQualityThumbnail));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!IsFullImageLoaded)
|
||||
{
|
||||
var thumbnailBitmap = await BitmapHelper.GetBitmapFromHBitmapAsync(highQualityThumbnail, IsPng(Item), cancellationToken);
|
||||
highQualityThumbnailPreview = thumbnailBitmap;
|
||||
}
|
||||
Preview = await ThumbnailHelper.GetCachedThumbnailAsync(Item.Path, IsPng(), cancellationToken);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -252,7 +170,7 @@ namespace Peek.FilePreviewer.Previewers
|
|||
|
||||
using FileStream stream = ReadHelper.OpenReadOnly(Item.Path);
|
||||
|
||||
if (IsSvg(Item))
|
||||
if (IsSvg())
|
||||
{
|
||||
var source = new SvgImageSource();
|
||||
source.RasterizePixelHeight = ImageSize?.Height ?? 0;
|
||||
|
@ -267,7 +185,7 @@ namespace Peek.FilePreviewer.Previewers
|
|||
|
||||
Preview = source;
|
||||
}
|
||||
else if (IsQoi(Item))
|
||||
else if (IsQoi())
|
||||
{
|
||||
using var bitmap = QoiImage.FromStream(stream);
|
||||
|
||||
|
@ -275,55 +193,13 @@ namespace Peek.FilePreviewer.Previewers
|
|||
}
|
||||
else
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
Preview = bitmap;
|
||||
await bitmap.SetSourceAsync(stream.AsRandomAccessStream());
|
||||
Preview = new BitmapImage();
|
||||
await ((BitmapImage)Preview).SetSourceAsync(stream.AsRandomAccessStream());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private bool HasFailedLoadingPreview()
|
||||
{
|
||||
var hasFailedLoadingLowQualityThumbnail = !(LowQualityThumbnailTask?.Result ?? true);
|
||||
var hasFailedLoadingHighQualityThumbnail = !(HighQualityThumbnailTask?.Result ?? true);
|
||||
var hasFailedLoadingFullQualityImage = !(FullQualityImageTask?.Result ?? true);
|
||||
|
||||
return hasFailedLoadingLowQualityThumbnail && hasFailedLoadingHighQualityThumbnail && hasFailedLoadingFullQualityImage;
|
||||
}
|
||||
|
||||
private bool IsPng(IFileSystemItem item)
|
||||
{
|
||||
return item.Extension == ".png";
|
||||
}
|
||||
|
||||
private bool IsSvg(IFileSystemItem item)
|
||||
{
|
||||
return item.Extension == ".svg";
|
||||
}
|
||||
|
||||
private bool IsQoi(IFileSystemItem item)
|
||||
{
|
||||
return item.Extension == ".qoi";
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
lowQualityThumbnailPreview = null;
|
||||
highQualityThumbnailPreview = null;
|
||||
Preview = null;
|
||||
|
||||
if (lowQualityThumbnail != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.DeleteObject(lowQualityThumbnail);
|
||||
}
|
||||
|
||||
if (highQualityThumbnail != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.DeleteObject(highQualityThumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes = new HashSet<string>
|
||||
{
|
||||
// Image types
|
||||
|
|
|
@ -106,7 +106,7 @@ public partial class SpecialFolderPreviewer : ObservableObject, ISpecialFolderPr
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var iconBitmap = await IconHelper.GetIconAsync(Item.ParsingName, cancellationToken);
|
||||
var iconBitmap = await ThumbnailHelper.GetIconAsync(Item.ParsingName, cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
|
|
|
@ -108,8 +108,8 @@ namespace Peek.FilePreviewer.Previewers
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var iconBitmap = await IconHelper.GetThumbnailAsync(Item.Path, cancellationToken)
|
||||
?? await IconHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
var iconBitmap = await ThumbnailHelper.GetThumbnailAsync(Item.Path, cancellationToken)
|
||||
?? await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче