Todd Ditchendorf
Todd Ditchendorf

Reputation: 11337

Quartz2D: How to convert a clipping rect to an inverted mask at runtime?

Given:

  1. a CGContextRef (ctx) with frame {0,0,100,100}
  2. and a rect (r), with frame {25,25,50,50}

It's easy to clip the context to that rect:

CGContextClipToRect(ctx, r);

to mask out the red area below (red == mask):

enter image description here

But I want to invert this clipping rect to convert it into a clipping mask. The desired outcome is to mask the red portion below (red == mask):

enter image description here

I want to do this programmatically at runtime.

I do not want to manually prepare a bitmap image to ship statically with my app.

Given ctx and r, how can this be done at runtime most easily/straightforwardly?

Upvotes: 8

Views: 2275

Answers (4)

garafajon
garafajon

Reputation: 1468

Here is a helpful extension for implementing rob's answer

extension UIBezierPath {
    func addClipInverse() {
        let paths = UIBezierPath()
        paths.append(self)
        paths.append(.init(rect: .infinite))
        paths.usesEvenOddFillRule = true
        paths.addClip()
    }
}

Upvotes: 0

rob mayoff
rob mayoff

Reputation: 386038

Read about fill rules in the “Filling a Path” section of the Quartz 2D Programming Guide.

In your case, the easiest thing to do is use the even-odd fill rule. Create a path consisting of your small rectangle, and a much larger rectangle:

CGContextBeginPath(ctx);
CGContextAddRect(ctx, CGRectMake(25,25,50,50));
CGContextAddRect(ctx, CGRectInfinite);

Then, intersect this path into the clipping path using the even-odd fill rule:

CGContextEOClip(ctx);

Upvotes: 19

lbrndnr
lbrndnr

Reputation: 3389

You could clip the context with CGContextClipToRects() by passing rects that make up the red frame you've wanted.

Upvotes: 3

Dave DeLong
Dave DeLong

Reputation: 243156

Can you just do all your painting as normal, and then do:

CGContextClearRect(ctx, r);

after everything has been done?

Upvotes: 1

Related Questions