jamesmontemagno / MediaPlugin

Take & Pick Photos and Video Plugin for Xamarin and Windows
MIT License
712 stars 358 forks source link

Photos captured on iOS display rotated on Android and Web(base64). Using GetStreamWithImageRotatedForExternalStorage #501

Open Bejasc opened 6 years ago

Bejasc commented 6 years ago

Similar to #333. - though it is closed, and slightly differs in experience.

Interestingly enough, this issue only happens for us for photos either picked or taken on iOS (Android is fine). On any iOS device, this image will display fine, however on any Android or Web device (using base64 string) - the image appears to be rotated.

I've also tried this on the newest pre-release (3.1.3.222) however the issue still remains.

Image captured on Android Android Display: Okay iOS Display : Okay Web Display (base64) : Okay

Image Selected on Android Android Display: Okay iOS Display: Okay Web Display (base64) : Okay

Image Captured on iOS Android Display: Rotated iOS Display: Okay Web Display(base64): Rotated

Image Selected on iOS Android Display: Rotated iOS Display: Okay Web Display(base64): Rotated

Tested with.. Xamarin Forms 4.8.0.757 MediaPlugin 3.1.3, iOS 11.2 iPad Pro, Samsung Galaxy S8, Samsung Galaxy S8+, Web browser (custom application) reading the base64 image.

Our code - Passed the image control (to update the 'confirm image', and a page for referencing where to use DisplayAlert.

public static async Task<byte[]> GetImage(Image ImageXAMLControl,ContentPage page)
        {
            if (await page.DisplayAlert("Choose Source", "Take new image or choose from Gallery?", "New Photo", "Gallery"))
            {
                return await ImageFromCamera(ImageXAMLControl);
            }
            else
            {
                return await ImageFromGallery(ImageXAMLControl);
            }
        }

ImageFromCamera method, similar method for ImageFromGallery (uses PickPhotoAsync instead). As you can see, it's using GetStreamWithImageRotatedForExternalStorage when getting the stream. Previously using just GetStream - but this modification seems to be making no difference for us.

        private static async Task<byte[]> ImageFromCamera(Image ImageXAMLControl)
        {
            try
            {
                await CrossMedia.Current.Initialize();

                var file = await CrossMedia.Current.TakePhotoAsync(
                    new StoreCameraMediaOptions
                    {
                        SaveToAlbum = true,
                        PhotoSize = PhotoSize.Medium,
                        CompressionQuality = 92
                    });

                if (file == null)
                {
                    return new byte[0];
                    //return;
                }

                //https://github.com/jamesmontemagno/MediaPlugin/issues/333
                //was just GetStream, iOS rotate issue
                ImageXAMLControl.Source = ImageSource.FromStream(() =>
                {
                    var stream = file.GetStreamWithImageRotatedForExternalStorage();
                    return stream;
                });

                byte[] byteImage;

                using (var memoryStream = new MemoryStream())
                {
                    file.GetStream().CopyTo(memoryStream);
                    byteImage = memoryStream.ToArray();
                    file.Dispose();
                }

                return byteImage;
            }
            catch(Exception ex)
            {
                return null;
            }
        }
100s commented 6 years ago

I'm having the same issue.

jorgedevs commented 6 years ago

Hello, Is there any followup to fix this issue?

100s commented 6 years ago

Using GetStreamWithImageRotatedForExternalStorage works for me on iOS Android and Windows

Bejasc commented 6 years ago

Thanks @100s . Please note however, I've reported this as an issue where it is already using GetStreamWithImageRotatedForExternalStorage

@iruel865 as a workaround, we're using a combination of MetadataExtractor and FFImageLoading, which allows us to detect if any exif data is saved with the images. We extract the orientation from that information, and then use a custom renderer, extended from one of FFImageLoading's controls to apply a transformation directly to the image control, and rotate the display of the image.

Similarly, where we're displaying these images on web, I believe we've used some javascript/jquery to perform the same method.

It's a fairly complicated and long winded work around, but it's the only way we were able to have our images display properly. The issue is definitely still present, I would expect to be able to take images and display them properly using only this plugin, without having to rely on any other plugins/workarounds like I've detailed above.

jorgedevs commented 6 years ago

Hello @Bejasc ,

I will try that. Thank you very much for your input. I even wanted to just lock the orientation of the camera altogether, but forking the repo, I couldnt even get it to run, I dont know the version stack this plugin is in to get it working.

edamreed commented 6 years ago

I have a similar problem with 4.0.1.1. I'm storing the image in a db and also setting the source directly to an image control.

 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 stream = file.GetStream())
                {
                    using (var ms = new MemoryStream())
                    {
                        stream.CopyTo(ms);
                        details.LabelImage = ms.ToArray();
                        WineImage.Source = ImageSource.FromStream(() => new MemoryStream(details.LabelImage));
                    }
                }
            }
        }

When using GetStream, on iOS, the image (if taken in portrait) is rotated 90 deg clockwise. If I use GetStreamWithImageRotatedForExternalStorage, I don't get an image at all, no error, just no image. On Android and UWP, the image is correctly displayed and saved.

Also, I can't debug this issue as I dont have access to a Mac with the required OS version. I'm using AppCenter to do the build / deploy.

Bejasc commented 6 years ago

https://stackoverflow.com/questions/51200013/xamarin-forms-xam-plugin-media-take-picture-on-ios?answertab=active#tab-top

I've shared some more details on my workaround here. As you can see it's a rather large workaround, but there's more information there.

saidbehaine commented 4 years ago

This configuration worked for me:

  1. Take the photo
    var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
    {
    PhotoSize = PhotoSize.MaxWidthHeight,
    MaxWidthHeight = 500,
    CompressionQuality = Device.RuntimePlatform == Device.Android ? 60 : 60,
    DefaultCamera = CameraDevice.Rear,
    AllowCropping = false,
    SaveMetaData = true
    });
  2. Save as ImageSource to use it in your XAML
    
    if (file == null)
    return;

var photoSource = ImageSource.FromStream(() => { var stream = file.GetStream();

return stream;

});

3. Convert to byte array for send to any API
```c#
byte[] photoArray = null;
if (file != null)
{
    using (var ms = new MemoryStream())
    {
        file.GetStreamWithImageRotatedForExternalStorage().CopyTo(ms);

        photoArray = ms.ToArray();
    }
}

MediaPlugin: v5.0.1 Xamarin.Forms : v4.6 iOS : v13.5 Version of VS (for Mac): v8.6.2 Device Tested On: iPhone 11