diff --git a/src/nanovg.c b/src/nanovg.c index 3276b7c..81d33e4 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -52,12 +52,6 @@ enum NVGpointFlags NVG_PR_INNERBEVEL = 0x08, }; -enum NVGexpandFeatures { - NVG_FILL = 0x01, - NVG_STROKE = 0x02, - NVG_CAPS = 0x04, -}; - struct NVGstate { struct NVGpaint fill; struct NVGpaint stroke; @@ -1368,31 +1362,94 @@ static struct NVGvertex* nvg__bevelJoin(struct NVGvertex* dst, struct NVGpoint* return dst; } -static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w, int lineCap, int lineJoin, float miterLimit) +static struct NVGvertex* nvg__buttCapStart(struct NVGvertex* dst, struct NVGpoint* p, + float dx, float dy, float w, float d, float aa) +{ + float px = p->x - dx*d; + float py = p->y - dy*d; + float dlx = dy; + float dly = -dx; + nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, 0,0); dst++; + nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, 1,0); dst++; + nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; + return dst; +} + +static struct NVGvertex* nvg__buttCapEnd(struct NVGvertex* dst, struct NVGpoint* p, + float dx, float dy, float w, float d, float aa) +{ + float px = p->x + dx*d; + float py = p->y + dy*d; + float dlx = dy; + float dly = -dx; + nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; + nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, 0,0); dst++; + nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, 1,0); dst++; + return dst; +} + + +static struct NVGvertex* nvg__roundCapStart(struct NVGvertex* dst, struct NVGpoint* p, + float dx, float dy, float w, int ncap, float aa) +{ + int i; + float px = p->x; + float py = p->y; + float dlx = dy; + float dly = -dx; + NVG_NOTUSED(aa); + for (i = 0; i < ncap; i++) { + float a = i/(float)(ncap-1)*NVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, 0,1); dst++; + nvg__vset(dst, px, py, 0.5f,1); dst++; + } + nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; + return dst; +} + +static struct NVGvertex* nvg__roundCapEnd(struct NVGvertex* dst, struct NVGpoint* p, + float dx, float dy, float w, int ncap, float aa) +{ + int i; + float px = p->x; + float py = p->y; + float dlx = dy; + float dly = -dx; + NVG_NOTUSED(aa); + nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; + for (i = 0; i < ncap; i++) { + float a = i/(float)(ncap-1)*NVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + nvg__vset(dst, px, py, 0.5f,1); dst++; + nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, 0,1); dst++; + } + return dst; +} + + +static void nvg__calculateJoins(struct NVGcontext* ctx, float w, int lineJoin, float miterLimit) { struct NVGpathCache* cache = ctx->cache; - struct NVGpath* path; - struct NVGpoint* pts; - struct NVGvertex* verts; - struct NVGvertex* dst; - struct NVGpoint* p0; - struct NVGpoint* p1; - int cverts, convex, i, j, s, e; - float wo = 0, iw = 0, aa = ctx->fringeWidth; - int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol / 4.0f); - int nleft = 0; + int i, j; + float iw; if (w > 0.0f) iw = 1.0f / w; // Calculate which joins needs extra vertices to append, and gather vertex count. for (i = 0; i < cache->npaths; i++) { - path = &cache->paths[i]; - pts = &cache->points[path->first]; + struct NVGpath* path = &cache->paths[i]; + struct NVGpoint* pts = &cache->points[path->first]; + struct NVGpoint* p0 = &pts[path->count-1]; + struct NVGpoint* p1 = &pts[0]; + int nleft = 0; + path->nbevel = 0; - nleft = 0; - p0 = &pts[path->count-1]; - p1 = &pts[0]; for (j = 0; j < path->count; j++) { float dlx0, dly0, dlx1, dly1, dmr2, cross, limit; dlx0 = p0->dy; @@ -1442,160 +1499,222 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w, path->convex = (nleft == path->count) ? 1 : 0; } +} + + +static int nvg__expandStroke(struct NVGcontext* ctx, float w, int lineCap, int lineJoin, float miterLimit) +{ + struct NVGpathCache* cache = ctx->cache; + struct NVGvertex* verts; + struct NVGvertex* dst; + int cverts, i, j; + float aa = ctx->fringeWidth; + int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol / 4.0f); + + nvg__calculateJoins(ctx, w, lineJoin, miterLimit); // Calculate max vertex usage. cverts = 0; for (i = 0; i < cache->npaths; i++) { - path = &cache->paths[i]; - if (feats & NVG_FILL) - cverts += path->count + path->nbevel + 1; - if (feats & NVG_STROKE) { - int loop = ((feats & NVG_CAPS) && path->closed == 0) ? 0 : 1; - if (lineCap == NVG_ROUND) - cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop - else - cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop - if (loop == 0) { - // space for caps - if (lineCap == NVG_ROUND) { - cverts += (ncap*2 + 2)*2; + struct NVGpath* path = &cache->paths[i]; + int loop = (path->closed == 0) ? 0 : 1; + if (lineCap == NVG_ROUND) + cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop + else + cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop + if (loop == 0) { + // space for caps + if (lineCap == NVG_ROUND) { + cverts += (ncap*2 + 2)*2; + } else { + cverts += (3+3)*2; + } + } + } + + verts = nvg__allocTempVerts(ctx, cverts); + if (verts == NULL) return 0; + + for (i = 0; i < cache->npaths; i++) { + struct NVGpath* path = &cache->paths[i]; + struct NVGpoint* pts = &cache->points[path->first]; + struct NVGpoint* p0; + struct NVGpoint* p1; + int s, e, loop; + float dx, dy; + + path->fill = 0; + path->nfill = 0; + + // Calculate fringe or stroke + loop = (path->closed == 0) ? 0 : 1; + dst = verts; + path->stroke = dst; + + if (loop) { + // Looping + p0 = &pts[path->count-1]; + p1 = &pts[0]; + s = 0; + e = path->count; + } else { + // Add cap + p0 = &pts[0]; + p1 = &pts[1]; + s = 1; + e = path->count-1; + } + + if (loop == 0) { + // Add cap + dx = p1->x - p0->x; + dy = p1->y - p0->y; + nvg__normalize(&dx, &dy); + if (lineCap == NVG_BUTT) + dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa); + else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) + dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa); + else if (lineCap == NVG_ROUND) + dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa); + } + + for (j = s; j < e; ++j) { + if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { + if (lineJoin == NVG_ROUND) { + dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa); } else { - cverts += (3+3)*2; + dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa); } + } else { + nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), 0,1); dst++; + nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), 1,1); dst++; } + p0 = p1++; } + + if (loop) { + // Loop it + nvg__vset(dst, verts[0].x, verts[0].y, 0,1); dst++; + nvg__vset(dst, verts[1].x, verts[1].y, 1,1); dst++; + } else { + // Add cap + dx = p1->x - p0->x; + dy = p1->y - p0->y; + nvg__normalize(&dx, &dy); + if (lineCap == NVG_BUTT) + dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa); + else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) + dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa); + else if (lineCap == NVG_ROUND) + dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa); + } + + path->nstroke = (int)(dst - verts); + + verts = dst; + } + + return 1; +} + +static int nvg__expandFill(struct NVGcontext* ctx, float w, int lineJoin, float miterLimit) +{ + struct NVGpathCache* cache = ctx->cache; + struct NVGvertex* verts; + struct NVGvertex* dst; + int cverts, convex, i, j; + float aa = ctx->fringeWidth; + int fringe = w > 0.0f; + + nvg__calculateJoins(ctx, w, lineJoin, miterLimit); + + // Calculate max vertex usage. + cverts = 0; + for (i = 0; i < cache->npaths; i++) { + struct NVGpath* path = &cache->paths[i]; + cverts += path->count + path->nbevel + 1; + if (fringe) + cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop } verts = nvg__allocTempVerts(ctx, cverts); if (verts == NULL) return 0; - if ((feats & NVG_FILL) && cache->npaths == 1 && cache->paths[0].convex) - convex = 1; - else - convex = 0; + convex = cache->npaths == 1 && cache->paths[0].convex; for (i = 0; i < cache->npaths; i++) { - path = &cache->paths[i]; - pts = &cache->points[path->first]; + struct NVGpath* path = &cache->paths[i]; + struct NVGpoint* pts = &cache->points[path->first]; + struct NVGpoint* p0; + struct NVGpoint* p1; + float rw, lw, woff; + float ru, lu; // Calculate shape vertices. - if (feats & NVG_FILL) { - wo = 0.5f*aa; - dst = verts; - path->fill = dst; + woff = 0.5f*aa; + dst = verts; + path->fill = dst; - if (w == 0.0f) { - for (j = 0; j < path->count; ++j) { - nvg__vset(dst, pts[j].x, pts[j].y, 0.5f,1); - dst++; - } - } else { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - for (j = 0; j < path->count; ++j) { - if (p1->flags & NVG_PT_BEVEL) { - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - if (p1->flags & NVG_PT_LEFT) { - float lx = p1->x + p1->dmx * wo; - float ly = p1->y + p1->dmy * wo; - nvg__vset(dst, lx, ly, 0.5f,1); dst++; - } else { - float lx0 = p1->x + dlx0 * wo; - float ly0 = p1->y + dly0 * wo; - float lx1 = p1->x + dlx1 * wo; - float ly1 = p1->y + dly1 * wo; - nvg__vset(dst, lx0, ly0, 0.5f,1); dst++; - nvg__vset(dst, lx1, ly1, 0.5f,1); dst++; - } + if (fringe) { + // Looping + p0 = &pts[path->count-1]; + p1 = &pts[0]; + for (j = 0; j < path->count; ++j) { + if (p1->flags & NVG_PT_BEVEL) { + float dlx0 = p0->dy; + float dly0 = -p0->dx; + float dlx1 = p1->dy; + float dly1 = -p1->dx; + if (p1->flags & NVG_PT_LEFT) { + float lx = p1->x + p1->dmx * woff; + float ly = p1->y + p1->dmy * woff; + nvg__vset(dst, lx, ly, 0.5f,1); dst++; } else { - nvg__vset(dst, p1->x + (p1->dmx * wo), p1->y + (p1->dmy * wo), 0.5f,1); dst++; + float lx0 = p1->x + dlx0 * woff; + float ly0 = p1->y + dly0 * woff; + float lx1 = p1->x + dlx1 * woff; + float ly1 = p1->y + dly1 * woff; + nvg__vset(dst, lx0, ly0, 0.5f,1); dst++; + nvg__vset(dst, lx1, ly1, 0.5f,1); dst++; } - p0 = p1++; + } else { + nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1); dst++; } + p0 = p1++; } - - path->nfill = (int)(dst - verts); - verts = dst; } else { - wo = 0.0f; - path->fill = 0; - path->nfill = 0; + for (j = 0; j < path->count; ++j) { + nvg__vset(dst, pts[j].x, pts[j].y, 0.5f,1); + dst++; + } } - // Calculate fringe or stroke - if (feats & NVG_STROKE) { - float lw = w + wo, rw = w - wo; - float lu = 0, ru = 1; - int loop = ((feats & NVG_CAPS) && path->closed == 0) ? 0 : 1; + path->nfill = (int)(dst - verts); + verts = dst; + + // Calculate fringe + if (fringe) { + lw = w + woff; + rw = w - woff; + lu = 0; + ru = 1; dst = verts; path->stroke = dst; // Create only half a fringe for convex shapes so that // the shape can be rendered without stenciling. if (convex) { - lw = wo; // This should generate the same vertex as fill inset above. + lw = woff; // This should generate the same vertex as fill inset above. lu = 0.5f; // Set outline fade at middle. } - if (loop) { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - s = 0; - e = path->count; - } else { - // Add cap - p0 = &pts[0]; - p1 = &pts[1]; - s = 1; - e = path->count-1; - } - - if (loop == 0) { - // Add cap - float dx, dy, dlx, dly, px, py; - dx = p1->x - p0->x; - dy = p1->y - p0->y; - nvg__normalize(&dx, &dy); - dlx = dy; - dly = -dx; - if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) { - if (lineCap == NVG_BUTT) { - px = p0->x + dx*ctx->fringeWidth*0.5f; - py = p0->y + dy*ctx->fringeWidth*0.5f; - } else /*if (lineCap == NVG_SQUARE)*/ { - px = p0->x - dx*(w - ctx->fringeWidth); - py = p0->y - dy*(w - ctx->fringeWidth); - } - nvg__vset(dst, px + dlx*lw - dx*aa, py + dly*lw - dy*aa, lu,0); dst++; - nvg__vset(dst, px - dlx*rw - dx*aa, py - dly*rw - dy*aa, ru,0); dst++; - nvg__vset(dst, px + dlx*lw, py + dly * lw, lu,1); dst++; - nvg__vset(dst, px - dlx*rw, py - dly * rw, ru,1); dst++; - } else if (lineCap == NVG_ROUND) { - px = p0->x; - py = p0->y; - for (j = 0; j < ncap; j++) { - float a = j/(float)(ncap-1)*NVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, lu,1); dst++; - nvg__vset(dst, px, py, 0.5f,1); dst++; - } - nvg__vset(dst, px + dlx*lw, py + dly * lw, lu,1); dst++; - nvg__vset(dst, px - dlx*rw, py - dly * rw, ru,1); dst++; - } - } + // Looping + p0 = &pts[path->count-1]; + p1 = &pts[0]; - for (j = s; j < e; ++j) { + for (j = 0; j < path->count; ++j) { if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { - if (lineJoin == NVG_ROUND) { - dst = nvg__roundJoin(dst, p0, p1, lw, rw, lu, ru, ncap, ctx->fringeWidth); - } else { - dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth); - } + dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth); } else { nvg__vset(dst, p1->x + (p1->dmx * lw), p1->y + (p1->dmy * lw), lu,1); dst++; nvg__vset(dst, p1->x - (p1->dmx * rw), p1->y - (p1->dmy * rw), ru,1); dst++; @@ -1603,49 +1722,14 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w, p0 = p1++; } - if (loop) { - // Loop it - nvg__vset(dst, verts[0].x, verts[0].y, lu,1); dst++; - nvg__vset(dst, verts[1].x, verts[1].y, ru,1); dst++; - } else { - // Add cap - float dx, dy, dlx, dly, px, py; - dx = p1->x - p0->x; - dy = p1->y - p0->y; - nvg__normalize(&dx, &dy); - dlx = dy; - dly = -dx; - if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) { - if (lineCap == NVG_BUTT) { - px = p1->x - dx*ctx->fringeWidth*0.5f; - py = p1->y - dy*ctx->fringeWidth*0.5f; - } else /*if (lineCap == NVG_SQUARE)*/ { - px = p1->x + dx*(w - ctx->fringeWidth); - py = p1->y + dy*(w - ctx->fringeWidth); - } - nvg__vset(dst, px + dlx*lw, py + dly * lw, lu,1); dst++; - nvg__vset(dst, px - dlx*rw, py - dly * rw, ru,1); dst++; - nvg__vset(dst, px + dlx*lw + dx*aa, py + dly*lw + dy*aa, lu,0); dst++; - nvg__vset(dst, px - dlx*rw + dx*aa, py - dly*rw + dy*aa, ru,0); dst++; - } else if (lineCap == NVG_ROUND) { - px = p1->x; - py = p1->y; - nvg__vset(dst, px + dlx*lw, py + dly * lw, lu,1); dst++; - nvg__vset(dst, px - dlx*rw, py - dly * rw, ru,1); dst++; - for (j = 0; j < ncap; j++) { - float a = j/(float)(ncap-1)*NVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - nvg__vset(dst, px, py, 0.5f,1); dst++; - nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, lu,1); dst++; - } - } - } + // Loop it + nvg__vset(dst, verts[0].x, verts[0].y, lu,1); dst++; + nvg__vset(dst, verts[1].x, verts[1].y, ru,1); dst++; path->nstroke = (int)(dst - verts); - verts = dst; } else { - path->stroke = 0; + path->stroke = NULL; path->nstroke = 0; } } @@ -1896,9 +1980,9 @@ void nvgFill(struct NVGcontext* ctx) nvg__flattenPaths(ctx); if (ctx->params.edgeAntiAlias) - nvg__expandStrokeAndFill(ctx, NVG_FILL|NVG_STROKE, ctx->fringeWidth, NVG_BUTT, NVG_MITER, 3.6f); + nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); else - nvg__expandStrokeAndFill(ctx, NVG_FILL, 0.0f, NVG_BUTT, NVG_MITER, 1.2f); + nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); ctx->params.renderFill(ctx->params.userPtr, &state->fill, &state->scissor, ctx->fringeWidth, ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); @@ -1931,10 +2015,11 @@ void nvgStroke(struct NVGcontext* ctx) } nvg__flattenPaths(ctx); + if (ctx->params.edgeAntiAlias) - nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f + ctx->fringeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); + nvg__expandStroke(ctx, strokeWidth*0.5f + ctx->fringeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); else - nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); + nvg__expandStroke(ctx, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, &state->scissor, ctx->fringeWidth, strokeWidth, ctx->cache->paths, ctx->cache->npaths);