CGContextClipToMask returning blank image

clipping, masking, objective-c, quartz-2d, quartz-graphics

Solution

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);

Problem

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): 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.

Original source