Browse Source

Added support for line caps and joins

shared-context
Mikko Mononen 11 years ago
parent
commit
0ec2be7a6c
3 changed files with 322 additions and 65 deletions
  1. +57
    -1
      example/demo.c
  2. +253
    -64
      src/nanovg.c
  3. +12
    -0
      src/nanovg.h

+ 57
- 1
example/demo.c View File

@@ -167,7 +167,7 @@ void drawDropDown(struct NVGcontext* vg, const char* text, float x, float y, flo
void drawLabel(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
{
NVG_NOTUSED(w);
nvgFontSize(vg, 18.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,128));
@@ -694,6 +694,59 @@ void drawColorwheel(struct NVGcontext* vg, float x, float y, float w, float h, f
nvgRestore(vg);
}

void drawLines(struct NVGcontext* vg, float x, float y, float w, float h, float t)
{
int i, j;
float pad = 5.0f, s = w/9.0f - pad*2;
float pts[4*2], fx, fy;
int joins[3] = {NVG_MITER, NVG_ROUND, NVG_BEVEL};
int caps[3] = {NVG_BUTT, NVG_ROUND, NVG_SQUARE};
NVG_NOTUSED(h);

nvgSave(vg);
pts[0] = -s*0.25f + cosf(t*0.3f) * s*0.5f;
pts[1] = sinf(t*0.3f) * s*0.5f;
pts[2] = -s*0.25;
pts[3] = 0;
pts[4] = s*0.25f;
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;
fy = y - s*0.5f + pad;

nvgLineCap(vg, caps[i]);
nvgLineJoin(vg, joins[j]);

nvgStrokeWidth(vg, s*0.3f);
nvgStrokeColor(vg, nvgRGBA(0,0,0,160));
nvgBeginPath(vg);
nvgMoveTo(vg, fx+pts[0], fy+pts[1]);
nvgLineTo(vg, fx+pts[2], fy+pts[3]);
nvgLineTo(vg, fx+pts[4], fy+pts[5]);
nvgLineTo(vg, fx+pts[6], fy+pts[7]);
nvgStroke(vg);

nvgLineCap(vg, NVG_BUTT);
nvgLineJoin(vg, NVG_BEVEL);

nvgStrokeWidth(vg, 1.0f);
nvgStrokeColor(vg, nvgRGBA(0,192,255,255));
nvgBeginPath(vg);
nvgMoveTo(vg, fx+pts[0], fy+pts[1]);
nvgLineTo(vg, fx+pts[2], fy+pts[3]);
nvgLineTo(vg, fx+pts[4], fy+pts[5]);
nvgLineTo(vg, fx+pts[6], fy+pts[7]);
nvgStroke(vg);
}
}


nvgRestore(vg);
}

int loadDemoData(struct NVGcontext* vg, struct DemoData* data)
{
@@ -751,6 +804,9 @@ void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float he
drawGraph(vg, 0, height/2, width, height/2, t);
drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t);

// Line joints
drawLines(vg, 50, height-50, 600, 50, t);

nvgSave(vg);
if (blowup) {
nvgRotate(vg, sinf(t*0.3f)*5.0f/180.0f*NVG_PI);


+ 253
- 64
src/nanovg.c View File

@@ -43,9 +43,9 @@ enum NVGcommands {

enum NVGpointFlags
{
NVG_BEVEL = 0x01,
NVG_LEFT = 0x02,
NVG_CUSP = 0x04,
NVG_PT_BEVEL = 0x01,
NVG_PT_LEFT = 0x02,
NVG_PT_CUSP = 0x04,
};

enum NVGexpandFeatures {
@@ -59,6 +59,8 @@ struct NVGstate {
struct NVGpaint stroke;
float strokeWidth;
float miterLimit;
int lineJoin;
int lineCap;
float xform[6];
struct NVGscissor scissor;
float fontSize;
@@ -120,6 +122,7 @@ static float nvg__acosf(float a) { return acosf(a); }

static int nvg__mini(int a, int b) { return a < b ? a : b; }
static int nvg__maxi(int a, int b) { return a > b ? a : b; }
static int nvg__clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); }
static float nvg__minf(float a, float b) { return a < b ? a : b; }
static float nvg__maxf(float a, float b) { return a > b ? a : b; }
static float nvg__absf(float a) { return a >= 0.0f ? a : -a; }
@@ -422,7 +425,9 @@ void nvgReset(struct NVGcontext* ctx)
nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255));
nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255));
state->strokeWidth = 1.0f;
state->miterLimit = 1.2f;
state->miterLimit = 10.0f;
state->lineCap = NVG_BUTT;
state->lineJoin = NVG_MITER;
nvg__xformIdentity(state->xform);

state->scissor.extent[0] = 0.0f;
@@ -448,6 +453,19 @@ void nvgMiterLimit(struct NVGcontext* ctx, float limit)
state->miterLimit = limit;
}

void nvgLineCap(struct NVGcontext* ctx, int cap)
{
struct NVGstate* state = nvg__getState(ctx);
state->lineCap = cap;
}

void nvgLineJoin(struct NVGcontext* ctx, int join)
{
struct NVGstate* state = nvg__getState(ctx);
state->lineJoin = join;
}


void nvgTransform(struct NVGcontext* ctx, float a, float b, float c, float d, float e, float f)
{
struct NVGstate* state = nvg__getState(ctx);
@@ -950,7 +968,7 @@ static void nvg__tesselateBezier(struct NVGcontext* ctx,
nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1);
}

static void nvg__flattenPaths(struct NVGcontext* ctx, float m)
static void nvg__flattenPaths(struct NVGcontext* ctx, int lineCap, int lineJoin, float miterLimit)
{
struct NVGpathCache* cache = ctx->cache;
// struct NVGstate* state = nvg__getState(ctx);
@@ -1058,7 +1076,7 @@ static void nvg__flattenPaths(struct NVGcontext* ctx, float m)
p0 = &pts[path->count-1];
p1 = &pts[0];
for(i = 0; i < path->count; ++i) {
float dlx0, dly0, dlx1, dly1, dmr2, scale, cross;
float dlx0, dly0, dlx1, dly1, dmr2, scale, cross, dot, d0, d1;
dlx0 = p0->dy;
dly0 = -p0->dx;
dlx1 = p1->dy;
@@ -1069,27 +1087,170 @@ static void nvg__flattenPaths(struct NVGcontext* ctx, float m)
dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
if (dmr2 > 0.000001f) {
scale = 1.0f / dmr2;
if (scale > 200.0f) scale = 200.0f;
if (scale > 600.0f) {
scale = 600.0f;
}
p1->dmx *= scale;
p1->dmy *= scale;
}

// TODO: improve this logic
dot = p0->dx*p1->dx + p0->dy*p1->dy;
if (dot < -0.75f)
p1->flags |= NVG_PT_CUSP;

// Keep track of left turns.
cross = p1->dx * p0->dy - p0->dx * p1->dy;
if (cross > 0.0f) {
nleft++;
p1->flags |= NVG_PT_LEFT;
}

// Check to see if the corner needs to be beveled.
if ((dmr2 * m*m) < 1.0f) {
cross = p1->dx * p0->dy - p0->dx * p1->dy;
p1->flags |= NVG_BEVEL;
if (cross > 0.0f) {
p1->flags |= NVG_LEFT;
nleft++;
}
path->nbevel++;
if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) {
p1->flags |= NVG_PT_BEVEL;
}

if ((p1->flags & (NVG_PT_BEVEL | NVG_PT_CUSP)) != 0)
path->nbevel++;

p0 = p1++;
}
path->convex = nleft == 0 ? 1 : 0;
}
}

static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)
static int nvg__curveDivs(float r, float arc, float tol)
{
float da = acosf(r / (r + tol)) * 2.0f;
return nvg__maxi(2, (int)ceilf(arc / da));
}

static void nvg__chooseBevel(int bevel, struct NVGpoint* p0, struct NVGpoint* p1, float w,
float* x0, float* y0, float* x1, float* y1, float fringe)
{
if (bevel) {
*x0 = p1->x + p0->dy * w + p0->dx * fringe;
*y0 = p1->y - p0->dx * w + p0->dy * fringe;
*x1 = p1->x + p1->dy * w - p1->dx * fringe;
*y1 = p1->y - p1->dx * w - p1->dy * fringe;
} else {
*x0 = p1->x + p1->dmx * w;
*y0 = p1->y + p1->dmy * w;
*x1 = p1->x + p1->dmx * w;
*y1 = p1->y + p1->dmy * w;
}
}

static struct NVGvertex* nvg__roundJoin(struct NVGvertex* dst, struct NVGpoint* p0, struct NVGpoint* p1,
float lw, float rw, float lu, float ru, int ncap, float fringe)
{
int i, n;
float dlx0 = p0->dy;
float dly0 = -p0->dx;
float dlx1 = p1->dy;
float dly1 = -p1->dx;

if (p1->flags & NVG_PT_LEFT) {
float lx0,ly0,lx1,ly1,a0,a1;
nvg__chooseBevel(p1->flags & NVG_PT_CUSP, p0, p1, lw, &lx0,&ly0, &lx1,&ly1, -fringe);
a0 = atan2f(-dly0, -dlx0);
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++;

n = nvg__clampi((int)ceilf(((a0 - a1) / NVG_PI) * ncap), 2, ncap);
for (i = 0; i < n; i++) {
float u = i/(float)(n-1);
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, lx1, ly1, lu,1); dst++;
nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;

} else {
float rx0,ry0,rx1,ry1,a0,a1;
nvg__chooseBevel(p1->flags & NVG_PT_CUSP, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1, -fringe);
a0 = atan2f(dly0, dlx0);
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++;

n = nvg__clampi((int)ceilf(((a1 - a0) / NVG_PI) * ncap), 2, ncap);
for (i = 0; i < n; i++) {
float u = i/(float)(n-1);
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, p1->x + dlx1*rw, p1->y + dly1*rw, lu,1); dst++;
nvg__vset(dst, rx1, ry1, ru,1); dst++;

}
return dst;
}

static struct NVGvertex* nvg__bevelJoin(struct NVGvertex* dst, struct NVGpoint* p0, struct NVGpoint* p1,
float lw, float rw, float lu, float ru, float fringe)
{
int left = 0, right = 0;
float dot, rf, lf;
float rx0,ry0,rx1,ry1;
float lx0,ly0,lx1,ly1;

if (p1->flags & NVG_PT_LEFT) {
left = p1->flags & NVG_PT_CUSP;
right = p1->flags & NVG_PT_BEVEL;
lf = 0;
rf = fringe;
} else {
left = p1->flags & NVG_PT_BEVEL;
right = p1->flags & NVG_PT_CUSP;
lf = fringe;
rf = 0;
}

nvg__chooseBevel(left, p0, p1, lw, &lx0,&ly0, &lx1,&ly1, lf);
nvg__chooseBevel(right, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1, rf);

dot = p0->dx*p1->dx + p0->dy*p1->dy;
dot = (1.0f - dot) * 0.5f;

nvg__vset(dst, lx0, ly0, lu,1); dst++;
nvg__vset(dst, rx0, ry0, ru,1); dst++;

if (p1->flags & NVG_PT_CUSP) {
if (p1->flags & NVG_PT_LEFT) {
nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
nvg__vset(dst, rx0, ry0, ru,1); dst++;
nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
nvg__vset(dst, rx1, ry1, ru,1); dst++;
} else {
nvg__vset(dst, lx0, ly0, lu,1); dst++;
nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
nvg__vset(dst, lx1, ly1, lu,1); dst++;
nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
}
}

nvg__vset(dst, lx1, ly1, lu,1); dst++;
nvg__vset(dst, rx1, ry1, ru,1); dst++;

return dst;
}

static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w, int lineCap, int lineJoin, float miterLimit)
{
struct NVGpathCache* cache = ctx->cache;
struct NVGpath* path;
@@ -1103,6 +1264,7 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)
int i, j, s, e;
float wo = 0;
float aa = ctx->fringeWidth;
int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol / 4.0f);

// Calculate max vertex usage.
cverts = 0;
@@ -1112,9 +1274,18 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)
cverts += path->count + path->nbevel + 1;
if (feats & NVG_STROKE) {
int loop = ((feats & NVG_CAPS) && path->closed == 0) ? 0 : 1;
cverts += (path->count + path->nbevel + 1) * 2; // plus one for loop
if (loop == 0)
cverts += (3+3)*2; // space for caps
if (lineCap == NVG_ROUND)
cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop
else
cverts += (path->count + path->nbevel*3 + 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;
}
}
}
}

@@ -1143,12 +1314,12 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)
p0 = &pts[path->count-1];
p1 = &pts[0];
for (j = 0; j < path->count; ++j) {
if (p1->flags & NVG_BEVEL) {
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_LEFT) {
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++;
@@ -1178,7 +1349,7 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)
path->nfill = 0;
}

// Calculate fringe
// Calculate fringe or stroke
if (feats & NVG_STROKE) {
float lw = w + wo, rw = w - wo;
float ru = 1, lu = 0;
@@ -1209,46 +1380,44 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)

if (loop == 0) {
// Add cap
float dx, dy, dlx, dly;
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;
nvg__vset(dst, p0->x + dlx*lw - dx*aa, p0->y + dly*lw - dy*aa, lu,0); dst++;
nvg__vset(dst, p0->x - dlx*rw - dx*aa, p0->y - dly*rw - dy*aa, ru,0); dst++;
nvg__vset(dst, p0->x + dlx*lw, p0->y + dly * lw, lu,1); dst++;
nvg__vset(dst, p0->x - dlx*rw, p0->y - dly * rw, ru,1); dst++;
if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) {
if (lineCap == NVG_BUTT) {
px = p0->x;
py = p0->y;
} else if (lineCap == NVG_SQUARE) {
px = p0->x - dx*w;
py = p0->y - dy*w;
}
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++;
}
}

for (j = s; j < e; ++j) {
if (p1->flags & NVG_BEVEL) {
float dlx0 = p0->dy;
float dly0 = -p0->dx;
float dlx1 = p1->dy;
float dly1 = -p1->dx;
if (p1->flags & NVG_LEFT) {
float rx0 = p1->x - dlx0 * rw;
float ry0 = p1->y - dly0 * rw;
float rx1 = p1->x - dlx1 * rw;
float ry1 = p1->y - dly1 * rw;
float lx = p1->x + p1->dmx * lw;
float ly = p1->y + p1->dmy * lw;
nvg__vset(dst, lx, ly, lu,1); dst++;
nvg__vset(dst, rx0, ry0, ru,1); dst++;
nvg__vset(dst, lx, ly, lu,1); dst++;
nvg__vset(dst, rx1, ry1, ru,1); dst++;
if ((p1->flags & (NVG_PT_BEVEL | NVG_PT_CUSP)) != 0) {
if (lineJoin == NVG_ROUND) {
dst = nvg__roundJoin(dst, p0, p1, lw, rw, lu, ru, ncap, ctx->fringeWidth);
} else {
float rx = p1->x - p1->dmx * rw;
float ry = p1->y - p1->dmy * rw;
float lx0 = p1->x + dlx0 * lw;
float ly0 = p1->y + dly0 * lw;
float lx1 = p1->x + dlx1 * lw;
float ly1 = p1->y + dly1 * lw;
nvg__vset(dst, lx0, ly0, lu,1); dst++;
nvg__vset(dst, rx, ry, ru,1); dst++;
nvg__vset(dst, lx1, ly1, lu,1); dst++;
nvg__vset(dst, rx, ry, ru,1); dst++;
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++;
@@ -1263,16 +1432,36 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w)
nvg__vset(dst, verts[1].x, verts[1].y, ru,1); dst++;
} else {
// Add cap
float dx, dy, dlx, dly;
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;
nvg__vset(dst, p1->x + dlx*lw, p1->y + dly * lw, lu,1); dst++;
nvg__vset(dst, p1->x - dlx*rw, p1->y - dly * rw, ru,1); dst++;
nvg__vset(dst, p1->x + dlx*lw + dx*aa, p1->y + dly*lw + dy*aa, lu,0); dst++;
nvg__vset(dst, p1->x - dlx*rw + dx*aa, p1->y - dly*rw + dy*aa, ru,0); dst++;
if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) {
if (lineCap == NVG_BUTT) {
px = p1->x;
py = p1->y;
} else if (lineCap == NVG_SQUARE) {
px = p1->x + dx*w;
py = p1->y + dy*w;
}
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++;
}
}
}

path->nstroke = (int)(dst - verts);
@@ -1505,11 +1694,11 @@ void nvgFill(struct NVGcontext* ctx)
const struct NVGpath* path;
int i;

nvg__flattenPaths(ctx, state->miterLimit);
nvg__flattenPaths(ctx, NVG_BUTT, NVG_MITER, 1.2f);
if (ctx->params.edgeAntiAlias)
nvg__expandStrokeAndFill(ctx, NVG_FILL|NVG_STROKE, ctx->fringeWidth);
nvg__expandStrokeAndFill(ctx, NVG_FILL|NVG_STROKE, ctx->fringeWidth, NVG_BUTT, NVG_MITER, 1.2f);
else
nvg__expandStrokeAndFill(ctx, NVG_FILL, 0.0f);
nvg__expandStrokeAndFill(ctx, NVG_FILL, 0.0f, NVG_BUTT, NVG_MITER, 1.2f);

ctx->params.renderFill(ctx->params.userPtr, &state->fill, &state->scissor,
ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths);
@@ -1531,11 +1720,11 @@ void nvgStroke(struct NVGcontext* ctx)
const struct NVGpath* path;
int i;

nvg__flattenPaths(ctx, state->miterLimit);
nvg__flattenPaths(ctx, state->lineCap, state->lineJoin, state->miterLimit);
if (ctx->params.edgeAntiAlias)
nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f + ctx->fringeWidth/2.0f);
nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f + ctx->fringeWidth/2.0f, state->lineCap, state->lineJoin, state->miterLimit);
else
nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f);
nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit);

ctx->params.renderStroke(ctx->params.userPtr, &state->stroke, &state->scissor,
strokeWidth, ctx->cache->paths, ctx->cache->npaths);


+ 12
- 0
src/nanovg.h View File

@@ -43,11 +43,20 @@ enum NVGwinding {
NVG_CCW = 1, // Winding for solid shapes
NVG_CW = 2, // Winding for holes
};

enum NVGsolidity {
NVG_SOLID = 1, // CCW
NVG_HOLE = 2, // CW
};

enum NVGlineCap {
NVG_BUTT,
NVG_ROUND,
NVG_SQUARE,
NVG_BEVEL,
NVG_MITER,
};

enum NVGpatternRepeat {
NVG_REPEATX = 0x01, // Repeat image pattern in X direction
NVG_REPEATY = 0x02, // Repeat image pattern in Y direction
@@ -149,6 +158,9 @@ void nvgMiterLimit(struct NVGcontext* ctx, float limit);
// Sets the stroke witdth of the stroke style.
void nvgStrokeWidth(struct NVGcontext* ctx, float size);

void nvgLineCap(struct NVGcontext* ctx, int cap);
void nvgLineJoin(struct NVGcontext* ctx, int join);

//
// Transforms
//


Loading…
Cancel
Save