Clipping the current context so that it's masked by a path

4.8k views Asked by At

Given a path which is drawn to the context and then converted to a CGImageRef, how would I clip the context (which has a supplied foreground image drawn) to it so that the said context is masked by the CGImage (that previously was a path)?

The code in question below should better illustrate my point if it's not totally clear. Note that all of this is called in a UIView's drawRect: method and that the scaledImageRectangle(UIImage *image, CGRect rect) function merely returns a rectangle.

    // Close the path
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke);

    // Mask the image
    CGImageRef mask = CGBitmapContextCreateImage(context);
    [foregroundImage drawInRect:scaledImageRectangle(foregroundImage, rect)];
    CGContextClipToMask(context, scaledImageRectangle(foregroundImage, rect), mask);

    // Finally draw the masked image
    UIImage *maskedImage = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
    [maskedImage drawInRect:scaledImageRectangle(foregroundImage, rect)];

For example, here is an image with the black stroke representing the path Before mask

And here is what the image would look like when masked After mask

1

There are 1 answers

0
Rob On

I might be inclined to build a path as touches come in (I use UIBezierPath), and then your custom view can use that path for both clipping the image, as well as for stroking the path around that clipped image:

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSaveGState(context);

    if (self.isClipped) {
        CGContextAddPath(context, [self.path CGPath]);
        CGContextClip(context);
    }

    [self.image drawInRect:self.bounds];

    CGContextRestoreGState(context);

    [[UIColor blackColor] setStroke];
    self.path.lineWidth = 4.0;
    [self.path stroke];
}

unclipped clipped

If you want to draw the path as touches come in, I personally would render that via a separate CAShapeLayer (to avoid the unnecessary redrawing of the image).