Decoding image from stream using WIC

0

I'm trying to use WIC to load images in C#, with SharpDX as a wrapper (this is a Direct2D application written in .NET). I can load my image perfectly fine by creating a BitmapDecoder like so:

C# Code:

new BitmapDecoder(Factory, fileName, NativeFileAccess.Read, DecodeOptions.CacheOnLoad)

C++ Equivalent:

hr = m_pIWICFactory->CreateDecoderFromFilename(
    fileName,                
    NULL,                     
    GENERIC_READ,              
    WICDecodeMetadataCacheOnLoad, 
    &pIDecoder);

By the way, fileName contains the path to a JPEG image. Now, this works perfectly well, but it breaks down if I try to load the image using a stream instead:

C# Code:

new BitmapDecoder(Factory, stream, DecodeOptions.CacheOnLoad)

C++ Equivalent:

hr = m_pIWICFactory->CreateDecoderFromStream(
    pIWICStream,                   
    NULL,
    WICDecodeMetadataCacheOnLoad,
    &pIDecoder);

This is literally the same data as is present in the JPEG file, and it works for the most part just like the previous way. But it breaks when I call SharpDX.Direct2D1.Bitmap.FromWicBitmap() (ID2D1RenderTarget::CreateBitmapFromWicBitmap). The former approach works flawlessly, while the latter approach causes this function to return HRESULT 0x88982F60 (WINCODEC_ERR_BADIMAGE).

To be clear: there is no difference in how the image is loaded other than loading it from a stream instead of a file name.

Why is this happening, and how do I fix it? I need to be able to load images that I have access to only as streams, and I do not want to save them to temporary files to accomplish that.

c#
windows
direct2d
wic
asked on Stack Overflow Feb 26, 2018 by laptou • edited Feb 26, 2018 by laptou

1 Answer

0

These are the methods that I created for decoding the images:

    internal void Load(Stream stream)
    {
        using(var decoder = new BitmapDecoder(Factory, stream, DecodeOptions.CacheOnLoad))
            Decode(decoder);
    }

    internal void Load(string fn)
    {
        using (var decoder =
            new BitmapDecoder(Factory, fn, NativeFileAccess.Read, DecodeOptions.CacheOnLoad))
            Decode(decoder);
    }

Apparently, if a stream is used, then the decoder cannot be disposed of while you're still reading the image. Go figure. Anyway, this ended up working:

    internal void Load(Stream stream)
    {
        var decoder = new BitmapDecoder(Factory, stream, DecodeOptions.CacheOnLoad);
        Decode(decoder);
    }

    internal void Load(string fn)
    {
        using (var decoder =
            new BitmapDecoder(Factory, fn, NativeFileAccess.Read, DecodeOptions.CacheOnLoad))
            Decode(decoder);
    }

But now I have to worry about disposing of the decoder later.

Update:

This bizarre difference in behaviour is caused by an implementation detail of SharpDX:

public BitmapDecoder(ImagingFactory factory, Stream streamRef, SharpDX.WIC.DecodeOptions metadataOptions)
{
    internalWICStream = new WICStream(factory, streamRef);
    factory.CreateDecoderFromStream(internalWICStream, null, metadataOptions, this);
}

internalWICStream is a field held by the BitmapDecoder class, and it is disposed when the class is disposed. This is probably the root of the problem. Unlike the overload which uses a file name instead:

public BitmapDecoder(ImagingFactory factory, string filename, System.Guid? guidVendorRef, NativeFileAccess desiredAccess, SharpDX.WIC.DecodeOptions metadataOptions)
{
    factory.CreateDecoderFromFilename(filename, guidVendorRef, (int)desiredAccess, metadataOptions, this);
}

internalWICStream is not set, because the stream is managed by Windows. Thus, no problems appear when the managed BitmapDecoder object is disposed.

answered on Stack Overflow Feb 26, 2018 by laptou • edited Feb 26, 2018 by laptou

User contributions licensed under CC BY-SA 3.0