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
- Verify the source SKImage is not disposed at the point of the call (use the SKObject_Exts.IsDisposed() guard before invoking ReadPixelsInto).
- 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.
- Ensure no concurrent disposal: hold a reference / use the MipmapTileCache lease pattern so the underlying pixels stay alive across ReadPixels.
- 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
- Hold a reference/lease (MipmapTileCache lease pattern) so the source SKImage is not disposed mid-read.
- Rasterize GPU-backed images to a CPU SKBitmap before handing them to the plugin encode path.
- Always check SKObject_Exts.IsDisposed() before calling ReadPixels.
- Verify the source SKColorType is convertible to BGRA8888; convert via SKCanvas first if needed.
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
- IGE: frame too large to encode.
- IGE: Native codec '{CodecId}' returned an unsupported pixel
- IGE: Native codec '{CodecId}' returned an unsupported pixel
- IGE: Unsupported image format.
- IGE: '{codec.CodecName}' could not write the image. {result.
AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13).
Data as JSON: /api/errors/5eeb7dd6226f40b2.
Report an issue: GitHub.