浏览代码

Merge pull request #3597 from barronpm/jellyfin-drawing-skia-cleanup

Jellyfin.Drawing.Skia Cleanup
Anthony Lavado 4 年之前
父节点
当前提交
2f315bb0dd

+ 9 - 13
Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs

@@ -19,22 +19,18 @@ namespace Jellyfin.Drawing.Skia
         /// <param name="percent">The percentage played to display with the indicator.</param>
         public static void Process(SKCanvas canvas, ImageDimensions imageSize, double percent)
         {
-            using (var paint = new SKPaint())
-            {
-                var endX = imageSize.Width - 1;
-                var endY = imageSize.Height - 1;
+            using var paint = new SKPaint();
+            var endX = imageSize.Width - 1;
+            var endY = imageSize.Height - 1;
 
-                paint.Color = SKColor.Parse("#99000000");
-                paint.Style = SKPaintStyle.Fill;
-                canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, (float)endX, (float)endY), paint);
+            paint.Color = SKColor.Parse("#99000000");
+            paint.Style = SKPaintStyle.Fill;
+            canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, endX, endY), paint);
 
-                double foregroundWidth = endX;
-                foregroundWidth *= percent;
-                foregroundWidth /= 100;
+            double foregroundWidth = (endX * percent) / 100;
 
-                paint.Color = SKColor.Parse("#FF00A4DC");
-                canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(foregroundWidth), (float)endY), paint);
-            }
+            paint.Color = SKColor.Parse("#FF00A4DC");
+            canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(foregroundWidth), endY), paint);
         }
     }
 }

+ 15 - 19
Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs

@@ -22,31 +22,27 @@ namespace Jellyfin.Drawing.Skia
         {
             var x = imageSize.Width - OffsetFromTopRightCorner;
 
-            using (var paint = new SKPaint())
+            using var paint = new SKPaint
             {
-                paint.Color = SKColor.Parse("#CC00A4DC");
-                paint.Style = SKPaintStyle.Fill;
-                canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
-            }
+                Color = SKColor.Parse("#CC00A4DC"),
+                Style = SKPaintStyle.Fill
+            };
 
-            using (var paint = new SKPaint())
-            {
-                paint.Color = new SKColor(255, 255, 255, 255);
-                paint.Style = SKPaintStyle.Fill;
+            canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
 
-                paint.TextSize = 30;
-                paint.IsAntialias = true;
+            paint.Color = new SKColor(255, 255, 255, 255);
+            paint.TextSize = 30;
+            paint.IsAntialias = true;
 
-                // or:
-                // var emojiChar = 0x1F680;
-                const string Text = "✔️";
-                var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32);
+            // or:
+            // var emojiChar = 0x1F680;
+            const string Text = "✔️";
+            var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32);
 
-                // ask the font manager for a font with that character
-                paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
+            // ask the font manager for a font with that character
+            paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
 
-                canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
-            }
+            canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
         }
     }
 }

+ 1 - 1
Jellyfin.Drawing.Skia/SkiaCodecException.cs

@@ -12,7 +12,7 @@ namespace Jellyfin.Drawing.Skia
         /// Initializes a new instance of the <see cref="SkiaCodecException" /> class.
         /// </summary>
         /// <param name="result">The non-successful codec result returned by Skia.</param>
-        public SkiaCodecException(SKCodecResult result) : base()
+        public SkiaCodecException(SKCodecResult result)
         {
             CodecResult = result;
         }

+ 162 - 310
Jellyfin.Drawing.Skia/SkiaEncoder.cs

@@ -29,9 +29,7 @@ namespace Jellyfin.Drawing.Skia
         /// </summary>
         /// <param name="logger">The application logger.</param>
         /// <param name="appPaths">The application paths.</param>
-        public SkiaEncoder(
-            ILogger<SkiaEncoder> logger,
-            IApplicationPaths appPaths)
+        public SkiaEncoder(ILogger<SkiaEncoder> logger, IApplicationPaths appPaths)
         {
             _logger = logger;
             _appPaths = appPaths;
@@ -102,19 +100,14 @@ namespace Jellyfin.Drawing.Skia
         /// <returns>The converted format.</returns>
         public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
         {
-            switch (selectedFormat)
-            {
-                case ImageFormat.Bmp:
-                    return SKEncodedImageFormat.Bmp;
-                case ImageFormat.Jpg:
-                    return SKEncodedImageFormat.Jpeg;
-                case ImageFormat.Gif:
-                    return SKEncodedImageFormat.Gif;
-                case ImageFormat.Webp:
-                    return SKEncodedImageFormat.Webp;
-                default:
-                    return SKEncodedImageFormat.Png;
-            }
+            return selectedFormat switch
+            {
+                ImageFormat.Bmp => SKEncodedImageFormat.Bmp,
+                ImageFormat.Jpg => SKEncodedImageFormat.Jpeg,
+                ImageFormat.Gif => SKEncodedImageFormat.Gif,
+                ImageFormat.Webp => SKEncodedImageFormat.Webp,
+                _ => SKEncodedImageFormat.Png
+            };
         }
 
         private static bool IsTransparentRow(SKBitmap bmp, int row)
@@ -146,63 +139,34 @@ namespace Jellyfin.Drawing.Skia
         private SKBitmap CropWhiteSpace(SKBitmap bitmap)
         {
             var topmost = 0;
-            for (int row = 0; row < bitmap.Height; ++row)
+            while (topmost < bitmap.Height && IsTransparentRow(bitmap, topmost))
             {
-                if (IsTransparentRow(bitmap, row))
-                {
-                    topmost = row + 1;
-                }
-                else
-                {
-                    break;
-                }
+                topmost++;
             }
 
             int bottommost = bitmap.Height;
-            for (int row = bitmap.Height - 1; row >= 0; --row)
+            while (bottommost >= 0 && IsTransparentRow(bitmap, bottommost - 1))
             {
-                if (IsTransparentRow(bitmap, row))
-                {
-                    bottommost = row;
-                }
-                else
-                {
-                    break;
-                }
+                bottommost--;
             }
 
-            int leftmost = 0, rightmost = bitmap.Width;
-            for (int col = 0; col < bitmap.Width; ++col)
+            var leftmost = 0;
+            while (leftmost < bitmap.Width && IsTransparentColumn(bitmap, leftmost))
             {
-                if (IsTransparentColumn(bitmap, col))
-                {
-                    leftmost = col + 1;
-                }
-                else
-                {
-                    break;
-                }
+                leftmost++;
             }
 
-            for (int col = bitmap.Width - 1; col >= 0; --col)
+            var rightmost = bitmap.Width;
+            while (rightmost >= 0 && IsTransparentColumn(bitmap, rightmost - 1))
             {
-                if (IsTransparentColumn(bitmap, col))
-                {
-                    rightmost = col;
-                }
-                else
-                {
-                    break;
-                }
+                rightmost--;
             }
 
             var newRect = SKRectI.Create(leftmost, topmost, rightmost - leftmost, bottommost - topmost);
 
-            using (var image = SKImage.FromBitmap(bitmap))
-            using (var subset = image.Subset(newRect))
-            {
-                return SKBitmap.FromImage(subset);
-            }
+            using var image = SKImage.FromBitmap(bitmap);
+            using var subset = image.Subset(newRect);
+            return SKBitmap.FromImage(subset);
         }
 
         /// <inheritdoc />
@@ -216,14 +180,12 @@ namespace Jellyfin.Drawing.Skia
                 throw new FileNotFoundException("File not found", path);
             }
 
-            using (var codec = SKCodec.Create(path, out SKCodecResult result))
-            {
-                EnsureSuccess(result);
+            using var codec = SKCodec.Create(path, out SKCodecResult result);
+            EnsureSuccess(result);
 
-                var info = codec.Info;
+            var info = codec.Info;
 
-                return new ImageDimensions(info.Width, info.Height);
-            }
+            return new ImageDimensions(info.Width, info.Height);
         }
 
         /// <inheritdoc />
@@ -253,12 +215,7 @@ namespace Jellyfin.Drawing.Skia
                 }
             }
 
-            if (HasDiacritics(path))
-            {
-                return true;
-            }
-
-            return false;
+            return HasDiacritics(path);
         }
 
         private string NormalizePath(string path)
@@ -283,25 +240,17 @@ namespace Jellyfin.Drawing.Skia
                 return SKEncodedOrigin.TopLeft;
             }
 
-            switch (orientation.Value)
-            {
-                case ImageOrientation.TopRight:
-                    return SKEncodedOrigin.TopRight;
-                case ImageOrientation.RightTop:
-                    return SKEncodedOrigin.RightTop;
-                case ImageOrientation.RightBottom:
-                    return SKEncodedOrigin.RightBottom;
-                case ImageOrientation.LeftTop:
-                    return SKEncodedOrigin.LeftTop;
-                case ImageOrientation.LeftBottom:
-                    return SKEncodedOrigin.LeftBottom;
-                case ImageOrientation.BottomRight:
-                    return SKEncodedOrigin.BottomRight;
-                case ImageOrientation.BottomLeft:
-                    return SKEncodedOrigin.BottomLeft;
-                default:
-                    return SKEncodedOrigin.TopLeft;
-            }
+            return orientation.Value switch
+            {
+                ImageOrientation.TopRight => SKEncodedOrigin.TopRight,
+                ImageOrientation.RightTop => SKEncodedOrigin.RightTop,
+                ImageOrientation.RightBottom => SKEncodedOrigin.RightBottom,
+                ImageOrientation.LeftTop => SKEncodedOrigin.LeftTop,
+                ImageOrientation.LeftBottom => SKEncodedOrigin.LeftBottom,
+                ImageOrientation.BottomRight => SKEncodedOrigin.BottomRight,
+                ImageOrientation.BottomLeft => SKEncodedOrigin.BottomLeft,
+                _ => SKEncodedOrigin.TopLeft
+            };
         }
 
         /// <summary>
@@ -323,24 +272,22 @@ namespace Jellyfin.Drawing.Skia
 
             if (requiresTransparencyHack || forceCleanBitmap)
             {
-                using (var codec = SKCodec.Create(NormalizePath(path)))
+                using var codec = SKCodec.Create(NormalizePath(path));
+                if (codec == null)
                 {
-                    if (codec == null)
-                    {
-                        origin = GetSKEncodedOrigin(orientation);
-                        return null;
-                    }
+                    origin = GetSKEncodedOrigin(orientation);
+                    return null;
+                }
 
-                    // create the bitmap
-                    var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
+                // create the bitmap
+                var 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;
             }
 
             var resultBitmap = SKBitmap.Decode(NormalizePath(path));
@@ -367,15 +314,8 @@ namespace Jellyfin.Drawing.Skia
         {
             if (cropWhitespace)
             {
-                using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin))
-                {
-                    if (bitmap == null)
-                    {
-                        return null;
-                    }
-
-                    return CropWhiteSpace(bitmap);
-                }
+                using var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin);
+                return bitmap == null ? null : CropWhiteSpace(bitmap);
             }
 
             return Decode(path, forceAnalyzeBitmap, orientation, out origin);
@@ -403,133 +343,55 @@ namespace Jellyfin.Drawing.Skia
 
         private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
         {
+            if (origin == SKEncodedOrigin.Default)
+            {
+                return bitmap;
+            }
+
+            var needsFlip = origin == SKEncodedOrigin.LeftBottom
+                            || origin == SKEncodedOrigin.LeftTop
+                            || origin == SKEncodedOrigin.RightBottom
+                            || origin == 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:
-                    {
-                        var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
-                        using (var surface = new SKCanvas(rotated))
-                        {
-                            surface.Translate(rotated.Width, 0);
-                            surface.Scale(-1, 1);
-                            surface.DrawBitmap(bitmap, 0, 0);
-                        }
-
-                        return rotated;
-                    }
-
+                    surface.Scale(-1, 1, midX, midY);
+                    break;
                 case SKEncodedOrigin.BottomRight:
-                    {
-                        var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
-                        using (var surface = new SKCanvas(rotated))
-                        {
-                            float px = (float)bitmap.Width / 2;
-                            float py = (float)bitmap.Height / 2;
-
-                            surface.RotateDegrees(180, px, py);
-                            surface.DrawBitmap(bitmap, 0, 0);
-                        }
-
-                        return rotated;
-                    }
-
+                    surface.RotateDegrees(180, midX, midY);
+                    break;
                 case SKEncodedOrigin.BottomLeft:
-                    {
-                        var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
-                        using (var surface = new SKCanvas(rotated))
-                        {
-                            float px = (float)bitmap.Width / 2;
-
-                            float py = (float)bitmap.Height / 2;
-
-                            surface.Translate(rotated.Width, 0);
-                            surface.Scale(-1, 1);
-
-                            surface.RotateDegrees(180, px, py);
-                            surface.DrawBitmap(bitmap, 0, 0);
-                        }
-
-                        return rotated;
-                    }
-
+                    surface.Scale(1, -1, midX, midY);
+                    break;
                 case SKEncodedOrigin.LeftTop:
-                    {
-                        // TODO: Remove dual canvases, had trouble with flipping
-                        using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width))
-                        {
-                            using (var surface = new SKCanvas(rotated))
-                            {
-                                surface.Translate(rotated.Width, 0);
-
-                                surface.RotateDegrees(90);
-
-                                surface.DrawBitmap(bitmap, 0, 0);
-                            }
-
-                            var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height);
-                            using (var flippedCanvas = new SKCanvas(flippedBitmap))
-                            {
-                                flippedCanvas.Translate(flippedBitmap.Width, 0);
-                                flippedCanvas.Scale(-1, 1);
-                                flippedCanvas.DrawBitmap(rotated, 0, 0);
-                            }
-
-                            return flippedBitmap;
-                        }
-                    }
-
+                    surface.Translate(0, -rotated.Height);
+                    surface.Scale(1, -1, midX, midY);
+                    surface.RotateDegrees(-90);
+                    break;
                 case SKEncodedOrigin.RightTop:
-                    {
-                        var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
-                        using (var surface = new SKCanvas(rotated))
-                        {
-                            surface.Translate(rotated.Width, 0);
-                            surface.RotateDegrees(90);
-                            surface.DrawBitmap(bitmap, 0, 0);
-                        }
-
-                        return rotated;
-                    }
-
+                    surface.Translate(rotated.Width, 0);
+                    surface.RotateDegrees(90);
+                    break;
                 case SKEncodedOrigin.RightBottom:
-                    {
-                        // TODO: Remove dual canvases, had trouble with flipping
-                        using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width))
-                        {
-                            using (var surface = new SKCanvas(rotated))
-                            {
-                                surface.Translate(0, rotated.Height);
-                                surface.RotateDegrees(270);
-                                surface.DrawBitmap(bitmap, 0, 0);
-                            }
-
-                            var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height);
-                            using (var flippedCanvas = new SKCanvas(flippedBitmap))
-                            {
-                                flippedCanvas.Translate(flippedBitmap.Width, 0);
-                                flippedCanvas.Scale(-1, 1);
-                                flippedCanvas.DrawBitmap(rotated, 0, 0);
-                            }
-
-                            return flippedBitmap;
-                        }
-                    }
-
+                    surface.Translate(rotated.Width, 0);
+                    surface.Scale(1, -1, midX, midY);
+                    surface.RotateDegrees(90);
+                    break;
                 case SKEncodedOrigin.LeftBottom:
-                    {
-                        var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
-                        using (var surface = new SKCanvas(rotated))
-                        {
-                            surface.Translate(0, rotated.Height);
-                            surface.RotateDegrees(270);
-                            surface.DrawBitmap(bitmap, 0, 0);
-                        }
-
-                        return rotated;
-                    }
-
-                default: return bitmap;
+                    surface.Translate(0, rotated.Height);
+                    surface.RotateDegrees(-90);
+                    break;
             }
+
+            surface.DrawBitmap(bitmap, 0, 0);
+            return rotated;
         }
 
         /// <inheritdoc/>
@@ -552,97 +414,87 @@ namespace Jellyfin.Drawing.Skia
             var blur = options.Blur ?? 0;
             var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
 
-            using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation))
+            using var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation);
+            if (bitmap == null)
             {
-                if (bitmap == null)
-                {
-                    throw new InvalidDataException($"Skia unable to read image {inputPath}");
-                }
+                throw new InvalidDataException($"Skia unable to read image {inputPath}");
+            }
 
-                var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);
+            var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);
 
-                if (!options.CropWhiteSpace
-                    && options.HasDefaultOptions(inputPath, originalImageSize)
-                    && !autoOrient)
-                {
-                    // Just spit out the original file if all the options are default
-                    return inputPath;
-                }
+            if (!options.CropWhiteSpace
+                && options.HasDefaultOptions(inputPath, originalImageSize)
+                && !autoOrient)
+            {
+                // Just spit out the original file if all the options are default
+                return inputPath;
+            }
 
-                var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize);
+            var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize);
 
-                var width = newImageSize.Width;
-                var height = newImageSize.Height;
+            var width = newImageSize.Width;
+            var height = newImageSize.Height;
+
+            using var resizedBitmap = new SKBitmap(width, height, bitmap.ColorType, bitmap.AlphaType);
+            // scale image
+            bitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+
+            // If all we're doing is resizing then we can stop now
+            if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+                using var outputStream = new SKFileWStream(outputPath);
+                using var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels());
+                pixmap.Encode(outputStream, skiaOutputFormat, quality);
+                return outputPath;
+            }
 
-                using (var resizedBitmap = new SKBitmap(width, height, bitmap.ColorType, bitmap.AlphaType))
+            // create bitmap to use for canvas drawing used to draw into bitmap
+            using var saveBitmap = new SKBitmap(width, height);
+            using var canvas = new SKCanvas(saveBitmap);
+            // set background color if present
+            if (hasBackgroundColor)
+            {
+                canvas.Clear(SKColor.Parse(options.BackgroundColor));
+            }
+
+            // Add blur if option is present
+            if (blur > 0)
+            {
+                // create image from resized bitmap to apply blur
+                using var paint = new SKPaint();
+                using var filter = SKImageFilter.CreateBlur(blur, blur);
+                paint.ImageFilter = filter;
+                canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint);
+            }
+            else
+            {
+                // draw resized bitmap onto canvas
+                canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height));
+            }
+
+            // If foreground layer present then draw
+            if (hasForegroundColor)
+            {
+                if (!double.TryParse(options.ForegroundLayer, out double opacity))
                 {
-                    // scale image
-                    bitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+                    opacity = .4;
+                }
 
-                    // If all we're doing is resizing then we can stop now
-                    if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
-                    {
-                        Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
-                        using (var outputStream = new SKFileWStream(outputPath))
-                        using (var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels()))
-                        {
-                            pixmap.Encode(outputStream, skiaOutputFormat, quality);
-                            return outputPath;
-                        }
-                    }
+                canvas.DrawColor(new SKColor(0, 0, 0, (byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver);
+            }
 
-                    // create bitmap to use for canvas drawing used to draw into bitmap
-                    using (var saveBitmap = new SKBitmap(width, height)) // , bitmap.ColorType, bitmap.AlphaType))
-                    using (var canvas = new SKCanvas(saveBitmap))
-                    {
-                        // set background color if present
-                        if (hasBackgroundColor)
-                        {
-                            canvas.Clear(SKColor.Parse(options.BackgroundColor));
-                        }
-
-                        // Add blur if option is present
-                        if (blur > 0)
-                        {
-                            // create image from resized bitmap to apply blur
-                            using (var paint = new SKPaint())
-                            using (var filter = SKImageFilter.CreateBlur(blur, blur))
-                            {
-                                paint.ImageFilter = filter;
-                                canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint);
-                            }
-                        }
-                        else
-                        {
-                            // draw resized bitmap onto canvas
-                            canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height));
-                        }
-
-                        // If foreground layer present then draw
-                        if (hasForegroundColor)
-                        {
-                            if (!double.TryParse(options.ForegroundLayer, out double opacity))
-                            {
-                                opacity = .4;
-                            }
-
-                            canvas.DrawColor(new SKColor(0, 0, 0, (byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver);
-                        }
-
-                        if (hasIndicator)
-                        {
-                            DrawIndicator(canvas, width, height, options);
-                        }
-
-                        Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
-                        using (var outputStream = new SKFileWStream(outputPath))
-                        {
-                            using (var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels()))
-                            {
-                                pixmap.Encode(outputStream, skiaOutputFormat, quality);
-                            }
-                        }
-                    }
+            if (hasIndicator)
+            {
+                DrawIndicator(canvas, width, height, options);
+            }
+
+            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+            using (var outputStream = new SKFileWStream(outputPath))
+            {
+                using (var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels()))
+                {
+                    pixmap.Encode(outputStream, skiaOutputFormat, quality);
                 }
             }
 

+ 1 - 1
Jellyfin.Drawing.Skia/SkiaException.cs

@@ -10,7 +10,7 @@ namespace Jellyfin.Drawing.Skia
         /// <summary>
         /// Initializes a new instance of the <see cref="SkiaException"/> class.
         /// </summary>
-        public SkiaException() : base()
+        public SkiaException()
         {
         }
 

+ 51 - 69
Jellyfin.Drawing.Skia/StripCollageBuilder.cs

@@ -69,12 +69,10 @@ namespace Jellyfin.Drawing.Skia
         /// <param name="height">The desired height of the collage.</param>
         public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
         {
-            using (var bitmap = BuildSquareCollageBitmap(paths, width, height))
-            using (var outputStream = new SKFileWStream(outputPath))
-            using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels()))
-            {
-                pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
-            }
+            using var bitmap = BuildSquareCollageBitmap(paths, width, height);
+            using var outputStream = new SKFileWStream(outputPath);
+            using var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels());
+            pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
         }
 
         /// <summary>
@@ -86,56 +84,46 @@ namespace Jellyfin.Drawing.Skia
         /// <param name="height">The desired height of the collage.</param>
         public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
         {
-            using (var bitmap = BuildThumbCollageBitmap(paths, width, height))
-            using (var outputStream = new SKFileWStream(outputPath))
-            using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels()))
-            {
-                pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
-            }
+            using var bitmap = BuildThumbCollageBitmap(paths, width, height);
+            using var outputStream = new SKFileWStream(outputPath);
+            using var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels());
+            pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
         }
 
         private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height)
         {
             var bitmap = new SKBitmap(width, height);
 
-            using (var canvas = new SKCanvas(bitmap))
-            {
-                canvas.Clear(SKColors.Black);
+            using var canvas = new SKCanvas(bitmap);
+            canvas.Clear(SKColors.Black);
 
-                // number of images used in the thumbnail
-                var iCount = 3;
+            // number of images used in the thumbnail
+            var iCount = 3;
 
-                // determine sizes for each image that will composited into the final image
-                var iSlice = Convert.ToInt32(width / iCount);
-                int iHeight = Convert.ToInt32(height * 1.00);
-                int imageIndex = 0;
-                for (int i = 0; i < iCount; i++)
+            // determine sizes for each image that will composited into the final image
+            var iSlice = Convert.ToInt32(width / iCount);
+            int iHeight = Convert.ToInt32(height * 1.00);
+            int imageIndex = 0;
+            for (int i = 0; i < iCount; i++)
+            {
+                using var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex);
+                imageIndex = newIndex;
+                if (currentBitmap == null)
                 {
-                    using (var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex))
-                    {
-                        imageIndex = newIndex;
-                        if (currentBitmap == null)
-                        {
-                            continue;
-                        }
-
-                        // resize to the same aspect as the original
-                        int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
-                        using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
-                        {
-                            currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
-
-                            // crop image
-                            int ix = Math.Abs((iWidth - iSlice) / 2);
-                            using (var image = SKImage.FromBitmap(resizeBitmap))
-                            using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
-                            {
-                                // draw image onto canvas
-                                canvas.DrawImage(subset ?? image, iSlice * i, 0);
-                            }
-                        }
-                    }
+                    continue;
                 }
+
+                // resize to the same aspect as the original
+                int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
+                using var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType);
+                currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
+
+                // crop image
+                int ix = Math.Abs((iWidth - iSlice) / 2);
+                using var image = SKImage.FromBitmap(resizeBitmap);
+                using var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight));
+                // draw image onto canvas
+                canvas.DrawImage(subset ?? image, iSlice * i, 0);
             }
 
             return bitmap;
@@ -176,33 +164,27 @@ namespace Jellyfin.Drawing.Skia
             var cellWidth = width / 2;
             var cellHeight = height / 2;
 
-            using (var canvas = new SKCanvas(bitmap))
+            using var canvas = new SKCanvas(bitmap);
+            for (var x = 0; x < 2; x++)
             {
-                for (var x = 0; x < 2; x++)
+                for (var y = 0; y < 2; y++)
                 {
-                    for (var y = 0; y < 2; y++)
+                    using var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex);
+                    imageIndex = newIndex;
+
+                    if (currentBitmap == null)
                     {
-                        using (var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex))
-                        {
-                            imageIndex = newIndex;
-
-                            if (currentBitmap == null)
-                            {
-                                continue;
-                            }
-
-                            using (var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
-                            {
-                                // scale image
-                                currentBitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
-
-                                // draw this image into the strip at the next position
-                                var xPos = x * cellWidth;
-                                var yPos = y * cellHeight;
-                                canvas.DrawBitmap(resizedBitmap, xPos, yPos);
-                            }
-                        }
+                        continue;
                     }
+
+                    using var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType);
+                    // scale image
+                    currentBitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+
+                    // draw this image into the strip at the next position
+                    var xPos = x * cellWidth;
+                    var yPos = y * cellHeight;
+                    canvas.DrawBitmap(resizedBitmap, xPos, yPos);
                 }
             }
 

+ 24 - 28
Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs

@@ -28,41 +28,37 @@ namespace Jellyfin.Drawing.Skia
             var x = imageSize.Width - OffsetFromTopRightCorner;
             var text = count.ToString(CultureInfo.InvariantCulture);
 
-            using (var paint = new SKPaint())
+            using var paint = new SKPaint
             {
-                paint.Color = SKColor.Parse("#CC00A4DC");
-                paint.Style = SKPaintStyle.Fill;
-                canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
-            }
-
-            using (var paint = new SKPaint())
-            {
-                paint.Color = new SKColor(255, 255, 255, 255);
-                paint.Style = SKPaintStyle.Fill;
+                Color = SKColor.Parse("#CC00A4DC"),
+                Style = SKPaintStyle.Fill
+            };
 
-                paint.TextSize = 24;
-                paint.IsAntialias = true;
+            canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
 
-                var y = OffsetFromTopRightCorner + 9;
+            paint.Color = new SKColor(255, 255, 255, 255);
+            paint.TextSize = 24;
+            paint.IsAntialias = true;
 
-                if (text.Length == 1)
-                {
-                    x -= 7;
-                }
+            var y = OffsetFromTopRightCorner + 9;
 
-                if (text.Length == 2)
-                {
-                    x -= 13;
-                }
-                else if (text.Length >= 3)
-                {
-                    x -= 15;
-                    y -= 2;
-                    paint.TextSize = 18;
-                }
+            if (text.Length == 1)
+            {
+                x -= 7;
+            }
 
-                canvas.DrawText(text, x, y, paint);
+            if (text.Length == 2)
+            {
+                x -= 13;
+            }
+            else if (text.Length >= 3)
+            {
+                x -= 15;
+                y -= 2;
+                paint.TextSize = 18;
             }
+
+            canvas.DrawText(text, x, y, paint);
         }
     }
 }