|
|
@ -1,14 +1,14 @@ |
|
|
|
/*
|
|
|
|
* Copyright (c) 2013-14 Mikko Mononen memon@inside.org |
|
|
|
* |
|
|
|
* |
|
|
|
* This software is provided 'as-is', without any express or implied |
|
|
|
* warranty. In no event will the authors be held liable for any damages |
|
|
|
* arising from the use of this software. |
|
|
|
* |
|
|
|
* |
|
|
|
* Permission is granted to anyone to use this software for any purpose, |
|
|
|
* including commercial applications, and to alter it and redistribute it |
|
|
|
* freely, subject to the following restrictions: |
|
|
|
* |
|
|
|
* |
|
|
|
* 1. The origin of this software must not be misrepresented; you must not |
|
|
|
* claim that you wrote the original software. If you use this software |
|
|
|
* in a product, an acknowledgment in the product documentation would be |
|
|
@ -43,7 +43,7 @@ extern "C" { |
|
|
|
// That is, you should get the same looking data as your designed in your favorite app.
|
|
|
|
//
|
|
|
|
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
|
|
|
|
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
|
|
|
|
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
|
|
|
|
//
|
|
|
|
// The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
|
|
|
|
// DPI (dots-per-inch) controls how the unit conversion is done.
|
|
|
@ -94,6 +94,11 @@ enum NSVGlineCap { |
|
|
|
NSVG_CAP_SQUARE = 2, |
|
|
|
}; |
|
|
|
|
|
|
|
enum NSVGfillRule { |
|
|
|
NSVG_FILLRULE_NONZERO = 0, |
|
|
|
NSVG_FILLRULE_EVENODD = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
typedef struct NSVGgradientStop { |
|
|
|
unsigned int color; |
|
|
|
float offset; |
|
|
@ -133,6 +138,7 @@ typedef struct NSVGshape |
|
|
|
float strokeWidth; // Stroke width (scaled).
|
|
|
|
char strokeLineJoin; // Stroke join type.
|
|
|
|
char strokeLineCap; // Stroke cap type.
|
|
|
|
char fillRule; // Fille rule, see NSVGfillRule.
|
|
|
|
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.
|
|
|
@ -224,7 +230,7 @@ static void nsvg__parseContent(char* s, |
|
|
|
// Trim start white spaces
|
|
|
|
while (*s && nsvg__isspace(*s)) s++; |
|
|
|
if (!*s) return; |
|
|
|
|
|
|
|
|
|
|
|
if (contentCb) |
|
|
|
(*contentCb)(ud, s); |
|
|
|
} |
|
|
@ -240,7 +246,7 @@ static void nsvg__parseElement(char* s, |
|
|
|
int start = 0; |
|
|
|
int end = 0; |
|
|
|
char quote; |
|
|
|
|
|
|
|
|
|
|
|
// Skip white space after the '<'
|
|
|
|
while (*s && nsvg__isspace(*s)) s++; |
|
|
|
|
|
|
@ -284,7 +290,7 @@ static void nsvg__parseElement(char* s, |
|
|
|
while (*s && *s != quote) s++; |
|
|
|
if (*s) { *s++ = '\0'; } |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// List terminator
|
|
|
|
attr[nattr++] = 0; |
|
|
|
attr[nattr++] = 0; |
|
|
@ -322,7 +328,7 @@ int nsvg__parseXML(char* input, |
|
|
|
s++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
@ -373,6 +379,7 @@ typedef struct NSVGattrib |
|
|
|
float strokeWidth; |
|
|
|
char strokeLineJoin; |
|
|
|
char strokeLineCap; |
|
|
|
char fillRule; |
|
|
|
float fontSize; |
|
|
|
unsigned int stopColor; |
|
|
|
float stopOpacity; |
|
|
@ -396,7 +403,7 @@ typedef struct NSVGparser |
|
|
|
int alignX, alignY, alignType; |
|
|
|
float dpi; |
|
|
|
char pathFlag; |
|
|
|
char defsFlag; |
|
|
|
char defsFlag; |
|
|
|
} NSVGparser; |
|
|
|
|
|
|
|
static void nsvg__xformIdentity(float* t) |
|
|
@ -579,6 +586,7 @@ static NSVGparser* nsvg__createParser() |
|
|
|
p->attr[0].strokeWidth = 1; |
|
|
|
p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; |
|
|
|
p->attr[0].strokeLineCap = NSVG_CAP_BUTT; |
|
|
|
p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; |
|
|
|
p->attr[0].hasFill = 1; |
|
|
|
p->attr[0].hasStroke = 0; |
|
|
|
p->attr[0].visible = 1; |
|
|
@ -805,6 +813,7 @@ static void nsvg__addShape(NSVGparser* p) |
|
|
|
shape->strokeWidth = attr->strokeWidth * scale; |
|
|
|
shape->strokeLineJoin = attr->strokeLineJoin; |
|
|
|
shape->strokeLineCap = attr->strokeLineCap; |
|
|
|
shape->fillRule = attr->fillRule; |
|
|
|
shape->opacity = attr->opacity; |
|
|
|
|
|
|
|
shape->paths = p->plist; |
|
|
@ -874,7 +883,7 @@ static void nsvg__addPath(NSVGparser* p, char closed) |
|
|
|
float bounds[4]; |
|
|
|
float* curve; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
if (p->npts < 4) |
|
|
|
return; |
|
|
|
|
|
|
@ -889,11 +898,11 @@ static void nsvg__addPath(NSVGparser* p, char closed) |
|
|
|
if (path->pts == NULL) goto error; |
|
|
|
path->closed = closed; |
|
|
|
path->npts = p->npts; |
|
|
|
|
|
|
|
|
|
|
|
// Transform path.
|
|
|
|
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], attr->xform); |
|
|
|
|
|
|
|
|
|
|
|
// Find bounds
|
|
|
|
for (i = 0; i < path->npts-1; i += 3) { |
|
|
|
curve = &path->pts[i*2]; |
|
|
@ -927,7 +936,7 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size) |
|
|
|
{ |
|
|
|
const int last = size-1; |
|
|
|
int i = 0; |
|
|
|
|
|
|
|
|
|
|
|
// sign
|
|
|
|
if (*s == '-' || *s == '+') { |
|
|
|
if (i < last) it[i++] = *s; |
|
|
@ -997,7 +1006,7 @@ static float nsvg__actualHeight(NSVGparser* p) |
|
|
|
static float nsvg__actualLength(NSVGparser* p) |
|
|
|
{ |
|
|
|
float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); |
|
|
|
return sqrtf(w*w + h*h) / sqrtf(2.0f); |
|
|
|
return sqrtf(w*w + h*h) / sqrtf(2.0f); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1196,7 +1205,7 @@ NSVGNamedColor nsvg__colors[] = { |
|
|
|
static unsigned int nsvg__parseColorName(const char* str) |
|
|
|
{ |
|
|
|
int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ncolors; i++) { |
|
|
|
if (strcmp(nsvg__colors[i].name, str) == 0) { |
|
|
|
return nsvg__colors[i].color; |
|
|
@ -1287,7 +1296,7 @@ static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int |
|
|
|
const char* end; |
|
|
|
const char* ptr; |
|
|
|
char it[64]; |
|
|
|
|
|
|
|
|
|
|
|
*na = 0; |
|
|
|
ptr = str; |
|
|
|
while (*ptr && *ptr != '(') ++ptr; |
|
|
@ -1297,7 +1306,7 @@ static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int |
|
|
|
while (*end && *end != ')') ++end; |
|
|
|
if (*end == 0) |
|
|
|
return 1; |
|
|
|
|
|
|
|
|
|
|
|
while (ptr < end) { |
|
|
|
if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { |
|
|
|
if (*na >= maxNa) return 0; |
|
|
@ -1419,7 +1428,7 @@ static void nsvg__parseTransform(float* xform, const char* str) |
|
|
|
++str; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
nsvg__xformPremultiply(xform, t); |
|
|
|
} |
|
|
|
} |
|
|
@ -1461,6 +1470,16 @@ static char nsvg__parseLineJoin(const char* str) |
|
|
|
return NSVG_CAP_BUTT; |
|
|
|
} |
|
|
|
|
|
|
|
static char nsvg__parseFillRule(const char* str) |
|
|
|
{ |
|
|
|
if (strcmp(str, "nonzero") == 0) |
|
|
|
return NSVG_FILLRULE_NONZERO; |
|
|
|
else if (strcmp(str, "evenodd") == 0) |
|
|
|
return NSVG_FILLRULE_EVENODD; |
|
|
|
// TODO: handle inherit.
|
|
|
|
return NSVG_FILLRULE_NONZERO; |
|
|
|
} |
|
|
|
|
|
|
|
static void nsvg__parseStyle(NSVGparser* p, const char* str); |
|
|
|
|
|
|
|
static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) |
|
|
@ -1468,7 +1487,7 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) |
|
|
|
float xform[6]; |
|
|
|
NSVGattrib* attr = nsvg__getAttr(p); |
|
|
|
if (!attr) return 0; |
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(name, "style") == 0) { |
|
|
|
nsvg__parseStyle(p, value); |
|
|
|
} else if (strcmp(name, "display") == 0) { |
|
|
@ -1508,6 +1527,8 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) |
|
|
|
attr->strokeLineCap = nsvg__parseLineCap(value); |
|
|
|
} else if (strcmp(name, "stroke-linejoin") == 0) { |
|
|
|
attr->strokeLineJoin = nsvg__parseLineJoin(value); |
|
|
|
} else if (strcmp(name, "fill-rule") == 0) { |
|
|
|
attr->fillRule = nsvg__parseFillRule(value); |
|
|
|
} else if (strcmp(name, "font-size") == 0) { |
|
|
|
attr->fontSize = nsvg__parseFloat(p, value, 2); |
|
|
|
} else if (strcmp(name, "transform") == 0) { |
|
|
@ -1535,28 +1556,28 @@ static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* en |
|
|
|
char name[512]; |
|
|
|
char value[512]; |
|
|
|
int n; |
|
|
|
|
|
|
|
|
|
|
|
str = start; |
|
|
|
while (str < end && *str != ':') ++str; |
|
|
|
|
|
|
|
|
|
|
|
val = str; |
|
|
|
|
|
|
|
|
|
|
|
// Right Trim
|
|
|
|
while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; |
|
|
|
++str; |
|
|
|
|
|
|
|
|
|
|
|
n = (int)(str - start); |
|
|
|
if (n > 511) n = 511; |
|
|
|
if (n) memcpy(name, start, n); |
|
|
|
name[n] = 0; |
|
|
|
|
|
|
|
|
|
|
|
while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; |
|
|
|
|
|
|
|
|
|
|
|
n = (int)(end - val); |
|
|
|
if (n > 511) n = 511; |
|
|
|
if (n) memcpy(value, val, n); |
|
|
|
value[n] = 0; |
|
|
|
|
|
|
|
|
|
|
|
return nsvg__parseAttr(p, name, value); |
|
|
|
} |
|
|
|
|
|
|
@ -1564,18 +1585,18 @@ static void nsvg__parseStyle(NSVGparser* p, const char* str) |
|
|
|
{ |
|
|
|
const char* start; |
|
|
|
const char* end; |
|
|
|
|
|
|
|
|
|
|
|
while (*str) { |
|
|
|
// Left Trim
|
|
|
|
while(*str && nsvg__isspace(*str)) ++str; |
|
|
|
start = str; |
|
|
|
while(*str && *str != ';') ++str; |
|
|
|
end = str; |
|
|
|
|
|
|
|
|
|
|
|
// Right Trim
|
|
|
|
while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; |
|
|
|
++end; |
|
|
|
|
|
|
|
|
|
|
|
nsvg__parseNameValue(p, start, end); |
|
|
|
if (*str) ++str; |
|
|
|
} |
|
|
@ -1669,7 +1690,7 @@ static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
float* cpx2, float* cpy2, float* args, int rel) |
|
|
|
{ |
|
|
|
float x2, y2, cx1, cy1, cx2, cy2; |
|
|
|
|
|
|
|
|
|
|
|
if (rel) { |
|
|
|
cx1 = *cpx + args[0]; |
|
|
|
cy1 = *cpy + args[1]; |
|
|
@ -1687,7 +1708,7 @@ static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
} |
|
|
|
|
|
|
|
nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); |
|
|
|
|
|
|
|
|
|
|
|
*cpx2 = cx2; |
|
|
|
*cpy2 = cy2; |
|
|
|
*cpx = x2; |
|
|
@ -1698,7 +1719,7 @@ static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
float* cpx2, float* cpy2, float* args, int rel) |
|
|
|
{ |
|
|
|
float x1, y1, x2, y2, cx1, cy1, cx2, cy2; |
|
|
|
|
|
|
|
|
|
|
|
x1 = *cpx; |
|
|
|
y1 = *cpy; |
|
|
|
if (rel) { |
|
|
@ -1712,12 +1733,12 @@ static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
x2 = args[2]; |
|
|
|
y2 = args[3]; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cx1 = 2*x1 - *cpx2; |
|
|
|
cy1 = 2*y1 - *cpy2; |
|
|
|
|
|
|
|
nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); |
|
|
|
|
|
|
|
|
|
|
|
*cpx2 = cx2; |
|
|
|
*cpy2 = cy2; |
|
|
|
*cpx = x2; |
|
|
@ -1729,7 +1750,7 @@ static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
{ |
|
|
|
float x1, y1, x2, y2, cx, cy; |
|
|
|
float cx1, cy1, cx2, cy2; |
|
|
|
|
|
|
|
|
|
|
|
x1 = *cpx; |
|
|
|
y1 = *cpy; |
|
|
|
if (rel) { |
|
|
@ -1751,7 +1772,7 @@ static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
cy2 = y2 + 2.0f/3.0f*(cy - y2); |
|
|
|
|
|
|
|
nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); |
|
|
|
|
|
|
|
|
|
|
|
*cpx2 = cx; |
|
|
|
*cpy2 = cy; |
|
|
|
*cpx = x2; |
|
|
@ -1763,7 +1784,7 @@ static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
{ |
|
|
|
float x1, y1, x2, y2, cx, cy; |
|
|
|
float cx1, cy1, cx2, cy2; |
|
|
|
|
|
|
|
|
|
|
|
x1 = *cpx; |
|
|
|
y1 = *cpy; |
|
|
|
if (rel) { |
|
|
@ -1784,7 +1805,7 @@ static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, |
|
|
|
cy2 = y2 + 2.0f/3.0f*(cy - y2); |
|
|
|
|
|
|
|
nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); |
|
|
|
|
|
|
|
|
|
|
|
*cpx2 = cx; |
|
|
|
*cpy2 = cy; |
|
|
|
*cpx = x2; |
|
|
@ -1804,7 +1825,7 @@ static float nsvg__vecang(float ux, float uy, float vx, float vy) |
|
|
|
float r = nsvg__vecrat(ux,uy, vx,vy); |
|
|
|
if (r < -1.0f) r = -1.0f; |
|
|
|
if (r > 1.0f) r = 1.0f; |
|
|
|
return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); |
|
|
|
return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); |
|
|
|
} |
|
|
|
|
|
|
|
static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) |
|
|
@ -1849,7 +1870,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, |
|
|
|
sinrx = sinf(rotx); |
|
|
|
cosrx = cosf(rotx); |
|
|
|
|
|
|
|
// Convert to center point parameterization.
|
|
|
|
// Convert to center point parameterization.
|
|
|
|
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
|
|
// 1) Compute x1', y1'
|
|
|
|
x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; |
|
|
@ -1861,7 +1882,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, |
|
|
|
ry *= d; |
|
|
|
} |
|
|
|
// 2) Compute cx', cy'
|
|
|
|
s = 0.0f; |
|
|
|
s = 0.0f; |
|
|
|
sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); |
|
|
|
sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); |
|
|
|
if (sa < 0.0f) sa = 0.0f; |
|
|
@ -1938,7 +1959,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr) |
|
|
|
char closedFlag; |
|
|
|
int i; |
|
|
|
char item[64]; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (strcmp(attr[i], "d") == 0) { |
|
|
|
s = attr[i + 1]; |
|
|
@ -1957,7 +1978,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr) |
|
|
|
cpx2 = 0; cpy2 = 0; |
|
|
|
closedFlag = 0; |
|
|
|
nargs = 0; |
|
|
|
|
|
|
|
|
|
|
|
while (*s) { |
|
|
|
s = nsvg__getNextPathItem(s, item); |
|
|
|
if (!*item) break; |
|
|
@ -2052,7 +2073,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr) |
|
|
|
} |
|
|
|
// Commit path.
|
|
|
|
if (p->npts) |
|
|
|
nsvg__addPath(p, closedFlag); |
|
|
|
nsvg__addPath(p, closedFlag); |
|
|
|
} |
|
|
|
|
|
|
|
nsvg__addShape(p); |
|
|
@ -2067,7 +2088,7 @@ static void nsvg__parseRect(NSVGparser* p, const char** attr) |
|
|
|
float rx = -1.0f; // marks not set
|
|
|
|
float ry = -1.0f; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { |
|
|
|
if (strcmp(attr[i], "x") == 0) x = nsvg__parseFloat(p, attr[i+1], 0); |
|
|
@ -2085,7 +2106,7 @@ static void nsvg__parseRect(NSVGparser* p, const char** attr) |
|
|
|
if (ry < 0.0f) ry = 0.0f; |
|
|
|
if (rx > w/2.0f) rx = w/2.0f; |
|
|
|
if (ry > h/2.0f) ry = h/2.0f; |
|
|
|
|
|
|
|
|
|
|
|
if (w != 0.0f && h != 0.0f) { |
|
|
|
nsvg__resetPath(p); |
|
|
|
|
|
|
@ -2106,7 +2127,7 @@ static void nsvg__parseRect(NSVGparser* p, const char** attr) |
|
|
|
nsvg__lineTo(p, x, y+ry); |
|
|
|
nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
nsvg__addPath(p, 1); |
|
|
|
|
|
|
|
nsvg__addShape(p); |
|
|
@ -2119,7 +2140,7 @@ static void nsvg__parseCircle(NSVGparser* p, const char** attr) |
|
|
|
float cy = 0.0f; |
|
|
|
float r = 0.0f; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { |
|
|
|
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(p, attr[i+1], 0); |
|
|
@ -2127,7 +2148,7 @@ static void nsvg__parseCircle(NSVGparser* p, const char** attr) |
|
|
|
if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseFloat(p, attr[i+1], 2)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (r > 0.0f) { |
|
|
|
nsvg__resetPath(p); |
|
|
|
|
|
|
@ -2136,7 +2157,7 @@ static void nsvg__parseCircle(NSVGparser* p, const char** attr) |
|
|
|
nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); |
|
|
|
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__addPath(p, 1); |
|
|
|
|
|
|
|
nsvg__addShape(p); |
|
|
@ -2150,7 +2171,7 @@ static void nsvg__parseEllipse(NSVGparser* p, const char** attr) |
|
|
|
float rx = 0.0f; |
|
|
|
float ry = 0.0f; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { |
|
|
|
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(p, attr[i+1], 0); |
|
|
@ -2159,7 +2180,7 @@ static void nsvg__parseEllipse(NSVGparser* p, const char** attr) |
|
|
|
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (rx > 0.0f && ry > 0.0f) { |
|
|
|
|
|
|
|
nsvg__resetPath(p); |
|
|
@ -2169,7 +2190,7 @@ static void nsvg__parseEllipse(NSVGparser* p, const char** attr) |
|
|
|
nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); |
|
|
|
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__addPath(p, 1); |
|
|
|
|
|
|
|
nsvg__addShape(p); |
|
|
@ -2183,7 +2204,7 @@ static void nsvg__parseLine(NSVGparser* p, const char** attr) |
|
|
|
float x2 = 0.0; |
|
|
|
float y2 = 0.0; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { |
|
|
|
if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
@ -2192,12 +2213,12 @@ static void nsvg__parseLine(NSVGparser* p, const char** attr) |
|
|
|
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
nsvg__resetPath(p); |
|
|
|
|
|
|
|
|
|
|
|
nsvg__moveTo(p, x1, y1); |
|
|
|
nsvg__lineTo(p, x2, y2); |
|
|
|
|
|
|
|
|
|
|
|
nsvg__addPath(p, 0); |
|
|
|
|
|
|
|
nsvg__addShape(p); |
|
|
@ -2210,9 +2231,9 @@ static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) |
|
|
|
float args[2]; |
|
|
|
int nargs, npts = 0; |
|
|
|
char item[64]; |
|
|
|
|
|
|
|
|
|
|
|
nsvg__resetPath(p); |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { |
|
|
|
if (strcmp(attr[i], "points") == 0) { |
|
|
@ -2233,7 +2254,7 @@ static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
nsvg__addPath(p, (char)closeFlag); |
|
|
|
|
|
|
|
nsvg__addShape(p); |
|
|
@ -2389,7 +2410,7 @@ static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) |
|
|
|
static void nsvg__startElement(void* ud, const char* el, const char** attr) |
|
|
|
{ |
|
|
|
NSVGparser* p = (NSVGparser*)ud; |
|
|
|
|
|
|
|
|
|
|
|
if (p->defsFlag) { |
|
|
|
// Skip everything but gradients in defs
|
|
|
|
if (strcmp(el, "linearGradient") == 0) { |
|
|
@ -2401,7 +2422,7 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr) |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(el, "g") == 0) { |
|
|
|
nsvg__pushAttr(p); |
|
|
|
nsvg__parseAttribs(p, attr); |
|
|
@ -2451,7 +2472,7 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr) |
|
|
|
static void nsvg__endElement(void* ud, const char* el) |
|
|
|
{ |
|
|
|
NSVGparser* p = (NSVGparser*)ud; |
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(el, "g") == 0) { |
|
|
|
nsvg__popAttr(p); |
|
|
|
} else if (strcmp(el, "path") == 0) { |
|
|
@ -2535,7 +2556,7 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) |
|
|
|
if (p->image->height == 0) |
|
|
|
p->image->height = p->viewHeight; |
|
|
|
|
|
|
|
tx = -p->viewMinx; |
|
|
|
tx = -p->viewMinx; |
|
|
|
ty = -p->viewMiny; |
|
|
|
sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; |
|
|
|
sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; |
|
|
@ -2595,7 +2616,7 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi) |
|
|
|
{ |
|
|
|
NSVGparser* p; |
|
|
|
NSVGimage* ret = 0; |
|
|
|
|
|
|
|
|
|
|
|
p = nsvg__createParser(); |
|
|
|
if (p == NULL) { |
|
|
|
return NULL; |
|
|
|