mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
[Peek]Adds QOI file support (#29919)
* Adds QOI support to Peek * Reduce allocations on QoiImage * Add to QOI to Peek's NOTICE as well. * Ensure file stream is closed after reading QOI
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// 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.Buffers.Binary;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -77,6 +77,31 @@ namespace Peek.Common.Extensions
|
||||
return size;
|
||||
}
|
||||
|
||||
public static Size? GetQoiSize(this IFileSystemItem item)
|
||||
{
|
||||
Size? size = null;
|
||||
using (FileStream stream = new FileStream(item.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
|
||||
{
|
||||
if (stream.Length >= 12)
|
||||
{
|
||||
stream.Position = 4;
|
||||
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
uint widthValue = BinaryPrimitives.ReadUInt32BigEndian(reader.ReadBytes(4));
|
||||
uint heightValue = BinaryPrimitives.ReadUInt32BigEndian(reader.ReadBytes(4));
|
||||
|
||||
if (widthValue > 0 && heightValue > 0)
|
||||
{
|
||||
size = new Size(widthValue, heightValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public static ulong GetSizeInBytes(this IFileSystemItem item)
|
||||
{
|
||||
ulong sizeInBytes = 0;
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
public static async Task<BitmapSource> GetBitmapFromHBitmapAsync(IntPtr hbitmap, bool isSupportingTransparency, CancellationToken cancellationToken)
|
||||
{
|
||||
Bitmap? bitmap = null;
|
||||
Bitmap? tempBitmapForDeletion = null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -26,15 +25,29 @@ namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
return await BitmapToImageSource(bitmap, isSupportingTransparency, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap?.Dispose();
|
||||
|
||||
// delete HBitmap to avoid memory leaks
|
||||
NativeMethods.DeleteObject(hbitmap);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<BitmapSource> BitmapToImageSource(Bitmap bitmap, bool isSupportingTransparency, CancellationToken cancellationToken)
|
||||
{
|
||||
Bitmap? transparentBitmap = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (isSupportingTransparency && bitmap.PixelFormat == PixelFormat.Format32bppRgb)
|
||||
{
|
||||
var bitmapRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
|
||||
var bitmapData = bitmap.LockBits(bitmapRectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
|
||||
|
||||
var transparentBitmap = new Bitmap(bitmapData.Width, bitmapData.Height, bitmapData.Stride, PixelFormat.Format32bppArgb, bitmapData.Scan0);
|
||||
|
||||
// Can't dispose of original bitmap yet as that causes crashes on png files. Saving it for later disposal after saving to stream.
|
||||
tempBitmapForDeletion = bitmap;
|
||||
transparentBitmap = new Bitmap(bitmapData.Width, bitmapData.Height, bitmapData.Stride, PixelFormat.Format32bppArgb, bitmapData.Scan0);
|
||||
bitmap = transparentBitmap;
|
||||
}
|
||||
|
||||
@@ -54,11 +67,7 @@ namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap?.Dispose();
|
||||
tempBitmapForDeletion?.Dispose();
|
||||
|
||||
// delete HBitmap to avoid memory leaks
|
||||
NativeMethods.DeleteObject(hbitmap);
|
||||
transparentBitmap?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.FilePreviewCommon;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
@@ -91,6 +92,14 @@ namespace Peek.FilePreviewer.Previewers
|
||||
ImageSize = size.Value;
|
||||
}
|
||||
}
|
||||
else if (IsQoi(Item))
|
||||
{
|
||||
var size = await Task.Run(Item.GetQoiSize);
|
||||
if (size != null)
|
||||
{
|
||||
ImageSize = size.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImageSize = await Task.Run(Item.GetImageSize);
|
||||
@@ -257,6 +266,12 @@ namespace Peek.FilePreviewer.Previewers
|
||||
|
||||
Preview = source;
|
||||
}
|
||||
else if (IsQoi(Item))
|
||||
{
|
||||
using var bitmap = QoiImage.FromStream(stream);
|
||||
|
||||
Preview = await BitmapHelper.BitmapToImageSource(bitmap, true, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
@@ -286,6 +301,11 @@ namespace Peek.FilePreviewer.Previewers
|
||||
return item.Extension == ".svg";
|
||||
}
|
||||
|
||||
private bool IsQoi(IFileSystemItem item)
|
||||
{
|
||||
return item.Extension == ".qoi";
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
lowQualityThumbnailPreview = null;
|
||||
@@ -367,6 +387,8 @@ namespace Peek.FilePreviewer.Previewers
|
||||
".cr3",
|
||||
|
||||
".svg",
|
||||
|
||||
".qoi",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user