142 lines
4.4 KiB
C#
142 lines
4.4 KiB
C#
using System.IO;
|
|
using SkiaSharp;
|
|
|
|
namespace Jellyfin.Plugin.SRFPlay.Utilities;
|
|
|
|
/// <summary>
|
|
/// Generates placeholder images for content without thumbnails.
|
|
/// </summary>
|
|
public static class PlaceholderImageGenerator
|
|
{
|
|
private const int Width = 640;
|
|
private const int Height = 360; // 16:9 aspect ratio
|
|
|
|
/// <summary>
|
|
/// Generates a placeholder image with the given text centered.
|
|
/// </summary>
|
|
/// <param name="text">The text to display (typically channel/show name).</param>
|
|
/// <returns>A memory stream containing the PNG image.</returns>
|
|
public static MemoryStream GeneratePlaceholder(string text)
|
|
{
|
|
using var surface = SKSurface.Create(new SKImageInfo(Width, Height));
|
|
var canvas = surface.Canvas;
|
|
|
|
// Dark gradient background
|
|
using var backgroundPaint = new SKPaint();
|
|
using var shader = SKShader.CreateLinearGradient(
|
|
new SKPoint(0, 0),
|
|
new SKPoint(Width, Height),
|
|
new[] { new SKColor(45, 45, 48), new SKColor(28, 28, 30) },
|
|
null,
|
|
SKShaderTileMode.Clamp);
|
|
backgroundPaint.Shader = shader;
|
|
canvas.DrawRect(0, 0, Width, Height, backgroundPaint);
|
|
|
|
// Text
|
|
using var textPaint = new SKPaint
|
|
{
|
|
Color = SKColors.White,
|
|
IsAntialias = true,
|
|
TextAlign = SKTextAlign.Center,
|
|
Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold)
|
|
};
|
|
|
|
// Calculate font size and wrap text if needed
|
|
float fontSize = 48;
|
|
textPaint.TextSize = fontSize;
|
|
var maxWidth = Width * 0.85f;
|
|
var maxHeight = Height * 0.8f;
|
|
|
|
var lines = WrapText(text, textPaint, maxWidth, maxHeight, ref fontSize);
|
|
|
|
// Draw each line centered
|
|
var lineHeight = fontSize * 1.2f;
|
|
var totalHeight = lines.Count * lineHeight;
|
|
var startY = ((Height - totalHeight) / 2) + fontSize;
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
canvas.DrawText(line, Width / 2, startY, textPaint);
|
|
startY += lineHeight;
|
|
}
|
|
|
|
// Encode to PNG
|
|
using var image = surface.Snapshot();
|
|
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
|
|
|
|
var stream = new MemoryStream();
|
|
data.SaveTo(stream);
|
|
stream.Position = 0;
|
|
return stream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps text to fit within the specified width and height constraints.
|
|
/// </summary>
|
|
/// <param name="text">The text to wrap.</param>
|
|
/// <param name="paint">The paint to use for measuring.</param>
|
|
/// <param name="maxWidth">Maximum width for each line.</param>
|
|
/// <param name="maxHeight">Maximum total height.</param>
|
|
/// <param name="fontSize">Font size (will be adjusted if needed).</param>
|
|
/// <returns>List of text lines.</returns>
|
|
private static System.Collections.Generic.List<string> WrapText(
|
|
string text,
|
|
SKPaint paint,
|
|
float maxWidth,
|
|
float maxHeight,
|
|
ref float fontSize)
|
|
{
|
|
const float minFontSize = 20;
|
|
var words = text.Split(' ');
|
|
var lines = new System.Collections.Generic.List<string>();
|
|
|
|
while (fontSize >= minFontSize)
|
|
{
|
|
paint.TextSize = fontSize;
|
|
lines.Clear();
|
|
|
|
var currentLine = string.Empty;
|
|
foreach (var word in words)
|
|
{
|
|
var testLine = string.IsNullOrEmpty(currentLine) ? word : $"{currentLine} {word}";
|
|
var testWidth = paint.MeasureText(testLine);
|
|
|
|
if (testWidth > maxWidth && !string.IsNullOrEmpty(currentLine))
|
|
{
|
|
lines.Add(currentLine);
|
|
currentLine = word;
|
|
}
|
|
else
|
|
{
|
|
currentLine = testLine;
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(currentLine))
|
|
{
|
|
lines.Add(currentLine);
|
|
}
|
|
|
|
// Check if total height fits
|
|
var lineHeight = fontSize * 1.2f;
|
|
var totalHeight = lines.Count * lineHeight;
|
|
|
|
if (totalHeight <= maxHeight)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Reduce font size and try again
|
|
fontSize -= 2;
|
|
}
|
|
|
|
// If still doesn't fit, just return what we have
|
|
if (lines.Count == 0)
|
|
{
|
|
lines.Add(text);
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
}
|