d2phap/ImageGlass · error · InvalidDataException
IGE: frame too large to encode.
Error message
IGE: frame too large to encode.
What it means
Thrown by NativeCodecProxy.ReadPixelsInto when the decoded frame's byte count (stride * height, where stride = width * 4 for BGRA8888) exceeds int.MaxValue (~2 GiB). This is the plugin-host analog of the SKImage/SKBitmap pixel ceiling documented in CLAUDE.md: no managed single-allocation pixel buffer can exceed int.MaxValue bytes, so the host refuses rather than allocate a buffer it cannot index.
Source
Thrown at source/ImageGlass.Lib/Plugins/NativeCodecProxy.cs:520
cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
/// <summary>
/// Copies an <see cref="SKImage"/> into a freshly allocated host-owned BGRA8 unpremultiplied
/// buffer, reusing <paramref name="pixels"/> when it is already big enough.
/// </summary>
private static void ReadPixelsInto(SKImage image, ref byte* pixels, ref nuint capacity,
out IGPixelBuffer buffer)
{
// Unpremul because that is what IGPixelFormat.Bgra8Unorm means to a plugin. Do NOT copy
// SkiaCodec.ToMagick, which uses Premul: handing premultiplied bytes over is a silent
// dark-halo bug on any image with alpha.
var info = new SKImageInfo(image.Width, image.Height, SKColorType.Bgra8888, SKAlphaType.Unpremul);
var stride = checked(info.Width * 4);
var byteCount = (long)stride * info.Height;
if (byteCount > int.MaxValue) throw new InvalidDataException("IGE: frame too large to encode.");
if (capacity < (nuint)byteCount)
{
if (pixels != null) System.Runtime.InteropServices.NativeMemory.Free(pixels);
pixels = (byte*)System.Runtime.InteropServices.NativeMemory.Alloc((nuint)byteCount);
capacity = (nuint)byteCount;
}
// Always copy: PeekPixels returns null for a GPU-backed image, its layout is whatever the
// image happens to be, and a plugin-decoded image is backed by ANOTHER plugin's memory.
if (!image.ReadPixels(info, (nint)pixels, stride, 0, 0))
{
throw new InvalidDataException("IGE: could not read the source pixels.");
}
buffer = new IGPixelBuffer
{
Data = pixels,View on GitHub (pinned to 4a3c4fecef)
Solutions
- Ensure the source SKImage is downscaled before reaching ReadPixelsInto — apply the same shrink-to-fit logic used in SkiaCodec.GetDecodableImageInfo.
- Reject files whose declared dimensions would exceed the int.MaxValue byte ceiling at the codec selection stage, before decode.
- If you control the input, feed a smaller image; if not, document that the codec cannot encode frames past ~32767² at 4 bpp.
- Cap the decoded image dimensions in the upstream pipeline (ViewerControl / PhotoManager) before invoking the plugin encoder.
Example fix
// before
var stride = checked(info.Width * 4);
var byteCount = (long)stride * info.Height;
if (byteCount > int.MaxValue) throw new InvalidDataException("IGE: frame too large to encode.");
// after — refuse earlier and tell the caller the safe ceiling
var stride = checked(info.Width * 4);
var byteCount = (long)stride * info.Height;
if (byteCount > int.MaxValue)
throw new InvalidDataException($"IGE: frame too large to encode: {info.Width}x{info.Height} = {byteCount} bytes " +
$"(max {int.MaxValue}). Downscale the source image before encoding."); Defensive patterns
Strategy: validation
Validate before calling
// Reject frames whose BGRA byte count would overflow Int32 before encoding.
const long MaxBytes = int.MaxValue;
long byteCount = (long)image.Width * image.Height * 4L;
if (byteCount > MaxBytes)
throw new InvalidDataException($"Frame {image.Width}x{image.Height} ({byteCount} bytes) exceeds the {MaxBytes}-byte ceiling; downscale first."); Type guard
static bool FitsInt32PixelCeiling(SKImage img, int bytesPerPixel) =>
(long)img.Width * img.Height * bytesPerPixel <= int.MaxValue; Try / catch
try { NativeCodecProxy.ReadPixelsInto(image, ref pixels, ref capacity, out var buf); }
catch (InvalidDataException ex) when (ex.Message.Contains("frame too large"))
{ image = DownscaleToFit(image, int.MaxValue, 4); /* retry */ } Prevention
- Apply the same shrink-to-fit logic used in SkiaCodec.GetDecodableImageInfo before invoking the plugin encoder path.
- Always compute the pixel byte count as long (width*height*bytesPerPixel) and compare against int.MaxValue.
- Reject absurd header dimensions at codec-selection time before any decode runs.
- Document the per-codec pixel ceiling so callers pre-downscale.
When it happens
Trigger: Produced at NativeCodecProxy.cs:520 when (long)stride * info.Height > int.MaxValue after the checked multiply. Happens for extremely large decoded frames — e.g. a >32767×32767 BGRA image, or any frame whose width*height*4 overflows Int32.
Common situations: An oversized raster being handed to a plugin codec as source pixels (e.g. feeding a re-encode of a gigapixel image); a codec that ignores downscale requests and decodes at full native resolution; a malformed file with absurd header dimensions that the codec naively honored.
Related errors
- IGE: could not read the source pixels.
- IGE: Unsupported image format.
- IGE: '{codec.CodecName}' could not write the image. {result.
- The image is too large to open: {fullInfo.Width:n0}x{fullInf
- IGE: Unsupported image format.
AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13).
Data as JSON: /api/errors/2eea9e7c82836413.
Report an issue: GitHub.