Add synchronous icon source materialization

This commit is contained in:
Jiří Polášek
2026-08-09 07:14:36 +02:00
parent 998bfe85ea
commit 1dc528ba51

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.UI.Xaml.Controls;
@@ -48,7 +49,35 @@ internal static partial class IconPathConverter
return PreparedIcon.FromGlyph(glyph, family, targetSize > 0 ? targetSize : 8);
}
public static async Task<IconSource> CreateIconSourceAsync(PreparedIcon icon)
public static Task<IconSource> CreateIconSourceAsync(PreparedIcon icon)
{
try
{
if (TryCreateIconSourceSynchronously(icon, out var iconSource))
{
return Task.FromResult(iconSource);
}
return CompleteIconSourceCreationAsync(icon);
}
catch (Exception exception)
{
// Preserve async exception delivery for invalid callers now that this
// entry point no longer has an async state machine.
return Task.FromException<IconSource>(exception);
}
}
/// <summary>
/// Attempts to create an icon source without an asynchronous bitmap transfer.
/// </summary>
/// <returns>
/// <see langword="false"/> only when a populated binary icon must be transferred
/// to a <see cref="SoftwareBitmapSource"/> asynchronously.
/// </returns>
public static bool TryCreateIconSourceSynchronously(
PreparedIcon icon,
[MaybeNullWhen(false)] out IconSource iconSource)
{
try
{
@@ -60,7 +89,8 @@ internal static partial class IconPathConverter
DecodePixelWidth = icon.TargetSize > 0 ? icon.TargetSize : 0,
UriSource = icon.Uri!,
};
return new ImageIconSource { ImageSource = bitmap };
iconSource = new ImageIconSource { ImageSource = bitmap };
return true;
case PreparedIconKind.SvgUri:
var svg = new SvgImageSource(icon.Uri!);
@@ -69,23 +99,53 @@ internal static partial class IconPathConverter
svg.RasterizePixelWidth = icon.TargetSize;
}
return new ImageIconSource { ImageSource = svg };
iconSource = new ImageIconSource { ImageSource = svg };
return true;
case PreparedIconKind.Glyph:
return new FontIconSource
iconSource = new FontIconSource
{
FontFamily = new FontFamily(icon.FontFamily!),
FontSize = icon.TargetSize,
Glyph = icon.Glyph!,
};
return true;
case PreparedIconKind.Binary:
var softwareBitmap = icon.TakeSoftwareBitmap();
if (softwareBitmap is null)
if (icon.SoftwareBitmap is not null)
{
return new ImageIconSource();
iconSource = null!;
return false;
}
iconSource = new ImageIconSource();
return true;
default:
iconSource = CreateEmptyIconSource();
return true;
}
}
catch
{
iconSource = icon.Kind == PreparedIconKind.Binary
? new ImageIconSource()
: CreateEmptyIconSource();
return true;
}
}
/// <summary>
/// Completes icon-source creation after <see cref="TryCreateIconSourceSynchronously"/>
/// returned <see langword="false"/> for the same prepared icon.
/// </summary>
public static Task<IconSource> CompleteIconSourceCreationAsync(PreparedIcon icon) =>
icon.TakeSoftwareBitmap() is { } softwareBitmap
? CreateBinaryIconSourceAsync(softwareBitmap)
: Task.FromResult<IconSource>(new ImageIconSource());
private static async Task<IconSource> CreateBinaryIconSourceAsync(SoftwareBitmap softwareBitmap)
{
var ownershipTransferred = false;
try
{
@@ -110,6 +170,10 @@ internal static partial class IconPathConverter
throw;
}
}
catch
{
return new ImageIconSource();
}
finally
{
if (!ownershipTransferred)
@@ -117,17 +181,6 @@ internal static partial class IconPathConverter
softwareBitmap.Dispose();
}
}
default:
return CreateEmptyIconSource();
}
}
catch
{
return icon.Kind == PreparedIconKind.Binary
? new ImageIconSource()
: CreateEmptyIconSource();
}
}
// Keep the empty value non-null. A virtualized ListView can crash when a