Skip to content

Commit 4fe381f

Browse files
committed
Adds hardware acceleration for SVG icons
Adds hardware acceleration support for SVG icons on Android to improve rendering performance. - Introduces a `HardwareAcceleration` property to `NoCacheFileImageSource` for enabling/disabling hardware acceleration. - Modifies `ImageSourceExtensions` to pass hardware acceleration flag from the element to the `IconCache`. - Implements logic to detect and conditionally enable hardware bitmaps based on system capabilities and memory constraints.
1 parent 1544358 commit 4fe381f

File tree

6 files changed

+111
-57
lines changed

6 files changed

+111
-57
lines changed

AuroraControlsMaui/Extensions/ImageSourceExtensions.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,36 +266,36 @@ public static UriImageSource AsAsyncUriImageSource(this Task<UriImageSource> ima
266266

267267
public static Button SetSvgIcon(this Button imageElement, string svgName, double squareSize = 24d, Color? colorOverride = null) =>
268268
IconCache
269-
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride)
269+
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride, hardwareAcceleration: imageElement.SupportsHardwareAcceleration())
270270
.AsAsyncSourceFor(imageElement);
271271

272272
public static ImageButton SetSvgIcon(this ImageButton imageButton, string svgName, double squareSize = 24d, Color? colorOverride = null)
273273
{
274274
IconCache
275-
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride)
276-
.AsAsyncSourceFor(x => imageButton.Source = x);
275+
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride, hardwareAcceleration: imageButton.SupportsHardwareAcceleration())
276+
.AsAsyncSourceFor(imageButton);
277277

278278
return imageButton;
279279
}
280280

281281
public static ToolbarItem SetSvgIcon(this ToolbarItem toolbarItem, string svgName, double squareSize = 24d, Color? colorOverride = null) =>
282282
IconCache
283-
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride)
283+
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride, hardwareAcceleration: toolbarItem.SupportsHardwareAcceleration())
284284
.AsAsyncSourceFor(toolbarItem);
285285

286286
public static MenuItem SetSvgIcon(this MenuItem menuItem, string svgName, double squareSize = 24d, Color? colorOverride = null) =>
287287
IconCache
288-
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride)
288+
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride, hardwareAcceleration: menuItem.SupportsHardwareAcceleration())
289289
.AsAsyncSourceFor(menuItem);
290290

291291
public static Image SetSvgIcon(this Image image, string svgName, double squareSize = 24d, Color? colorOverride = null) =>
292292
IconCache
293-
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride)
293+
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride, hardwareAcceleration: image.SupportsHardwareAcceleration())
294294
.AsAsyncSourceFor(image);
295295

296296
public static Page SetSvgIcon(this Page page, string svgName, double squareSize = 24d, Color? colorOverride = null) =>
297297
IconCache
298-
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride)
298+
.ImageSourceFromSvg(svgName, squareSize, colorOverride: colorOverride, hardwareAcceleration: page.SupportsHardwareAcceleration())
299299
.AsAsyncSourceFor(page);
300300

301301
public static async Task<SKBitmap?> ToSKBitmapAsync(this ImageSource imageSource)
@@ -331,4 +331,19 @@ public static Page SetSvgIcon(this Page page, string svgName, double squareSize
331331
return SKBitmap.Decode(stream);
332332
}
333333
}
334+
335+
private static bool SupportsHardwareAcceleration(this Element view)
336+
{
337+
bool supportsHardwareAcceleration = true;
338+
/*
339+
#if ANDROID
340+
supportsHardwareAcceleration =
341+
view.Handler?.PlatformView is Android.Views.View
342+
{
343+
IsHardwareAccelerated: true,
344+
};
345+
#endif
346+
*/
347+
return supportsHardwareAcceleration;
348+
}
334349
}

AuroraControlsMaui/IIconCache.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ public interface IIconCache
1515
/// <param name="squareSize">The square size of the icon.</param>
1616
/// <param name="additionalCacheKey">Allows for setting an addiitonal cache key.</param>
1717
/// <param name="colorOverride">Allows for setting the color of the icon.</param>
18-
Task<Image> IconFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = default);
18+
/// <param name="hardwareAcceleration">Allows for setting the hardware acceleration.</param>
19+
Task<Image> IconFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = default, bool hardwareAcceleration = true);
1920

2021
/// <summary>
2122
/// Fetches an SVG icon by name.
@@ -25,7 +26,8 @@ public interface IIconCache
2526
/// <param name="size">A Size representing the desired size of the icon.</param>
2627
/// <param name="additionalCacheKey">Allows for setting an addiitonal cache key.</param>
2728
/// <param name="colorOverride">Allows for setting the color of the icon.</param>
28-
Task<Image> IconFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = default);
29+
/// <param name="hardwareAcceleration">Allows for setting the hardware acceleration.</param>
30+
Task<Image> IconFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = default, bool hardwareAcceleration = true);
2931

3032
/// <summary>
3133
/// Fetches an SVG source by name.
@@ -35,7 +37,8 @@ public interface IIconCache
3537
/// <param name="squareSize">The square size of the icon.</param>
3638
/// <param name="additionalCacheKey">Allows for setting an addiitonal cache key.</param>
3739
/// <param name="colorOverride">Allows for setting the color of the icon.</param>
38-
Task<ImageSource> ImageSourceFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = default);
40+
/// <param name="hardwareAcceleration">Allows for setting the hardware acceleration.</param>
41+
Task<ImageSource> ImageSourceFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = default, bool hardwareAcceleration = true);
3942

4043
/// <summary>
4144
/// Fetches an SVG icon by name.
@@ -45,7 +48,8 @@ public interface IIconCache
4548
/// <param name="size">A Size representing the desired size of the icon.</param>
4649
/// <param name="additionalCacheKey">Allows for setting an addiitonal cache key.</param>
4750
/// <param name="colorOverride">Allows for setting the color of the icon.</param>
48-
Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = default);
51+
/// <param name="hardwareAcceleration">Allows for setting the hardware acceleration.</param>
52+
Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = default, bool hardwareAcceleration = true);
4953

5054
/// <summary>
5155
/// Fetches an SVG icon by name.
@@ -56,7 +60,8 @@ public interface IIconCache
5660
/// <param name="squareSize">A double representing the desired size of the icon.</param>
5761
/// <param name="additionalCacheKey">Allows for setting an addiitonal cache key.</param>
5862
/// <param name="colorOverride">Allows for setting the color of the icon.</param>
59-
Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = default);
63+
/// <param name="hardwareAcceleration">Allows for setting the hardware acceleration.</param>
64+
Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = default, bool hardwareAcceleration = true);
6065

6166
/// <summary>
6267
/// Fetches an SVG icon by name.
@@ -67,7 +72,8 @@ public interface IIconCache
6772
/// <param name="size">A Size representing the desired size of the icon.</param>
6873
/// <param name="additionalCacheKey">Allows for setting an addiitonal cache key.</param>
6974
/// <param name="colorOverride">Allows for setting the color of the icon.</param>
70-
Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, Size size, string additionalCacheKey = "", Color? colorOverride = default);
75+
/// <param name="hardwareAcceleration">Allows for setting the hardware acceleration.</param>
76+
Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, Size size, string additionalCacheKey = "", Color? colorOverride = default, bool hardwareAcceleration = true);
7177

7278
/// <summary>
7379
/// Loads the assembly.

AuroraControlsMaui/INoCacheFileImageSource.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ namespace AuroraControls;
33
internal interface INoCacheFileImageSource : IImageSource
44
{
55
string File { get; }
6+
7+
bool HardwareAcceleration { get; }
68
}

AuroraControlsMaui/IconCacheBase.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,19 @@ public IconCacheBase()
2828
_platformScalingFactor = (float)PlatformInfo.ScalingFactor;
2929
}
3030

31-
public Task<Image> IconFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = null) => IconFromSvg(svgName, new Size(squareSize, squareSize), additionalCacheKey, colorOverride);
31+
public Task<Image> IconFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = null, bool hardwareAcceleration = true) =>
32+
IconFromSvg(svgName, new Size(squareSize, squareSize), additionalCacheKey, colorOverride, hardwareAcceleration);
3233

33-
public async Task<Image> IconFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = null) =>
34+
public async Task<Image> IconFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = null, bool hardwareAcceleration = true) =>
3435
new()
3536
{
36-
Source = await ImageSourceFromSvg(svgName, size, additionalCacheKey, colorOverride),
37+
Source = await ImageSourceFromSvg(svgName, size, additionalCacheKey, colorOverride, hardwareAcceleration),
3738
};
3839

39-
public Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = null) => ImageSourceFromRawSvg(svgName, svgValue, new Size(squareSize, squareSize), additionalCacheKey, colorOverride);
40+
public Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = null, bool hardwareAcceleration = true) =>
41+
ImageSourceFromRawSvg(svgName, svgValue, new Size(squareSize, squareSize), additionalCacheKey, colorOverride, hardwareAcceleration);
4042

41-
public async Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, Size size, string additionalCacheKey = "", Color? colorOverride = null)
43+
public async Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgValue, Size size, string additionalCacheKey = "", Color? colorOverride = null, bool hardwareAcceleration = true)
4244
{
4345
try
4446
{
@@ -48,15 +50,15 @@ public async Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgV
4850

4951
if (_resolvedIcons.ContainsKey(key))
5052
{
51-
return GetPlatformImageSource(_resolvedIcons[key]);
53+
return GetPlatformImageSource(_resolvedIcons[key], hardwareAcceleration);
5254
}
5355

5456
string? diskCachedImage = GetImagePathFromDiskCache(key);
5557

5658
if (!string.IsNullOrEmpty(diskCachedImage))
5759
{
5860
_resolvedIcons[key] = diskCachedImage;
59-
return GetPlatformImageSource(diskCachedImage);
61+
return GetPlatformImageSource(diskCachedImage, hardwareAcceleration);
6062
}
6163

6264
await GenerateImageFromRaw(key, svgValue, size, colorOverride).ConfigureAwait(false);
@@ -65,18 +67,18 @@ public async Task<ImageSource> ImageSourceFromRawSvg(string svgName, string svgV
6567

6668
_resolvedIcons[key] = diskCachedImage;
6769

68-
return GetPlatformImageSource(diskCachedImage);
70+
return GetPlatformImageSource(diskCachedImage, hardwareAcceleration);
6971
}
7072
finally
7173
{
7274
_iconLock.Release();
7375
}
7476
}
7577

76-
public Task<ImageSource> ImageSourceFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = null) =>
77-
ImageSourceFromSvg(svgName, new Size(squareSize, squareSize), additionalCacheKey, colorOverride);
78+
public Task<ImageSource> ImageSourceFromSvg(string svgName, double squareSize = 22d, string additionalCacheKey = "", Color? colorOverride = null, bool hardwareAcceleration = true) =>
79+
ImageSourceFromSvg(svgName, new Size(squareSize, squareSize), additionalCacheKey, colorOverride, hardwareAcceleration);
7880

79-
public async Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = null)
81+
public async Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, string additionalCacheKey = "", Color? colorOverride = null, bool hardwareAcceleration = true)
8082
{
8183
try
8284
{
@@ -86,15 +88,15 @@ public async Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, str
8688

8789
if (_resolvedIcons.ContainsKey(key))
8890
{
89-
return GetPlatformImageSource(_resolvedIcons[key]);
91+
return GetPlatformImageSource(_resolvedIcons[key], hardwareAcceleration);
9092
}
9193

9294
string? diskCachedImage = GetImagePathFromDiskCache(key);
9395

9496
if (!string.IsNullOrEmpty(diskCachedImage))
9597
{
9698
_resolvedIcons[key] = diskCachedImage;
97-
return GetPlatformImageSource(diskCachedImage);
99+
return GetPlatformImageSource(diskCachedImage, hardwareAcceleration);
98100
}
99101

100102
await GenerateImageFromEmbedded(key, svgName, size, colorOverride).ConfigureAwait(false);
@@ -103,7 +105,7 @@ public async Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, str
103105

104106
_resolvedIcons[key] = diskCachedImage;
105107

106-
return GetPlatformImageSource(diskCachedImage);
108+
return GetPlatformImageSource(diskCachedImage, hardwareAcceleration);
107109
}
108110
catch (Exception ex)
109111
{
@@ -117,11 +119,11 @@ public async Task<ImageSource> ImageSourceFromSvg(string svgName, Size size, str
117119
}
118120
}
119121

120-
private ImageSource GetPlatformImageSource(string? file = null)
122+
private ImageSource GetPlatformImageSource(string? file = null, bool hardwareAcceleration = true)
121123
{
122124
if (DeviceInfo.Current.Platform == DevicePlatform.Android)
123125
{
124-
return new NoCacheFileImageSource { File = file };
126+
return new NoCacheFileImageSource { File = file, HardwareAcceleration = hardwareAcceleration };
125127
}
126128

127129
return new FileImageSource { File = file };

AuroraControlsMaui/NoCacheFileImageSource.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ internal class NoCacheFileImageSource : ImageSource, INoCacheFileImageSource
44
{
55
public static readonly BindableProperty FileProperty = BindableProperty.Create(nameof(File), typeof(string), typeof(NoCacheFileImageSource));
66

7+
public static readonly BindableProperty HardwareAccelerationProperty = BindableProperty.Create(nameof(HardwareAcceleration), typeof(bool), typeof(NoCacheFileImageSource), defaultValue: true);
8+
79
public override bool IsEmpty => string.IsNullOrEmpty(File);
810

911
public string File
@@ -12,6 +14,12 @@ public string File
1214
set => SetValue(FileProperty, value);
1315
}
1416

17+
public bool HardwareAcceleration
18+
{
19+
get => (bool)GetValue(HardwareAccelerationProperty);
20+
set => SetValue(HardwareAccelerationProperty, value);
21+
}
22+
1523
public override Task<bool> Cancel() => Task.FromResult(false);
1624

1725
public override string ToString() => $"File: {File}";

0 commit comments

Comments
 (0)