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

  1. Ensure the source SKImage is downscaled before reaching ReadPixelsInto — apply the same shrink-to-fit logic used in SkiaCodec.GetDecodableImageInfo.
  2. Reject files whose declared dimensions would exceed the int.MaxValue byte ceiling at the codec selection stage, before decode.
  3. If you control the input, feed a smaller image; if not, document that the codec cannot encode frames past ~32767² at 4 bpp.
  4. 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

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


AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13). Data as JSON: /api/errors/2eea9e7c82836413. Report an issue: GitHub.