nanovg
nanovg copied to clipboard
Anti-aliased holes have 1 pixel fringe
When a hole touches or exceeds the edge of its parent shape, there is a 1 pixel fringe drawn around it. Appears to be related to anti-aliasing, as this effect is not shown when disabled. Here is some code to demonstrate this:
for (int pass = 0; pass < 2; pass++)
{
for (int i = 0; i < 8; i++)
{
nvgBeginPath(vg);
nvgRect(vg, i * 18, pass * 20, 9, 9);
nvgRect(vg, i * 18 + 3, pass * 20 + 3, 3, 3 + i);
nvgPathWinding(vg, NVG_HOLE);
nvgFillColor(vg, nvgRGBA(255, 255, 255, 128));
nvgFill(vg);
}
// Repeat with anti-alias off.
nvgShapeAntiAlias(vg, 0);
}
Each step the hole is made one pixel longer. The following enlarged image shows the result. The first pass with default anti-aliasing enabled. The second pass without. The fill is 50% white, highlighting overlapped pixels.
The latter is the result I would hope for in both cases; that which matches Canvas.

This is limitation of the antialiasing method used. The antialiasing is done by deflating the shape by 0.5px and rendering 1px wide "fringe" around the shape. It fails when there is a lot of geometry per pixel. If that is common in your use case, i recommend the MSAA mode instead.
This is limitation of the antialiasing method used. The antialiasing is done by deflating the shape by 0.5px and rendering 1px wide "fringe" around the shape.
Hmm, even if the shape in this case should be non-existent? Or are you just talking about the overlapping pixels? Take for example the fourth column examples. The 'n' shape at the bottom is a common case. The top example has an extra 1 pixel border drawn around the 3x0 sized position where the 9x9 shape and the 3x6 hole align.
Yes. A couple of things going on in there, really hard to explain with words.
- The bright pixels happen because two transparent lines are drawn on top of each other, there's no whole shape accumulation of coverage
- The too wide bottom part happens because the whole shape is deflated, once the winding is reversed outside, it's deflated instead in wrong direction
- the extra line happens because the winding stencil is rendering using the inflated shapes, so in some cases the outer most pixels which influence the AA are rendered outside the stencil mask, which means that they do not follow the stencil rules correctly
One more limitation is small (close to sub pixel) or really sharp features, like rendering a glyph. The inflating/insetting is quite naive so it will fail and also the lack of per shape coverage accumulation makes things look ugly.
@memononen
I see. That's very interesting, thank you for the explanation. I'll have to see how I can work around this for my particular needs. It's this fourth column example I wanted to create. Making a hole that touches the edge without the extra line artefact would have been so much easier. I'll have to see if I can construct the shape I need as a single path. Thanks again. 👍
@memononen could this be fixed following the approach used here https://github.com/styluslabs/nanovgXC?