diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs index cfa2631c8dd..8304369dbe4 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs @@ -7,6 +7,8 @@ namespace Peek.FilePreviewer.Previewers 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; @@ -59,5 +61,32 @@ public static HResult GetThumbnail(string filename, out IntPtr hbitmap, NativeSi return hr; } + + public static async Task GetThumbnailAsync(string path, uint size) + { + BitmapImage? bitmapImage = null; + + // preview image + var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(path); + if (file == null) + { + return bitmapImage; + } + + var imageStream = await file.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; + } } } diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs index 5ca9fde224a..a6b4f0b091b 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs @@ -13,7 +13,6 @@ namespace Peek.FilePreviewer.Previewers using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.UI.Dispatching; - using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; using Peek.Common; using Peek.Common.Extensions; @@ -233,7 +232,8 @@ public static bool IsFileTypeSupported(string fileExt) ".jif", ".jpeg", ".jpe", - ".png", + + // ".png", // The current ImagePreviewer logic does not support transparency so PNG has it's own logic in PngPreviewer ".tif", ".tiff", ".dib", diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/PngPreviewer/PngPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/PngPreviewer/PngPreviewer.cs new file mode 100644 index 00000000000..213e67120a1 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/PngPreviewer/PngPreviewer.cs @@ -0,0 +1,117 @@ +// 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. + +namespace Peek.FilePreviewer.Previewers +{ + using System; + using System.Collections.Generic; + using System.Drawing.Imaging; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using CommunityToolkit.Mvvm.ComponentModel; + using Microsoft.UI.Dispatching; + using Microsoft.UI.Xaml.Media.Imaging; + using Peek.Common; + using Windows.Foundation; + using Windows.Graphics.Imaging; + using Windows.Storage.Streams; + using File = Peek.Common.Models.File; + + public partial class PngPreviewer : ObservableObject, IBitmapPreviewer + { + private readonly uint _png_image_size = 1280; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsPreviewLoaded))] + private BitmapSource? preview; + + [ObservableProperty] + private PreviewState state; + + public PngPreviewer(File file) + { + File = file; + Dispatcher = DispatcherQueue.GetForCurrentThread(); + + PropertyChanged += OnPropertyChanged; + } + + public bool IsPreviewLoaded => preview != null; + + private File File { get; } + + private DispatcherQueue Dispatcher { get; } + + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + + private CancellationToken CancellationToken => _cancellationTokenSource.Token; + + public void Dispose() + { + _cancellationTokenSource.Dispose(); + GC.SuppressFinalize(this); + } + + public async Task GetPreviewSizeAsync() + { + var propertyImageSize = await PropertyHelper.GetImageSize(File.Path); + if (propertyImageSize != Size.Empty) + { + return propertyImageSize; + } + + return await WICHelper.GetImageSize(File.Path); + } + + public async Task LoadPreviewAsync() + { + State = PreviewState.Loading; + + var previewTask = LoadPreviewImageAsync(); + + await Task.WhenAll(previewTask); + + if (Preview == null) + { + State = PreviewState.Error; + } + } + + public static bool IsFileTypeSupported(string fileExt) + { + return fileExt == ".png" ? true : false; + } + + private void OnPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Preview)) + { + if (Preview != null) + { + State = PreviewState.Loaded; + } + } + } + + private Task LoadPreviewImageAsync() + { + var thumbnailTCS = new TaskCompletionSource(); + Dispatcher.TryEnqueue(async () => + { + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + Preview = await ThumbnailHelper.GetThumbnailAsync(File.Path, _png_image_size); + + thumbnailTCS.SetResult(); + }); + + return thumbnailTCS.Task; + } + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs index 97f36fa81a4..ab562e592ea 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs @@ -10,7 +10,11 @@ public class PreviewerFactory { public IPreviewer Create(File file) { - if (ImagePreviewer.IsFileTypeSupported(file.Extension)) + if (PngPreviewer.IsFileTypeSupported(file.Extension)) + { + return new PngPreviewer(file); + } + else if (ImagePreviewer.IsFileTypeSupported(file.Extension)) { return new ImagePreviewer(file); }