Files
Tornado3_2026Election/Tornado3_2026Election/Services/ThumbnailLayoutResolver.cs
2026-04-22 13:30:24 +09:00

138 lines
4.5 KiB
C#

using System;
using Tornado3_2026Election.Domain;
namespace Tornado3_2026Election.Services;
public enum ThumbnailDisplayContext
{
CutList,
Queue,
Preview
}
public readonly record struct ThumbnailDisplayMetrics(double Width, double Height);
public static class ThumbnailLayoutResolver
{
private const double HdAspectRatio = 1920d / 1080d;
private const double StandardVideoWallAspectRatio = 5760d / 1080d;
private const double UltraWideVideoWallAspectRatio = 11520d / 1080d;
public static ThumbnailDisplayMetrics ResolveDisplayMetrics(
FormatTemplateDefinition template,
VideoWallLayoutPreset videoWallLayoutPreset,
ThumbnailDisplayContext context)
{
return ResolveDisplayMetrics(template.RecommendedChannel, template.SceneWidth, template.SceneHeight, videoWallLayoutPreset, context);
}
public static ThumbnailDisplayMetrics ResolveDisplayMetrics(
BroadcastChannel channel,
VideoWallLayoutPreset videoWallLayoutPreset,
ThumbnailDisplayContext context)
{
return ResolveDisplayMetrics(channel, null, null, videoWallLayoutPreset, context);
}
public static (int Width, int Height) ResolveGenerationSize(
FormatTemplateDefinition template,
VideoWallLayoutPreset videoWallLayoutPreset)
{
const int thumbnailHeight = 180;
var aspectRatio = ResolveAspectRatio(template.RecommendedChannel, template.SceneWidth, template.SceneHeight, videoWallLayoutPreset);
var thumbnailWidth = Math.Max(1, (int)Math.Round(thumbnailHeight * aspectRatio, MidpointRounding.AwayFromZero));
return (thumbnailWidth, thumbnailHeight);
}
private static ThumbnailDisplayMetrics ResolveDisplayMetrics(
BroadcastChannel channel,
int? sceneWidth,
int? sceneHeight,
VideoWallLayoutPreset videoWallLayoutPreset,
ThumbnailDisplayContext context)
{
var aspectRatio = ResolveAspectRatio(channel, sceneWidth, sceneHeight, videoWallLayoutPreset);
var (maxWidth, maxHeight) = context switch
{
ThumbnailDisplayContext.Preview => (480d, 180d),
ThumbnailDisplayContext.CutList => (320d, 90d),
ThumbnailDisplayContext.Queue => (320d, 90d),
_ => (320d, 180d)
};
return FitWithin(aspectRatio, maxWidth, maxHeight);
}
private static double ResolveAspectRatio(
BroadcastChannel channel,
int? sceneWidth,
int? sceneHeight,
VideoWallLayoutPreset videoWallLayoutPreset)
{
if (channel == BroadcastChannel.VideoWall)
{
if (TryGetPresetAspectRatio(videoWallLayoutPreset, out var presetAspectRatio))
{
return presetAspectRatio;
}
if (TryGetSceneAspectRatio(sceneWidth, sceneHeight, out var sceneAspectRatio))
{
return sceneAspectRatio;
}
return StandardVideoWallAspectRatio;
}
if (TryGetSceneAspectRatio(sceneWidth, sceneHeight, out var resolvedSceneAspectRatio))
{
return resolvedSceneAspectRatio;
}
return HdAspectRatio;
}
private static bool TryGetPresetAspectRatio(VideoWallLayoutPreset videoWallLayoutPreset, out double aspectRatio)
{
switch (videoWallLayoutPreset)
{
case VideoWallLayoutPreset.Standard5760x1080:
aspectRatio = StandardVideoWallAspectRatio;
return true;
case VideoWallLayoutPreset.UltraWide11520x1080:
aspectRatio = UltraWideVideoWallAspectRatio;
return true;
default:
aspectRatio = 0;
return false;
}
}
private static bool TryGetSceneAspectRatio(int? sceneWidth, int? sceneHeight, out double aspectRatio)
{
if (sceneWidth is > 0 && sceneHeight is > 0)
{
aspectRatio = (double)sceneWidth.Value / sceneHeight.Value;
return true;
}
aspectRatio = 0;
return false;
}
private static ThumbnailDisplayMetrics FitWithin(double aspectRatio, double maxWidth, double maxHeight)
{
var width = maxWidth;
var height = width / aspectRatio;
if (height > maxHeight)
{
height = maxHeight;
width = height * aspectRatio;
}
return new ThumbnailDisplayMetrics(
Math.Max(1, Math.Round(width, 0, MidpointRounding.AwayFromZero)),
Math.Max(1, Math.Round(height, 0, MidpointRounding.AwayFromZero)));
}
}