memononen / nanovg

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

nvgArc does some ugly overdraw #659

Open mulle-nat opened 11 months ago

mulle-nat commented 11 months ago

I suspect, that when the area to fill gets less than a pixel, something goes awry at the very top of the circle hole:

image

In this special case, it can be mitigated with nvgIntersectScissor.

For reference this is how it's drawn (it uses a few "UIKit"isms, that should be easy to understand)

void drawSplitRectWithHole(NVGcontext* vg, CGRect rect, CGFloat x, CGFloat radius)
{
    CGFloat y = CGRectGetMidY( rect);
    CGFloat angle = asinf( rect.size.height / (2 * radius));

    nvgSave( vg);
    //nvgIntersectScissor( vg, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);

    // Draw the left part
    nvgBeginPath(vg);
    nvgMoveTo(vg, CGRectGetMinX( rect), CGRectGetMinY( rect));
    nvgLineTo(vg, x - radius * cosf(angle), CGRectGetMinY( rect));
    nvgArc(vg, x, y, radius, M_PI_f + angle, M_PI_f - angle, NVG_CCW);
    nvgLineTo(vg, CGRectGetMinX( rect), CGRectGetMaxY( rect));
    nvgClosePath(vg);
    nvgFillColor( vg, CGColorMakeIndigo());
    nvgFill(vg);

    // Draw the right part 
    nvgBeginPath(vg);
    nvgMoveTo(vg, x + radius * cosf(angle), CGRectGetMinY( rect));
    nvgLineTo(vg, CGRectGetMaxX( rect), CGRectGetMinY( rect));
    nvgLineTo(vg, CGRectGetMaxX( rect), CGRectGetMaxY( rect));
    nvgLineTo(vg, x + radius * cosf(angle), CGRectGetMaxY( rect));
    nvgArc(vg, x, y, radius, angle, -angle, NVG_CCW);
    nvgClosePath(vg);
    nvgFillColor( vg, CGColorMakeOrange());
    nvgFill(vg);

    nvgRestore( vg);
}
memononen commented 11 months ago

This is unfortunately due to the antialiasing algorithm used by nanovg.

An alternative is to use MSAA, one of the examples shows how to set that up.

mulle-nat commented 11 months ago

Thanks, I wasn't aware that I could use MSAA to replace nanovg edge antialiasing. I am trying to use MSAA (4) as the default now and EdgeAntialiasing as a fallback option.

It seems though, that there is more to do than just adding glfwWindowHint( GLFW_SAMPLES, 4). For example at my current understanding (being mostly this stackoverflow.) nvgluCreateFramebuffer doesn't seem to be coded to be MSAA aware. I am also not sure how this will pertain to textures, so I have no real feel for how much more work this will be.

Just to get a better understanding of things: