Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid polygon edge stitching artifacts in HTML5 Canvas?

I maintain parallel Flash and HTML5/Canvas renderers for the OpenHeatMap open-source project. I'm plagued by an inconsistency in the rendering of filled polygons with fractional coordinates between the two versions. If you render two polygons that share an edge, Canvas will show a visible join along that edge, whereas Flash will seamlessly meld the two together, with no difference visible if they're the same color. I've put together a minimal page here to show the issue:

http://web.mailana.com/labs/stitchingbug/

[Gak, spam prevention stops this from being an image, but see here for a screenshot web.mailana.com/labs/stitchingbug.png ]

The source, along with a Flash project doing the same thing, is here:

github.com/petewarden/stitchingbug

The fundamental issue is that it's impossible to do any complex polygonal rendering if you can't stitch polygons together without seams. I don't know exactly what Flash's fill rules are, but they produce the correct results, as do 3D renderers. Does anyone have a client-side fix to work around this problem? It's cross-browser, which makes it seem deliberate, so any references to the rules used would also be appreciated. Here's the Canvas code:

    var ctx = canvas.getContext('2d');
    ctx.fillStyle = 'rgb(0,0,0)';

    ctx.beginPath()
    ctx.moveTo(0, 0);
    ctx.lineTo(50.5, 0);
    ctx.lineTo(50.5, 100);
    ctx.lineTo(0, 100);
    ctx.closePath();
    ctx.fill();

    ctx.beginPath()
    ctx.moveTo(50.5, 0);
    ctx.lineTo(100, 0);
    ctx.lineTo(100, 100);
    ctx.lineTo(50.5, 100);
    ctx.closePath();
    ctx.fill();
like image 823
Pete Warden Avatar asked Jan 31 '11 01:01

Pete Warden


1 Answers

This is an old question, but I had the same problem.

I find that drawing a single line between the two polygons (or an outline around them) solves the problem (in firefox and chrome at least).

That seems to fill the gap in chrome and firefox at least. It is sufficient to call ctx.stroke() after ctx.fill(), or draw a single line as follows:

ctx.beginPath()
ctx.moveTo(50.5, 0);
ctx.lineTo(50.5, 100);
ctx.stroke();

and be sure to set the strokeStyle of course.

like image 79
zod Avatar answered Sep 30 '22 16:09

zod