3
votes

I'm using the Xam.Plugin.Media in my Forms app to take pictures. I take the Image stream (GetStream) and convert to a byte[] and store in my DB. I then use that as the image source. On Android and UWP, its working fine. On iOS, if the picture is taken in portrait mode, the image once, selected is always rotated 90 deg. I will later, upload this to a server and that image could be used on a different device. For this, I also tried the GetStreamWithImageRotatedForExternalStorage but in this case, I cant see the image at all. There are bytes in the stream (I DisplayAlert the length) but the image does not display.

Any idea what I might be doing wrong?

My code:-

private async Task TakePicture(WineDetails details)
{
    await CrossMedia.Current.Initialize();

    if (CrossMedia.Current.IsCameraAvailable && CrossMedia.Current.IsTakePhotoSupported)
    {
        var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
        {
            AllowCropping = true,
            PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
            SaveToAlbum = false,
            RotateImage = true                    
        });

        if (file == null)
            return;

        using (var ms = new MemoryStream())
        {
            var stream = file.GetStreamWithImageRotatedForExternalStorage();

            stream.CopyTo(ms);
            details.LabelImage = ms.ToArray();

            details.NotifyChange("ImageSource");
        }
    }
}

The image is updated in the page via the NotifyChange and looks like this:- ImageSource.FromStream(() => new MemoryStream(this.LabelImage))

This works fine in all cases on Android and UWP, works on iOS using GetStream (except the image is incorrectly rotated) but does not work using GetStreamWithImageRotatedForExternalStorage on iOS.

Anyone else using this plugin? Any idea why GetStream returns a rotated image? Any idea why GetStreamWithImageRotatedForExternalStorage is not working?

Thanks

Update:-

Changed SaveToAlbum = true and when I open the gallery, the image is rotated 90 deg. Have RotateImage = true which could cause the issue? I'll try setting it to false. I still can't set the image source to the byte array of the image using GetStreamWithImageRotatedForExternalStorage.

using (var ms = new MemoryStream())
{                    
     file.GetStreamWithImageRotatedForExternalStorage().CopyTo(ms);
     details.LabelImage = ms.ToArray();
}

using the byte array for an image

return ImageSource.FromStream(() => new MemoryStream(this.LabelImage));

This does not work for me, GetStream works ok.

Update:-

Ok so, RotateImage = false + GetStreamWithImageRotatedForExternalStorage allows me to display the image but its still incorrectly rotated in my app and the gallery.

2
I'm using xamain forms v 3.0.0.482510 and Xam.Plugin.Media v 4.0.1.1 - Edamreed

2 Answers

3
votes

I'm using this plugin, which is similar (if not the same thing - I know James Montemagno has recently packaged/bundled his work with Xamarin).

If you check the issues board there, you'll see there are quite a few people that have similar troubles (image rotation on iOS). Almost every 'solution' mentions using GetStreamWithImageRotatedForExternalStorage.

My issue was similar - I was unable to take a photo on iOS in portrait mode, without other (non-ios Devices) rotating the image. I have tried for weeks to solve this issue, but support on the plugin seems to be quite limited.


Ultimately I had to solve this with a huge workaround - using a custom renderer extending from FFImageLoading to display our images and MetadataExtractor. We were then able to extract the EXIF data from the stream and apply a rotation transformation to the FFImageLoding image control.

The rotation information was stored in a sort of weird way, as a string. This is the method I'm using to extract the rotation information, and return the amount it needs to be rotated as an int. Note that for me, iOS was able to display the image correctly still, so it's only returned a rotation change for Android devices.

    public static int GetImageRotationCorrection(byte[] image)
    {
        try
        {
            var directories = ImageMetadataReader.ReadMetadata(new MemoryStream(image));
            if (Device.Android == "Android")
            {
                foreach (var directory in directories)
                {
                    foreach (var tag in directory.Tags)
                    {
                        if (tag.Name == "Orientation")
                        {
                            if (tag.Description == "Top, left side(Horizontal / normal)")
                                return 0;
                            else if (tag.Description == "Left side, bottom (Rotate 270 CW)")
                                return 270;
                            else if (tag.Description == "Right side, top (Rotate 90 CW")
                                return 90;
                        }
                    }
                }
            }

            return 0;
        }
        catch (Exception ex)
        {
            return 0;
        }
    }

Note that there is also a custom renderer for the image for FFImage Loading.

public class RotatedImage : CachedImage
{
    public static BindableProperty MyRotationProperty = BindableProperty.Create(nameof(MyRotation), typeof(int), typeof(RotatedImage), 0, propertyChanged: UpdateRotation);

    public int MyRotation
    {
        get { return (int)GetValue(MyRotationProperty); }
        set { SetValue(MyRotationProperty, value); }
    }

    private static void UpdateRotation(BindableObject bindable, object oldRotation, object newRotation)
    {
        var _oldRotation = (int)oldRotation;
        var _newRotation = (int)newRotation;

        if (!_oldRotation.Equals(_newRotation))
        {
            var view = (RotatedImage)bindable;
            var transformations = new System.Collections.Generic.List<ITransformation>() {
                new RotateTransformation(_newRotation)
            };
            view.Transformations = transformations;
        }
    }
}

So, in my XAML - I had declared a RotatedImage instead of the standard Image. With the custom renderer, I'm able to do this and have the image display rotated the correct amount.

image.MyRotation = GetImageRotationCorrection(imageAsBytes)

It's a totally unnecessary workaround - but these are the lengths that I had to go to to get around this issue.

I'll definitely be following along with this question, there might be someone in the community who could help us both!

2
votes

The SaveMetaData flag is causing the rotation issue. Setting it to false (default is true) now displays the photo correctly.

One side effect of that, the image no longer appears in the gallery if SaveToAlbum=true.

Still can't use an image take when using GetStreamWithImageRotatedForExternalStorage, even using FFImageLoading.