mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-10-31 10:37:22 -04:00 
			
		
		
		
	Add support for converting from svg to other image types
This commit is contained in:
		
							parent
							
								
									4f0f364ac9
								
							
						
					
					
						commit
						c5e723bccd
					
				| @ -11,7 +11,6 @@ using System.Security.Cryptography; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Jellyfin.Api.Attributes; | ||||
| using Jellyfin.Api.Constants; | ||||
| using Jellyfin.Api.Helpers; | ||||
| using MediaBrowser.Common.Api; | ||||
| using MediaBrowser.Common.Configuration; | ||||
| @ -1993,7 +1992,7 @@ public class ImageController : BaseJellyfinApiController | ||||
|     { | ||||
|         if (format.HasValue) | ||||
|         { | ||||
|             return new[] { format.Value }; | ||||
|             return [format.Value]; | ||||
|         } | ||||
| 
 | ||||
|         return GetClientSupportedFormats(); | ||||
|  | ||||
| @ -28,6 +28,11 @@ namespace MediaBrowser.Model.Drawing | ||||
|         /// <summary> | ||||
|         /// The webp. | ||||
|         /// </summary> | ||||
|         Webp | ||||
|         Webp, | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The svg format. | ||||
|         /// </summary> | ||||
|         Svg, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -22,6 +22,7 @@ public static class ImageFormatExtensions | ||||
|             ImageFormat.Jpg => MediaTypeNames.Image.Jpeg, | ||||
|             ImageFormat.Png => "image/png", | ||||
|             ImageFormat.Webp => "image/webp", | ||||
|             ImageFormat.Svg => "image/svg+xml", | ||||
|             _ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat)) | ||||
|         }; | ||||
| 
 | ||||
| @ -39,6 +40,7 @@ public static class ImageFormatExtensions | ||||
|             ImageFormat.Jpg => ".jpg", | ||||
|             ImageFormat.Png => ".png", | ||||
|             ImageFormat.Webp => ".webp", | ||||
|             ImageFormat.Svg => ".svg", | ||||
|             _ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat)) | ||||
|         }; | ||||
| } | ||||
|  | ||||
| @ -19,8 +19,8 @@ namespace Jellyfin.Drawing.Skia; | ||||
| /// </summary> | ||||
| public class SkiaEncoder : IImageEncoder | ||||
| { | ||||
|     private const string SvgFormat = "svg"; | ||||
|     private static readonly HashSet<string> _transparentImageTypes = new(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; | ||||
| 
 | ||||
|     private readonly ILogger<SkiaEncoder> _logger; | ||||
|     private readonly IApplicationPaths _appPaths; | ||||
|     private static readonly SKImageFilter _imageFilter; | ||||
| @ -89,12 +89,13 @@ public class SkiaEncoder : IImageEncoder | ||||
|             // working on windows at least | ||||
|             "cr2", | ||||
|             "nef", | ||||
|             "arw" | ||||
|             "arw", | ||||
|             SvgFormat | ||||
|         }; | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public IReadOnlyCollection<ImageFormat> SupportedOutputFormats | ||||
|         => new HashSet<ImageFormat> { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; | ||||
|         => new HashSet<ImageFormat> { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png, ImageFormat.Svg }; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Check if the native lib is available. | ||||
| @ -312,6 +313,31 @@ public class SkiaEncoder : IImageEncoder | ||||
|         return Decode(path, false, orientation, out _); | ||||
|     } | ||||
| 
 | ||||
|     private SKBitmap? GetBitmapFromSvg(string path) | ||||
|     { | ||||
|         if (!File.Exists(path)) | ||||
|         { | ||||
|             throw new FileNotFoundException("File not found", path); | ||||
|         } | ||||
| 
 | ||||
|         using var svg = SKSvg.CreateFromFile(path); | ||||
|         if (svg.Drawable is null) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         var width = (int)Math.Round(svg.Drawable.Bounds.Width); | ||||
|         var height = (int)Math.Round(svg.Drawable.Bounds.Height); | ||||
| 
 | ||||
|         var bitmap = new SKBitmap(width, height); | ||||
|         using var canvas = new SKCanvas(bitmap); | ||||
|         canvas.DrawPicture(svg.Picture); | ||||
|         canvas.Flush(); | ||||
|         canvas.Save(); | ||||
| 
 | ||||
|         return bitmap; | ||||
|     } | ||||
| 
 | ||||
|     private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) | ||||
|     { | ||||
|         var needsFlip = origin is SKEncodedOrigin.LeftBottom or SKEncodedOrigin.LeftTop or SKEncodedOrigin.RightBottom or SKEncodedOrigin.RightTop; | ||||
| @ -402,6 +428,12 @@ public class SkiaEncoder : IImageEncoder | ||||
|             return inputPath; | ||||
|         } | ||||
| 
 | ||||
|         if (outputFormat == ImageFormat.Svg | ||||
|             && !inputFormat.Equals(SvgFormat, StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Requested svg output from {inputFormat} input"); | ||||
|         } | ||||
| 
 | ||||
|         var skiaOutputFormat = GetImageFormat(outputFormat); | ||||
| 
 | ||||
|         var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor); | ||||
| @ -409,7 +441,10 @@ public class SkiaEncoder : IImageEncoder | ||||
|         var blur = options.Blur ?? 0; | ||||
|         var hasIndicator = options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0); | ||||
| 
 | ||||
|         using var bitmap = GetBitmap(inputPath, autoOrient, orientation); | ||||
|         using var bitmap = inputFormat.Equals(SvgFormat, StringComparison.OrdinalIgnoreCase) | ||||
|             ? GetBitmapFromSvg(inputPath) | ||||
|             : GetBitmap(inputPath, autoOrient, orientation); | ||||
| 
 | ||||
|         if (bitmap is null) | ||||
|         { | ||||
|             throw new InvalidDataException($"Skia unable to read image {inputPath}"); | ||||
|  | ||||
| @ -27,7 +27,7 @@ public static class ImageFormatExtensionsTests | ||||
|     [InlineData((ImageFormat)int.MinValue)] | ||||
|     [InlineData((ImageFormat)int.MaxValue)] | ||||
|     [InlineData((ImageFormat)(-1))] | ||||
|     [InlineData((ImageFormat)5)] | ||||
|     [InlineData((ImageFormat)6)] | ||||
|     public static void GetMimeType_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format) | ||||
|         => Assert.Throws<InvalidEnumArgumentException>(() => format.GetMimeType()); | ||||
| 
 | ||||
| @ -40,7 +40,7 @@ public static class ImageFormatExtensionsTests | ||||
|     [InlineData((ImageFormat)int.MinValue)] | ||||
|     [InlineData((ImageFormat)int.MaxValue)] | ||||
|     [InlineData((ImageFormat)(-1))] | ||||
|     [InlineData((ImageFormat)5)] | ||||
|     [InlineData((ImageFormat)6)] | ||||
|     public static void GetExtension_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format) | ||||
|         => Assert.Throws<InvalidEnumArgumentException>(() => format.GetExtension()); | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user