Skip to content

Commit 978d5a2

Browse files
mlazarimeta-codesync[bot]
authored andcommitted
Fix expo-image-manipulator cropped image orientation (#55379)
Summary: #54184 introduced a change that uses `CGImageSourceCreateImageAtIndex` instead of `CGImageSourceCreateThumbnailAtIndex` to decode full-sized images. However, unlike `CGImageSourceCreateThumbnailAtIndex` which rotates the image according to the orientation in the image's metadata, `CGImageSourceCreateThumbnailAtIndex` doesn't do that. Since the UIImage initializer is always passed `UIImageOrientationUp` for orientation, this results in the returned `UIImage` having wrong orientation. This can be reproduced for example if you take a photo with `react-native-vision-camera` in an orientation different from "up" (e.g. taking a photo on an iPhone SE 2020 in a vertical position results in a photo with "landscape-right" orientation), then use PhotoManipulator.crop() to crop the image, then show the resulted image URI in an Image component - the image is shown in a wrong orientation. This change fixes that by making sure the UIImage is passed the correct image orientation when `CGImageSourceCreateImageAtIndex` is used. ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [IOS] [FIXED] - Fix expo-image-manipulator cropped image orientation bypass-github-export-checks Pull Request resolved: #55379 Test Plan: ### 0.82.1 https://github.com/mlazari/RNImageRepro/tree/rn-0.82.1 https://github.com/user-attachments/assets/297f6419-980b-49b5-873c-46c43bcc0fd6 ### 0.83.1 https://github.com/mlazari/RNImageRepro/tree/0.83.1 https://github.com/user-attachments/assets/286144e7-2196-49cb-b151-26f1a6ff274d ### 0.83.1 + patch with the changes in this PR https://github.com/mlazari/RNImageRepro/tree/0.83.1-with-patch https://github.com/user-attachments/assets/72ed6e74-a2cd-4b0d-b529-d91d0fdb3d52 Reviewed By: javache Differential Revision: D92148021 Pulled By: cipolleschi fbshipit-source-id: 0e485379b533fb415cb9d7946276a3de41657892
1 parent 485bbda commit 978d5a2

1 file changed

Lines changed: 37 additions & 1 deletion

File tree

packages/react-native/Libraries/Image/RCTImageUtils.mm

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ static CGImagePropertyOrientation CGImagePropertyOrientationFromUIImageOrientati
5555
}
5656
}
5757

58+
static UIImageOrientation UIImageOrientationFromCGImagePropertyOrientation(CGImagePropertyOrientation imageOrientation)
59+
{
60+
switch (imageOrientation) {
61+
case kCGImagePropertyOrientationUp:
62+
return UIImageOrientationUp;
63+
case kCGImagePropertyOrientationDown:
64+
return UIImageOrientationDown;
65+
case kCGImagePropertyOrientationLeft:
66+
return UIImageOrientationLeft;
67+
case kCGImagePropertyOrientationRight:
68+
return UIImageOrientationRight;
69+
case kCGImagePropertyOrientationUpMirrored:
70+
return UIImageOrientationUpMirrored;
71+
case kCGImagePropertyOrientationDownMirrored:
72+
return UIImageOrientationDownMirrored;
73+
case kCGImagePropertyOrientationLeftMirrored:
74+
return UIImageOrientationLeftMirrored;
75+
case kCGImagePropertyOrientationRightMirrored:
76+
return UIImageOrientationRightMirrored;
77+
default:
78+
return UIImageOrientationUp;
79+
}
80+
}
81+
5882
CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode)
5983
{
6084
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
@@ -269,6 +293,7 @@ BOOL RCTUpscalingRequired(
269293
}
270294
NSNumber *width = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
271295
NSNumber *height = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
296+
NSNumber *orientationNum = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
272297
CGSize sourceSize = {width.doubleValue, height.doubleValue};
273298
CFRelease(imageProperties);
274299

@@ -293,12 +318,15 @@ BOOL RCTUpscalingRequired(
293318
CGImageRef imageRef;
294319
BOOL createThumbnail = targetPixelSize.width != 0 && targetPixelSize.height != 0 &&
295320
(sourceSize.width > targetPixelSize.width || sourceSize.height > targetPixelSize.height);
321+
UIImageOrientation orientation = UIImageOrientationUp;
296322

297323
if (createThumbnail) {
298324
CGFloat maxPixelSize = fmax(targetPixelSize.width, targetPixelSize.height);
299325

300326
// Get a thumbnail of the source image. This is usually slower than creating a full-sized image,
301327
// but takes up less memory once it's done.
328+
// It rotates the image according to the orientation from metadata, so we'll pass `UIImageOrientationUp`
329+
// to the `UIImage` initializer
302330
imageRef = CGImageSourceCreateThumbnailAtIndex(
303331
sourceRef, 0, (__bridge CFDictionaryRef) @{
304332
(id)kCGImageSourceShouldAllowFloat : @YES,
@@ -313,6 +341,14 @@ BOOL RCTUpscalingRequired(
313341
sourceRef, 0, (__bridge CFDictionaryRef) @{
314342
(id)kCGImageSourceShouldAllowFloat : @YES,
315343
});
344+
345+
// Unlike `CGImageSourceCreateThumbnailAtIndex` (with `kCGImageSourceCreateThumbnailWithTransform` set to YES),
346+
// `CGImageSourceCreateImageAtIndex` doesn't rotate the image to keep the orientation, so we'll need to pass
347+
// the actual orientation (if present) to the `UIImage` initializer
348+
if (orientationNum) {
349+
orientation = UIImageOrientationFromCGImagePropertyOrientation(
350+
(CGImagePropertyOrientation)[orientationNum unsignedIntValue]);
351+
}
316352
}
317353

318354
CFRelease(sourceRef);
@@ -321,7 +357,7 @@ BOOL RCTUpscalingRequired(
321357
}
322358

323359
// Return image
324-
UIImage *image = [UIImage imageWithCGImage:imageRef scale:destScale orientation:UIImageOrientationUp];
360+
UIImage *image = [UIImage imageWithCGImage:imageRef scale:destScale orientation:orientation];
325361
CGImageRelease(imageRef);
326362
return image;
327363
}

0 commit comments

Comments
 (0)