Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to anti-alias clip() edges in html5 canvas under Chrome Windows?

I'm using the clip() function on a canvas.

Results: using canvas clip in various browsers

As you can see the chrome version has horrible jaggies / aliasing along the edges. How do I fix this?

Code to reproduce:

http://jsfiddle.net/ZRA76/ :

<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>
like image 370
Simon Epskamp Avatar asked Mar 02 '12 15:03

Simon Epskamp


4 Answers

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.

like image 143
Daniel Yankowsky Avatar answered Oct 22 '22 08:10

Daniel Yankowsky


My fix for this is to draw a thin (2px) white stroke at the same radius after drawing the image. It covers up the aliasing nicely and looks fine across browsers.

like image 36
cacheflowe Avatar answered Oct 22 '22 06:10

cacheflowe


I came across the same issue with Chrome and clip().

In my circumstance I achieved better browser compatibility by setting the canvas globalCompositeOperation.

context.globalCompositeOperation = 'source-atop';

So draw your shape, a circle in this case. Then switch to 'source-atop' and draw your kitten image.

Note, this is a quick fix for basic drawing and assumes a blank canvas. Previous canvas drawing will affect your clip.

like image 5
Edward Avatar answered Oct 22 '22 07:10

Edward


from the answers on Can I turn off antialiasing on an HTML <canvas> element? it appears that it is browser specific. It is even an active bug report on the google code chromium project. Sorry, but it looks like you're out of luck for now.

like image 2
Scott M. Avatar answered Oct 22 '22 06:10

Scott M.