11

I'm working with an image where the user has selected part of it using UIBezierPath. How can I delete/clear out/make transparent everything that is not part of that selection?

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
MB.
  • 4,167
  • 8
  • 52
  • 79

2 Answers2

20

With one path it's very easy. Just set the path as the clipping path:

- (UIImage *)maskImage:(UIImage *)originalImage toPath:(UIBezierPath *)path {
    UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, 0);
    [path addClip];
    [originalImage drawAtPoint:CGPointZero];
    UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return maskedImage;
}

If you want to use the union of multiple paths, it's harder, because Quartz doesn't have any functions that directly compute the union of two paths. One way is to fill each path one by one into a mask, and then draw the image through the mask:

- (UIImage *)maskedImage
{
    CGRect rect = CGRectZero;
    rect.size = self.originalImage.size;
    UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0); {
        [[UIColor blackColor] setFill];
        UIRectFill(rect);
        [[UIColor whiteColor] setFill];
        for (UIBezierPath *path in self.paths)
            [path fill];
    }
    UIImage *mask = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); {
        CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
        [self.originalImage drawAtPoint:CGPointZero];
    }
    UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return maskedImage;
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Wow. Had no idea it was so easy. I was looking in all the wrong places. Clipping restricts the drawing area, right? – MB. Nov 29 '11 at 10:15
  • Huh, I have multiple nsbezierpaths, or if I have one nsbezierpath where strokes are on top of each other this gets screwed. – MB. Nov 29 '11 at 10:22
  • Sorry, this actually doesn't work. I have multiple UIBezierPath which don't necessarily represent shapes that clip wants, but selections. Clipping on multiple UIBezierPaths that sometimes are one over the other doesn't return the proper image. – MB. Nov 29 '11 at 10:52
  • @robmayoff Following your first example work well except that my image keep its original file size. I cannot adjust the image to its content using the ContentMode or changing its frame. Do you know hoy can I do it? [CurrentResult](https://postimg.org/image/qcaoqc4dj/) – dario Nov 23 '16 at 11:36
  • This Swift answer shows how to clip using a UIBezierPath and get an image with an alpha channel in PNG format. It also show how to save the clip to the camera roll and how to place it into the pasteboard. https://stackoverflow.com/questions/49853122/swift-clipping-image-with-uibezierpath/65533953#65533953 – user3408691 Jan 01 '21 at 22:04
0

I tried the code of union multiple paths, and it doesn't work.

Actually, if the union of paths don't overlap with each other, we append one path to another and crop with the final path.

UIGraphicsBeginImageContextWithOptions(originalImg.size, NO, 0);
[path1 appendPath:path2]; // append path2 to path1
[path1 addClip];

[originalImg drawAtPoint:CGPointZero];
result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
FreeTymeKiyan
  • 309
  • 1
  • 6