Search code examples
c#windowsdirect2dwic

Decoding image from stream using WIC


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.


Solution

  • 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.