jamesmontemagno / MediaPlugin

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

GetStreamWithImageRotatedForExternalStorage on iOS generates invalid image #485

Open ozzy1873 opened 6 years ago

ozzy1873 commented 6 years ago

Bug Information

Version Number of Plugin: 3.1.3 Device Tested On: iPhone 5S, iPhone X simulator Simulator Tested On: 11.2 Version of VS: 15.5.7 Version of Xamarin: XF 2.5.0.280555 On the server: .NET Framework 4.6.1

Steps to reproduce the Behavior

Pick an existing photo and call GetStreamWithImageRotatedForExternalStorage to get the stream. Then load the stream with System.Drawing.Image.FromStream.

Expected Behavior

A valid System.Drawing.Image should be created.

Actual Behavior

The file stream is invalid and causes an exception to be thrown with "Parameter is not valid". This happens nearly 100% of the time on iOS, never seen it on Android. Once in a while it will work OK on iOS, but very seldom. If I change GetStreamWithImageRotatedForExternalStorage to GetStream, the exception is never thrown; however, the photos are rotated incorrectly.

Code snippet

MediaFile _file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions()
{
CompressionQuality = 100,
PhotoSize = PhotoSize.MaxWidthHeight,
MaxWidthHeight = 1024,
RotateImage = true,
SaveMetaData = true
});
using (var strmToUpload = _file.GetStreamWithImageRotatedForExternalStorage())
{
   ... upload to server ...
}

On the server:

try
{
   using (Image srcImage = Image.FromStream(filestream))
   {
      ...do something with srcImage...
   }
}
catch (Exception e)
{
   Exception caught with e.Message == "Parameter is not valid"
}
reense commented 6 years ago

I have exactly the same issue. On the backend I use System.Drawing to read the base64 image, however i'm also getting "Parameter is not valid". @ozzy1873 Did you ever figure out a workaround?

ozzy1873 commented 6 years ago

Sorry it took so long for me to get back to you. Yes, I found a workaround. This code has been working well for months:

` file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions() { CompressionQuality = 100, PhotoSize = PhotoSize.MaxWidthHeight, MaxWidthHeight = MAX_WIDTH_HEIGHT, RotateImage = Device.RuntimePlatform != Device.iOS, SaveMetaData = true });

using (var strmToUpload = await PhotoUtils.GetPhotoStreamToUpload(file)) { if (strmToUpload == null) { await _dialogService.ShowMessage("Sorry, there is a problem with that photo.", "Camera"); return; }

                …do something with photo...
            }

    public static async Task<Stream> GetPhotoStreamToUpload(MediaFile file)
    {
        byte[] resizedImage = null;

            var resizer = DependencyService.Get<IImageResizer>();

            try
            {
                resizedImage = await resizer.ResizeImage(file.GetStream(), MAX_WIDTH_HEIGHT, MAX_WIDTH_HEIGHT);
            }
            catch (Exception e)
            {
            }
        }

        return new MemoryStream(resizedImage);
    }

    public Task<byte[]> ResizeImageIOS(System.IO.Stream imageData, float width, float height)
    {
        UIImage originalImage = ImageFromStream(imageData);
        var resizedImage = RotateCameraImageToProperOrientation(originalImage, Math.Max(width, height));
        var imageBytes = resizedImage.AsJPEG().ToArray();
        resizedImage.Dispose();
        var bytes = Task.FromResult(imageBytes);
        return bytes;
    }

    public UIKit.UIImage ImageFromStream(System.IO.Stream data)
    {
        if (data == null)
        {
            return null;
        }
        //
        UIKit.UIImage image;
        try
        {
            image = new UIKit.UIImage(Foundation.NSData.FromStream(data));
        }
        catch (Exception e)
        {
            Console.WriteLine("Image load failed: " + e.Message);
            return null;
        }
        return image;
    }

    public static UIImage RotateCameraImageToProperOrientation(UIImage imageSource, nfloat maxResolution)
    {
        var imgRef = imageSource.CGImage;

        var width = (nfloat)imgRef.Width;
        var height = (nfloat)imgRef.Height;

        var bounds = new CGRect(0, 0, width, height);

        nfloat scaleRatio = 1;

        if (width > maxResolution || height > maxResolution)
        {
            scaleRatio = (nfloat)Math.Min(maxResolution / bounds.Width, maxResolution / bounds.Height);
            bounds.Height = bounds.Height * scaleRatio;
            bounds.Width = bounds.Width * scaleRatio;
        }

        var transform = CGAffineTransform.MakeIdentity();
        var orient = imageSource.Orientation;
        var imageSize = new CGSize(imgRef.Width, imgRef.Height);
        nfloat storedHeight;

        switch (imageSource.Orientation)
        {
            case UIImageOrientation.Up:
                transform = CGAffineTransform.MakeIdentity();
                break;

            case UIImageOrientation.UpMirrored:
                transform = CGAffineTransform.MakeTranslation(imageSize.Width, 0.0f);
                transform = CGAffineTransform.Scale(transform, -1.0f, 1.0f);
                break;

            case UIImageOrientation.Down:
                transform = CGAffineTransform.MakeTranslation(imageSize.Width, imageSize.Height);
                transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI);
                break;

            case UIImageOrientation.DownMirrored:
                transform = CGAffineTransform.MakeTranslation(0.0f, imageSize.Height);
                transform = CGAffineTransform.Scale(transform, 1.0f, -1.0f);
                break;

            case UIImageOrientation.Left:
                storedHeight = bounds.Height;
                bounds.Height = bounds.Width;
                bounds.Width = storedHeight;
                transform = CGAffineTransform.MakeTranslation(0.0f, imageSize.Width);
                transform = CGAffineTransform.Rotate(transform, 3.0f * (nfloat)Math.PI / 2.0f);
                break;

            case UIImageOrientation.LeftMirrored:
                storedHeight = bounds.Height;
                bounds.Height = bounds.Width;
                bounds.Width = storedHeight;
                transform = CGAffineTransform.MakeTranslation(imageSize.Height, imageSize.Width);
                transform = CGAffineTransform.Scale(transform, -1.0f, 1.0f);
                transform = CGAffineTransform.Rotate(transform, 3.0f * (nfloat)Math.PI / 2.0f);
                break;

            case UIImageOrientation.Right:
                storedHeight = bounds.Height;
                bounds.Height = bounds.Width;
                bounds.Width = storedHeight;
                transform = CGAffineTransform.MakeTranslation(imageSize.Height, 0.0f);
                transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI / 2.0f);
                break;

            case UIImageOrientation.RightMirrored:
                storedHeight = bounds.Height;
                bounds.Height = bounds.Width;
                bounds.Width = storedHeight;
                transform = CGAffineTransform.MakeScale(-1.0f, 1.0f);
                transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI / 2.0f);
                break;

            default:
                break;
        }

        UIGraphics.BeginImageContext(bounds.Size);
        var context = UIGraphics.GetCurrentContext();

        if (orient == UIImageOrientation.Right || orient == UIImageOrientation.Left)
        {
            context.ScaleCTM(-scaleRatio, scaleRatio);
            context.TranslateCTM(-height, 0);
        }
        else
        {
            context.ScaleCTM(scaleRatio, -scaleRatio);
            context.TranslateCTM(0, -height);
        }

        context.ConcatCTM(transform);
        context.DrawImage(new CGRect(0, 0, width, height), imgRef);

        var imageCopy = UIGraphics.GetImageFromCurrentImageContext();
        UIGraphics.EndImageContext();

        return imageCopy;
    }

`