diff options
Diffstat (limited to 'Jellyfin.Drawing.Skia/SplashscreenBuilder.cs')
| -rw-r--r-- | Jellyfin.Drawing.Skia/SplashscreenBuilder.cs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs b/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs new file mode 100644 index 000000000..8b6942be0 --- /dev/null +++ b/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Controller.Drawing; +using SkiaSharp; + +namespace Jellyfin.Drawing.Skia +{ + /// <summary> + /// Used to build the splashscreen. + /// </summary> + public class SplashscreenBuilder + { + private const int Rows = 6; + private const int Spacing = 20; + + private readonly SkiaEncoder _skiaEncoder; + + private Random? _random; + private int _finalWidth; + private int _finalHeight; + + /// <summary> + /// Initializes a new instance of the <see cref="SplashscreenBuilder"/> class. + /// </summary> + /// <param name="skiaEncoder">The SkiaEncoder.</param> + public SplashscreenBuilder(SkiaEncoder skiaEncoder) + { + _skiaEncoder = skiaEncoder; + } + + /// <summary> + /// Generate a splashscreen. + /// </summary> + /// <param name="options">The options to generate the splashscreen.</param> + public void GenerateSplash(SplashscreenOptions options) + { + _finalWidth = options.Width; + _finalHeight = options.Height; + var wall = GenerateCollage(options.PortraitInputPaths, options.LandscapeInputPaths, options.ApplyFilter); + var transformed = Transform3D(wall); + + using var outputStream = new SKFileWStream(options.OutputPath); + using var pixmap = new SKPixmap(new SKImageInfo(_finalWidth, _finalHeight), transformed.GetPixels()); + pixmap.Encode(outputStream, StripCollageBuilder.GetEncodedFormat(options.OutputPath), 90); + } + + /// <summary> + /// Generates a collage of posters and landscape pictures. + /// </summary> + /// <param name="poster">The poster paths.</param> + /// <param name="backdrop">The landscape paths.</param> + /// <param name="applyFilter">Whether to apply the darkening filter.</param> + /// <returns>The created collage as a bitmap.</returns> + private SKBitmap GenerateCollage(IReadOnlyList<string> poster, IReadOnlyList<string> backdrop, bool applyFilter) + { + _random = new Random(); + + var posterIndex = 0; + var backdropIndex = 0; + + // use higher resolution than final image + var bitmap = new SKBitmap(_finalWidth * 3, _finalHeight * 2); + using var canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.Black); + + int posterHeight = _finalHeight * 2 / 6; + + for (int i = 0; i < Rows; i++) + { + int imageCounter = _random.Next(0, 5); + int currentWidthPos = i * 75; + int currentHeight = i * (posterHeight + Spacing); + + while (currentWidthPos < _finalWidth * 3) + { + SKBitmap? currentImage; + + switch (imageCounter) + { + case 0: + case 2: + case 3: + currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, poster, posterIndex, out int newPosterIndex); + posterIndex = newPosterIndex; + break; + default: + currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrop, backdropIndex, out int newBackdropIndex); + backdropIndex = newBackdropIndex; + break; + } + + if (currentImage == 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); + + currentWidthPos += imageWidth + Spacing; + + currentImage.Dispose(); + + if (imageCounter >= 4) + { + imageCounter = 0; + } + else + { + imageCounter++; + } + } + } + + if (applyFilter) + { + var paintColor = new SKPaint + { + Color = SKColors.Black.WithAlpha(0x50), + Style = SKPaintStyle.Fill + }; + canvas.DrawRect(0, 0, _finalWidth * 3, _finalHeight * 2, paintColor); + } + + return bitmap; + } + + /// <summary> + /// Transform the collage in 3D space. + /// </summary> + /// <param name="input">The bitmap to transform.</param> + /// <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 + { + 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; + } + } +} |
