1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using SkiaSharp;
namespace Jellyfin.Drawing.Skia;
/// <summary>
/// Used to build the splashscreen.
/// </summary>
public class SplashscreenBuilder
{
private const int FinalWidth = 1920;
private const int FinalHeight = 1080;
// generated collage resolution should be greater than the final resolution
private const int WallWidth = FinalWidth * 3;
private const int WallHeight = FinalHeight * 2;
private const int Rows = 6;
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>
/// <param name="logger">The logger.</param>
public SplashscreenBuilder(SkiaEncoder skiaEncoder, ILogger logger)
{
_skiaEncoder = skiaEncoder;
_logger = logger;
}
/// <summary>
/// Generate a splashscreen.
/// </summary>
/// <param name="posters">The poster paths.</param>
/// <param name="backdrops">The landscape paths.</param>
/// <param name="outputPath">The output path.</param>
public void GenerateSplash(IReadOnlyList<string> posters, IReadOnlyList<string> backdrops, string outputPath)
{
using var wall = GenerateCollage(posters, backdrops);
using var transformed = Transform3D(wall);
using var outputStream = new SKFileWStream(outputPath);
using var pixmap = new SKPixmap(new SKImageInfo(FinalWidth, FinalHeight), transformed.GetPixels());
pixmap.Encode(outputStream, StripCollageBuilder.GetEncodedFormat(outputPath), 90);
}
/// <summary>
/// Generates a collage of posters and landscape pictures.
/// </summary>
/// <param name="posters">The poster paths.</param>
/// <param name="backdrops">The landscape paths.</param>
/// <returns>The created collage as a bitmap.</returns>
private SKBitmap GenerateCollage(IReadOnlyList<string> posters, IReadOnlyList<string> backdrops)
{
var posterIndex = 0;
var backdropIndex = 0;
SKBitmap? bitmap = null;
try
{
bitmap = new SKBitmap(WallWidth, WallHeight);
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Black);
int posterHeight = WallHeight / 6;
for (int i = 0; i < Rows; i++)
{
int imageCounter = Random.Shared.Next(0, 5);
int currentWidthPos = i * 75;
int currentHeight = i * (posterHeight + Spacing);
while (currentWidthPos < WallWidth)
{
SKBitmap? currentImage;
switch (imageCounter)
{
case 0:
case 2:
case 3:
currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, posters, posterIndex, out int newPosterIndex);
posterIndex = newPosterIndex;
break;
default:
currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrops, backdropIndex, out int newBackdropIndex);
backdropIndex = newBackdropIndex;
break;
}
if (currentImage is null)
{
throw new ArgumentException("Not enough valid pictures provided to create a splashscreen!");
}
using (currentImage)
{
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);
// resize to the same aspect as the original
currentWidthPos += imageWidth + Spacing;
}
if (imageCounter >= 4)
{
imageCounter = 0;
}
else
{
imageCounter++;
}
}
}
return bitmap;
}
catch (Exception e)
{
_logger.LogError(e, "Detected intermediary error creating splashscreen image");
bitmap?.Dispose();
throw;
}
}
/// <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)
{
SKBitmap? bitmap = null;
try
{
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;
}
catch (Exception e)
{
_logger.LogError(e, "Detected intermediary error creating splashscreen image transforming the image");
bitmap?.Dispose();
throw;
}
}
}
|