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

fix(ios): corrupted image on ios 13 #501

Closed
wants to merge 18 commits into from

Conversation

l0stpenguin
Copy link

@l0stpenguin l0stpenguin commented Sep 13, 2019

Platforms affected

ios 13.0, 13.1

Motivation and Context

When taking an image on ios 13 via camera, the path is returned correctly but the temp image is corrupted.
Here is the issue:
#492

Description

Upon debugging, i found that the image data in self.data in CDVCamera.m is being released/corrupted after setting the metadata via imagePickerControllerReturnImageResult
The solution is to create a copy of the NSData and use that to write the image on disk.

Testing

Open camera on ios 13. Take an image. The image returned should be displayed correctly

Checklist

  • I've run the tests to see all new and existing tests pass
  • I added automated test coverage as appropriate for this change
  • Commit is prefixed with (platform) if this change only applies to one platform (e.g. (android))
  • If this Pull Request resolves an issue, I linked to the issue in the text above (and used the correct keyword to close issues using keywords)
  • I've updated the documentation if necessary

Copy link
Member

@timbru31 timbru31 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the PR and the debugging! Some minor comments from my side.

Besides that, does the change work on lower iOS versions, too?

src/ios/CDVCamera.m Outdated Show resolved Hide resolved
@@ -670,17 +670,21 @@ - (void)imagePickerControllerReturnImageResult
{
CDVPictureOptions* options = self.pickerController.pictureOptions;
CDVPluginResult* result = nil;

NSMutableData *dest_data = [NSMutableData data];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use camelCase for variable names. Besides that, can we find a better name than dest_data?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timbru31 i renamed it to imageDataWithExif. is that fine for you?

@l0stpenguin
Copy link
Author

l0stpenguin commented Sep 17, 2019

Thanks a lot for the PR and the debugging! Some minor comments from my side.

Besides that, does the change work on lower iOS versions, too?

Yes i have tested it on ios 12 too.

Edit: This was tested successfully on ios 10 also as denoted by this comment:
#492 (comment)

@amayvs
Copy link

amayvs commented Oct 3, 2019

@mevinDhun @timbru31 Is there any ETA on merging this PR?

@anubhavberry
Copy link

anubhavberry commented Oct 19, 2019

@mevinDhun I've taken the latest from the fork and tried to test on 13.1.3. The photo doesn't show up

@eastwillie
Copy link

I've tried on 13.1.3 wkWebview, it didn't work.

@l0stpenguin
Copy link
Author

@eastwillie @anubhavberry i have tested it on ios 13.1 and 13.2 and confirm it is working properly.

@eastwillie
Copy link

eastwillie commented Oct 31, 2019

@mevinDhun
Here is my code and after I take a photo, there was an error:
finalize:2667: image destination must have at least one image.
Seems to me that the picture hasn't been saved into tmp somehow? Or I made a config mistake?

`
openCamera(camera = true) {

        const options = {

            quality: 50,
            targetWidth: 750,
            targetHeight: 750,
            destinationType: window.Camera.DestinationType.FILE_URI,
            sourceType: camera ? window.Camera.DestinationType.FILE_URI : window.Camera.PictureSourceType.SAVEDPHOTOALBUM,
            encodingType: window.Camera.EncodingType.JPEG,
            mediaType: window.Camera.MediaType.PICTURE,
            allowEdit: true,
            correctOrientation: true,
        };
        navigator.camera.getPicture((imageUri) => {
            this.$emit('upload', {
                success: true,
                uri: [imageUri],
            });
        }, () => {
            this.$emit('upload', {
                success: false,
                uri: [],
            });
        }, options);

},
`

Here is all the error:

2019-10-31 15:04:39.376208+0400 Cluelez[3788:1203412] getCFDataBytesAtOffset:1209: : *** ERROR *** CGImageSource was created with data size: 50178 - current size is only: 0
2019-10-31 15:04:39.376632+0400 Cluelez[3788:1203412] getCFDataBytesAtOffset:1217: : *** ERROR *** requested range: 0-4096 - is outside input CFData range (0-0)
2019-10-31 15:04:39.379552+0400 Cluelez[3788:1203412] addImageFromSource:2367: image 0 of source is nil
2019-10-31 15:04:39.379766+0400 Cluelez[3788:1203412] finalize:2667: image destination must have at least one image
2019-10-31 15:04:39.606316+0400 Cluelez[3788:1203412] THREAD WARNING: ['File'] took '10.974121' ms. Plugin should use a background thread.
2019-10-31 15:04:53.006347+0400 Cluelez[3788:1203412] Can't end BackgroundTask: no background task exists with identifier 4 (0x4), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.

@ryaa
Copy link

ryaa commented Nov 3, 2019

I have been experience the same problem on iOS 13.2 and the problem seems to be that when we copy the image with metadata (exif data) from the source to destination in imagePickerControllerReturnImageResult method of CDVCamera.m the resulting file is of 0 size and CGImageDestinationFinalize returns false (fails!). If I disable geolocation for camera by setting the below in config.xml and everything works well.

<preference name="CameraUsesGeolocation" value="false" />

I tried two fixes and both seems to be working:

  1. I changed imagePickerControllerReturnImageResult in CDVCamera.m based on this suggestion vlinde/cordova-plugin-camera-with-exif@99465b4

I removed

NSData* data_content = [self.data mutableCopy];

and added instead

UIImage *image = [UIImage imageWithData:self.data];
CGImageRef imageRef = image.CGImage;

plus I replaced this line

 CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);

with this line

CGImageDestinationAddImage(destinationImage , imageRef, (CFDictionaryRef)self.metadata);
  1. I also tried the fix as suggested by @mevinDhun

Both fixes seems to be working.

I'm still wondering why CGImageDestinationAddImageFromSource fails while CGImageDestinationAddImage works fine when saving the image with the metadata. Any ideas?

@eastwillie
Copy link

@ryaa Thanks, I saw a dialog ask permission for location, but less than 1 second and disappeared. That explained it.

Copy link
Member

@erisu erisu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes in package.json and plugin.xml should be reverted. Also the branch will need to be rebased with master as there is conflicting files.

@erisu erisu changed the title Fixed corrupted image on ios 13 when using camera fix(ios): corrupted image on ios 13 Aug 11, 2021
@erisu
Copy link
Member

erisu commented Mar 22, 2022

Closed by #712

@erisu erisu closed this Mar 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants