Search code examples
c#wic

which filter for encoding a png interlaced using windows imaging components (wic) with the c# wrapper?


I'm trying to encode an image as an interlaced png using WIC. I can save the image as png without any problems and set the interlaced mode. But if i try to set a filtermode (any filter), i get the following error:

[System.Runtime.InteropServices.COMException] = {"The bitmap property type is unexpected.   (Exception from HRESULT: 0x88982F8E)"}

Do i set the value of the property bag in a wrong way? This is the code, the exception is thrown at propBag.Write.

[...]
var arg = new IPropertyBag2[1];
encoder.CreateNewFrame(out outputFrame, arg);
var propBag = arg[0];
var propertyBagOptions = new PROPBAG2[2];

propertyBagOptions[0].pstrName = "InterlaceOption";
propertyBagOptions[1].pstrName = "FilterOption";
propBag.Write(2, propertyBagOption1, new object[] { true, WICPngFilterOption.WICPngFilterAdaptive});
[...]

Thanks for Help, Stephanie


Solution

  • I believe this is because the FilterOption property needs to be an Unsigned Byte:

    | Property Name |VARTYPE|Value Range       | Default Value          | 
    |---------------|-------|------------------|------------------------|
    |InterlaceOption|VT_BOOL|TRUE/FALSE        |FALSE                   |
    |FilterOption   |VT_UI1 |WICPngFilterOption|WICPngFilterUnspecified |
    

    The underlying Write method is marked to marshall the Value as a VARIANT (i.e. UnmanagedType.Struct), which is correct:

    void Write(
       uint cProperties, 
       [MarshalAs(UnmanagedType.LPArray)] PROPBAG2[] pPropBag, 
       [MarshalAs(UnmanagedType.Struct)] ref object pvarValue
    );
    

    I'm not going to test it; because i can't, but i think the fix is to cast your PNG filter option to the C# equivalent of an unsigned byte byte.

    propBag.Write(
       1, 
       propertyBagOption, 
       UInt8(WICPngFilterOption.WICPngFilterAdaptive));
    

    The reason i think this is because from my native code, i was giving the property value as a variant. But the variant was actually a signed 32-bit (aka Int32). This caused the error:

    0x88982F8E
    

    When you look at WinCodec.h (which is the native code that .NET imaging is using), it corresponds to error:

      WINCODEC_ERR_PROPERTYUNEXPECTEDTYPE = HRESULT($88982f8E);
    

    I has to make sure to force the variant to contain a VT_UI1:

    propertyBagHelper.Write('FilterOption', VarAsType(WICPngFilterAdaptive, VT_UI1));
    

    Then it succeeded.

    This is all great from native code, when you know what's going on. The .NET/CLR/C# wrapper world loves to obfuscate everything; so you don't really know what the parameters contain when you pass them.

    Note: Any code released into public domain. No attribution required.