d2phap/ImageGlass · error · InvalidDataException

IGE: Native codec '{CodecId}' returned an unsupported pixel

Error message

IGE: Native codec '{CodecId}' returned an unsupported pixel buffer layout.

What it means

Thrown by NativeCodecProxy.WrapPluginBufferAsImage when SKImage.FromPixels returns null even though the pixel buffer, format, and dimensions individually passed validation. Skia rejected the combined layout — usually because the stride is too small for the declared width/color type, or the total byte count (stride * height) does not match the SKData length passed to FromPixels.

Source

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

            CodecApiPtr = (nint)_codecApi,
            Buffer = buffer,
            PluginId = _plugin.PluginId,
            LiveToken = _plugin.LiveToken,
        };

        // Wrap the plugin pointer in SKData with a release callback, then build the SKImage.
        var byteCount = checked(buffer.Stride * buffer.Height);
        var data = SKData.Create((nint)buffer.Data, byteCount,
            PluginPixelBufferRelease.ReleaseData, carrier);

        var image = SKImage.FromPixels(info, data, buffer.Stride);

        if (image is null)
        {
            // Skia rejected the layout; release the plugin buffer ourselves.
            data?.Dispose();
            carrier.ReleaseFromHost();
            throw new InvalidDataException(
                $"IGE: Native codec '{CodecId}' returned an unsupported pixel buffer layout.");
        }

        return image;
    }


    /// <summary>
    /// Maps an <see cref="IGPixelFormat"/> to the corresponding Skia color/alpha types.
    /// Returns (<see cref="SKColorType.Unknown"/>, <see cref="SKAlphaType.Unknown"/>) when the
    /// host has no compatible Skia format for the buffer.
    /// </summary>
    internal static (SKColorType ColorType, SKAlphaType AlphaType) MapPixelFormat(IGPixelFormat format)
    {
        return format switch
        {
            IGPixelFormat.Bgra8Unorm => (SKColorType.Bgra8888, SKAlphaType.Unpremul),
            IGPixelFormat.Rgba8Unorm => (SKColorType.Rgba8888, SKAlphaType.Unpremul),

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Have the plugin compute Stride as at least width * bytesPerPixel for the chosen IGPixelFormat, with any padding it actually writes.
  2. Verify the byte count the plugin allocated matches stride * height exactly (the host wraps stride*height in SKData at NativeCodecProxy.cs:837).
  3. Check the IGPixelBuffer struct field order in the plugin matches the SDK header exactly — a swapped Stride is the most subtle cause.
  4. Disable the plugin and decode with a built-in codec until the layout bug is fixed upstream.

Example fix

// before
var image = SKImage.FromPixels(info, data, buffer.Stride);
if (image is null) { data?.Dispose(); carrier.ReleaseFromHost(); throw new InvalidDataException(...); }

// after — validate stride before Skia sees it so the error names the real problem
var minStride = info.RowBytes;
if (buffer.Stride < minStride)
    throw new InvalidDataException(
        $"IGE: Native codec '{CodecId}' returned stride {buffer.Stride} < required {minStride} " +
        $"for {buffer.Width}x{buffer.Height} {info.ColorType}.");
var image = SKImage.FromPixels(info, data, buffer.Stride);
if (image is null) { data?.Dispose(); carrier.ReleaseFromHost(); throw new InvalidDataException(...); }
Defensive patterns

Strategy: validation

Validate before calling

// Validate stride vs width/bytesPerPixel before handing to Skia.
int bpp = info.BytesPerPixel;
if (buffer.Stride < info.RowBytes)
    throw new InvalidDataException($"Plugin stride {buffer.Stride} < required {info.RowBytes}");
if ((long)buffer.Stride * buffer.Height > int.MaxValue)
    throw new InvalidDataException("Plugin buffer byte count overflows Int32");

Type guard

static bool IsConsistentLayout(in IGPixelBuffer b, SKImageInfo info) =>
    b.Stride >= info.RowBytes && (long)b.Stride * b.Height <= int.MaxValue;

Try / catch

try { image = proxy.WrapPluginBufferAsImage(in buffer, colorSpace); }
catch (InvalidDataException ex) when (ex.Message.Contains("unsupported pixel buffer layout"))
{ _log.Warn($"Plugin {proxy.CodecId} stride/layout rejected by Skia (stride={buffer.Stride}, w={buffer.Width}, h={buffer.Height})"); return BuiltInFallback(metadata); }

Prevention

When it happens

Trigger: Produced at NativeCodecProxy.cs:839 when SKImage.FromPixels(info, data, buffer.Stride) returns null. The plugin's IGPixelBuffer passed field-level checks but the stride/width/height combination is inconsistent for Skia.

Common situations: Plugin sets Stride smaller than width*bytesPerPixel (Skia requires stride >= info.RowBytes); plugin computes Stride without alignment padding the plugin actually wrote; plugin declares a width in pixels but lays out bytes with a different bpp than MapPixelFormat assumed; ABI field-order mismatch swapping Stride with another field.

Related errors


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