Vinay Putta
Vinay Putta

Reputation: 71

Merge two PNG UIImages in iOS without losing transparency

I have two png format images and both have transparency defined. I need to merge these together into a new png image but without losing any of the transparency in the result.

+(UIImage *) combineImage:(UIImage *)firstImage  colorImage:(UIImage *)secondImage
{
   UIGraphicsBeginImageContext(firstImage.size);
   CGContextRef context = UIGraphicsGetCurrentContext();

   CGContextSaveGState(context);

   CGContextTranslateCTM(context, 0, firstImage.size.height);
   CGContextScaleCTM(context, 1.0, -1.0);
   CGRect rect = CGRectMake(0, 0, firstImage.size.width, firstImage.size.height);
   // draw white background to preserve color of transparent pixels
   CGContextSetBlendMode(context, kCGBlendModeDarken);
   [[UIColor whiteColor] setFill];
   CGContextFillRect(context, rect);

   CGContextSaveGState(context);
   CGContextRestoreGState(context);

   // draw original image
   CGContextSetBlendMode(context, kCGBlendModeDarken);
   CGContextDrawImage(context, rect, firstImage.CGImage);

   // tint image (loosing alpha) - the luminosity of the original image is preserved
   CGContextSetBlendMode(context, kCGBlendModeDarken); 
   //CGContextSetAlpha(context, .85);
   [[UIColor colorWithPatternImage:secondImage] setFill];
   CGContextFillRect(context, rect);


   CGContextSaveGState(context);
   CGContextRestoreGState(context);

   // mask by alpha values of original image
   CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
   CGContextDrawImage(context, rect, firstImage.CGImage);

   // image drawing code here
   CGContextRestoreGState(context);
   UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();

   return coloredImage;
}

needed any help to improve my code in performance.

Thanks in advance

Upvotes: 1

Views: 1598

Answers (2)

Kevin Owens
Kevin Owens

Reputation: 548

Used the code given in Vinay's question and Aaron's comments to develop this hybrid that overlays any number of images:

/**
 Returns the images overplayed atop each other according to their array position, with the first image being bottom-most, and the last image being top-most.
 - parameter images: The images to overlay.
 - parameter size: The size of resulting image. Any images not matching this size will show a loss in fidelity.
 */
func combinedImageFromImages(images: [UIImage], withSize size: CGSize) -> UIImage
{
  // Setup the graphics context (allocation, translation/scaling, size)
  UIGraphicsBeginImageContext(size)       
  let context = UIGraphicsGetCurrentContext()
  CGContextTranslateCTM(context, 0, size.height)
  CGContextScaleCTM(context, 1.0, -1.0)
  let rect = CGRectMake(0, 0, size.width, size.height)

  // Combine the images
  for image in images {
    CGContextDrawImage(context, rect, image.CGImage)
  }
  let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()

  return combinedImage
}

Upvotes: 0

Aaron Golden
Aaron Golden

Reputation: 7102

First of all, those calls to CGContextSaveGState and CGContextRestoreGState, one after the other with nothing in between, isn't doing anything for you. See this other answer for an explanation of what CGContextSaveGState and CGContextRestoreGState do: CGContextSaveGState vs UIGraphicsPushContext

Now, it's not 100% clear to me what you mean by "merging" the images. If you just want to draw one on top of the other, and blend their colors using a standard blending mode then you just need to change those blend mode calls to pass kCGBlendModeNormal (or just leave out the calls to CGContextSetBlendMode entirely. If you want to mask the second image by the first image's alpha value then you should draw the second image with the normal blend mode, then switch to kCGBlendModeDestinationIn and draw the first image.

I'm afraid I'm not really sure what you're trying to do with the image tinting code in the middle, but my instinct is that you won't end up needing it. You should be able to get most merging effects by just drawing one image, then setting the blending mode, then drawing the other image.

Also, the code you've got there under the comment "draw white background to preserve color of transparent pixels" might draw white through the whole image, but it certainly doesn't preserve the color of transparent pixels, it makes those pixels white! You should remove that code too unless you really want your "transparent" color to be white.

Upvotes: 3

Related Questions