10

I'm new to Quartz. I have 2 images, a background, and a mask with cutout shape that I want to lay over the background in order to cut out a section. The resulting image should be the shape of the cutout. This is my mask (the shape in the middle is 0 alpha):

image mask

And this is my code:

UIView *canvas = [[[sender superview] subviews] objectAtIndex:0];

UIGraphicsBeginImageContext(canvas.bounds.size);
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef cgContext = CGBitmapContextCreate(NULL, canvas.bounds.size.width, canvas.bounds.size.height, 8, 0, colourSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colourSpace);

CGImageRef maskImage = [[UIImage imageNamed:@"Mask.png"] CGImage];
CGContextClipToMask(cgContext, CGRectMake(0, 0, canvas.frame.size.width, canvas.frame.size.height), maskImage);

CGImageRef maskedImageRef = CGBitmapContextCreateImage(cgContext);
CGContextRelease(cgContext);

UIImage *maskedImage = [UIImage imageWithCGImage:maskedImageRef];
UIGraphicsEndImageContext();

[button setBackgroundImage:maskedImage forState:UIControlStateNormal];

Except nothing appears... Any idea where I'm going wrong? Many thanks.

Smikey
  • 8,106
  • 3
  • 46
  • 74
  • You don't seem to draw anything. You clip out nothing from nothing, and get nothing. As expected. Between clipping to mask and creating the image, you should draw something. – Svein Halvor Halvorsen May 30 '12 at 11:47

1 Answers1

11

After you clip context with mask you should actually draw something on that context.

Besides that:

  • you should use UIGraphicsBeginImageContextWithOptions to support scale factor.
  • you're creating two context, one with UIGraphicsBeginImageContext and the other with CGBitmapContextCreate. One is enough :)
  • you should "flip" alpha of your mask, because according to docs The source samples of mask determine which points of the clipping area are changed. The other words transparent will become transparent

Simple example code, where I clip image inside imageView and then set it back:

UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(context, 0.0, self.imageView.frame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGImageRef maskImage = [[UIImage imageNamed:@"mask.png"] CGImage];
CGContextClipToMask(context, self.imageView.bounds, maskImage); 

CGContextTranslateCTM(context, 0.0, self.imageView.frame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

[[self.imageView image] drawInRect:self.imageView.bounds];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

self.imageView.image = image;

Example using CGImageCreateWithMask:

UIImage *image = [UIImage imageNamed:@"image"];

CGImageRef originalMask = [UIImage imageNamed:@"mask"].CGImage;
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(originalMask),
                                    CGImageGetHeight(originalMask),
                                    CGImageGetBitsPerComponent(originalMask),
                                    CGImageGetBitsPerPixel(originalMask),
                                    CGImageGetBytesPerRow(originalMask),
                                    CGImageGetDataProvider(originalMask), nil, YES);

CGImageRef maskedImageRef = CGImageCreateWithMask(image.CGImage, mask);

UIImage *maskedImage = [UIImage imageWithCGImage:maskedImageRef scale:image.scale orientation:image.imageOrientation];

CGImageRelease(mask);
CGImageRelease(maskedImageRef);
Evgeny Shurakov
  • 6,062
  • 2
  • 28
  • 22
  • I see, thanks! Just a couple of questions. Why do you need to call TranslateCTM twice? Also, I'm getting the inverse effect - the alpha 0 part of the mask is being cut out, whereas the black part of the image is being drawn. According to the docs, I need to mask the image with an image, not a mask, by using CGImageCreateWithMask but where do I use this? – Smikey May 30 '12 at 10:33
  • UIKit and CGGraphics have different coordinate systems, so we need to translate matrix. "The alpha 0 part of the mask is being cut out" - that's exactly what documentation for `CGContextClipToMask` and `CGImageCreateWithMask` tells you. Example of `CGImageCreateWithMask` is added. – Evgeny Shurakov May 30 '12 at 11:44
  • Unfortunately non of the solutions here work now in ios 11 as I tested, I tried to mask two UIImages and used both these methods. the first one squeezes the image after clipping and the second one returns {0.0} @Evgeny Shurakov – Reza.Ab Feb 01 '18 at 01:43