Nicke Manarin
Nicke Manarin

Reputation: 3358

BitmapImage from Stream returns 1x1px instead of the whole image

I have a method that opens a FileStream and creates a BitmapImage, by using the StreamSource property.

Somehow, in a single machine, trying to open a big image (6000x4000px) results in the method returning a 1x1px image instead.

First I thought that the image was being loaded from a shared folder on the local network, but It was stored in the downloads folder of the same computer.

I saw that the image was "blocked/locked" by Windows because it was downloaded from an unverified source, so I opened Properties and unlocked it. Trying to load the image again resulted in the same problem.

The image was fully downloaded.

Background information:

The machine with the problem:

My machine (it works as expected):

Code:

public static BitmapSource SourceFrom(this string fileSource, int? size = null)
{
    using (var stream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;

        if (size.HasValue)
        {
            //It's not possible to get the size of image while opening, so get from another place.
            var refSize = fileSource.ScaledSize(); //Gets the size of the image.

            if (refSize.Height > refSize.Width)
                bitmapImage.DecodePixelHeight = size.Value;
            else
                bitmapImage.DecodePixelWidth = size.Value;
        }

        bitmapImage.StreamSource = stream;               
        bitmapImage.EndInit();
        bitmapImage.Freeze(); //Just in case you want to load the image in another thread.
        return bitmapImage;
    }
}

Usage:

var image = "C:\Image.jpg".SourceFrom(); //Without any other parameter.

Question:

Probable answer:

Working code:

using (var stream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
    using (var memory = new MemoryStream())
    {
        stream.CopyTo(memory);
        memory.Position = 0;
//...

Just replace this part of the code and use the memory variable instead of stream while setting the StreamSource object.

Upvotes: 1

Views: 1445

Answers (3)

Elias Alter
Elias Alter

Reputation: 31

The BitmapImage creates a default image with 1x1px when decoding of the image fails. You need to register the DecodeFailed event to detect this.

Exception decodeEx = null;
using (var fileStream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
    bitmapImage.StreamSource = fileStream;
    bitmapImage.DecodeFailed += (_, e) => decodeEx = e.ErrorException;
    bitmapImage.EndInit();

    if (decodeEx != null)
        throw decodeEx;

    bitmapImage.Freeze();
    return bitmapImage;
}

In my case it turned out to be a OutOfMemoryException. Indeed decoding only failed when the memory usage was high and the native functionwhich is actually called by BitmapImage (using unmanaged memory) was probably unable to allocate enough memory.

Upvotes: 2

Bilal
Bilal

Reputation: 1382

i had the same problem, the CacheOption is not in the right place on your code!! juste add it befor the endInit();

 source.CacheOption = BitmapCacheOption.OnLoad;

like this ->

                BitmapImage source = new BitmapImage();
                source.BeginInit();
                source.CacheOption = BitmapCacheOption.OnLoad;
                source.StreamSource = fs;                  
                source.EndInit();

Upvotes: 1

Clemens
Clemens

Reputation: 128061

It seems that when the image file is very large, or for some other reason the source stream can't be read immediately, you'll have to copy the source stream to an intermediate MemoryStream, and assign that to the StreamSource property of the BitmapImage:

using (var fileStream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
using (var memoryStream = new MemoryStream())
{
    fileStream.CopyTo(memoryStream);
    memoryStream.Position = 0;

    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.CacheOption = BitmapCacheOption.OnLoad
    bitmapImage.StreamSource = memoryStream;               
    bitmapImage.EndInit();
    bitmapImage.Freeze();
    return bitmapImage;
}

Upvotes: 2

Related Questions