d2phap/ImageGlass · error · InvalidDataException

IGE: could not read the source pixels.

Error message

IGE: could not read the source pixels.

What it means

Thrown by NativeCodecProxy.ReadPixelsInto when SKImage.ReadPixels returns false. ReadPixels converts the source image into the host-supplied BGRA8888 Unpremul buffer; a false return means Skia could not read or convert the pixels — typically because the source image is GPU-backed, disposed, or in a color type that cannot be read into the requested layout.

Source

Thrown at source/ImageGlass.Lib/Plugins/NativeCodecProxy.cs:533

        // 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,
            Width = info.Width,
            Height = info.Height,
            Stride = stride,
            PixelFormat = (int)IGPixelFormat.Bgra8Unorm,
            ReleaseContext = HOST_OWNED_BUFFER,
        };
    }


    /// <summary>
    /// Sentinel the host puts in an encode input's <c>ReleaseContext</c> so a plugin that
    /// symmetrically frees its buffers can detect and refuse host memory.
    /// </summary>

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Verify the source SKImage is not disposed at the point of the call (use the SKObject_Exts.IsDisposed() guard before invoking ReadPixelsInto).
  2. If the image is GPU-backed, rasterize it to a CPU-backed SKBitmap first (SKBitmap.ReadFrom or encode to a memory stream and re-decode) before handing it to the plugin path.
  3. Ensure no concurrent disposal: hold a reference / use the MipmapTileCache lease pattern so the underlying pixels stay alive across ReadPixels.
  4. Check the source SKColorType against BGRA8888 compatibility and convert via SKCanvas first if needed.

Example fix

// before
if (!image.ReadPixels(info, (nint)pixels, stride, 0, 0))
    throw new InvalidDataException("IGE: could not read the source pixels.");

// after — guard disposal and GPU backing explicitly
if (image.IsDisposed())
    throw new ObjectDisposedException(nameof(SKImage), "IGE: source image disposed before pixel read.");
if (!image.ReadPixels(info, (nint)pixels, stride, 0, 0))
    throw new InvalidDataException($"IGE: could not read the source pixels " +
        $"(colorType={image.ColorType}, peekPixels={(image.PeekPixels().IsNull ? "null" : "ok")}).");
Defensive patterns

Strategy: validation

Validate before calling

// Use the project's IsDisposed() extension before reading pixels.
if (image.IsDisposed()) throw new ObjectDisposedException(nameof(SKImage));
if (image.PeekPixels().IsNull) throw new InvalidOperationException("Image has no readable pixel backing");

Type guard

static bool IsReadableRaster(SKImage image) =>
    !image.IsDisposed() && !image.PeekPixels().IsNull;

Try / catch

try { NativeCodecProxy.ReadPixelsInto(image, ref pixels, ref capacity, out var buf); }
catch (InvalidDataException ex) when (ex.Message.Contains("could not read the source pixels"))
{ _log.Error($"ReadPixels failed for {image.ColorType}; was the image disposed or GPU-backed?"); throw; }

Prevention

When it happens

Trigger: Produced at NativeCodecProxy.cs:533 when image.ReadPixels(info, (nint)pixels, stride, 0, 0) returns false. The source SKImage was created but its pixels cannot be copied out into the destination info (BGRA8888, Unpremul).

Common situations: Source SKImage was already disposed by another thread; the image is GPU-backed (texture-backed) and the current context cannot read it back; the source color type is incompatible with BGRA8888 readback; concurrent disposal of the underlying SKData during read.

Related errors


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