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
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());
// 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;
}
}
/// <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);
}