10
10
using CommunityToolkit . Mvvm . DependencyInjection ;
11
11
using Flow . Launcher . Infrastructure . Storage ;
12
12
using Flow . Launcher . Plugin ;
13
+ using SharpVectors . Converters ;
14
+ using SharpVectors . Renderers . Wpf ;
13
15
14
16
namespace Flow . Launcher . Infrastructure . Image
15
17
{
@@ -32,8 +34,10 @@ public static class ImageLoader
32
34
public static ImageSource LoadingImage { get ; } = new BitmapImage ( new Uri ( Constant . LoadingImgIcon ) ) ;
33
35
public const int SmallIconSize = 64 ;
34
36
public const int FullIconSize = 256 ;
37
+ public const int FullImageSize = 320 ;
35
38
36
39
private static readonly string [ ] ImageExtensions = { ".png" , ".jpg" , ".jpeg" , ".gif" , ".bmp" , ".tiff" , ".ico" } ;
40
+ private static readonly string SvgExtension = ".svg" ;
37
41
38
42
public static async Task InitializeAsync ( )
39
43
{
@@ -235,10 +239,11 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
235
239
image = LoadFullImage ( path ) ;
236
240
type = ImageType . FullImageFile ;
237
241
}
238
- catch ( NotSupportedException )
242
+ catch ( NotSupportedException ex )
239
243
{
240
244
image = Image ;
241
245
type = ImageType . Error ;
246
+ API . LogException ( ClassName , $ "Failed to load image file from path { path } : { ex . Message } ", ex ) ;
242
247
}
243
248
}
244
249
else
@@ -251,6 +256,20 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
251
256
image = GetThumbnail ( path , ThumbnailOptions . ThumbnailOnly ) ;
252
257
}
253
258
}
259
+ else if ( extension == SvgExtension )
260
+ {
261
+ try
262
+ {
263
+ image = LoadSvgImage ( path , loadFullImage ) ;
264
+ type = ImageType . FullImageFile ;
265
+ }
266
+ catch ( System . Exception ex )
267
+ {
268
+ image = Image ;
269
+ type = ImageType . Error ;
270
+ API . LogException ( ClassName , $ "Failed to load SVG image from path { path } : { ex . Message } ", ex ) ;
271
+ }
272
+ }
254
273
else
255
274
{
256
275
type = ImageType . File ;
@@ -324,7 +343,7 @@ public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullI
324
343
return img ;
325
344
}
326
345
327
- private static BitmapImage LoadFullImage ( string path )
346
+ private static ImageSource LoadFullImage ( string path )
328
347
{
329
348
BitmapImage image = new BitmapImage ( ) ;
330
349
image . BeginInit ( ) ;
@@ -333,24 +352,24 @@ private static BitmapImage LoadFullImage(string path)
333
352
image . CreateOptions = BitmapCreateOptions . IgnoreColorProfile ;
334
353
image . EndInit ( ) ;
335
354
336
- if ( image . PixelWidth > 320 )
355
+ if ( image . PixelWidth > FullImageSize )
337
356
{
338
357
BitmapImage resizedWidth = new BitmapImage ( ) ;
339
358
resizedWidth . BeginInit ( ) ;
340
359
resizedWidth . CacheOption = BitmapCacheOption . OnLoad ;
341
360
resizedWidth . UriSource = new Uri ( path ) ;
342
361
resizedWidth . CreateOptions = BitmapCreateOptions . IgnoreColorProfile ;
343
- resizedWidth . DecodePixelWidth = 320 ;
362
+ resizedWidth . DecodePixelWidth = FullImageSize ;
344
363
resizedWidth . EndInit ( ) ;
345
364
346
- if ( resizedWidth . PixelHeight > 320 )
365
+ if ( resizedWidth . PixelHeight > FullImageSize )
347
366
{
348
367
BitmapImage resizedHeight = new BitmapImage ( ) ;
349
368
resizedHeight . BeginInit ( ) ;
350
369
resizedHeight . CacheOption = BitmapCacheOption . OnLoad ;
351
370
resizedHeight . UriSource = new Uri ( path ) ;
352
371
resizedHeight . CreateOptions = BitmapCreateOptions . IgnoreColorProfile ;
353
- resizedHeight . DecodePixelHeight = 320 ;
372
+ resizedHeight . DecodePixelHeight = FullImageSize ;
354
373
resizedHeight . EndInit ( ) ;
355
374
return resizedHeight ;
356
375
}
@@ -360,5 +379,50 @@ private static BitmapImage LoadFullImage(string path)
360
379
361
380
return image ;
362
381
}
382
+
383
+ private static ImageSource LoadSvgImage ( string path , bool loadFullImage = false )
384
+ {
385
+ // Set up drawing settings
386
+ var desiredHeight = loadFullImage ? FullImageSize : SmallIconSize ;
387
+ var drawingSettings = new WpfDrawingSettings
388
+ {
389
+ IncludeRuntime = true ,
390
+ // Set IgnoreRootViewbox to false to respect the SVG's viewBox
391
+ IgnoreRootViewbox = false
392
+ } ;
393
+
394
+ // Load and render the SVG
395
+ var converter = new FileSvgReader ( drawingSettings ) ;
396
+ var drawing = converter . Read ( new Uri ( path ) ) ;
397
+
398
+ // Calculate scale to achieve desired height
399
+ var drawingBounds = drawing . Bounds ;
400
+ if ( drawingBounds . Height <= 0 )
401
+ {
402
+ throw new InvalidOperationException ( $ "Invalid SVG dimensions: Height must be greater than zero in { path } ") ;
403
+ }
404
+ var scale = desiredHeight / drawingBounds . Height ;
405
+ var scaledWidth = drawingBounds . Width * scale ;
406
+ var scaledHeight = drawingBounds . Height * scale ;
407
+
408
+ // Convert the Drawing to a Bitmap
409
+ var drawingVisual = new DrawingVisual ( ) ;
410
+ using ( DrawingContext drawingContext = drawingVisual . RenderOpen ( ) )
411
+ {
412
+ drawingContext . PushTransform ( new ScaleTransform ( scale , scale ) ) ;
413
+ drawingContext . DrawDrawing ( drawing ) ;
414
+ }
415
+
416
+ // Create a RenderTargetBitmap to hold the rendered image
417
+ var bitmap = new RenderTargetBitmap (
418
+ ( int ) Math . Ceiling ( scaledWidth ) ,
419
+ ( int ) Math . Ceiling ( scaledHeight ) ,
420
+ 96 , // DpiX
421
+ 96 , // DpiY
422
+ PixelFormats . Pbgra32 ) ;
423
+ bitmap . Render ( drawingVisual ) ;
424
+
425
+ return bitmap ;
426
+ }
363
427
}
364
428
}
0 commit comments