Instead of scan-line rendering or loop-blin (which is patented IIRC), nanovg uses the stencil buffer to render complex fills:
static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
{
GLNVGpath* paths = &gl->paths[call->pathOffset];
int i, npaths = call->pathCount;
// Draw shapes
glEnable(GL_STENCIL_TEST);
glnvg__stencilMask(gl, 0xff);
glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xff);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// set bindpoint for solid loc
glnvg__setUniforms(gl, call->uniformOffset, 0);
glnvg__checkError(gl, "fill simple");
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
glDisable(GL_CULL_FACE);
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
glEnable(GL_CULL_FACE);
// Draw anti-aliased pixels
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image);
glnvg__checkError(gl, "fill fill");
if (gl->flags & NVG_ANTIALIAS) {
glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// Draw fringes
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
}
// Draw fill
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount);
glDisable(GL_STENCIL_TEST);
}