aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJPVenson <ger-delta-07@hotmail.de>2024-10-17 15:35:03 +0200
committerGitHub <noreply@github.com>2024-10-17 07:35:03 -0600
commit8b4fa42e49e305df544dd12fedb7a55a5bdaf74b (patch)
treee3c4582e16f75e252d008a05207fc14ed4863d64 /src
parent4251cbc27799f50eb58fda6fb30f735bbc781a0d (diff)
Ensure Skia images are always disposed (#12786)
Diffstat (limited to 'src')
-rw-r--r--src/Jellyfin.Drawing.Skia/SkiaEncoder.cs157
-rw-r--r--src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs167
2 files changed, 194 insertions, 130 deletions
diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index ede93aaa5..c5aadc890 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)
- {
- 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;
+ SKBitmap? rotated = null;
+ try
+ {
+ 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)
+ {
+ 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;
+ }
}
/// <summary>
@@ -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 990556623..7af77758b 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;
/// <summary>
/// Initializes a new instance of the <see cref="SplashscreenBuilder"/> class.
/// </summary>
/// <param name="skiaEncoder">The SkiaEncoder.</param>
- public SplashscreenBuilder(SkiaEncoder skiaEncoder)
+ /// <param name="logger">The logger.</param>
+ public SplashscreenBuilder(SkiaEncoder skiaEncoder, ILogger logger)
{
_skiaEncoder = skiaEncoder;
+ _logger = logger;
}
/// <summary>
@@ -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);
-
- while (currentWidthPos < WallWidth)
- {
- SKBitmap? currentImage;
-
- 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;
- }
-
- if (currentImage is null)
- {
- throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!");
- }
-
- // 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);
-
- // draw on canvas
- canvas.DrawBitmap(resizedBitmap, currentWidthPos, currentHeight);
+ bitmap = new SKBitmap(WallWidth, WallHeight);
+ using var canvas = new SKCanvas(bitmap);
+ canvas.Clear(SKColors.Black);
- currentWidthPos += imageWidth + Spacing;
+ int posterHeight = WallHeight / 6;
- currentImage.Dispose();
+ for (int i = 0; i < Rows; i++)
+ {
+ int imageCounter = Random.Shared.Next(0, 5);
+ int currentWidthPos = i * 75;
+ int currentHeight = i * (posterHeight + Spacing);
- if (imageCounter >= 4)
- {
- imageCounter = 0;
- }
- else
+ while (currentWidthPos < WallWidth)
{
- imageCounter++;
+ SKBitmap? currentImage;
+
+ 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;
+ }
+
+ if (currentImage is null)
+ {
+ throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!");
+ }
+
+ using (currentImage)
+ {
+ var imageWidth = Math.Abs(posterHeight * currentImage.Width / currentImage.Height);
+ using var resizedBitmap = new SKBitmap(imageWidth, posterHeight);
+ currentImage.ScalePixels(resizedBitmap, SKFilterQuality.High);
+
+ // draw on canvas
+ canvas.DrawBitmap(resizedBitmap, currentWidthPos, currentHeight);
+
+ // resize to the same aspect as the original
+ currentWidthPos += imageWidth + Spacing;
+ }
+
+ 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;
+ }
}
/// <summary>
@@ -123,25 +138,35 @@ public class SplashscreenBuilder
/// <returns>The transformed image.</returns>
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
- };
-
- canvas.SetMatrix(matrix);
- canvas.DrawBitmap(input, 0, 0);
-
- return bitmap;
+ 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);
+
+ return bitmap;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Detected intermediary error creating splashscreen image transforming the image");
+ bitmap?.Dispose();
+ throw;
+ }
}
}