Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

On Android how do I make oddly shaped clipping areas?

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.

like image 269
HappyEngineer Avatar asked Feb 14 '12 22:02

HappyEngineer


1 Answers

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...

like image 187
jarrad Avatar answered Sep 25 '22 22:09

jarrad