Ensure Skia images are always disposed (#12786)

This commit is contained in:
JPVenson 2024-10-17 15:35:03 +02:00 committed by GitHub
parent 4251cbc277
commit 8b4fa42e49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 184 additions and 120 deletions

View File

@ -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());
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,6 +296,8 @@ public class SkiaEncoder : IImageEncoder
return Decode(path, true, orientation, out origin);
}
try
{
// If we have to resize these they often end up distorted
if (resultBitmap.ColorType == SKColorType.Gray8)
{
@ -298,6 +310,13 @@ public class SkiaEncoder : IImageEncoder
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,19 +354,32 @@ 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);
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
SKBitmap? rotated = null;
try
{
rotated = needsFlip
? new SKBitmap(bitmap.Height, bitmap.Width)
: new SKBitmap(bitmap.Width, bitmap.Height);
using var surface = new SKCanvas(rotated);
@ -388,6 +420,13 @@ public class SkiaEncoder : IImageEncoder
surface.DrawBitmap(bitmap, 0, 0);
return rotated;
}
catch (Exception e)
{
_logger.LogError(e, "Detected intermediary error rotating image");
rotated?.Dispose();
throw;
}
}
/// <summary>
/// Resizes an image on the CPU, by utilizing a surface and canvas.
@ -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);
}

View File

@ -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,7 +59,10 @@ public class SplashscreenBuilder
var posterIndex = 0;
var backdropIndex = 0;
var bitmap = new SKBitmap(WallWidth, WallHeight);
SKBitmap? bitmap = null;
try
{
bitmap = new SKBitmap(WallWidth, WallHeight);
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Black);
@ -90,7 +97,8 @@ public class SplashscreenBuilder
throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!");
}
// resize to the same aspect as the original
using (currentImage)
{
var imageWidth = Math.Abs(posterHeight * currentImage.Width / currentImage.Height);
using var resizedBitmap = new SKBitmap(imageWidth, posterHeight);
currentImage.ScalePixels(resizedBitmap, SKFilterQuality.High);
@ -98,9 +106,9 @@ public class SplashscreenBuilder
// draw on canvas
canvas.DrawBitmap(resizedBitmap, currentWidthPos, currentHeight);
// resize to the same aspect as the original
currentWidthPos += imageWidth + Spacing;
currentImage.Dispose();
}
if (imageCounter >= 4)
{
@ -115,6 +123,13 @@ public class SplashscreenBuilder
return bitmap;
}
catch (Exception e)
{
_logger.LogError(e, "Detected intermediary error creating splashscreen image");
bitmap?.Dispose();
throw;
}
}
/// <summary>
/// Transform the collage in 3D space.
@ -123,7 +138,10 @@ public class SplashscreenBuilder
/// <returns>The transformed image.</returns>
private SKBitmap Transform3D(SKBitmap input)
{
var bitmap = new SKBitmap(FinalWidth, FinalHeight);
SKBitmap? bitmap = null;
try
{
bitmap = new SKBitmap(FinalWidth, FinalHeight);
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Black);
var matrix = new SKMatrix
@ -144,4 +162,11 @@ public class SplashscreenBuilder
return bitmap;
}
catch (Exception e)
{
_logger.LogError(e, "Detected intermediary error creating splashscreen image transforming the image");
bitmap?.Dispose();
throw;
}
}
}