Search code examples
iphoneiosuiimageretina-display

How to merge two images for retina display (@2x)?


I want to add an UIImage in the UIImageView of the UITableViewCell inside the UITableViewController. This works fine:

cell.imageView.image = [UIImage imageNamed:@"myImage.png"];

But now, I want to merge two images and use the code I've found here:

UIImage *mergedImage = [MyUtils mergeImage:@"myBackImage.png" withImage:@"myFrontImage.png"];

This works very good with an old iPhone (320x480) but not on retina display. In my project, there are "@2x"-versions of every image. I analyze the code and have an explanation for the problem.

a) on iPhone 3 without merging (WORKS)
- image width: 28.0
- image height: 29.0
- image scale: 1.0

b) on iPhone 3 with merging (WORKS)
- image width: 28.0
- image height: 29.0
- image scale: 1.0

c) on iPhone 4 without merging (WORKS)
- image width: 28.0
- image height: 29.0
- image scale: 2.0

d) on iPhone 4 with merging (WORKS NOT)
- image width: 56.0
- image height: 58.0
- image scale: 1.0

As you can see, the merging converts the "28.0/29.0/2.0" to an "56.0/58.0/1.0" image. And this images gets displayed to big on retina display.

How can I correctly merge two images to get an image with a scale of 2.0?


Appendix: the code I'm using for merging two images (from here)

+ (UIImage*)mergeImage:(UIImage*)first withImage:(UIImage*)second
{
 // get size of the first image
 CGImageRef firstImageRef = first.CGImage;
 CGFloat firstWidth = CGImageGetWidth(firstImageRef);
 CGFloat firstHeight = CGImageGetHeight(firstImageRef);

 // get size of the second image
 CGImageRef secondImageRef = second.CGImage;
 CGFloat secondWidth = CGImageGetWidth(secondImageRef);
 CGFloat secondHeight = CGImageGetHeight(secondImageRef);

 // build merged size
 CGSize mergedSize = CGSizeMake(MAX(firstWidth, secondWidth), MAX(firstHeight, secondHeight));

 // capture image context ref
 UIGraphicsBeginImageContext(mergedSize);

 //Draw images onto the context
 [first drawInRect:CGRectMake(0, 0, firstWidth, firstHeight)];
 [second drawInRect:CGRectMake(0, 0, secondWidth, secondHeight)];

 // assign context to new UIImage
 UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

 // end context
 UIGraphicsEndImageContext();

 return newImage;
}

Solution

  • You need to compute the merged size based on the UIImage sizes, not the CGImage sizes, and you need to create the graphics context using the largest of the image's scales. Try this:

    CGSize firstSize = first.size;
    CGSize secondSize = second.size;
    CGSize mergedSize = CGSizeMake(MAX(firstSize.width, secondSize.width),
        MAX(firstSize.height, secondSize.height));
    CGFloat mergedScale = MAX(first.scale, second.scale);
    
    UIGraphicsBeginImageContextWithOptions(mergedSize, NO, mergedScale);
    
    [first drawAtPoint:CGPointZero];
    [second drawAtPoint:CGPointZero];
    
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;