Canvas.clipPath(Path) not clipping as expected
Asked Answered
M

2

22

I'm trying to clip a canvas drawing operation to an arc-shaped wedge. However, I'm not getting the intended result after setting the clipping path to the canvas.

For illustration, here is what I'm doing:

enter image description here

path.reset();

//Move to point #1
path.moveTo(rect.centerX(), rect.centerY());

//Per the documentation, this will draw a connecting line from the current
//position to the starting position of the arc (at 0 degrees), add the arc
//and my current position now lies at #2.
path.arcTo(rect, 0, -30);

//This should then close the path, finishing back at the center point (#3)
path.close();

This works, and when I simply draw this path (canvas.drawPath(path, paint)) it draws the wedge as shown above. However, when I set this path as the canvas's clipping path and draw into it:

//I've tried it with and without the Region.Op parameter
canvas.clipPath(path, Region.Op.REPLACE);
canvas.drawColor(Color.BLUE);

I get the following result instead (the wedge is left just to show reference):

enter image description here

So it instead seems to clip to the bounding rect of the Path, and not the Path itself. Any ideas what's happening here?

EDIT Just as an update, I've found a much more efficient way of doing this that allows for hardware acceleration. First, draw the entire image (that you'd be clipping) into an offscreen bitmap. Make a BitmapShader using this Bitmap, set that shader to a Paint, then draw the wedge path using that Paint object:

drawMyBitmap(bitmap);
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(shader);

@Override
public void onDraw(Canvas canvas) {
    canvas.drawArc(rect,         //The rectangle bounding the circle
                   startAngle,   //The angle (CW from 3 o'clock) to start
                   sweepAngle,   //The angle (CW from 3 o'clock) of the arc
                   true,         //Boolean of whether to draw a filled arc (wedge)
                   paint         //The paint with the shader attached
    );
}
Mandarin answered 2/12, 2012 at 19:19 Comment(5)
Are you using HC or above or otherwise using hardware acceleration? developer.android.com/guide/topics/graphics/hardware-accel.html. If so, clipPath is unsupported and problematic.Mckoy
@Simon: Oh my god. Heh. Sadly I've been referring to that document a lot this week, but I totally overlooked that. Disabling HW Accel worked perfectly! If you'll post this as an answer I'll accept it. You're a lifesaver!Mandarin
Glad to have helped. Good luck.Mckoy
Beautiful question!Adagio
How exactly do you accomplish this?: "First, draw the entire image (that you'd be clipping) into an offscreen bitmap"Stopcock
M
14

Are you using HC or above or otherwise using hardware acceleration?

If so, clipPath is unsupported and problematic.

developer.android.com/guide/topics/graphics/hardware-accel.html.

Mckoy answered 2/12, 2012 at 19:42 Comment(3)
That was the problem. :) Moved the clipping part of the drawing operation into an offscreen bitmap, then drew it back to the HW accelerated canvas and it works like a charm! Thanks again!Mandarin
@kcoppock That sounds a bit backwards. Why don't you just convert the view's layer type into software, and still do your drawing in onDraw()?Herzegovina
@RichardJ.RossIII That would have made more sense. :) I was a bit unfamiliar with that at the time. I've edited in the new way that I'm handling this problem now, thanks for the input though!Mandarin
G
4

OP's question is specifically about using a clipping region and has been answered by @Simon. Bear in mind, however, that there's a more straightforward way of drawing a filled arc:

Create a Paint:

mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Style.FILL);
mPaint.setAntiAlias(true);

When drawing, simply draw the path:

canvas.drawPath(path, mPaint);
Grasmere answered 5/9, 2013 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.