I'm using the clip() function on a canvas.
Results:
As you can see the chrome version has horrible jaggies / aliasing along the edges. How do I fix this?
Code to reproduce:
<canvas id="test" width="300" height="300"></canvas>
<script type="text/javascript">
cv = document.getElementById("test");
ctx = cv.getContext("2d");
var im = new Image();
im.onload = function () {
ctx.beginPath();
ctx.arc(110, 110, 100, 0, 2*Math.PI, true);
ctx.clip();
ctx.drawImage(im, 0, 0);
}
im.src = "http://placekitten.com/300/300";
</script>
If you're doing complex, layered drawing, you can use globalCompositeOperation to emulate clipping in a second, scratch canvas. You can then use drawImage to copy the scratch canvas back into the original canvas. I can't guarantee the performance of this approach, but it's the only way I know to get what you want.
//set-up - probably only needs to be done once
var scratchCanvas = document.createElement('canvas');
scratchCanvas.width = 100;
scratchCanvas.height = 100;
var scratchCtx = scratchCanvas.getContext('2d');
//drawing code
scratchCtx.clearRect(0, 0, scratchCanvas.width, scratchCanvas.height);
scratchCtx.globalCompositeOperation = 'source-over'; //default
//Do whatever drawing you want. In your case, draw your image.
scratchCtx.drawImage(imageToCrop, ...);
//As long as we can represent our clipping region as a single path,
//we can perform our clipping by using a non-default composite operation.
//You can think of destination-in as "write alpha". It will not touch
//the color channel of the canvas, but will replace the alpha channel.
//(Actually, it will multiply the already drawn alpha with the alpha
//currently being drawn - meaning that things look good where two anti-
//aliased pixels overlap.)
//
//If you can't represent the clipping region as a single path, you can
//always draw your clip shape into yet another scratch canvas.
scratchCtx.fillStyle = '#fff'; //color doesn't matter, but we want full opacity
scratchCtx.globalCompositeOperation = 'destination-in';
scratchCtx.beginPath();
scratchCtx.arc(50, 50, 50, 0, 2 * Math.PI, true);
scratchCtx.closePath();
scratchCtx.fill();
//Now that we have a nice, cropped image, we can draw it in our
//actual canvas. We can even draw it over top existing pixels, and
//everything will look great!
ctx.drawImage(scratchCanvas, ...);
The reason that we do this in a scratch canvas is that destination-in is a pretty destructive operation. If you had already drawn some things into the main canvas (perhaps you put down a nice gradient in the background), and then wanted to draw a clipped image, the clipping circle would also clip out everything you had already drawn. Of course, if your particular situation is simpler (maybe ALL you want to draw is a clipped image), then you can forego the scratch canvas.
You can play around with the different clipping modes on my demo page. The bottom row (with the gradients) is not too useful to you, but the top row (with the circle and square) is much more relevant.
edit
Whoops, I accidentally forked your JSFiddle to demonstrate the technique.