On Android how do I make oddly shaped clipping areas?

HappyEngineer picture HappyEngineer · Feb 14, 2012 · Viewed 16.2k times · Source

Here is how to create a clipping area the shape of a circle:

Path path = new Path();
path.addCircle(200,200,100,Direction.CW);
c.clipPath(path); // c is a Canvas

Now there's a clipping area on the Canvas which prevents drawing anything outside the bounds of that circle. But, what if I want to have the clipping area be shaped like a donut (or whatever)?

I tried playing around with creating a second Path and using toggleInverseFillType on it and then adding that to the original path, but that doesn't seem to work.

Alternatively, instead of using a Path, is it possible to just create a Bitmap to use as a mask and set it as a clipping mask on the Canvas somehow?

EDIT: The answer is exactly what I needed with one small addition. When doing multiple operations on a canvas, always use Op.REPLACE on the first clipPath call. That will replace any existing clipPath on that Canvas.

For reference, here is what I discovered what the 6 different Region.Op values mean. Imagine a venn diagram with 2 circles. "B" is the part where the 2 circles overlap. "A" is the non-overlapping left circle. "C" is the non-overlapping right circle.

c.clipPath(a,Region.Op.REPLACE);
c.clipPath(b,???);

Region.Op.DIFFERENCE         -> A..            
Region.Op.INTERSECT          -> .B.            
Region.Op.REPLACE            -> .BC            
Region.Op.REVERSE_DIFFERENCE -> ..C            
Region.Op.UNION              -> ABC
Region.Op.XOR                -> A.C

The "." indicates the part that isn't drawn. Sorry if that's not particularly clear. It's hard to describe well without graphics.

Answer

jarrad picture jarrad · Feb 15, 2012

From the Canvas javadoc:

Canvas#clipPath(Path path, Region.Op op) - Modify the current clip with the specified path.

So, for your donut example:

  1. Create 2 Paths. One for the larger circle, one for the smaller circle.
  2. Canvas#clipPath( Path ) with larger circle Path.
  3. Call the Canvas#clipPath( Path, Region.Op ) method on your canvas with the smaller circle Path for the first argument and the appropriate Region.Op enum value for the second argument.

    Path largePath = new Path();
    largePath.addCircle(200,200,100,Direction.CW);
    Path smallPath = new Path();
    smallPath.addCircle(200,200,40,Direction.CW);
    c.clipPath(largePath); // c is a Canvas
    c.clipPath(smallPath, Region.Op.DIFFERENCE);
    

Again, modify the Region.Op enum value to get different effects...