From 193af23a60a46b4425a7392f8fb8f6be3f045cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20R=C3=B6ver?= Date: Sat, 20 Mar 2021 19:01:42 +0100 Subject: [PATCH] implement the paint-order attribute for paths --- src/nanosvg.h | 25 +++++++++ src/nanosvgrast.h | 131 ++++++++++++++++++++++++++++------------------ 2 files changed, 105 insertions(+), 51 deletions(-) diff --git a/src/nanosvg.h b/src/nanosvg.h index 4c03ee58..d9d477a6 100644 --- a/src/nanosvg.h +++ b/src/nanosvg.h @@ -101,6 +101,15 @@ enum NSVGfillRule { NSVG_FILLRULE_EVENODD = 1 }; +enum NSVGpaintOrder { + NSVG_PAINTORDER_FILL_STROKE_MARKERS = 0, + NSVG_PAINTORDER_FILL_MARKERS_STROKE = 1, + NSVG_PAINTORDER_STROKE_FILL_MARKERS = 2, + NSVG_PAINTORDER_STROKE_MARKERS_FILL = 3, + NSVG_PAINTORDER_MARKERS_FILL_STROKE = 4, + NSVG_PAINTORDER_MARKERS_STROKE_FILL = 5 +}; + enum NSVGflags { NSVG_FLAGS_VISIBLE = 0x01 }; @@ -153,6 +162,7 @@ typedef struct NSVGshape float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. NSVGpath* paths; // Linked list of paths in the image. struct NSVGshape* next; // Pointer to next shape, or NULL if last element. + char paintOrder; // see NSVGpaintOrder } NSVGshape; typedef struct NSVGimage @@ -434,6 +444,7 @@ typedef struct NSVGattrib char hasFill; char hasStroke; char visible; + char paintOrder; } NSVGattrib; typedef struct NSVGparser @@ -636,6 +647,7 @@ static NSVGparser* nsvg__createParser() p->attr[0].strokeLineCap = NSVG_CAP_BUTT; p->attr[0].miterLimit = 4; p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].paintOrder = NSVG_PAINTORDER_FILL_STROKE_MARKERS; p->attr[0].hasFill = 1; p->attr[0].visible = 1; @@ -966,6 +978,7 @@ static void nsvg__addShape(NSVGparser* p) shape->strokeLineCap = attr->strokeLineCap; shape->miterLimit = attr->miterLimit; shape->fillRule = attr->fillRule; + shape->paintOrder = attr->paintOrder; shape->opacity = attr->opacity; shape->paths = p->plist; @@ -1694,6 +1707,16 @@ static char nsvg__parseFillRule(const char* str) return NSVG_FILLRULE_NONZERO; } +static char nsvg__parsePaintOrder(const char * str) +{ + if (strcmp(str, "stroke") == 0) + return NSVG_PAINTORDER_STROKE_FILL_MARKERS; + else if (strcmp(str, "markers") == 0) + return NSVG_PAINTORDER_MARKERS_FILL_STROKE; + // TODO: handle inherit. + return NSVG_PAINTORDER_FILL_STROKE_MARKERS; +} + static const char* nsvg__getNextDashItem(const char* s, char* it) { int n = 0; @@ -1789,6 +1812,8 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) attr->strokeLineJoin = nsvg__parseLineJoin(value); } else if (strcmp(name, "stroke-miterlimit") == 0) { attr->miterLimit = nsvg__parseMiterLimit(value); + } else if (strcmp(name, "paint-order") == 0) { + attr->paintOrder = nsvg__parsePaintOrder(value); } else if (strcmp(name, "fill-rule") == 0) { attr->fillRule = nsvg__parseFillRule(value); } else if (strcmp(name, "font-size") == 0) { diff --git a/src/nanosvgrast.h b/src/nanosvgrast.h index b740c316..6281564b 100644 --- a/src/nanosvgrast.h +++ b/src/nanosvgrast.h @@ -1362,12 +1362,75 @@ static void dumpEdges(NSVGrasterizer* r, const char* name) } */ +static void drawStroke(NSVGrasterizer* r, const NSVGshape *shape, NSVGcachedPaint * cache, float tx, float ty, float scale) +{ + int i; + NSVGedge *e = NULL; + + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, cache, NSVG_FILLRULE_NONZERO); + } +} + +static void drawFill(NSVGrasterizer* r, const NSVGshape *shape, NSVGcachedPaint * cache, float tx, float ty, float scale) +{ + int i; + NSVGedge *e = NULL; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, cache, shape->fillRule); + } +} + void nsvgRasterize(NSVGrasterizer* r, NSVGimage* image, float tx, float ty, float scale, unsigned char* dst, int w, int h, int stride) { - NSVGshape *shape = NULL; - NSVGedge *e = NULL; + const NSVGshape *shape = NULL; NSVGcachedPaint cache; int i; @@ -1389,55 +1452,21 @@ void nsvgRasterize(NSVGrasterizer* r, if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue; - if (shape->fill.type != NSVG_PAINT_NONE) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShape(r, shape, scale); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaint(&cache, &shape->fill, shape->opacity); - - nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); - } - if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShapeStroke(r, shape, scale); - -// dumpEdges(r, "edge.svg"); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaint(&cache, &shape->stroke, shape->opacity); - - nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + switch (shape->paintOrder) + { + case NSVG_PAINTORDER_FILL_STROKE_MARKERS: + case NSVG_PAINTORDER_FILL_MARKERS_STROKE: + case NSVG_PAINTORDER_MARKERS_FILL_STROKE: + drawFill(r, shape, &cache, tx, ty, scale); + drawStroke(r, shape, &cache, tx, ty, scale); + break; + + case NSVG_PAINTORDER_STROKE_FILL_MARKERS: + case NSVG_PAINTORDER_STROKE_MARKERS_FILL: + case NSVG_PAINTORDER_MARKERS_STROKE_FILL: + drawStroke(r, shape, &cache, tx, ty, scale); + drawFill(r, shape, &cache, tx, ty, scale); + break; } }