When I fill adjacent areas in HTML5 Canvas or similar systems like Quartz or GDI+, I see a light colored seam on the shared edge. Here's an example, (code below):
I think I understand what's going on but don't have a fix. Each edge is anti-aliased against white, so instead of a mix of orange and blue you get a mix of orange, blue and white.
Essentially the same question has been asked before but with less discussion and no good answer.
Some work-arounds that involve drawing the first shape a little bigger at the seam or drawing a line over the seam do not hold up over variations like with transparency or odd shape combinations.
Turning off anti-aliasing avoids the seam for APIs that support it, but then, of course, all the edges are jagged.
Is there a way to provide a hint to the renderer about the intended adjacency?
<canvas id="myCanvas" width="300" height="150" /> <script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(100,0); ctx.lineTo(115,150); ctx.lineTo(300, 50);
ctx.closePath(); ctx.fillStyle="#885533"; ctx.fill();
ctx.beginPath();
ctx.moveTo(0,50); ctx.lineTo(100,0); ctx.lineTo(115,150);
ctx.closePath(); ctx.fillStyle="#335588"; ctx.fill();
</script> </body> </html>
If you’re drawing on a transparent canvas and your shapes are nonoverlapping, you can simply set ctx.globalCompositeOperation = "lighter"
:
That means each shape will combine its contribution with the previous contents of its pixels using simple addition instead of typical alpha blending. That’s exactly what you want in this case.
Is there a way to provide a hint to the renderer about the intended adjacency?
Unfortunately no, anti-aliasing/sub-pixeling is on by default and we cannot turn it off.
There are some ways I can think of to solve this:
1) Use the technique markE mention in comments and use stroke n
to extrude the polygon which will cause them to overlap. You can reduce the impact by first setting the lineWidth
property to 0.67
.
2) Overlap manually by translating the coordinates (either by adding to the coordinate or by using translate) for the overlapping lines by one pixel. This gives a better result than 1 as this will only extrude the polygon in the overlapping area and not on all sides. The drawback however is that you need to manually calculate each overlapping which quickly becomes painful especially if you want to animate them as well.
3) implement a line and fill algorithm yourselves (for example Bresenham). This is not so hard but it will slow down the drawing, and noticeably so if you update the graphics often. The benefit is that you know exactly where the lines are drawn. The drawback is that you get no anti-aliasing unless you also implement this and at its own price performance wise.
4) A combination of 1 and 3 where you use canvas' fill and overdraw overlapping lines with a Bresenham line algorithm.
5) Use a large off-screen canvas and draw everything up-scaled. Then finally draw everything to the on-screen canvas to the target size (or instead set target size using CSS). This will help camouflaging the gap and also give you sharper lines and you can keep your original coordinates as well, but it require more processing of pixels and more memory so it's slower but not as slow as a manual line/fill approach as with 3.