diff --git a/README.md b/README.md index e837e88..6151c37 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,20 @@ NanoSVG supports a wide range of SVG features, if somehing is missing, feel free ``` C // Load -struct SNVGPath* plist; -plist = nsvgParseFromFile("test.svg."); - -// Use -for (NSVGPath* it = plist; it; it = it->next) { - for (i = 0; i < npts-1; i += 3) { - float* p = &pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); +struct SNVGImage* image; +image = nsvgParseFromFile("test.svg."); +printf("size: %f x %f\n", image->width, image->height); +// Use... +for (shape = image->shapes; shape != NULL; shape = shape->next) { + for (path = shape->paths; path != NULL; path = path->next) { + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } } } - // Delete -nsvgDelete(plist); +nsvgDelete(image); ``` ## Using NanoSVG in your project diff --git a/example/main.c b/example/main.c index 8a8f158..d290dc2 100644 --- a/example/main.c +++ b/example/main.c @@ -24,7 +24,7 @@ #define NANOSVG_IMPLEMENTATION #include "nanosvg.h" -struct NSVGPath* g_plist = NULL; +struct NSVGImage* g_image = NULL; static unsigned char bgColor[4] = {205,202,200,255}; static unsigned char lineColor[4] = {0,160,192,255}; @@ -80,21 +80,24 @@ static void cubicBez(float x1, float y1, float x2, float y2, } } -static void calcBounds(struct NSVGPath* plist, float* bounds) +static void calcBounds(struct NSVGImage* image, float* bounds) { - struct NSVGPath* it; + struct NSVGShape* shape; + struct NSVGPath* path; int i; bounds[0] = FLT_MAX; bounds[1] = FLT_MAX; bounds[2] = -FLT_MAX; bounds[3] = -FLT_MAX; - for (it = plist; it; it = it->next) { - for (i = 0; i < it->npts; i++) { - float* p = &it->pts[i*2]; - bounds[0] = minf(bounds[0], p[0]); - bounds[1] = minf(bounds[1], p[1]); - bounds[2] = maxf(bounds[2], p[0]); - bounds[3] = maxf(bounds[3], p[1]); + for (shape = image->shapes; shape != NULL; shape = shape->next) { + for (path = shape->paths; path != NULL; path = path->next) { + for (i = 0; i < path->npts; i++) { + float* p = &path->pts[i*2]; + bounds[0] = minf(bounds[0], p[0]); + bounds[1] = minf(bounds[1], p[1]); + bounds[2] = maxf(bounds[2], p[0]); + bounds[3] = maxf(bounds[3], p[1]); + } } } } @@ -164,7 +167,8 @@ void drawframe(GLFWwindow* window) { int width = 0, height = 0; float bounds[4], view[4], cx, cy, w, h, aspect, px; - struct NSVGPath* it; + struct NSVGShape* shape; + struct NSVGPath* path; glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwGetFramebufferSize(window, &width, &height); @@ -179,7 +183,7 @@ void drawframe(GLFWwindow* window) glLoadIdentity(); // Fit view to bounds - calcBounds(g_plist, bounds); + calcBounds(g_image, bounds); cx = (bounds[0]+bounds[2])/2; cy = (bounds[3]+bounds[1])/2; w = (bounds[2]-bounds[0])/2; @@ -210,9 +214,11 @@ void drawframe(GLFWwindow* window) glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - for (it = g_plist; it; it = it->next) { - drawPath(it->pts, it->npts, it->closed, px * 1.5f); - drawControlPts(it->pts, it->npts, it->closed); + for (shape = g_image->shapes; shape != NULL; shape = shape->next) { + for (path = shape->paths; path != NULL; path = path->next) { + drawPath(path->pts, path->npts, path->closed, px * 1.5f); + drawControlPts(path->pts, path->npts, path->closed); + } } glfwSwapBuffers(window); @@ -247,9 +253,9 @@ int main() glEnable(GL_LINE_SMOOTH); - g_plist = nsvgParseFromFile("../example/nano.svg"); - if (g_plist == NULL) { - printf("Could not open test.svg\n"); + g_image = nsvgParseFromFile("../example/nano.svg"); + if (g_image == NULL) { + printf("Could not open SVG image.\n"); glfwTerminate(); return -1; } @@ -260,7 +266,7 @@ int main() glfwPollEvents(); } - nsvgDelete(g_plist); + nsvgDelete(g_image); glfwTerminate(); return 0; diff --git a/src/nanosvg.h b/src/nanosvg.h index 5f39110..2a31c65 100644 --- a/src/nanosvg.h +++ b/src/nanosvg.h @@ -33,41 +33,58 @@ extern "C" { /* Example Usage: // Load - struct SNVGPath* plist; - plist = nsvgParseFromFile("test.svg."); + struct SNVGImage* image; + image = nsvgParseFromFile("test.svg."); + printf("size: %f x %f\n", image->width, image->height); // Use... - for (NSVGPath* it = plist; it; it = it->next) { - for (i = 0; i < npts-1; i += 3) { - float* p = &pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + for (shape = image->shapes; shape != NULL; shape = shape->next) { + for (path = shape->paths; path != NULL; path = path->next) { + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } } } // Delete - nsvgDelete(plist); + nsvgDelete(image); */ - + struct NSVGPath { float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... int npts; // Total number of bezier points. - unsigned int shapeId; // Sequence number if a SVG shape, used to identify paths belonging to same shape. + char closed; // Flag indicating if shapes should be treated as closed. + struct NSVGPath* next; // Pointer to next path, or NULL if last element. +}; + +struct NSVGShape +{ unsigned int fillColor; // Fill color unsigned int strokeColor; // Stroke color float strokeWidth; // Stroke width (scaled) char hasFill; // Flag indicating if fill exists. char hasStroke; // Flag indicating id store exists - char closed; // Flag indicating if shapes should be treated as closed. - struct NSVGPath* next; // Pointer to next path, or NULL if last element. + struct NSVGPath* paths; // Linked list of paths in the image. + struct NSVGShape* next; // Pointer to next shape, or NULL if last element. +}; + +struct NSVGImage +{ + float width; // Width of the image, or -1.0f of not set. + float height; // Height of the image, or -1.0f of not set. + char wunits[8]; // Units of the width attribute + char hunits[8]; // Units of the height attribute + struct NSVGShape* shapes; // Linked list of shapes in the image. }; // Parses SVG file from a file, returns linked list of paths. -struct NSVGPath* nsvgParseFromFile(const char* filename); +struct NSVGImage* nsvgParseFromFile(const char* filename); // Parses SVG file from a null terminated string, returns linked list of paths. -struct NSVGPath* nsvgParse(char* input); +struct NSVGImage* nsvgParse(char* input); // Deletes list of paths. -void nsvgDelete(struct NSVGPath* plist); +void nsvgDelete(struct NSVGImage* image); #ifdef __cplusplus }; @@ -99,6 +116,8 @@ static int nsvg__isnum(char c) return strchr("0123456789+-.eE", c) != 0; } +static inline float nsvg__maxf(float a, float b) { return a > b ? a : b; } + // Simple XML parser @@ -238,8 +257,8 @@ struct NSVGParser float* pts; int npts; int cpts; - unsigned int shapeId; struct NSVGPath* plist; + struct NSVGImage* image; char pathFlag; char defsFlag; }; @@ -324,10 +343,15 @@ static struct NSVGParser* nsvg__createParser() { struct NSVGParser* p; p = (struct NSVGParser*)malloc(sizeof(struct NSVGParser)); - if (!p) - return NULL; + if (p == NULL) goto error; memset(p, 0, sizeof(struct NSVGParser)); + p->image = (struct NSVGImage*)malloc(sizeof(struct NSVGImage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(struct NSVGImage)); + p->image->width = -1.0f; + p->image->height = -1.0f; + // Init style nsvg__xformSetIdentity(p->attr[0].xform); p->attr[0].fillColor = 0; @@ -340,13 +364,18 @@ static struct NSVGParser* nsvg__createParser() p->attr[0].visible = 1; return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; } -static void nsvg__deleteParser(struct NSVGParser* p) +static void nsvg__deletePaths(struct NSVGPath* path) { - struct NSVGPath* path; struct NSVGPath* next; - path = p->plist; while (path) { next = path->next; if (path->pts) @@ -354,9 +383,16 @@ static void nsvg__deleteParser(struct NSVGParser* p) free(path); path = next; } - if (p->pts) +} + +static void nsvg__deleteParser(struct NSVGParser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvgDelete(p->image); free(p->pts); - free(p); + free(p); + } } static void nsvg__resetPath(struct NSVGParser* p) @@ -429,24 +465,56 @@ static void nsvg__popAttr(struct NSVGParser* p) p->attrHead--; } -static void nsvg__createPath(struct NSVGParser* p, char closed) +static void nsvg__addShape(struct NSVGParser* p) +{ + struct NSVGAttrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + + if (p->plist == NULL) + return; + + struct NSVGShape* shape = (struct NSVGShape*)malloc(sizeof(struct NSVGShape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(struct NSVGShape)); + + scale = nsvg__maxf(fabsf(attr->xform[0]), fabsf(attr->xform[3])); + shape->hasFill = attr->hasFill; + shape->hasStroke = attr->hasStroke; + shape->strokeWidth = attr->strokeWidth * scale; + + shape->fillColor = attr->fillColor; + if (shape->hasFill) + shape->fillColor |= (unsigned int)(attr->fillOpacity*255) << 24; + + shape->strokeColor = attr->strokeColor; + if (shape->hasStroke) + shape->strokeColor |= (unsigned int)(attr->strokeOpacity*255) << 24; + + shape->paths = p->plist; + p->plist = NULL; + + shape->next = p->image->shapes; + p->image->shapes = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(struct NSVGParser* p, char closed) { float* t = NULL; - struct NSVGAttrib* attr = NULL; + struct NSVGAttrib* attr = nsvg__getAttr(p); struct NSVGPath* path = NULL; int i; - if (p == NULL) - return; - if (p->npts == 0) return; if (closed) nsvg__lineTo(p, p->pts[0], p->pts[1]); - attr = nsvg__getAttr(p); - path = (struct NSVGPath*)malloc(sizeof(struct NSVGPath)); if (path == NULL) goto error; memset(path, 0, sizeof(struct NSVGPath)); @@ -461,20 +529,6 @@ static void nsvg__createPath(struct NSVGParser* p, char closed) for (i = 0; i < p->npts; ++i) nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], t); - path->hasFill = attr->hasFill; - path->hasStroke = attr->hasStroke; - path->strokeWidth = attr->strokeWidth * fabsf(t[0]); - - path->fillColor = attr->fillColor; - if (path->hasFill) - path->fillColor |= (unsigned int)(attr->fillOpacity*255) << 24; - - path->strokeColor = attr->strokeColor; - if (path->hasStroke) - path->strokeColor |= (unsigned int)(attr->strokeOpacity*255) << 24; - - path->shapeId = p->shapeId; - path->next = p->plist; p->plist = path; @@ -1382,7 +1436,7 @@ static void nsvg__parsePath(struct NSVGParser* p, const char** attr) if (cmd == 'M' || cmd == 'm') { // Commit path. if (p->npts > 0) - nsvg__createPath(p, closedFlag); + nsvg__addPath(p, closedFlag); // Start new subpath. nsvg__resetPath(p); closedFlag = 0; @@ -1392,7 +1446,7 @@ static void nsvg__parsePath(struct NSVGParser* p, const char** attr) closedFlag = 1; // Commit path. if (p->npts > 0) - nsvg__createPath(p, closedFlag); + nsvg__addPath(p, closedFlag); // Start new subpath. nsvg__resetPath(p); closedFlag = 0; @@ -1402,7 +1456,7 @@ static void nsvg__parsePath(struct NSVGParser* p, const char** attr) } // Commit path. if (p->npts) - nsvg__createPath(p, closedFlag); + nsvg__addPath(p, closedFlag); } else { tmp[0] = attr[i]; tmp[1] = attr[i + 1]; @@ -1411,6 +1465,8 @@ static void nsvg__parsePath(struct NSVGParser* p, const char** attr) nsvg__parseAttribs(p, tmp); } } + + nsvg__addShape(p); } static void nsvg__parseRect(struct NSVGParser* p, const char** attr) @@ -1462,7 +1518,9 @@ static void nsvg__parseRect(struct NSVGParser* p, const char** attr) nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); } - nsvg__createPath(p, 1); + nsvg__addPath(p, 1); + + nsvg__addShape(p); } } @@ -1490,7 +1548,9 @@ static void nsvg__parseCircle(struct NSVGParser* p, const char** attr) nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); - nsvg__createPath(p, 1); + nsvg__addPath(p, 1); + + nsvg__addShape(p); } } @@ -1521,7 +1581,9 @@ static void nsvg__parseEllipse(struct NSVGParser* p, const char** attr) nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); - nsvg__createPath(p, 1); + nsvg__addPath(p, 1); + + nsvg__addShape(p); } } @@ -1547,7 +1609,9 @@ static void nsvg__parseLine(struct NSVGParser* p, const char** attr) nsvg__moveTo(p, x1, y1); nsvg__lineTo(p, x2, y2); - nsvg__createPath(p, 0); + nsvg__addPath(p, 0); + + nsvg__addShape(p); } static void nsvg__parsePoly(struct NSVGParser* p, const char** attr, int closeFlag) @@ -1581,7 +1645,27 @@ static void nsvg__parsePoly(struct NSVGParser* p, const char** attr, int closeFl } } - nsvg__createPath(p, closeFlag); + nsvg__addPath(p, closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(struct NSVGParser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->wunits[0] = '\0'; + sscanf(attr[i + 1], "%f%s", &p->image->width, p->image->wunits); + printf("W units='%s'\n", p->image->wunits); + } else if (strcmp(attr[i], "height") == 0) { + p->image->hunits[0] = '\0'; + sscanf(attr[i + 1], "%f%s", &p->image->height, p->image->hunits); + printf("H units='%s'\n", p->image->hunits); + } + } + } } static void nsvg__startElement(void* ud, const char* el, const char** attr) @@ -1600,40 +1684,35 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr) return; nsvg__pushAttr(p); nsvg__parsePath(p, attr); - p->pathFlag = 1; nsvg__popAttr(p); } else if (strcmp(el, "rect") == 0) { nsvg__pushAttr(p); nsvg__parseRect(p, attr); - p->shapeId++; nsvg__popAttr(p); } else if (strcmp(el, "circle") == 0) { nsvg__pushAttr(p); nsvg__parseCircle(p, attr); - p->shapeId++; nsvg__popAttr(p); } else if (strcmp(el, "ellipse") == 0) { nsvg__pushAttr(p); nsvg__parseEllipse(p, attr); - p->shapeId++; nsvg__popAttr(p); } else if (strcmp(el, "line") == 0) { nsvg__pushAttr(p); nsvg__parseLine(p, attr); - p->shapeId++; nsvg__popAttr(p); } else if (strcmp(el, "polyline") == 0) { nsvg__pushAttr(p); nsvg__parsePoly(p, attr, 0); - p->shapeId++; nsvg__popAttr(p); } else if (strcmp(el, "polygon") == 0) { nsvg__pushAttr(p); nsvg__parsePoly(p, attr, 1); - p->shapeId++; nsvg__popAttr(p); } else if (strcmp(el, "defs") == 0) { p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); } } @@ -1655,10 +1734,10 @@ static void nsvg__content(void* ud, const char* s) // empty } -struct NSVGPath* nsvgParse(char* input) +struct NSVGImage* nsvgParse(char* input) { struct NSVGParser* p; - struct NSVGPath* ret = 0; + struct NSVGImage* ret = 0; p = nsvg__createParser(); if (p == NULL) { @@ -1667,20 +1746,20 @@ struct NSVGPath* nsvgParse(char* input) nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); - ret = p->plist; - p->plist = NULL; + ret = p->image; + p->image = NULL; nsvg__deleteParser(p); return ret; } -struct NSVGPath* nsvgParseFromFile(const char* filename) +struct NSVGImage* nsvgParseFromFile(const char* filename) { FILE* fp = NULL; int size; char* data = NULL; - struct NSVGPath* plist = NULL; + struct NSVGImage* image = NULL; fp = fopen(filename, "rb"); if (!fp) goto error; @@ -1692,30 +1771,28 @@ struct NSVGPath* nsvgParseFromFile(const char* filename) fread(data, size, 1, fp); data[size] = '\0'; // Must be null terminated. fclose(fp); - plist = nsvgParse(data); + image = nsvgParse(data); free(data); - return plist; + return image; error: if (fp) fclose(fp); if (data) free(data); - if (plist) nsvgDelete(plist); + if (image) nsvgDelete(image); return NULL; } -void nsvgDelete(struct NSVGPath* plist) +void nsvgDelete(struct NSVGImage* image) { - struct NSVGPath* path; - struct NSVGPath* next; - if (plist == NULL) - return; - path = plist; - while (path) { - next = path->next; - if (path->pts) free(path->pts); - free(path); - path = next; + if (image) { + struct NSVGShape *next, *shape = image->shapes; + while (shape != NULL) { + next = shape->next; + nsvg__deletePaths(shape->paths); + free(shape); + shape = next; + } } }