Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BitmapImage.SetSource is inconsistent between platforms #2377

Open
SuperJMN opened this issue Dec 21, 2019 · 4 comments
Open

BitmapImage.SetSource is inconsistent between platforms #2377

SuperJMN opened this issue Dec 21, 2019 · 4 comments

Comments

@SuperJMN
Copy link
Contributor

@SuperJMN SuperJMN commented Dec 21, 2019

Giving this converter that converts bytes[] to a BitmapImage()

    public class ByteArrayToImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            if (!(value is byte[] bytes))
                return null;

            var image = new BitmapImage();

#if WINDOWS_UWP
            using (var stream = new InMemoryRandomAccessStream())
            {
              image.SetSource(stream);
              var s = stream.AsStreamForWrite();
              s.Write(bytes, 0, bytes.Length);
            }
#else
            using (Stream stream = new MemoryStream(bytes))
            {
                image.SetSource(stream);
            }
#endif
            return image;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }

The WINDOWS_UWP part and the part for other platforms is symmetric. However, when trying in, for instance, Android, this code produces the following exception:

System.ObjectDisposedException: : 'Can not access a closed Stream.'

As a workaround, the converter should be written as:

    public class ByteArrayToImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            if (!(value is byte[] bytes))
                return null;

            var image = new BitmapImage();

#if WINDOWS_UWP
            using (var stream = new InMemoryRandomAccessStream())
            {
              image.SetSource(stream);
              var s = stream.AsStreamForWrite();
              s.Write(bytes, 0, bytes.Length);
            }
#else
            Stream stream = new MemoryStream(bytes);
            image.SetSource(stream);
#endif
            return image;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }

Please, notice that the #else part no longer encloses the stream in a using block, so the stream isn't disposed.

I don't know what are the implications of this, but I would like to point out that the stream should be disposed eventually. We need to ensure that the resources are freed properly.

I would like to put into consideration that this converter raises the concert of SetSource not behaving the same between platforms, not only in the abstractions used, but also regarding the life cycle of the underlying stream.

I personally think this should be improved.

@jeromelaban
Copy link
Member

@jeromelaban jeromelaban commented Dec 23, 2019

There are two issues here:

  • The fact that SetSource can take a .NET Stream where the UWP one cannot. This is somehow temproary until Uno supports more of the IRandomAccessStream features.
  • The fact that SetSource does not capture the stream right away. Indeed it is not, and that's definitely an issue.

For the second part, interestingly, SetSourceAsync is already creating a copy of the original stream:

public void SetSource(Stream streamSource)
{
Stream = streamSource;
}
public async Task SetSourceAsync(Stream streamSource)
{
if (streamSource != null)
{
MemoryStream copy = new MemoryStream();
await streamSource.CopyToAsync(copy);
Stream = copy;
}
else
{
//Same behavior as windows, although the documentation does not mention it!!!
throw new ArgumentException(nameof(streamSource));
}
}

This should be an easy fix.

@TopperDEL TopperDEL mentioned this issue Feb 26, 2020
3 of 3 tasks complete
@francoistanguay francoistanguay added this to the 3.1 milestone Aug 29, 2020
@francoistanguay
Copy link
Contributor

@francoistanguay francoistanguay commented Aug 29, 2020

@TopperDEL Do you need help completing the related PR?

@TopperDEL
Copy link
Contributor

@TopperDEL TopperDEL commented Aug 29, 2020

I just do not find the time, currently, to finish it. I would have to digg into the Unit-Tests for this. Would love to do it - but to be realistic I won't be able to finish it currently. Sorry for this!
@francoistanguay

@francoistanguay
Copy link
Contributor

@francoistanguay francoistanguay commented Aug 29, 2020

No worries, just wanted to make sure we weren't doing parallel efforts. Will check at some point if team can pick up on your PR. Thanks for all the work so far!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.