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.
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.
User contributions licensed under CC BY-SA 3.0