[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:
Pedro Lamas
2023-12-18 15:54:17 +00:00
committed by GitHub
parent ee22581913
commit 4c3e5348f0
22 changed files with 294 additions and 205 deletions

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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",
};
}
}