diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index ede93aaa55..c5aadc8901 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -269,14 +269,24 @@ public class SkiaEncoder : IImageEncoder } // create the bitmap - var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack); + SKBitmap? bitmap = null; + try + { + bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack); - // decode - _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels()); + // decode + _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels()); - origin = codec.EncodedOrigin; + origin = codec.EncodedOrigin; - return bitmap; + return bitmap!; + } + catch (Exception e) + { + _logger.LogError(e, "Detected intermediary error decoding image {0}", path); + bitmap?.Dispose(); + throw; + } } var resultBitmap = SKBitmap.Decode(NormalizePath(path)); @@ -286,17 +296,26 @@ public class SkiaEncoder : IImageEncoder return Decode(path, true, orientation, out origin); } - // If we have to resize these they often end up distorted - if (resultBitmap.ColorType == SKColorType.Gray8) + try { - using (resultBitmap) + // If we have to resize these they often end up distorted + if (resultBitmap.ColorType == SKColorType.Gray8) { - return Decode(path, true, orientation, out origin); + using (resultBitmap) + { + return Decode(path, true, orientation, out origin); + } } - } - origin = SKEncodedOrigin.TopLeft; - return resultBitmap; + origin = SKEncodedOrigin.TopLeft; + return resultBitmap; + } + catch (Exception e) + { + _logger.LogError(e, "Detected intermediary error decoding image {0}", path); + resultBitmap?.Dispose(); + throw; + } } private SKBitmap? GetBitmap(string path, bool autoOrient, ImageOrientation? orientation) @@ -335,58 +354,78 @@ public class SkiaEncoder : IImageEncoder 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(); + SKBitmap? bitmap = null; + try + { + bitmap = new SKBitmap(width, height); + using var canvas = new SKCanvas(bitmap); + canvas.DrawPicture(svg.Picture); + canvas.Flush(); + canvas.Save(); - return bitmap; + return bitmap!; + } + catch (Exception e) + { + _logger.LogError(e, "Detected intermediary error extracting image {0}", path); + bitmap?.Dispose(); + throw; + } } private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) { var needsFlip = origin is SKEncodedOrigin.LeftBottom or SKEncodedOrigin.LeftTop or SKEncodedOrigin.RightBottom or SKEncodedOrigin.RightTop; - var rotated = needsFlip - ? new SKBitmap(bitmap.Height, bitmap.Width) - : new SKBitmap(bitmap.Width, bitmap.Height); - using var surface = new SKCanvas(rotated); - var midX = (float)rotated.Width / 2; - var midY = (float)rotated.Height / 2; - - switch (origin) + SKBitmap? rotated = null; + try { - case SKEncodedOrigin.TopRight: - surface.Scale(-1, 1, midX, midY); - break; - case SKEncodedOrigin.BottomRight: - surface.RotateDegrees(180, midX, midY); - break; - case SKEncodedOrigin.BottomLeft: - surface.Scale(1, -1, midX, midY); - break; - case SKEncodedOrigin.LeftTop: - surface.Translate(0, -rotated.Height); - surface.Scale(1, -1, midX, midY); - surface.RotateDegrees(-90); - break; - case SKEncodedOrigin.RightTop: - surface.Translate(rotated.Width, 0); - surface.RotateDegrees(90); - break; - case SKEncodedOrigin.RightBottom: - surface.Translate(rotated.Width, 0); - surface.Scale(1, -1, midX, midY); - surface.RotateDegrees(90); - break; - case SKEncodedOrigin.LeftBottom: - surface.Translate(0, rotated.Height); - surface.RotateDegrees(-90); - break; - } + rotated = needsFlip + ? new SKBitmap(bitmap.Height, bitmap.Width) + : new SKBitmap(bitmap.Width, bitmap.Height); + using var surface = new SKCanvas(rotated); + var midX = (float)rotated.Width / 2; + var midY = (float)rotated.Height / 2; - surface.DrawBitmap(bitmap, 0, 0); - return rotated; + switch (origin) + { + case SKEncodedOrigin.TopRight: + surface.Scale(-1, 1, midX, midY); + break; + case SKEncodedOrigin.BottomRight: + surface.RotateDegrees(180, midX, midY); + break; + case SKEncodedOrigin.BottomLeft: + surface.Scale(1, -1, midX, midY); + break; + case SKEncodedOrigin.LeftTop: + surface.Translate(0, -rotated.Height); + surface.Scale(1, -1, midX, midY); + surface.RotateDegrees(-90); + break; + case SKEncodedOrigin.RightTop: + surface.Translate(rotated.Width, 0); + surface.RotateDegrees(90); + break; + case SKEncodedOrigin.RightBottom: + surface.Translate(rotated.Width, 0); + surface.Scale(1, -1, midX, midY); + surface.RotateDegrees(90); + break; + case SKEncodedOrigin.LeftBottom: + surface.Translate(0, rotated.Height); + surface.RotateDegrees(-90); + break; + } + + surface.DrawBitmap(bitmap, 0, 0); + return rotated; + } + catch (Exception e) + { + _logger.LogError(e, "Detected intermediary error rotating image"); + rotated?.Dispose(); + throw; + } } /// @@ -562,7 +601,7 @@ public class SkiaEncoder : IImageEncoder // Only generate the splash screen if we have at least one poster and at least one backdrop/thumbnail. if (posters.Count > 0 && backdrops.Count > 0) { - var splashBuilder = new SplashscreenBuilder(this); + var splashBuilder = new SplashscreenBuilder(this, _logger); var outputPath = Path.Combine(_appPaths.DataPath, "splashscreen.png"); splashBuilder.GenerateSplash(posters, backdrops, outputPath); } diff --git a/src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs b/src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs index 9905566230..7af77758b4 100644 --- a/src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs +++ b/src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.Extensions.Logging; using SkiaSharp; namespace Jellyfin.Drawing.Skia; @@ -18,14 +19,17 @@ public class SplashscreenBuilder private const int Spacing = 20; private readonly SkiaEncoder _skiaEncoder; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The SkiaEncoder. - public SplashscreenBuilder(SkiaEncoder skiaEncoder) + /// The logger. + public SplashscreenBuilder(SkiaEncoder skiaEncoder, ILogger logger) { _skiaEncoder = skiaEncoder; + _logger = logger; } /// @@ -55,65 +59,76 @@ public class SplashscreenBuilder var posterIndex = 0; var backdropIndex = 0; - var bitmap = new SKBitmap(WallWidth, WallHeight); - using var canvas = new SKCanvas(bitmap); - canvas.Clear(SKColors.Black); - - int posterHeight = WallHeight / 6; - - for (int i = 0; i < Rows; i++) + SKBitmap? bitmap = null; + try { - int imageCounter = Random.Shared.Next(0, 5); - int currentWidthPos = i * 75; - int currentHeight = i * (posterHeight + Spacing); + bitmap = new SKBitmap(WallWidth, WallHeight); + using var canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.Black); - while (currentWidthPos < WallWidth) + int posterHeight = WallHeight / 6; + + for (int i = 0; i < Rows; i++) { - SKBitmap? currentImage; + int imageCounter = Random.Shared.Next(0, 5); + int currentWidthPos = i * 75; + int currentHeight = i * (posterHeight + Spacing); - switch (imageCounter) + while (currentWidthPos < WallWidth) { - case 0: - case 2: - case 3: - currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, posters, posterIndex, out int newPosterIndex); - posterIndex = newPosterIndex; - break; - default: - currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrops, backdropIndex, out int newBackdropIndex); - backdropIndex = newBackdropIndex; - break; - } + SKBitmap? currentImage; - if (currentImage is null) - { - throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!"); - } + switch (imageCounter) + { + case 0: + case 2: + case 3: + currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, posters, posterIndex, out int newPosterIndex); + posterIndex = newPosterIndex; + break; + default: + currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrops, backdropIndex, out int newBackdropIndex); + backdropIndex = newBackdropIndex; + break; + } - // resize to the same aspect as the original - var imageWidth = Math.Abs(posterHeight * currentImage.Width / currentImage.Height); - using var resizedBitmap = new SKBitmap(imageWidth, posterHeight); - currentImage.ScalePixels(resizedBitmap, SKFilterQuality.High); + if (currentImage is null) + { + throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!"); + } - // draw on canvas - canvas.DrawBitmap(resizedBitmap, currentWidthPos, currentHeight); + using (currentImage) + { + var imageWidth = Math.Abs(posterHeight * currentImage.Width / currentImage.Height); + using var resizedBitmap = new SKBitmap(imageWidth, posterHeight); + currentImage.ScalePixels(resizedBitmap, SKFilterQuality.High); - currentWidthPos += imageWidth + Spacing; + // draw on canvas + canvas.DrawBitmap(resizedBitmap, currentWidthPos, currentHeight); - currentImage.Dispose(); + // resize to the same aspect as the original + currentWidthPos += imageWidth + Spacing; + } - if (imageCounter >= 4) - { - imageCounter = 0; - } - else - { - imageCounter++; + if (imageCounter >= 4) + { + imageCounter = 0; + } + else + { + imageCounter++; + } } } - } - return bitmap; + return bitmap; + } + catch (Exception e) + { + _logger.LogError(e, "Detected intermediary error creating splashscreen image"); + bitmap?.Dispose(); + throw; + } } /// @@ -123,25 +138,35 @@ public class SplashscreenBuilder /// The transformed image. private SKBitmap Transform3D(SKBitmap input) { - var bitmap = new SKBitmap(FinalWidth, FinalHeight); - using var canvas = new SKCanvas(bitmap); - canvas.Clear(SKColors.Black); - var matrix = new SKMatrix + SKBitmap? bitmap = null; + try { - ScaleX = 0.324108899f, - ScaleY = 0.563934922f, - SkewX = -0.244337708f, - SkewY = 0.0377609022f, - TransX = 42.0407715f, - TransY = -198.104706f, - Persp0 = -9.08959337E-05f, - Persp1 = 6.85242048E-05f, - Persp2 = 0.988209724f - }; + bitmap = new SKBitmap(FinalWidth, FinalHeight); + using var canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.Black); + var matrix = new SKMatrix + { + ScaleX = 0.324108899f, + ScaleY = 0.563934922f, + SkewX = -0.244337708f, + SkewY = 0.0377609022f, + TransX = 42.0407715f, + TransY = -198.104706f, + Persp0 = -9.08959337E-05f, + Persp1 = 6.85242048E-05f, + Persp2 = 0.988209724f + }; - canvas.SetMatrix(matrix); - canvas.DrawBitmap(input, 0, 0); + canvas.SetMatrix(matrix); + canvas.DrawBitmap(input, 0, 0); - return bitmap; + return bitmap; + } + catch (Exception e) + { + _logger.LogError(e, "Detected intermediary error creating splashscreen image transforming the image"); + bitmap?.Dispose(); + throw; + } } }