21

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
    );
}
Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • 1
    Are you using HC or above or otherwise using hardware acceleration? http://developer.android.com/guide/topics/graphics/hardware-accel.html. If so, clipPath is unsupported and problematic. – Simon Dec 02 '12 at 19:30
  • 1
    @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! – Kevin Coppock Dec 02 '12 at 19:35
  • Glad to have helped. Good luck. – Simon Dec 02 '12 at 19:41
  • Beautiful question! – swooby Jun 22 '16 at 01:46
  • How exactly do you accomplish this?: "First, draw the entire image (that you'd be clipping) into an offscreen bitmap" – Interkot Oct 25 '17 at 00:36

2 Answers2

13

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.

Jong
  • 9,045
  • 3
  • 34
  • 66
Simon
  • 14,407
  • 8
  • 46
  • 61
  • 1
    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! – Kevin Coppock Dec 02 '12 at 19:43
  • 1
    @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()`? – Richard J. Ross III Mar 22 '13 at 17:10
  • @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! – Kevin Coppock Mar 22 '13 at 18:09
3

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);
Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187