diff --git a/example/demo.c b/example/demo.c index 33316229..1d6faec3 100644 --- a/example/demo.c +++ b/example/demo.c @@ -748,7 +748,18 @@ void drawColorwheel(NVGcontext* vg, float x, float y, float w, float h, float t) nvgRestore(vg); } -void drawLines(NVGcontext* vg, float x, float y, float w, float h, float t) +void drawStylizedLines(NVGcontext* vg, float x, float y, float w, float h, float t){ + nvgLineJoin(vg, NVG_ROUND); + nvgLineStyle(vg, NVG_LINE_DASHED); + nvgStrokeColor(vg,nvgRGBAf(0.6f,0.6f,1.0f,1.0f)); + nvgStrokeWidth(vg, 5.0f); + nvgBeginPath(vg); + nvgRect(vg, x, y, w, h); + nvgStroke(vg); + nvgLineStyle(vg, NVG_LINE_SOLID); +} + +void drawLines(NVGcontext* vg, float x, float y, float w, float h, float strokeWidth, NVGcolor color, float t) { int i, j; float pad = 5.0f, s = w/9.0f - pad*2; @@ -766,7 +777,6 @@ void drawLines(NVGcontext* vg, float x, float y, float w, float h, float t) pts[5] = 0; pts[6] = s*0.25f + cosf(-t*0.3f) * s*0.5f; pts[7] = sinf(-t*0.3f) * s*0.5f; - for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { fx = x + s*0.5f + (i*3+j)/9.0f*w + pad; @@ -775,8 +785,8 @@ void drawLines(NVGcontext* vg, float x, float y, float w, float h, float t) nvgLineCap(vg, caps[i]); nvgLineJoin(vg, joins[j]); - nvgStrokeWidth(vg, s*0.3f); - nvgStrokeColor(vg, nvgRGBA(0,0,0,160)); + nvgStrokeWidth(vg, strokeWidth); + nvgStrokeColor(vg, color); nvgBeginPath(vg); nvgMoveTo(vg, fx+pts[0], fy+pts[1]); nvgLineTo(vg, fx+pts[2], fy+pts[3]); @@ -1062,18 +1072,77 @@ void drawScissor(NVGcontext* vg, float x, float y, float t) nvgRestore(vg); } +void drawBezierCurve(NVGcontext* vg, float x0, float y0, float radius, float t){ + + float x1 = x0 + radius*cos(2*NVG_PI*t/5); + float y1 = y0 + radius*sin(2*NVG_PI*t/5); + + float cx0 = x0; + float cy0 = y0 + ((y1 - y0) * 0.75f); + float cx1 = x1; + float cy1 = y0 + ((y1 - y0) * 0.75f); + + nvgBeginPath(vg); + nvgMoveTo(vg, x0, y0); + nvgLineTo(vg, cx0, cy0); + nvgLineTo(vg, cx1, cy1); + nvgLineTo(vg, x1, y1); + nvgStrokeColor(vg,nvgRGBA(200,200,200,255)); + nvgStrokeWidth(vg,2.0f); + nvgStroke(vg); + + nvgLineCap(vg, NVG_ROUND); + nvgStrokeWidth(vg,5); + nvgLineJoin(vg, NVG_ROUND); + + nvgBeginPath(vg); + nvgMoveTo(vg, x0, y0); + nvgBezierTo(vg, cx0, cy0, cx1, cy1, x1, y1); + nvgLineStyle(vg, NVG_LINE_SOLID); + nvgStrokeColor(vg, nvgRGBA(40, 53, 147,255)); + nvgStroke(vg); + + nvgLineStyle(vg, NVG_LINE_DASHED); + nvgStrokeColor(vg, nvgRGBA(255, 195, 0,255)); + nvgStroke(vg); + + nvgBeginPath(vg); + nvgCircle(vg,x0,y0,5.0f); + nvgCircle(vg,cx0,cy0,5.0f); + nvgCircle(vg,cx1,cy1,5.0f); + nvgCircle(vg,x1,y1,5.0f); + nvgLineStyle(vg, NVG_LINE_SOLID); + nvgFillColor(vg,nvgRGBA(64,192,64,255)); + nvgFill(vg); +} + void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, float t, int blowup, DemoData* data) { float x,y,popy; - drawEyes(vg, width - 250, 50, 150, 100, mx, my, t); + drawEyes(vg, width - 230, 30, 150, 100, mx, my, t); + drawStylizedLines(vg, width - 245, 15, 180 ,130, t); drawParagraph(vg, width - 450, 50, 150, 100, mx, my); drawGraph(vg, 0, height/2, width, height/2, t); - drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t); + drawColorwheel(vg, width - 280, height - 320, 250.0f, 250.0f, t); // Line joints - drawLines(vg, 120, height-50, 600, 50, t); + + switch((int)(t/5.0)%3){ + case 0: + nvgLineStyle(vg, NVG_LINE_DASHED);break; + case 1: + nvgLineStyle(vg, NVG_LINE_DOTTED);break; + case 2: + nvgLineStyle(vg, NVG_LINE_GLOW);break; + default: + nvgLineStyle(vg, NVG_LINE_SOLID); + } + drawLines(vg, 100, height-5, 800, 100, 10.0f, nvgRGBA(255, 153, 0, 255), t*3); + + nvgLineStyle(vg, NVG_LINE_SOLID); + drawLines(vg, 120, height-75, 600, 50, 17.0f, nvgRGBA(0,0,0,160), t); // Line caps drawWidths(vg, 10, 50, 30); @@ -1123,6 +1192,8 @@ void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, drawThumbnails(vg, 365, popy-30, 160, 300, data->images, 12, t); nvgRestore(vg); + + drawBezierCurve(vg, width - 380, height - 220, 100, t); } static int mini(int a, int b) { return a < b ? a : b; } diff --git a/src/nanovg.c b/src/nanovg.c index 23f4bbe8..686aab61 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -80,6 +80,7 @@ struct NVGstate { float miterLimit; int lineJoin; int lineCap; + int lineStyle; float alpha; float xform[6]; NVGscissor scissor; @@ -657,6 +658,7 @@ void nvgReset(NVGcontext* ctx) state->miterLimit = 10.0f; state->lineCap = NVG_BUTT; state->lineJoin = NVG_MITER; + state->lineStyle = NVG_LINE_SOLID; state->alpha = 1.0f; nvgTransformIdentity(state->xform); @@ -690,6 +692,11 @@ void nvgMiterLimit(NVGcontext* ctx, float limit) state->miterLimit = limit; } +void nvgLineStyle(NVGcontext* ctx, int lineStyle) { + NVGstate* state = nvg__getState(ctx); + state->lineStyle = lineStyle; +} + void nvgLineCap(NVGcontext* ctx, int cap) { NVGstate* state = nvg__getState(ctx); @@ -1275,12 +1282,14 @@ static void nvg__polyReverse(NVGpoint* pts, int npts) } -static void nvg__vset(NVGvertex* vtx, float x, float y, float u, float v) +static void nvg__vset(NVGvertex* vtx, float x, float y, float u, float v, float s, float t) { vtx->x = x; vtx->y = y; vtx->u = u; vtx->v = v; + vtx->s = s; // Normalized line width [-1, 1] + vtx->t = t; // Path length normalized by stroke width } static void nvg__tesselateBezier(NVGcontext* ctx, @@ -1329,7 +1338,7 @@ static void nvg__tesselateBezier(NVGcontext* ctx, static void nvg__flattenPaths(NVGcontext* ctx) { NVGpathCache* cache = ctx->cache; -// NVGstate* state = nvg__getState(ctx); + // NVGstate* state = nvg__getState(ctx); NVGpoint* last; NVGpoint* p0; NVGpoint* p1; @@ -1401,12 +1410,17 @@ static void nvg__flattenPaths(NVGcontext* ctx) } // Enforce winding. + path->reversed = 0; if (path->count > 2) { area = nvg__polyArea(pts, path->count); - if (path->winding == NVG_CCW && area < 0.0f) + if (path->winding == NVG_CCW && area < 0.0f) { nvg__polyReverse(pts, path->count); - if (path->winding == NVG_CW && area > 0.0f) + path->reversed = 1; + } + if (path->winding == NVG_CW && area > 0.0f) { nvg__polyReverse(pts, path->count); + path->reversed = 1; + } } for(i = 0; i < path->count; i++) { @@ -1449,7 +1463,7 @@ static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w, static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, float lw, float rw, float lu, float ru, int ncap, - float fringe) + float fringe, float t) { int i, n; float dlx0 = p0->dy; @@ -1465,8 +1479,8 @@ static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, a1 = atan2f(-dly1, -dlx1); if (a1 > a0) a1 -= NVG_PI*2; - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; + nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; n = nvg__clampi((int)ceilf(((a0 - a1) / NVG_PI) * ncap), 2, ncap); for (i = 0; i < n; i++) { @@ -1474,12 +1488,12 @@ static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, float a = a0 + u*(a1-a0); float rx = p1->x + cosf(a) * rw; float ry = p1->y + sinf(a) * rw; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - nvg__vset(dst, rx, ry, ru,1); dst++; + nvg__vset(dst, p1->x, p1->y, 0.5f, 1, 0, t); dst++; + nvg__vset(dst, rx, ry, ru,1, 1, t); dst++; } - nvg__vset(dst, lx1, ly1, lu,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; + nvg__vset(dst, lx1, ly1, lu, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru, 1, 1, t); dst++; } else { float rx0,ry0,rx1,ry1,a0,a1; @@ -1488,8 +1502,8 @@ static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, a1 = atan2f(dly1, dlx1); if (a1 < a0) a1 += NVG_PI*2; - nvg__vset(dst, p1->x + dlx0*rw, p1->y + dly0*rw, lu,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; + nvg__vset(dst, p1->x + dlx0*rw, p1->y + dly0*rw, lu,1, -1, t);dst++; + nvg__vset(dst, rx0, ry0, ru, 1, 1, t); dst++; n = nvg__clampi((int)ceilf(((a1 - a0) / NVG_PI) * ncap), 2, ncap); for (i = 0; i < n; i++) { @@ -1497,19 +1511,19 @@ static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, float a = a0 + u*(a1-a0); float lx = p1->x + cosf(a) * lw; float ly = p1->y + sinf(a) * lw; - nvg__vset(dst, lx, ly, lu,1); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; + nvg__vset(dst, lx, ly, lu, 1, 1, t); dst++; + nvg__vset(dst, p1->x, p1->y, 0.5f, 1, 0.0, t); dst++; } - nvg__vset(dst, p1->x + dlx1*rw, p1->y + dly1*rw, lu,1); dst++; - nvg__vset(dst, rx1, ry1, ru,1); dst++; + nvg__vset(dst, p1->x + dlx1*rw, p1->y + dly1*rw, lu, 1, -1, t); dst++; + nvg__vset(dst, rx1, ry1, ru, 1, 1, t); dst++; } return dst; } static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, - float lw, float rw, float lu, float ru, float fringe) + float lw, float rw, float lu, float ru, float fringe, float t) { float rx0,ry0,rx1,ry1; float lx0,ly0,lx1,ly1; @@ -1522,99 +1536,109 @@ static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, if (p1->flags & NVG_PT_LEFT) { nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1); - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; + nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; if (p1->flags & NVG_PT_BEVEL) { - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; + nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; - nvg__vset(dst, lx1, ly1, lu,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; + nvg__vset(dst, lx1, ly1, lu, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru, 1, 1, t); dst++; } else { rx0 = p1->x - p1->dmx * rw; ry0 = p1->y - p1->dmy * rw; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; + nvg__vset(dst, p1->x, p1->y, 0.5f, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; + nvg__vset(dst, rx0, ry0, ru,1, -1, t); dst++; + nvg__vset(dst, rx0, ry0, ru,1, 1, t); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; + nvg__vset(dst, p1->x, p1->y, 0.5f, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru, 1, 1, t); dst++; } - nvg__vset(dst, lx1, ly1, lu,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; + nvg__vset(dst, lx1, ly1, lu, 1, -1, t); dst++; + nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1, 1, t); dst++; } else { nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1); - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; + nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu, 1, 1, t); dst++; + nvg__vset(dst, rx0, ry0, ru, 1, -1, t); dst++; if (p1->flags & NVG_PT_BEVEL) { - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; + nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu, 1, 1, t); dst++; + nvg__vset(dst, rx0, ry0, ru, 1, -1, t); dst++; - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++; - nvg__vset(dst, rx1, ry1, ru,1); dst++; + nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu, 1, 1, t); dst++; + nvg__vset(dst, rx1, ry1, ru, 1, -1, t); dst++; } else { lx0 = p1->x + p1->dmx * lw; ly0 = p1->y + p1->dmy * lw; - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; + nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu, 1, 1, t); dst++; + nvg__vset(dst, p1->x, p1->y, 0.5f,1, -1, t); dst++; - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, lx0, ly0, lu,1); dst++; + nvg__vset(dst, lx0, ly0, lu, 1, 1, t); dst++; + nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; + nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu, 1, 1, t); dst++; + nvg__vset(dst, p1->x, p1->y, 0.5f, 1, -1, t); dst++; } - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++; - nvg__vset(dst, rx1, ry1, ru,1); dst++; + nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu, 1, 1, t); dst++; + nvg__vset(dst, rx1, ry1, ru, 1, -1, t); dst++; } return dst; } +static NVGvertex* nvg_insertSpacer(NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, float u0, float u1, float t){ + float dlx = dy; + float dly = -dx; + float px = p->x; + float py = p->y; + nvg__vset(dst, px + dlx*w, py + dly*w, u0,1,-1, t); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, u1,1, 1, t); dst++; + return dst; +} + static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, float d, - float aa, float u0, float u1) + float aa, float u0, float u1, float t, int dir) { 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, u0,0); dst++; - nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++; - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; + nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0, -1, t - dir * aa / w); dst++; + nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0, 1, t - dir * aa / w); dst++; + nvg__vset(dst, px + dlx*w, py + dly*w, u0,1, -1, t); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, u1,1, 1, t); dst++; return dst; } static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, float d, - float aa, float u0, float u1) + float aa, float u0, float u1, float t, int dir) { 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, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; - nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++; - nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++; + nvg__vset(dst, px + dlx*w, py + dly*w, u0, 1 , -1, t); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, u1, 1 , 1, t); dst++; + nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0, 0, -1, t + dir * aa / w); dst++; + nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1, 0, 1, t + dir * aa / w); dst++; return dst; } static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, int ncap, - float aa, float u0, float u1) + float aa, float u0, float u1, float t, int dir) { int i; float px = p->x; @@ -1623,19 +1647,19 @@ static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, float dly = -dx; NVG_NOTUSED(aa); for (i = 0; i < ncap; i++) { - float a = i/(float)(ncap-1)*NVG_PI; + const 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, u0,1); dst++; - nvg__vset(dst, px, py, 0.5f,1); dst++; + nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0, 1, ax / w, t - dir * ay / w); dst++; + nvg__vset(dst, px, py, 0.5f, 1 , 0, t); dst++; } - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; + nvg__vset(dst, px + dlx*w, py + dly*w, u0, 1, 1, t); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, u1, 1, -1, t); dst++; return dst; } static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, int ncap, - float aa, float u0, float u1) + float aa, float u0, float u1, float t, int dir) { int i; float px = p->x; @@ -1643,13 +1667,13 @@ static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, float dlx = dy; float dly = -dx; NVG_NOTUSED(aa); - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; + nvg__vset(dst, px + dlx*w, py + dly*w, u0,1, w, t); dst++; + nvg__vset(dst, px - dlx*w, py - dly*w, u1,1, -w, t); 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, u0,1); dst++; + nvg__vset(dst, px, py, 0.5f, 1, 0, t); dst++; + nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0, 1, ax / w, t + dir * ay / w); dst++; } return dst; } @@ -1725,35 +1749,41 @@ static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float mi } -static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit) + +static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, int lineStyle, float miterLimit) { NVGpathCache* cache = ctx->cache; NVGvertex* verts; NVGvertex* dst; int cverts, i, j; + float t; float aa = fringe;//ctx->fringeWidth; float u0 = 0.0f, u1 = 1.0f; int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. w += aa * 0.5f; - + const float invStrokeWidth = 1.0 / w; // Disable the gradient used for antialiasing when antialiasing is not used. if (aa == 0.0f) { u0 = 0.5f; u1 = 0.5f; } - nvg__calculateJoins(ctx, w, lineJoin, miterLimit); + // Force round join to minimize distortion + if(lineStyle > 1) lineJoin = NVG_ROUND; + nvg__calculateJoins(ctx, w, lineJoin, miterLimit); // Calculate max vertex usage. cverts = 0; for (i = 0; i < cache->npaths; i++) { NVGpath* path = &cache->paths[i]; int loop = (path->closed == 0) ? 0 : 1; - if (lineJoin == NVG_ROUND) + if (lineJoin == NVG_ROUND) { cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop - else + } else { cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop + } + if(lineStyle > 1) cverts += 4 * path->count; // extra vertices for spacers if (loop == 0) { // space for caps if (lineCap == NVG_ROUND) { @@ -1797,52 +1827,84 @@ static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap e = path->count-1; } + t = 0; + + int dir = 1; + if(lineStyle > 1 && path->reversed) { + dir = -1; + for (j = s; j < path->count; ++j) { + dx = p1->x - p0->x; + dy = p1->y - p0->y; + t+=nvg__normalize(&dx, &dy) * invStrokeWidth; + p0 = p1++; + } + if (loop) { + // Looping + p0 = &pts[path->count-1]; + p1 = &pts[0]; + } else { + // Add cap + p0 = &pts[0]; + p1 = &pts[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, u0, u1); + dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1, t, dir); else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) - dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1); + dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1, t, dir); else if (lineCap == NVG_ROUND) - dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1); + dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1, t, dir); } - for (j = s; j < e; ++j) { + if(lineStyle > 1){ + dx = p1->x - p0->x; + dy = p1->y - p0->y; + float dt=nvg__normalize(&dx, &dy); + dst = nvg_insertSpacer(dst, p0, dx, dy, w, u0, u1, t); + t+=dir*dt*invStrokeWidth; + dst = nvg_insertSpacer(dst, p1, dx, dy, w, u0, u1, t); + } if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { if (lineJoin == NVG_ROUND) { - dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa); + dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa, t); } else { - dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa); + dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa, t); } } else { - nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++; - nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++; + nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0, 1, -1, t); dst++; + nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1, 1, 1, t); dst++; } p0 = p1++; } - + if (loop) { // Loop it - nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++; - nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++; + nvg__vset(dst, verts[0].x, verts[0].y, u0, 1, -1, t); dst++; + nvg__vset(dst, verts[1].x, verts[1].y, u1, 1, 1, t); dst++; } else { - // Add cap dx = p1->x - p0->x; dy = p1->y - p0->y; - nvg__normalize(&dx, &dy); + float dt = nvg__normalize(&dx, &dy); + if(lineStyle > 1){ + dst = nvg_insertSpacer(dst, p0, dx, dy, w, u0, u1, t); + t+=dir*dt*invStrokeWidth; + dst = nvg_insertSpacer(dst, p1, dx, dy, w, u0, u1, t); + } + // Add cap if (lineCap == NVG_BUTT) - dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1); + dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1, t, dir); else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) - dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1); + dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1, t, dir); else if (lineCap == NVG_ROUND) - dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1); + dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1, t, dir); } - path->nstroke = (int)(dst - verts); - verts = dst; } @@ -1900,23 +1962,23 @@ static int nvg__expandFill(NVGcontext* ctx, float w, int lineJoin, float miterLi 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++; + nvg__vset(dst, lx, ly, 0.5f, 1, 0, 0); dst++; } else { 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++; + nvg__vset(dst, lx0, ly0, 0.5f, 1, 0, 0); dst++; + nvg__vset(dst, lx1, ly1, 0.5f, 1, 0, 0); dst++; } } else { - nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1); dst++; + nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1, 0, 0); dst++; } p0 = p1++; } } else { for (j = 0; j < path->count; ++j) { - nvg__vset(dst, pts[j].x, pts[j].y, 0.5f,1); + nvg__vset(dst, pts[j].x, pts[j].y, 0.5f, 1, 0, 0); dst++; } } @@ -1946,17 +2008,17 @@ static int nvg__expandFill(NVGcontext* ctx, float w, int lineJoin, float miterLi for (j = 0; j < path->count; ++j) { if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { - dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth); + dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth, 0); } 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++; + nvg__vset(dst, p1->x + (p1->dmx * lw), p1->y + (p1->dmy * lw), lu,1,0, 0); dst++; + nvg__vset(dst, p1->x - (p1->dmx * rw), p1->y - (p1->dmy * rw), ru,1,0, 0); dst++; } p0 = p1++; } // 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++; + nvg__vset(dst, verts[0].x, verts[0].y, lu,1,0, 0); dst++; + nvg__vset(dst, verts[1].x, verts[1].y, ru,1,0, 0); dst++; path->nstroke = (int)(dst - verts); verts = dst; @@ -2279,12 +2341,12 @@ void nvgStroke(NVGcontext* ctx) nvg__flattenPaths(ctx); if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) - nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit); + nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->lineStyle, state->miterLimit); else - nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit); + nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->lineStyle, state->miterLimit); ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, - strokeWidth, ctx->cache->paths, ctx->cache->npaths); + strokeWidth, state->lineStyle, ctx->cache->paths, ctx->cache->npaths); // Count triangles for (i = 0; i < ctx->cache->npaths; i++) { @@ -2520,12 +2582,12 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* nvgTransformPoint(&c[6],&c[7], state->xform, q.x0*invscale, q.y1*invscale); // Create triangles if (nverts+6 <= cverts) { - nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++; - nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++; - nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); nverts++; - nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++; - nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1); nverts++; - nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++; + nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0, 0, 0); nverts++; + nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1, 0, 0); nverts++; + nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0, 0, 0); nverts++; + nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0, 0, 0); nverts++; + nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1, 0, 0); nverts++; + nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1, 0, 0); nverts++; } } diff --git a/src/nanovg.h b/src/nanovg.h index 0d90570f..94f7aaa6 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -63,6 +63,13 @@ enum NVGsolidity { NVG_HOLE = 2, // CW }; +enum NVGlineStyle { + NVG_LINE_SOLID = 1, + NVG_LINE_DASHED = 2, + NVG_LINE_DOTTED = 3, + NVG_LINE_GLOW = 4 +}; + enum NVGlineCap { NVG_BUTT, NVG_ROUND, @@ -260,6 +267,10 @@ void nvgMiterLimit(NVGcontext* ctx, float limit); // Sets the stroke width of the stroke style. void nvgStrokeWidth(NVGcontext* ctx, float size); +// Sets how line is drawn. +// Can be one of NVG_LINE_SOLID (default), NVG_LINE_GLOW, NVG_LINE_DASHED, NVG LINE_DOTTED +void nvgLineStyle(NVGcontext* ctx, int lineStyle); + // Sets how the end of the line (cap) is drawn, // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. void nvgLineCap(NVGcontext* ctx, int cap); @@ -639,13 +650,14 @@ struct NVGscissor { typedef struct NVGscissor NVGscissor; struct NVGvertex { - float x,y,u,v; + float x,y,u,v,s,t; }; typedef struct NVGvertex NVGvertex; struct NVGpath { int first; int count; + int reversed; unsigned char closed; int nbevel; NVGvertex* fill; @@ -669,7 +681,7 @@ struct NVGparams { void (*renderCancel)(void* uptr); void (*renderFlush)(void* uptr); void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); - void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); + void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, int lineStyle, const NVGpath* paths, int npaths); void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); void (*renderDelete)(void* uptr); }; diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 798b2369..3c82eba5 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -200,12 +200,13 @@ struct GLNVGfragUniforms { float feather; float strokeMult; float strokeThr; + int lineStyle; int texType; int type; #else // note: after modifying layout or size of uniform array, // don't forget to also update the fragment shader source! - #define NANOVG_GL_UNIFORMARRAY_SIZE 11 + #define NANOVG_GL_UNIFORMARRAY_SIZE 12 union { struct { float scissorMat[12]; // matrices are actually 3 vec4s @@ -219,8 +220,12 @@ struct GLNVGfragUniforms { float feather; float strokeMult; float strokeThr; + float lineStyle; float texType; float type; + float unused1; + float unused2; + float unused3; }; float uniformArray[NANOVG_GL_UNIFORMARRAY_SIZE][4]; }; @@ -528,7 +533,7 @@ static int glnvg__renderCreate(void* uptr) #if NANOVG_GL_USE_UNIFORMBUFFER "#define USE_UNIFORMBUFFER 1\n" #else - "#define UNIFORMARRAY_SIZE 11\n" + "#define UNIFORMARRAY_SIZE 12\n" #endif "\n"; @@ -536,18 +541,21 @@ static int glnvg__renderCreate(void* uptr) "#ifdef NANOVG_GL3\n" " uniform vec2 viewSize;\n" " in vec2 vertex;\n" - " in vec2 tcoord;\n" + " in vec4 tcoord;\n" " out vec2 ftcoord;\n" " out vec2 fpos;\n" + " smooth out vec2 uv;\n" "#else\n" " uniform vec2 viewSize;\n" " attribute vec2 vertex;\n" - " attribute vec2 tcoord;\n" + " attribute vec4 tcoord;\n" " varying vec2 ftcoord;\n" " varying vec2 fpos;\n" + " varying vec2 uv;\n" "#endif\n" "void main(void) {\n" - " ftcoord = tcoord;\n" + " ftcoord = tcoord.xy;\n" + " uv = 0.5 * tcoord.zw;\n" " fpos = vertex;\n" " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" "}\n"; @@ -574,6 +582,7 @@ static int glnvg__renderCreate(void* uptr) " float feather;\n" " float strokeMult;\n" " float strokeThr;\n" + " int lineStyle;\n" " int texType;\n" " int type;\n" " };\n" @@ -583,12 +592,14 @@ static int glnvg__renderCreate(void* uptr) " uniform sampler2D tex;\n" " in vec2 ftcoord;\n" " in vec2 fpos;\n" + " smooth in vec2 uv;\n" " out vec4 outColor;\n" "#else\n" // !NANOVG_GL3 " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" " uniform sampler2D tex;\n" " varying vec2 ftcoord;\n" " varying vec2 fpos;\n" + " varying vec2 uv;\n" "#endif\n" "#ifndef USE_UNIFORMBUFFER\n" " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" @@ -602,8 +613,9 @@ static int glnvg__renderCreate(void* uptr) " #define feather frag[9].w\n" " #define strokeMult frag[10].x\n" " #define strokeThr frag[10].y\n" - " #define texType int(frag[10].z)\n" - " #define type int(frag[10].w)\n" + " #define lineStyle int(frag[10].z)\n" + " #define texType int(frag[10].w)\n" + " #define type int(frag[11].x)\n" "#endif\n" "\n" "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" @@ -618,6 +630,27 @@ static int glnvg__renderCreate(void* uptr) " sc = vec2(0.5,0.5) - sc * scissorScale;\n" " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" "}\n" + "float glow(vec2 uv){\n" + " return smoothstep(0.0, 1.0, 1.0 - 2.0 * abs(uv.x));\n" + "}\n" + "float dashed(vec2 uv){\n" + " float fy = fract(uv.y / 4.0);\n" + " float w = step(fy, 0.5);\n" + " fy *= 4.0;\n" + " if(fy >= 1.5){\n" + " fy -= 1.5;\n" + " } else if(fy <= 0.5) {\n" + " fy = 0.5 - fy;\n" + "} else {\n" + " fy = 0.0;\n" + "}\n" + "w *= smoothstep(0.0, 1.0, 6.0 * (0.25 - (uv.x * uv.x + fy * fy)));\n" + " return w;\n" + "}\n" + "float dotted(vec2 uv){\n" + " float fy = 4.0 * fract(uv.y / (4.0)) - 0.5;\n" + " return smoothstep(0.0, 1.0, 6.0 * (0.25 - (uv.x * uv.x + fy * fy)));\n" + "}\n" "#ifdef EDGE_AA\n" "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" "float strokeMask() {\n" @@ -630,9 +663,16 @@ static int glnvg__renderCreate(void* uptr) " float scissor = scissorMask(fpos);\n" "#ifdef EDGE_AA\n" " float strokeAlpha = strokeMask();\n" + "if(lineStyle == 2) strokeAlpha*=dashed(uv);\n" + "if(lineStyle == 3) strokeAlpha*=dotted(uv);\n" + "if(lineStyle == 4) strokeAlpha*=glow(uv);\n" " if (strokeAlpha < strokeThr) discard;\n" "#else\n" " float strokeAlpha = 1.0;\n" + "if(lineStyle == 2) strokeAlpha*=dashed(uv);\n" + "if(lineStyle == 3) strokeAlpha*=dotted(uv);\n" + "if(lineStyle == 4) strokeAlpha*=glow(uv);\n" + "if (lineStyle > 1 && strokeAlpha < strokeThr) discard;\n" "#endif\n" " if (type == 0) { // Gradient\n" " // Calculate gradient color using box gradient\n" @@ -907,7 +947,7 @@ static NVGcolor glnvg__premulColor(NVGcolor c) } static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpaint* paint, - NVGscissor* scissor, float width, float fringe, float strokeThr) + NVGscissor* scissor, float width, float fringe, float strokeThr, int lineStyle) { GLNVGtexture* tex = NULL; float invxform[6]; @@ -916,7 +956,7 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai frag->innerCol = glnvg__premulColor(paint->innerColor); frag->outerCol = glnvg__premulColor(paint->outerColor); - + frag->lineStyle = lineStyle; if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) { memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); frag->scissorExt[0] = 1.0f; @@ -1225,7 +1265,7 @@ static void glnvg__renderFlush(void* uptr) glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(size_t)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); // Set view and texture just once per frame. glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); @@ -1419,12 +1459,12 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperation frag->strokeThr = -1.0f; frag->type = NSVG_SHADER_SIMPLE; // Fill shader - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f); + glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f, 0); } else { call->uniformOffset = glnvg__allocFragUniforms(gl, 1); if (call->uniformOffset == -1) goto error; // Fill shader - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); + glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f, 0); } return; @@ -1436,7 +1476,7 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperation } static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, - float strokeWidth, const NVGpath* paths, int npaths) + float strokeWidth, int lineStyle, const NVGpath* paths, int npaths) { GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcall* call = glnvg__allocCall(gl); @@ -1473,14 +1513,14 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperati call->uniformOffset = glnvg__allocFragUniforms(gl, 2); if (call->uniformOffset == -1) goto error; - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); + glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f, lineStyle); + glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f, lineStyle); } else { // Fill shader call->uniformOffset = glnvg__allocFragUniforms(gl, 1); if (call->uniformOffset == -1) goto error; - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); + glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f, lineStyle); } return; @@ -1515,7 +1555,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOper call->uniformOffset = glnvg__allocFragUniforms(gl, 1); if (call->uniformOffset == -1) goto error; frag = nvg__fragUniformPtr(gl, call->uniformOffset); - glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); + glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f, 0); frag->type = NSVG_SHADER_IMG; return;