{"record":{"id":"5eeb7dd6226f40b2","repo":"d2phap/ImageGlass","slug":"ige-could-not-read-the-source-pixels","errorCode":null,"errorMessage":"IGE: could not read the source pixels.","messagePattern":"IGE: could not read the source pixels\\.","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"source/ImageGlass.Lib/Plugins/NativeCodecProxy.cs","lineNumber":533,"sourceCode":"        // dark-halo bug on any image with alpha.\n        var info = new SKImageInfo(image.Width, image.Height, SKColorType.Bgra8888, SKAlphaType.Unpremul);\n        var stride = checked(info.Width * 4);\n        var byteCount = (long)stride * info.Height;\n\n        if (byteCount > int.MaxValue) throw new InvalidDataException(\"IGE: frame too large to encode.\");\n\n        if (capacity < (nuint)byteCount)\n        {\n            if (pixels != null) System.Runtime.InteropServices.NativeMemory.Free(pixels);\n            pixels = (byte*)System.Runtime.InteropServices.NativeMemory.Alloc((nuint)byteCount);\n            capacity = (nuint)byteCount;\n        }\n\n        // Always copy: PeekPixels returns null for a GPU-backed image, its layout is whatever the\n        // image happens to be, and a plugin-decoded image is backed by ANOTHER plugin's memory.\n        if (!image.ReadPixels(info, (nint)pixels, stride, 0, 0))\n        {\n            throw new InvalidDataException(\"IGE: could not read the source pixels.\");\n        }\n\n        buffer = new IGPixelBuffer\n        {\n            Data = pixels,\n            Width = info.Width,\n            Height = info.Height,\n            Stride = stride,\n            PixelFormat = (int)IGPixelFormat.Bgra8Unorm,\n            ReleaseContext = HOST_OWNED_BUFFER,\n        };\n    }\n\n\n    /// <summary>\n    /// Sentinel the host puts in an encode input's <c>ReleaseContext</c> so a plugin that\n    /// symmetrically frees its buffers can detect and refuse host memory.\n    /// </summary>","sourceCodeStart":515,"sourceCodeEnd":551,"githubUrl":"https://github.com/d2phap/ImageGlass/blob/4a3c4feceffc5a8bb5e56ba836509634aaae47a9/source/ImageGlass.Lib/Plugins/NativeCodecProxy.cs#L515-L551","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nif (!image.ReadPixels(info, (nint)pixels, stride, 0, 0))\n    throw new InvalidDataException(\"IGE: could not read the source pixels.\");\n\n// after — guard disposal and GPU backing explicitly\nif (image.IsDisposed())\n    throw new ObjectDisposedException(nameof(SKImage), \"IGE: source image disposed before pixel read.\");\nif (!image.ReadPixels(info, (nint)pixels, stride, 0, 0))\n    throw new InvalidDataException($\"IGE: could not read the source pixels \" +\n        $\"(colorType={image.ColorType}, peekPixels={(image.PeekPixels().IsNull ? \"null\" : \"ok\")}).\");","handlingStrategy":"validation","validationCode":"// Use the project's IsDisposed() extension before reading pixels.\nif (image.IsDisposed()) throw new ObjectDisposedException(nameof(SKImage));\nif (image.PeekPixels().IsNull) throw new InvalidOperationException(\"Image has no readable pixel backing\");","typeGuard":"static bool IsReadableRaster(SKImage image) =>\n    !image.IsDisposed() && !image.PeekPixels().IsNull;","tryCatchPattern":"try { NativeCodecProxy.ReadPixelsInto(image, ref pixels, ref capacity, out var buf); }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"could not read the source pixels\"))\n{ _log.Error($\"ReadPixels failed for {image.ColorType}; was the image disposed or GPU-backed?\"); throw; }","preventionTips":["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."],"tags":["codec","native","skiasharp","memory","encode"],"backgroundTag":null,"analyzedSha":"4a3c4feceffc5a8bb5e56ba836509634aaae47a9","analyzedAt":"2026-08-13T16:58:15.523Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}