8

I have divergent needs for the image returned from the iPhone camera. My app scales the image down for upload and display and, recently, I added the ability to save the image to the Photos app.

At first I was assigning the returned value to two separate variables, but it turned out that they were sharing the same object, so I was getting two scaled-down images instead of having one at full scale.

After figuring out that you can't do UIImage *copyImage = [myImage copy];, I made a copy using imageWithCGImage, per below. Unfortunately, this doesn't work because the copy (here croppedImage) ends up rotated 90º from the original.

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{   
    // Resize, crop, and correct orientation issues
    self.originalImage = [info valueForKey:@"UIImagePickerControllerOriginalImage"];
    UIImageWriteToSavedPhotosAlbum(originalImage, nil, nil, nil);
    UIImage *smallImage = [UIImage imageWithCGImage:[originalImage CGImage]]; // UIImage doesn't conform to NSCopy

    // This method is from a category on UIImage based on this discussion:
    // http://discussions.apple.com/message.jspa?messageID=7276709
    // It doesn't rotate smallImage, though: while imageWithCGImage returns
    // a rotated CGImage, the UIImageOrientation remains at UIImageOrientationUp!
    UIImage *fixedImage = [smallImage scaleAndRotateImageFromImagePickerWithLongestSide:480];
    ...
}

Is there a way to copy the UIImagePickerControllerOriginalImage image without modifying it in the process?

clozach
  • 5,118
  • 5
  • 41
  • 54
  • 2
    Did you end up fixing this? If so, can you edit the question with the solution? Thanks! – pws5068 Aug 09 '11 at 00:36
  • Sorry. I haven't had a chance to work on that code in awhile. Will definitely update this once I'm back in that project...which should be, er, some time this year. – clozach Aug 17 '11 at 12:46
  • UIImage is immutable. So it should not matter whether two pointers point to the same object or different objects. – user102008 Jan 16 '14 at 03:04
  • Yeah. If it's not obvious from the timestamps, I never did get back to that project, and as far as I know it's been iceboxed by the original owners, so I'm unlikely to ever pick a winning answer as I'm not in a position to test the newer responses. – clozach Jan 19 '14 at 02:52
  • Just FYI: I ran into similar issue and tried deep copy as suggested in [another thread](http://stackoverflow.com/questions/4002040/making-deep-copy-of-uiimage) and it served my need. The suggested answer below did not work for me as the two copies were still modified as if they shared the same data (I used GLKTextureLoader and sometimes they modified the data in UIImage with options like bottom left origin). – HuaTham Oct 03 '14 at 05:15

5 Answers5

17

This seems to work but you might face some memory problems depending on what you do with newImage:

CGImageRef newCgIm = CGImageCreateCopy(oldImage.CGImage);
UIImage *newImage = [UIImage imageWithCGImage:newCgIm scale:oldImage.scale orientation:oldImage.imageOrientation];
zuwhan
  • 178
  • 1
  • 6
  • I think, you also need to add `CGImageRelease(newCgIm);` after that. I used it without `CGImageRelease` in drawing app and it caused memory warnings and crashes. – tagirkaZ Mar 24 '16 at 16:27
9

This should work:

UIImage *newImage = [UIImage imageWithCGImage:oldImage.CGImage];
teradyl
  • 2,584
  • 1
  • 25
  • 34
jklapwyk
  • 113
  • 1
  • 8
5

Copy backing data and rotate it

This question asks a common question about UIImage in a slightly different way. Essentially, you have two related problems - deep copying and rotation. A UIImage is just a container and has an orientation property that is used for display. A UIImage can contain its backing data as a CGImage or CIImage, but most often as a CGImage. The CGImage is a struct of information that includes a pointer to the underlying data and if you read the docs, copying the struct does not copy the data. So...

Deep copying

As I'll get to in the next paragraph deep copying the data will leave the image rotated because the image is rotated in the underlying data.

UIImage *newImage = [UIImage imageWithData:UIImagePNGRepresentation(oldImage)];

This will copy the data but will require setting the orientation property before handing it to something like UIImageView for proper display.

Another way to deep copy would be to draw into the context and grab the result. Assume a zebra.

UIGraphicsBeginImageContext(zebra!.size)
zebra!.drawInRect(CGRectMake(0, 0, zebra!.size.width, zebra!.size.height))
let copy = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Deep copy and Rotation

Rotating a CGImage has been already been answered. It also happens that this rotated image is a new CGImage that can be used to create a UIImage.

Community
  • 1
  • 1
Cameron Lowell Palmer
  • 21,528
  • 7
  • 125
  • 126
  • 1
    Thanks, Cameron. As mentioned above, I can't really confirm that this solves the problem I was having 5 years ago, but, since you took the time to teach rather than just demonstrate, it's yours! – clozach Feb 08 '16 at 22:41
  • I encountered the two pieces of information separately over time. It isn't immediately obvious to the users of UIImage that it is just a wrapper or that the underlying data might be upside down and backwards. I suppose it makes sense when someone says it, but such is magic. – Cameron Lowell Palmer Feb 09 '16 at 06:40
3
UIImage *newImage = [UIImage imageWithData:UIImagePNGRepresentation(oldImage)];
tidwall
  • 6,881
  • 2
  • 36
  • 47
  • A quick test showed the same problem when using imageWithData: instead of imageWithCGImage:. :( – clozach Oct 11 '10 at 05:14
  • 1
    This is actually a working method that copies the underlying data and generates a new UIImage with a new CGImage backing. If the image is rotated, it is because the underlying data is rotated and the UIImage requires an orientation value to be set to correct the rotation. – Cameron Lowell Palmer Feb 06 '16 at 22:07
0

I think you need to create an image context (CGContextRef). Draw the UIImage.CGImage into the context with method CGContextDrawImage(...), then get the image from the context with CGBitmapContextCreateImage(...). With such routine, I'm sure you can get the real copy of the image you want. Hope it's helped you.

Alpha Law
  • 13
  • 3