CGContextClip() using an inverted path
Asked Answered
F

2

6

I want to have clipped drawing on my CGContext but I need it just the other way round, i.e. I want to add several rectangles to my clipping path using CGContextAddRect() and the clipping should be done in the way that all the rectangles that I have added to my clipping path stay unaffected by any drawing operations. Normally, it's just the other way round, i.e. drawing functions draw to the rectangles that have been added to the clipping path and leave the areas that haven't been added to the clipping path unaffected. So I guess what I need is just a way to reverse the clipping path before calling CGContextClip(). Back in the QuickDraw days, this could by easily done by using regions and then calling XorRgn() for every rectangle. But with Quartz it seems to be more complicated. Does anyone have an easy solution for achieving this or do I need to do all these path reversing calculations on my own?

Frida answered 17/5, 2012 at 14:53 Comment(0)
F
7

You could add your entire bounds as a rectangle, then add the rectangles you want to exclude from drawing and use CGContextEOClip.

Example:

- (void)drawRect:(NSRect)dirtyRect
    CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
    //Fill the background with gray:
    CGContextSetRGBFillColor(ctx, 0.5, 0.5, 0.5, 1);
    CGContextFillRect(ctx, NSRectToCGRect(self.bounds));
    CGContextAddRect(ctx, NSRectToCGRect(self.bounds));
    //Add some rectangles:
    CGContextAddRect(ctx, CGRectMake(10, 10, 100, 100));
    CGContextAddRect(ctx, CGRectMake(120, 120, 50, 100));
    //Clip:
    CGContextEOClip(ctx);
    //Fill the entire bounds with red:
    CGContextSetRGBFillColor(ctx, 1.0, 0.0, 0.0, 1.0);
    CGContextFillRect(ctx, NSRectToCGRect(self.bounds));
}

The effect becomes more obvious if you draw an image at the end instead of filling a red rectangle.

Flatcar answered 17/5, 2012 at 16:18 Comment(4)
Nice idea, but this doesn't work when the clipping rectangles overlap.Frida
Ok, I think I've got it now. I have to call CGContextEOClip() once for each clipping rectangle I'm adding. Then it works even with overlapping rectangles! Thanks a lot for the hint!Frida
many thanks @Flatcar and @Andreas! I've been looking for this answer for ages but never figured out how to fix the overlapping rectangles issue. perfect :)Princeling
What if your inverted path is made out of multiple subpaths? EOClip would create when if they overlap...Athamas
I
2

One way would be to draw the clipping path to a bitmap context, invert the resulting bitmap, and use it as a mask on the original image by calling CGContextClipToMask().

Intercourse answered 17/5, 2012 at 15:51 Comment(1)
Thanks, this looks like a feasible way indeed. Still, I'm wondering if there is a possibility without have to go through the bitmap mask route.Frida

© 2022 - 2024 — McMap. All rights reserved.