55 lines
2.2 KiB
C#
55 lines
2.2 KiB
C#
namespace ErsatzTV.Core.Images;
|
|
|
|
/// <summary>
|
|
/// The decode-budget policy for a remote image, as pure arithmetic so it can be enforced both at
|
|
/// render time (graphics engine) and at save time (logo download) without materializing
|
|
/// multi-gigabyte images. Extracted from ImageElementBase for reuse. (ersatztv#525, from #511.)
|
|
/// </summary>
|
|
public static class RemoteImageDecodeBudget
|
|
{
|
|
/// <summary>
|
|
/// Ceiling on TOTAL decoded pixels — width x height x frames, as one product. Checking
|
|
/// dimensions and frame count independently does not bound the decode: a 60 KiB 2500x2500 x600
|
|
/// GIF passes both a 50 MP dimension check and a 600 frame check and costs ~14 GiB.
|
|
/// </summary>
|
|
public const long MaxRemoteDecodedPixels = 50_000_000;
|
|
|
|
/// <summary>Frame ceiling, a cheap legible guard against absurd counts of tiny frames.</summary>
|
|
public const int MaxRemoteFrames = 600;
|
|
|
|
public static void EnsureDimensionsAffordable(int width, int height, Uri uri)
|
|
{
|
|
long pixels = (long)width * height;
|
|
if (pixels > MaxRemoteDecodedPixels)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Remote image {uri} is {width}x{height} ({pixels} pixels), over the "
|
|
+ $"{MaxRemoteDecodedPixels} pixel limit");
|
|
}
|
|
}
|
|
|
|
public static int AffordableFrames(int width, int height)
|
|
{
|
|
long perFrame = Math.Max((long)width * height, 1);
|
|
return (int)Math.Clamp(MaxRemoteDecodedPixels / perFrame, 1, MaxRemoteFrames);
|
|
}
|
|
|
|
public static void EnsureDecodeAffordable(int width, int height, int frameCount, Uri uri)
|
|
{
|
|
int frames = Math.Max(frameCount, 1);
|
|
if (frames > MaxRemoteFrames)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Remote image {uri} has {frames} frames, over the {MaxRemoteFrames} frame limit");
|
|
}
|
|
|
|
long totalPixels = (long)width * height * frames;
|
|
if (totalPixels > MaxRemoteDecodedPixels)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Remote image {uri} decodes to {width}x{height} x{frames} frames "
|
|
+ $"({totalPixels} pixels), over the {MaxRemoteDecodedPixels} pixel limit");
|
|
}
|
|
}
|
|
}
|