mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-24 02:02:29 -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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Attributes;
|
using Jellyfin.Api.Attributes;
|
||||||
using Jellyfin.Api.Constants;
|
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using MediaBrowser.Common.Api;
|
using MediaBrowser.Common.Api;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
@ -1993,7 +1992,7 @@ public class ImageController : BaseJellyfinApiController
|
|||||||
{
|
{
|
||||||
if (format.HasValue)
|
if (format.HasValue)
|
||||||
{
|
{
|
||||||
return new[] { format.Value };
|
return [format.Value];
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetClientSupportedFormats();
|
return GetClientSupportedFormats();
|
||||||
|
@ -28,6 +28,11 @@ namespace MediaBrowser.Model.Drawing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The webp.
|
/// The webp.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Webp
|
Webp,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The svg format.
|
||||||
|
/// </summary>
|
||||||
|
Svg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ public static class ImageFormatExtensions
|
|||||||
ImageFormat.Jpg => MediaTypeNames.Image.Jpeg,
|
ImageFormat.Jpg => MediaTypeNames.Image.Jpeg,
|
||||||
ImageFormat.Png => "image/png",
|
ImageFormat.Png => "image/png",
|
||||||
ImageFormat.Webp => "image/webp",
|
ImageFormat.Webp => "image/webp",
|
||||||
|
ImageFormat.Svg => "image/svg+xml",
|
||||||
_ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat))
|
_ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat))
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ public static class ImageFormatExtensions
|
|||||||
ImageFormat.Jpg => ".jpg",
|
ImageFormat.Jpg => ".jpg",
|
||||||
ImageFormat.Png => ".png",
|
ImageFormat.Png => ".png",
|
||||||
ImageFormat.Webp => ".webp",
|
ImageFormat.Webp => ".webp",
|
||||||
|
ImageFormat.Svg => ".svg",
|
||||||
_ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat))
|
_ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ namespace Jellyfin.Drawing.Skia;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class SkiaEncoder : IImageEncoder
|
public class SkiaEncoder : IImageEncoder
|
||||||
{
|
{
|
||||||
|
private const string SvgFormat = "svg";
|
||||||
private static readonly HashSet<string> _transparentImageTypes = new(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
|
private static readonly HashSet<string> _transparentImageTypes = new(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
|
||||||
|
|
||||||
private readonly ILogger<SkiaEncoder> _logger;
|
private readonly ILogger<SkiaEncoder> _logger;
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
private static readonly SKImageFilter _imageFilter;
|
private static readonly SKImageFilter _imageFilter;
|
||||||
@ -89,12 +89,13 @@ public class SkiaEncoder : IImageEncoder
|
|||||||
// working on windows at least
|
// working on windows at least
|
||||||
"cr2",
|
"cr2",
|
||||||
"nef",
|
"nef",
|
||||||
"arw"
|
"arw",
|
||||||
|
SvgFormat
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
|
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>
|
/// <summary>
|
||||||
/// Check if the native lib is available.
|
/// Check if the native lib is available.
|
||||||
@ -312,6 +313,31 @@ public class SkiaEncoder : IImageEncoder
|
|||||||
return Decode(path, false, orientation, out _);
|
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)
|
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
|
||||||
{
|
{
|
||||||
var needsFlip = origin is SKEncodedOrigin.LeftBottom or SKEncodedOrigin.LeftTop or SKEncodedOrigin.RightBottom or SKEncodedOrigin.RightTop;
|
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;
|
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 skiaOutputFormat = GetImageFormat(outputFormat);
|
||||||
|
|
||||||
var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor);
|
var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor);
|
||||||
@ -409,7 +441,10 @@ public class SkiaEncoder : IImageEncoder
|
|||||||
var blur = options.Blur ?? 0;
|
var blur = options.Blur ?? 0;
|
||||||
var hasIndicator = options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(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)
|
if (bitmap is null)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException($"Skia unable to read image {inputPath}");
|
throw new InvalidDataException($"Skia unable to read image {inputPath}");
|
||||||
|
@ -27,7 +27,7 @@ public static class ImageFormatExtensionsTests
|
|||||||
[InlineData((ImageFormat)int.MinValue)]
|
[InlineData((ImageFormat)int.MinValue)]
|
||||||
[InlineData((ImageFormat)int.MaxValue)]
|
[InlineData((ImageFormat)int.MaxValue)]
|
||||||
[InlineData((ImageFormat)(-1))]
|
[InlineData((ImageFormat)(-1))]
|
||||||
[InlineData((ImageFormat)5)]
|
[InlineData((ImageFormat)6)]
|
||||||
public static void GetMimeType_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format)
|
public static void GetMimeType_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format)
|
||||||
=> Assert.Throws<InvalidEnumArgumentException>(() => format.GetMimeType());
|
=> Assert.Throws<InvalidEnumArgumentException>(() => format.GetMimeType());
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ public static class ImageFormatExtensionsTests
|
|||||||
[InlineData((ImageFormat)int.MinValue)]
|
[InlineData((ImageFormat)int.MinValue)]
|
||||||
[InlineData((ImageFormat)int.MaxValue)]
|
[InlineData((ImageFormat)int.MaxValue)]
|
||||||
[InlineData((ImageFormat)(-1))]
|
[InlineData((ImageFormat)(-1))]
|
||||||
[InlineData((ImageFormat)5)]
|
[InlineData((ImageFormat)6)]
|
||||||
public static void GetExtension_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format)
|
public static void GetExtension_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format)
|
||||||
=> Assert.Throws<InvalidEnumArgumentException>(() => format.GetExtension());
|
=> Assert.Throws<InvalidEnumArgumentException>(() => format.GetExtension());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user