< Summary - Jellyfin

Information
Class: Jellyfin.Drawing.Skia.SplashscreenBuilder
Assembly: Jellyfin.Drawing.Skia
File(s): /srv/git/jellyfin/src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 71
Coverable lines: 71
Total lines: 174
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GenerateSplash(...)100%210%
GenerateCollage(...)0%342180%
Transform3D(...)0%620%

File(s)

/srv/git/jellyfin/src/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using Microsoft.Extensions.Logging;
 4using SkiaSharp;
 5
 6namespace Jellyfin.Drawing.Skia;
 7
 8/// <summary>
 9/// Used to build the splashscreen.
 10/// </summary>
 11public class SplashscreenBuilder
 12{
 13    private const int FinalWidth = 1920;
 14    private const int FinalHeight = 1080;
 15    // generated collage resolution should be greater than the final resolution
 16    private const int WallWidth = FinalWidth * 3;
 17    private const int WallHeight = FinalHeight * 2;
 18    private const int Rows = 6;
 19    private const int Spacing = 20;
 20
 21    private readonly SkiaEncoder _skiaEncoder;
 22    private readonly ILogger _logger;
 23
 24    /// <summary>
 25    /// Initializes a new instance of the <see cref="SplashscreenBuilder"/> class.
 26    /// </summary>
 27    /// <param name="skiaEncoder">The SkiaEncoder.</param>
 28    /// <param name="logger">The logger.</param>
 29    public SplashscreenBuilder(SkiaEncoder skiaEncoder, ILogger logger)
 30    {
 031        _skiaEncoder = skiaEncoder;
 032        _logger = logger;
 033    }
 34
 35    /// <summary>
 36    /// Generate a splashscreen.
 37    /// </summary>
 38    /// <param name="posters">The poster paths.</param>
 39    /// <param name="backdrops">The landscape paths.</param>
 40    /// <param name="outputPath">The output path.</param>
 41    public void GenerateSplash(IReadOnlyList<string> posters, IReadOnlyList<string> backdrops, string outputPath)
 42    {
 043        using var wall = GenerateCollage(posters, backdrops);
 044        using var transformed = Transform3D(wall);
 45
 046        using var outputStream = new SKFileWStream(outputPath);
 047        using var pixmap = new SKPixmap(new SKImageInfo(FinalWidth, FinalHeight), transformed.GetPixels());
 048        pixmap.Encode(outputStream, StripCollageBuilder.GetEncodedFormat(outputPath), 90);
 049    }
 50
 51    /// <summary>
 52    /// Generates a collage of posters and landscape pictures.
 53    /// </summary>
 54    /// <param name="posters">The poster paths.</param>
 55    /// <param name="backdrops">The landscape paths.</param>
 56    /// <returns>The created collage as a bitmap.</returns>
 57    private SKBitmap GenerateCollage(IReadOnlyList<string> posters, IReadOnlyList<string> backdrops)
 58    {
 059        var posterIndex = 0;
 060        var backdropIndex = 0;
 61
 062        SKBitmap? bitmap = null;
 63        try
 64        {
 065            bitmap = new SKBitmap(WallWidth, WallHeight);
 066            using var canvas = new SKCanvas(bitmap);
 067            canvas.Clear(SKColors.Black);
 68
 069            int posterHeight = WallHeight / 6;
 70
 071            for (int i = 0; i < Rows; i++)
 72            {
 073                int imageCounter = Random.Shared.Next(0, 5);
 074                int currentWidthPos = i * 75;
 075                int currentHeight = i * (posterHeight + Spacing);
 76
 077                while (currentWidthPos < WallWidth)
 78                {
 79                    SKBitmap? currentImage;
 80
 81                    switch (imageCounter)
 82                    {
 83                        case 0:
 84                        case 2:
 85                        case 3:
 086                            currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, posters, posterIndex, out int newP
 087                            posterIndex = newPosterIndex;
 088                            break;
 89                        default:
 090                            currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrops, backdropIndex, out int 
 091                            backdropIndex = newBackdropIndex;
 92                            break;
 93                    }
 94
 095                    if (currentImage is null)
 96                    {
 097                        throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!");
 98                    }
 99
 0100                    using (currentImage)
 101                    {
 0102                        var imageWidth = Math.Abs(posterHeight * currentImage.Width / currentImage.Height);
 0103                        using var resizedBitmap = new SKBitmap(imageWidth, posterHeight);
 0104                        var samplingOptions = currentImage.Width > imageWidth || currentImage.Height > posterHeight
 0105                            ? SkiaEncoder.DefaultSamplingOptions
 0106                            : SkiaEncoder.UpscaleSamplingOptions;
 0107                        currentImage.ScalePixels(resizedBitmap, samplingOptions);
 108                        // draw on canvas
 0109                        canvas.DrawBitmap(resizedBitmap, currentWidthPos, currentHeight, samplingOptions);
 110
 111                        // resize to the same aspect as the original
 0112                        currentWidthPos += imageWidth + Spacing;
 0113                    }
 114
 0115                    if (imageCounter >= 4)
 116                    {
 0117                        imageCounter = 0;
 118                    }
 119                    else
 120                    {
 0121                        imageCounter++;
 122                    }
 123                }
 124            }
 125
 0126            return bitmap;
 127        }
 0128        catch (Exception e)
 129        {
 0130            _logger.LogError(e, "Detected intermediary error creating splashscreen image");
 0131            bitmap?.Dispose();
 0132            throw;
 133        }
 0134    }
 135
 136    /// <summary>
 137    /// Transform the collage in 3D space.
 138    /// </summary>
 139    /// <param name="input">The bitmap to transform.</param>
 140    /// <returns>The transformed image.</returns>
 141    private SKBitmap Transform3D(SKBitmap input)
 142    {
 0143        SKBitmap? bitmap = null;
 144        try
 145        {
 0146            bitmap = new SKBitmap(FinalWidth, FinalHeight);
 0147            using var canvas = new SKCanvas(bitmap);
 0148            canvas.Clear(SKColors.Black);
 0149            var matrix = new SKMatrix
 0150            {
 0151                ScaleX = 0.324108899f,
 0152                ScaleY = 0.563934922f,
 0153                SkewX = -0.244337708f,
 0154                SkewY = 0.0377609022f,
 0155                TransX = 42.0407715f,
 0156                TransY = -198.104706f,
 0157                Persp0 = -9.08959337E-05f,
 0158                Persp1 = 6.85242048E-05f,
 0159                Persp2 = 0.988209724f
 0160            };
 161
 0162            canvas.SetMatrix(matrix);
 0163            canvas.DrawBitmap(input, 0, 0);
 164
 0165            return bitmap;
 166        }
 0167        catch (Exception e)
 168        {
 0169            _logger.LogError(e, "Detected intermediary error creating splashscreen image transforming the image");
 0170            bitmap?.Dispose();
 0171            throw;
 172        }
 0173    }
 174}