memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.07k stars 767 forks source link

Anti-aliased holes have 1 pixel fringe #571

Open mattkille opened 4 years ago

mattkille commented 4 years ago

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.

aaholes

memononen commented 4 years ago

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.

mattkille commented 4 years ago

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.

memononen commented 4 years ago

Yes. A couple of things going on in there, really hard to explain with words.

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.

mattkille commented 4 years ago

@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. 👍

daljit97 commented 3 years ago

@memononen could this be fixed following the approach used here https://github.com/styluslabs/nanovgXC?