From 205d5197d226d4e977224961bc2b2592945aa822 Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Wed, 5 Nov 2014 10:47:35 +0200 Subject: [PATCH] Issue #20: better handling of miter joins - better handling if stroke miter joins (inner bevel) - correct default miter 4 --- example/example2.c | 2 +- src/nanosvgrast.h | 97 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/example/example2.c b/example/example2.c index 4167dba..c63f8c8 100644 --- a/example/example2.c +++ b/example/example2.c @@ -32,7 +32,7 @@ int main() NSVGrasterizer *rast = NULL; unsigned char* img = NULL; int w, h; - const char* filename = "../example/23.svg"; + const char* filename = "../example/_web-spit.svg"; printf("parsing %s\n", filename); image = nsvgParseFromFile(filename, "px", 96.0f); diff --git a/src/nanosvgrast.h b/src/nanosvgrast.h index b88e243..a150939 100644 --- a/src/nanosvgrast.h +++ b/src/nanosvgrast.h @@ -360,6 +360,7 @@ enum NSVGpointFlags { NSVG_PT_CORNER = 0x01, NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04, }; static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) @@ -472,6 +473,42 @@ static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right right->x = rx1; right->y = ry1; } +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) { int i, n; @@ -540,7 +577,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float int s, e; NSVGpath* path; NSVGpoint* p0, *p1; - float miterLimit = 10; + float miterLimit = 4; int lineJoin = shape->strokeLineJoin; int lineCap = shape->strokeLineCap; float lineWidth = shape->strokeWidth * scale; @@ -582,7 +619,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float p0 = &r->points[r->npoints-1]; p1 = &r->points[0]; for (j = 0; j < r->npoints; j++) { - float dlx0, dly0, dlx1, dly1, dmr2; + float dlx0, dly0, dlx1, dly1, dmr2, cross; dlx0 = p0->dy; dly0 = -p0->dx; dlx1 = p1->dy; @@ -603,6 +640,11 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float // Clear flags, but keep the corner. p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + // Check to see if the corner needs to be beveled. if (p1->flags & NSVG_PT_CORNER) { if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { @@ -646,11 +688,14 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float } for (j = s; j < e; ++j) { - if (p1->flags & NSVG_PT_BEVEL) { +// if (p1->flags & NSVG_PT_BEVEL) { + if (p1->flags & NSVG_PT_CORNER) { if (lineJoin == NSVG_JOIN_ROUND) nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); - else + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); } else { nsvg__straightJoin(r, &left, &right, p1, lineWidth); } @@ -1131,6 +1176,48 @@ static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opac } +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + void nsvgRasterize(NSVGrasterizer* r, NSVGimage* image, float tx, float ty, float scale, unsigned char* dst, int w, int h, int stride) @@ -1186,6 +1273,8 @@ void nsvgRasterize(NSVGrasterizer* r, nsvg__flattenShapeStroke(r, shape, scale); +// dumpEdges(r, "edge.svg"); + // Scale and translate edges for (i = 0; i < r->nedges; i++) { e = &r->edges[i];