Merge pull request #598 from jrassa/nanosvg-update

update nanosvg to latest version
This commit is contained in:
Jools Wills 2019-08-29 03:07:03 +01:00 committed by GitHub
commit 4729de9fe9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 238 additions and 101 deletions

View file

@ -29,9 +29,11 @@
#ifndef NANOSVG_H #ifndef NANOSVG_H
#define NANOSVG_H #define NANOSVG_H
#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#endif
// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. // NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
// //
@ -45,15 +47,15 @@ extern "C" {
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose // 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'. // The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
// DPI (dots-per-inch) controls how the unit conversion is done. // DPI (dots-per-inch) controls how the unit conversion is done.
// //
// If you don't know or care about the units stuff, "px" and 96 should get you going. // If you don't know or care about the units stuff, "px" and 96 should get you going.
/* Example Usage: /* Example Usage:
// Load // Load SVG
NSVGImage* image; NSVGimage* image;
image = nsvgParseFromFile("test.svg", "px", 96); image = nsvgParseFromFile("test.svg", "px", 96);
printf("size: %f x %f\n", image->width, image->height); printf("size: %f x %f\n", image->width, image->height);
// Use... // Use...
@ -73,30 +75,30 @@ enum NSVGpaintType {
NSVG_PAINT_NONE = 0, NSVG_PAINT_NONE = 0,
NSVG_PAINT_COLOR = 1, NSVG_PAINT_COLOR = 1,
NSVG_PAINT_LINEAR_GRADIENT = 2, NSVG_PAINT_LINEAR_GRADIENT = 2,
NSVG_PAINT_RADIAL_GRADIENT = 3, NSVG_PAINT_RADIAL_GRADIENT = 3
}; };
enum NSVGspreadType { enum NSVGspreadType {
NSVG_SPREAD_PAD = 0, NSVG_SPREAD_PAD = 0,
NSVG_SPREAD_REFLECT = 1, NSVG_SPREAD_REFLECT = 1,
NSVG_SPREAD_REPEAT = 2, NSVG_SPREAD_REPEAT = 2
}; };
enum NSVGlineJoin { enum NSVGlineJoin {
NSVG_JOIN_MITER = 0, NSVG_JOIN_MITER = 0,
NSVG_JOIN_ROUND = 1, NSVG_JOIN_ROUND = 1,
NSVG_JOIN_BEVEL = 2, NSVG_JOIN_BEVEL = 2
}; };
enum NSVGlineCap { enum NSVGlineCap {
NSVG_CAP_BUTT = 0, NSVG_CAP_BUTT = 0,
NSVG_CAP_ROUND = 1, NSVG_CAP_ROUND = 1,
NSVG_CAP_SQUARE = 2, NSVG_CAP_SQUARE = 2
}; };
enum NSVGfillRule { enum NSVGfillRule {
NSVG_FILLRULE_NONZERO = 0, NSVG_FILLRULE_NONZERO = 0,
NSVG_FILLRULE_EVENODD = 1, NSVG_FILLRULE_EVENODD = 1
}; };
enum NSVGflags { enum NSVGflags {
@ -145,6 +147,7 @@ typedef struct NSVGshape
char strokeDashCount; // Number of dash values in dash array. char strokeDashCount; // Number of dash values in dash array.
char strokeLineJoin; // Stroke join type. char strokeLineJoin; // Stroke join type.
char strokeLineCap; // Stroke cap type. char strokeLineCap; // Stroke cap type.
float miterLimit; // Miter limit
char fillRule; // Fill rule, see NSVGfillRule. char fillRule; // Fill rule, see NSVGfillRule.
unsigned char flags; // Logical or of NSVG_FLAGS_* flags unsigned char flags; // Logical or of NSVG_FLAGS_* flags
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
@ -166,11 +169,16 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
// Important note: changes the string. // Important note: changes the string.
NSVGimage* nsvgParse(char* input, const char* units, float dpi); NSVGimage* nsvgParse(char* input, const char* units, float dpi);
// Deletes list of paths. // Duplicates a path.
NSVGpath* nsvgDuplicatePath(NSVGpath* p);
// Deletes an image.
void nsvgDelete(NSVGimage* image); void nsvgDelete(NSVGimage* image);
#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus #ifdef __cplusplus
}; }
#endif
#endif #endif
#endif // NANOSVG_H #endif // NANOSVG_H
@ -182,7 +190,7 @@ void nsvgDelete(NSVGimage* image);
#include <math.h> #include <math.h>
#define NSVG_PI (3.14159265358979323846264338327f) #define NSVG_PI (3.14159265358979323846264338327f)
#define NSVG_KAPPA90 (0.5522847493f) // Lenght proportional to radius of a cubic bezier handle for 90deg arcs. #define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
#define NSVG_ALIGN_MIN 0 #define NSVG_ALIGN_MIN 0
#define NSVG_ALIGN_MID 1 #define NSVG_ALIGN_MID 1
@ -195,7 +203,7 @@ void nsvgDelete(NSVGimage* image);
#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) #define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (disable: 4996) // Switch off security warnings #pragma warning (disable: 4996) // Switch off security warnings
#pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
#ifdef __cplusplus #ifdef __cplusplus
#define NSVG_INLINE inline #define NSVG_INLINE inline
@ -203,7 +211,7 @@ void nsvgDelete(NSVGimage* image);
#define NSVG_INLINE #define NSVG_INLINE
#endif #endif
#else #else
#define NSVG_INLINE inline #define NSVG_INLINE inline
#endif #endif
@ -214,7 +222,7 @@ static int nsvg__isspace(char c)
static int nsvg__isdigit(char c) static int nsvg__isdigit(char c)
{ {
return strchr("0123456789", c) != 0; return c >= '0' && c <= '9';
} }
static int nsvg__isnum(char c) static int nsvg__isnum(char c)
@ -278,6 +286,9 @@ static void nsvg__parseElement(char* s,
// Get attribs // Get attribs
while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
char* name = NULL;
char* value = NULL;
// Skip white space before the attrib name // Skip white space before the attrib name
while (*s && nsvg__isspace(*s)) s++; while (*s && nsvg__isspace(*s)) s++;
if (!*s) break; if (!*s) break;
@ -285,7 +296,7 @@ static void nsvg__parseElement(char* s,
end = 1; end = 1;
break; break;
} }
attr[nattr++] = s; name = s;
// Find end of the attrib name. // Find end of the attrib name.
while (*s && !nsvg__isspace(*s) && *s != '=') s++; while (*s && !nsvg__isspace(*s) && *s != '=') s++;
if (*s) { *s++ = '\0'; } if (*s) { *s++ = '\0'; }
@ -295,9 +306,15 @@ static void nsvg__parseElement(char* s,
quote = *s; quote = *s;
s++; s++;
// Store value and find the end of it. // Store value and find the end of it.
attr[nattr++] = s; value = s;
while (*s && *s != quote) s++; while (*s && *s != quote) s++;
if (*s) { *s++ = '\0'; } if (*s) { *s++ = '\0'; }
// Store only well formed attributes
if (name && value) {
attr[nattr++] = name;
attr[nattr++] = value;
}
} }
// List terminator // List terminator
@ -348,7 +365,7 @@ int nsvg__parseXML(char* input,
enum NSVGgradientUnits { enum NSVGgradientUnits {
NSVG_USER_SPACE = 0, NSVG_USER_SPACE = 0,
NSVG_OBJECT_SPACE = 1, NSVG_OBJECT_SPACE = 1
}; };
#define NSVG_MAX_DASHES 8 #define NSVG_MAX_DASHES 8
@ -363,7 +380,7 @@ enum NSVGunits {
NSVG_UNITS_IN, NSVG_UNITS_IN,
NSVG_UNITS_PERCENT, NSVG_UNITS_PERCENT,
NSVG_UNITS_EM, NSVG_UNITS_EM,
NSVG_UNITS_EX, NSVG_UNITS_EX
}; };
typedef struct NSVGcoordinate { typedef struct NSVGcoordinate {
@ -413,6 +430,7 @@ typedef struct NSVGattrib
int strokeDashCount; int strokeDashCount;
char strokeLineJoin; char strokeLineJoin;
char strokeLineCap; char strokeLineCap;
float miterLimit;
char fillRule; char fillRule;
float fontSize; float fontSize;
unsigned int stopColor; unsigned int stopColor;
@ -433,6 +451,7 @@ typedef struct NSVGparser
NSVGpath* plist; NSVGpath* plist;
NSVGimage* image; NSVGimage* image;
NSVGgradientData* gradients; NSVGgradientData* gradients;
NSVGshape* shapesTail;
float viewMinx, viewMiny, viewWidth, viewHeight; float viewMinx, viewMiny, viewWidth, viewHeight;
int alignX, alignY, alignType; int alignX, alignY, alignType;
float dpi; float dpi;
@ -620,13 +639,14 @@ static NSVGparser* nsvg__createParser()
p->attr[0].strokeWidth = 1; p->attr[0].strokeWidth = 1;
p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
p->attr[0].strokeLineCap = NSVG_CAP_BUTT; p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
p->attr[0].miterLimit = 4;
p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
p->attr[0].hasFill = 1; p->attr[0].hasFill = 1;
p->attr[0].visible = 1; p->attr[0].visible = 1;
return p; return p;
error: error:
if (p) { if (p) {
if (p->image) free(p->image); if (p->image) free(p->image);
free(p); free(p);
@ -918,7 +938,7 @@ static void nsvg__addShape(NSVGparser* p)
{ {
NSVGattrib* attr = nsvg__getAttr(p); NSVGattrib* attr = nsvg__getAttr(p);
float scale = 1.0f; float scale = 1.0f;
NSVGshape *shape, *cur, *prev; NSVGshape* shape;
NSVGpath* path; NSVGpath* path;
int i; int i;
@ -933,11 +953,12 @@ static void nsvg__addShape(NSVGparser* p)
scale = nsvg__getAverageScale(attr->xform); scale = nsvg__getAverageScale(attr->xform);
shape->strokeWidth = attr->strokeWidth * scale; shape->strokeWidth = attr->strokeWidth * scale;
shape->strokeDashOffset = attr->strokeDashOffset * scale; shape->strokeDashOffset = attr->strokeDashOffset * scale;
shape->strokeDashCount = attr->strokeDashCount; shape->strokeDashCount = (char)attr->strokeDashCount;
for (i = 0; i < attr->strokeDashCount; i++) for (i = 0; i < attr->strokeDashCount; i++)
shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
shape->strokeLineJoin = attr->strokeLineJoin; shape->strokeLineJoin = attr->strokeLineJoin;
shape->strokeLineCap = attr->strokeLineCap; shape->strokeLineCap = attr->strokeLineCap;
shape->miterLimit = attr->miterLimit;
shape->fillRule = attr->fillRule; shape->fillRule = attr->fillRule;
shape->opacity = attr->opacity; shape->opacity = attr->opacity;
@ -993,20 +1014,15 @@ static void nsvg__addShape(NSVGparser* p)
shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
// Add to tail // Add to tail
prev = NULL; if (p->image->shapes == NULL)
cur = p->image->shapes;
while (cur != NULL) {
prev = cur;
cur = cur->next;
}
if (prev == NULL)
p->image->shapes = shape; p->image->shapes = shape;
else else
prev->next = shape; p->shapesTail->next = shape;
p->shapesTail = shape;
return; return;
error: error:
if (shape) free(shape); if (shape) free(shape);
} }
@ -1059,13 +1075,73 @@ static void nsvg__addPath(NSVGparser* p, char closed)
return; return;
error: error:
if (path != NULL) { if (path != NULL) {
if (path->pts != NULL) free(path->pts); if (path->pts != NULL) free(path->pts);
free(path); free(path);
} }
} }
// We roll our own string to float because the std library one uses locale and messes things up.
static double nsvg__atof(const char* s)
{
char* cur = (char*)s;
char* end = NULL;
double res = 0.0, sign = 1.0;
long long intPart = 0, fracPart = 0;
char hasIntPart = 0, hasFracPart = 0;
// Parse optional sign
if (*cur == '+') {
cur++;
} else if (*cur == '-') {
sign = -1;
cur++;
}
// Parse integer part
if (nsvg__isdigit(*cur)) {
// Parse digit sequence
intPart = strtoll(cur, &end, 10);
if (cur != end) {
res = (double)intPart;
hasIntPart = 1;
cur = end;
}
}
// Parse fractional part.
if (*cur == '.') {
cur++; // Skip '.'
if (nsvg__isdigit(*cur)) {
// Parse digit sequence
fracPart = strtoll(cur, &end, 10);
if (cur != end) {
res += (double)fracPart / pow(10.0, (double)(end - cur));
hasFracPart = 1;
cur = end;
}
}
}
// A valid number should have integer or fractional part.
if (!hasIntPart && !hasFracPart)
return 0.0;
// Parse optional exponent
if (*cur == 'e' || *cur == 'E') {
long expPart = 0;
cur++; // skip 'E'
expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
if (cur != end) {
res *= pow(10.0, (double)expPart);
}
}
return res * sign;
}
static const char* nsvg__parseNumber(const char* s, char* it, const int size) static const char* nsvg__parseNumber(const char* s, char* it, const int size)
{ {
const int last = size-1; const int last = size-1;
@ -1092,7 +1168,7 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
} }
} }
// exponent // exponent
if (*s == 'e' || *s == 'E') { if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
if (i < last) it[i++] = *s; if (i < last) it[i++] = *s;
s++; s++;
if (*s == '-' || *s == '+') { if (*s == '-' || *s == '+') {
@ -1167,19 +1243,19 @@ typedef struct NSVGNamedColor {
NSVGNamedColor nsvg__colors[] = { NSVGNamedColor nsvg__colors[] = {
{ "red", NSVG_RGB(255, 0, 0) }, { "red", NSVG_RGB(255, 0, 0) },
{ "green", NSVG_RGB( 0, 128, 0) }, { "green", NSVG_RGB( 0, 128, 0) },
{ "blue", NSVG_RGB( 0, 0, 255) }, { "blue", NSVG_RGB( 0, 0, 255) },
{ "yellow", NSVG_RGB(255, 255, 0) }, { "yellow", NSVG_RGB(255, 255, 0) },
{ "cyan", NSVG_RGB( 0, 255, 255) }, { "cyan", NSVG_RGB( 0, 255, 255) },
{ "magenta", NSVG_RGB(255, 0, 255) }, { "magenta", NSVG_RGB(255, 0, 255) },
{ "black", NSVG_RGB( 0, 0, 0) }, { "black", NSVG_RGB( 0, 0, 0) },
{ "grey", NSVG_RGB(128, 128, 128) }, { "grey", NSVG_RGB(128, 128, 128) },
{ "gray", NSVG_RGB(128, 128, 128) }, { "gray", NSVG_RGB(128, 128, 128) },
{ "white", NSVG_RGB(255, 255, 255) }, { "white", NSVG_RGB(255, 255, 255) },
#ifdef NANOSVG_ALL_COLOR_KEYWORDS #ifdef NANOSVG_ALL_COLOR_KEYWORDS
{ "aliceblue", NSVG_RGB(240, 248, 255) }, { "aliceblue", NSVG_RGB(240, 248, 255) },
{ "antiquewhite", NSVG_RGB(250, 235, 215) }, { "antiquewhite", NSVG_RGB(250, 235, 215) },
{ "aqua", NSVG_RGB( 0, 255, 255) }, { "aqua", NSVG_RGB( 0, 255, 255) },
{ "aquamarine", NSVG_RGB(127, 255, 212) }, { "aquamarine", NSVG_RGB(127, 255, 212) },
@ -1346,13 +1422,19 @@ static unsigned int nsvg__parseColor(const char* str)
static float nsvg__parseOpacity(const char* str) static float nsvg__parseOpacity(const char* str)
{ {
float val = 0; float val = nsvg__atof(str);
sscanf(str, "%f", &val);
if (val < 0.0f) val = 0.0f; if (val < 0.0f) val = 0.0f;
if (val > 1.0f) val = 1.0f; if (val > 1.0f) val = 1.0f;
return val; return val;
} }
static float nsvg__parseMiterLimit(const char* str)
{
float val = nsvg__atof(str);
if (val < 0.0f) val = 0.0f;
return val;
}
static int nsvg__parseUnits(const char* units) static int nsvg__parseUnits(const char* units)
{ {
if (units[0] == 'p' && units[1] == 'x') if (units[0] == 'p' && units[1] == 'x')
@ -1379,9 +1461,9 @@ static int nsvg__parseUnits(const char* units)
static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
{ {
NSVGcoordinate coord = {0, NSVG_UNITS_USER}; NSVGcoordinate coord = {0, NSVG_UNITS_USER};
char units[32]=""; char buf[64];
sscanf(str, "%f%s", &coord.value, units); coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64));
coord.units = nsvg__parseUnits(units); coord.value = nsvg__atof(buf);
return coord; return coord;
} }
@ -1417,7 +1499,7 @@ static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int
if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
if (*na >= maxNa) return 0; if (*na >= maxNa) return 0;
ptr = nsvg__parseNumber(ptr, it, 64); ptr = nsvg__parseNumber(ptr, it, 64);
args[(*na)++] = (float)atof(it); args[(*na)++] = (float)nsvg__atof(it);
} else { } else {
++ptr; ++ptr;
} }
@ -1573,7 +1655,7 @@ static char nsvg__parseLineJoin(const char* str)
else if (strcmp(str, "bevel") == 0) else if (strcmp(str, "bevel") == 0)
return NSVG_JOIN_BEVEL; return NSVG_JOIN_BEVEL;
// TODO: handle inherit. // TODO: handle inherit.
return NSVG_CAP_BUTT; return NSVG_JOIN_MITER;
} }
static char nsvg__parseFillRule(const char* str) static char nsvg__parseFillRule(const char* str)
@ -1679,6 +1761,8 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
attr->strokeLineCap = nsvg__parseLineCap(value); attr->strokeLineCap = nsvg__parseLineCap(value);
} else if (strcmp(name, "stroke-linejoin") == 0) { } else if (strcmp(name, "stroke-linejoin") == 0) {
attr->strokeLineJoin = nsvg__parseLineJoin(value); attr->strokeLineJoin = nsvg__parseLineJoin(value);
} else if (strcmp(name, "stroke-miterlimit") == 0) {
attr->miterLimit = nsvg__parseMiterLimit(value);
} else if (strcmp(name, "fill-rule") == 0) { } else if (strcmp(name, "fill-rule") == 0) {
attr->fillRule = nsvg__parseFillRule(value); attr->fillRule = nsvg__parseFillRule(value);
} else if (strcmp(name, "font-size") == 0) { } else if (strcmp(name, "font-size") == 0) {
@ -1995,7 +2079,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args,
rx = fabsf(args[0]); // y radius rx = fabsf(args[0]); // y radius
ry = fabsf(args[1]); // x radius ry = fabsf(args[1]); // x radius
rotx = args[2] / 180.0f * NSVG_PI; // x rotation engle rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle
fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
x1 = *cpx; // start point x1 = *cpx; // start point
@ -2060,13 +2144,10 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args,
// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
if (fa) { if (fs == 0 && da > 0)
// Choose large arc da -= 2 * NSVG_PI;
if (da > 0.0f) else if (fs == 1 && da < 0)
da = da - 2*NSVG_PI; da += 2 * NSVG_PI;
else
da = 2*NSVG_PI + da;
}
// Approximate the arc using cubic spline segments. // Approximate the arc using cubic spline segments.
t[0] = cosrx; t[1] = sinrx; t[0] = cosrx; t[1] = sinrx;
@ -2082,7 +2163,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args,
kappa = -kappa; kappa = -kappa;
for (i = 0; i <= ndivs; i++) { for (i = 0; i <= ndivs; i++) {
a = a1 + da * (i/(float)ndivs); a = a1 + da * ((float)i/(float)ndivs);
dx = cosf(a); dx = cosf(a);
dy = sinf(a); dy = sinf(a);
nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
@ -2136,7 +2217,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
if (!*item) break; if (!*item) break;
if (nsvg__isnum(item[0])) { if (nsvg__isnum(item[0])) {
if (nargs < 10) if (nargs < 10)
args[nargs++] = (float)atof(item); args[nargs++] = (float)nsvg__atof(item);
if (nargs >= rargs) { if (nargs >= rargs) {
switch (cmd) { switch (cmd) {
case 'm': case 'm':
@ -2145,23 +2226,23 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
// Moveto can be followed by multiple coordinate pairs, // Moveto can be followed by multiple coordinate pairs,
// which should be treated as linetos. // which should be treated as linetos.
cmd = (cmd == 'm') ? 'l' : 'L'; cmd = (cmd == 'm') ? 'l' : 'L';
rargs = nsvg__getArgsPerElement(cmd); rargs = nsvg__getArgsPerElement(cmd);
cpx2 = cpx; cpy2 = cpy; cpx2 = cpx; cpy2 = cpy;
break; break;
case 'l': case 'l':
case 'L': case 'L':
nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy; cpx2 = cpx; cpy2 = cpy;
break; break;
case 'H': case 'H':
case 'h': case 'h':
nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy; cpx2 = cpx; cpy2 = cpy;
break; break;
case 'V': case 'V':
case 'v': case 'v':
nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy; cpx2 = cpx; cpy2 = cpy;
break; break;
case 'C': case 'C':
case 'c': case 'c':
@ -2182,13 +2263,13 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
case 'A': case 'A':
case 'a': case 'a':
nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy; cpx2 = cpx; cpy2 = cpy;
break; break;
default: default:
if (nargs >= 2) { if (nargs >= 2) {
cpx = args[nargs-2]; cpx = args[nargs-2];
cpy = args[nargs-1]; cpy = args[nargs-1];
cpx2 = cpx; cpy2 = cpy; cpx2 = cpx; cpy2 = cpy;
} }
break; break;
} }
@ -2393,7 +2474,7 @@ static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
nargs = 0; nargs = 0;
while (*s) { while (*s) {
s = nsvg__getNextPathItem(s, item); s = nsvg__getNextPathItem(s, item);
args[nargs++] = (float)atof(item); args[nargs++] = (float)nsvg__atof(item);
if (nargs >= 2) { if (nargs >= 2) {
if (npts == 0) if (npts == 0)
nsvg__moveTo(p, args[0], args[1]); nsvg__moveTo(p, args[0], args[1]);
@ -2418,11 +2499,26 @@ static void nsvg__parseSVG(NSVGparser* p, const char** attr)
for (i = 0; attr[i]; i += 2) { for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "width") == 0) { if (strcmp(attr[i], "width") == 0) {
p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
} else if (strcmp(attr[i], "height") == 0) { } else if (strcmp(attr[i], "height") == 0) {
p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
} else if (strcmp(attr[i], "viewBox") == 0) { } else if (strcmp(attr[i], "viewBox") == 0) {
sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight); const char *s = attr[i + 1];
char buf[64];
s = nsvg__parseNumber(s, buf, 64);
p->viewMinx = nsvg__atof(buf);
while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
if (!*s) return;
s = nsvg__parseNumber(s, buf, 64);
p->viewMiny = nsvg__atof(buf);
while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
if (!*s) return;
s = nsvg__parseNumber(s, buf, 64);
p->viewWidth = nsvg__atof(buf);
while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
if (!*s) return;
s = nsvg__parseNumber(s, buf, 64);
p->viewHeight = nsvg__atof(buf);
} else if (strcmp(attr[i], "preserveAspectRatio") == 0) { } else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
if (strstr(attr[i + 1], "none") != 0) { if (strstr(attr[i + 1], "none") != 0) {
// No uniform scaling // No uniform scaling
@ -2678,12 +2774,12 @@ static float nsvg__viewAlign(float content, float container, int type)
static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
{ {
grad->xform[0] *= sx; float t[6];
grad->xform[1] *= sx; nsvg__xformSetTranslation(t, tx, ty);
grad->xform[2] *= sy; nsvg__xformMultiply (grad->xform, t);
grad->xform[3] *= sy;
grad->xform[4] += tx*sx; nsvg__xformSetScale(t, sx, sy);
grad->xform[5] += ty*sx; nsvg__xformMultiply (grad->xform, t);
} }
static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
@ -2823,13 +2919,43 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
return image; return image;
error: error:
if (fp) fclose(fp); if (fp) fclose(fp);
if (data) free(data); if (data) free(data);
if (image) nsvgDelete(image); if (image) nsvgDelete(image);
return NULL; return NULL;
} }
NSVGpath* nsvgDuplicatePath(NSVGpath* p)
{
NSVGpath* res = NULL;
if (p == NULL)
return NULL;
res = (NSVGpath*)malloc(sizeof(NSVGpath));
if (res == NULL) goto error;
memset(res, 0, sizeof(NSVGpath));
res->pts = (float*)malloc(p->npts*2*sizeof(float));
if (res->pts == NULL) goto error;
memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2);
res->npts = p->npts;
memcpy(res->bounds, p->bounds, sizeof(p->bounds));
res->closed = p->closed;
return res;
error:
if (res != NULL) {
free(res->pts);
free(res);
}
return NULL;
}
void nsvgDelete(NSVGimage* image) void nsvgDelete(NSVGimage* image)
{ {
NSVGshape *snext, *shape; NSVGshape *snext, *shape;

View file

@ -25,15 +25,19 @@
#ifndef NANOSVGRAST_H #ifndef NANOSVGRAST_H
#define NANOSVGRAST_H #define NANOSVGRAST_H
#ifndef NANOSVGRAST_CPLUSPLUS
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#endif
typedef struct NSVGrasterizer NSVGrasterizer; typedef struct NSVGrasterizer NSVGrasterizer;
/* Example Usage: /* Example Usage:
// Load SVG // Load SVG
struct SNVGImage* image = nsvgParseFromFile("test.svg."); NSVGimage* image;
image = nsvgParseFromFile("test.svg", "px", 96);
// Create rasterizer (can be used to render multiple images). // Create rasterizer (can be used to render multiple images).
struct NSVGrasterizer* rast = nsvgCreateRasterizer(); struct NSVGrasterizer* rast = nsvgCreateRasterizer();
// Allocate memory for image // Allocate memory for image
@ -62,8 +66,10 @@ void nsvgRasterize(NSVGrasterizer* r,
void nsvgDeleteRasterizer(NSVGrasterizer*); void nsvgDeleteRasterizer(NSVGrasterizer*);
#ifndef NANOSVGRAST_CPLUSPLUS
#ifdef __cplusplus #ifdef __cplusplus
}; }
#endif
#endif #endif
#endif // NANOSVGRAST_H #endif // NANOSVGRAST_H
@ -153,7 +159,7 @@ NSVGrasterizer* nsvgCreateRasterizer()
return r; return r;
error: error:
nsvgDeleteRasterizer(r); nsvgDeleteRasterizer(r);
return NULL; return NULL;
} }
@ -238,7 +244,7 @@ static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
if (r->npoints > 0) { if (r->npoints > 0) {
pt = &r->points[r->npoints-1]; pt = &r->points[r->npoints-1];
if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
pt->flags |= flags; pt->flags = (unsigned char)(pt->flags | flags);
return; return;
} }
} }
@ -387,7 +393,7 @@ enum NSVGpointFlags
{ {
NSVG_PT_CORNER = 0x01, NSVG_PT_CORNER = 0x01,
NSVG_PT_BEVEL = 0x02, NSVG_PT_BEVEL = 0x02,
NSVG_PT_LEFT = 0x04, NSVG_PT_LEFT = 0x04
}; };
static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
@ -453,7 +459,7 @@ static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right,
float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0;
for (i = 0; i < ncap; i++) { for (i = 0; i < ncap; i++) {
float a = i/(float)(ncap-1)*NSVG_PI; float a = (float)i/(float)(ncap-1)*NSVG_PI;
float ax = cosf(a) * w, ay = sinf(a) * w; float ax = cosf(a) * w, ay = sinf(a) * w;
float x = px - dlx*ax - dx*ay; float x = px - dlx*ax - dx*ay;
float y = py - dly*ax - dy*ay; float y = py - dly*ax - dy*ay;
@ -550,7 +556,7 @@ static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
if (da < NSVG_PI) da += NSVG_PI*2; if (da < NSVG_PI) da += NSVG_PI*2;
if (da > NSVG_PI) da -= NSVG_PI*2; if (da > NSVG_PI) da -= NSVG_PI*2;
n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * ncap); n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap);
if (n < 2) n = 2; if (n < 2) n = 2;
if (n > ncap) n = ncap; if (n > ncap) n = ncap;
@ -560,7 +566,7 @@ static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
ry = right->y; ry = right->y;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
float u = i/(float)(n-1); float u = (float)i/(float)(n-1);
float a = a0 + u*da; float a = a0 + u*da;
float ax = cosf(a) * w, ay = sinf(a) * w; float ax = cosf(a) * w, ay = sinf(a) * w;
float lx1 = p1->x - ax, ly1 = p1->y - ay; float lx1 = p1->x - ax, ly1 = p1->y - ay;
@ -731,7 +737,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
int i, j, closed; int i, j, closed;
NSVGpath* path; NSVGpath* path;
NSVGpoint* p0, *p1; NSVGpoint* p0, *p1;
float miterLimit = 4; float miterLimit = shape->miterLimit;
int lineJoin = shape->strokeLineJoin; int lineJoin = shape->strokeLineJoin;
int lineCap = shape->strokeLineCap; int lineCap = shape->strokeLineCap;
float lineWidth = shape->strokeWidth * scale; float lineWidth = shape->strokeWidth * scale;
@ -770,7 +776,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
nsvg__duplicatePoints(r); nsvg__duplicatePoints(r);
r->npoints = 0; r->npoints = 0;
cur = r->points2[0]; cur = r->points2[0];
nsvg__appendPathPoint(r, cur); nsvg__appendPathPoint(r, cur);
// Figure out dash offset. // Figure out dash offset.
@ -837,8 +843,8 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
static int nsvg__cmpEdge(const void *p, const void *q) static int nsvg__cmpEdge(const void *p, const void *q)
{ {
NSVGedge* a = (NSVGedge*)p; const NSVGedge* a = (const NSVGedge*)p;
NSVGedge* b = (NSVGedge*)q; const NSVGedge* b = (const NSVGedge*)q;
if (a->y0 < b->y0) return -1; if (a->y0 < b->y0) return -1;
if (a->y0 > b->y0) return 1; if (a->y0 > b->y0) return 1;
@ -848,7 +854,7 @@ static int nsvg__cmpEdge(const void *p, const void *q)
static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint)
{ {
NSVGactiveEdge* z; NSVGactiveEdge* z;
if (r->freelist != NULL) { if (r->freelist != NULL) {
// Restore from freelist. // Restore from freelist.
@ -891,20 +897,20 @@ static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1,
if (i < len && j >= 0) { if (i < len && j >= 0) {
if (i == j) { if (i == j) {
// x0,x1 are the same pixel, so compute combined coverage // x0,x1 are the same pixel, so compute combined coverage
scanline[i] += (unsigned char)((x1 - x0) * maxWeight >> NSVG__FIXSHIFT); scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));
} else { } else {
if (i >= 0) // add antialiasing for x0 if (i >= 0) // add antialiasing for x0
scanline[i] += (unsigned char)(((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT); scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT));
else else
i = -1; // clip i = -1; // clip
if (j < len) // add antialiasing for x1 if (j < len) // add antialiasing for x1
scanline[j] += (unsigned char)(((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT); scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT));
else else
j = len; // clip j = len; // clip
for (++i; i < j; ++i) // fill pixels between x0 and x1 for (++i; i < j; ++i) // fill pixels between x0 and x1
scanline[i] += (unsigned char)maxWeight; scanline[i] = (unsigned char)(scanline[i] + maxWeight);
} }
} }
} }
@ -975,7 +981,7 @@ static unsigned int nsvg__applyOpacity(unsigned int c, float u)
static inline int nsvg__div255(int x) static inline int nsvg__div255(int x)
{ {
return ((x+1) * 257) >> 16; return ((x+1) * 257) >> 16;
} }
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
@ -1020,8 +1026,8 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
int i, cr, cg, cb, ca; int i, cr, cg, cb, ca;
unsigned int c; unsigned int c;
fx = (x - tx) / scale; fx = ((float)x - tx) / scale;
fy = (y - ty) / scale; fy = ((float)y - ty) / scale;
dx = 1.0f / scale; dx = 1.0f / scale;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
@ -1065,8 +1071,8 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
int i, cr, cg, cb, ca; int i, cr, cg, cb, ca;
unsigned int c; unsigned int c;
fx = (x - tx) / scale; fx = ((float)x - tx) / scale;
fy = (y - ty) / scale; fy = ((float)y - ty) / scale;
dx = 1.0f / scale; dx = 1.0f / scale;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
@ -1120,7 +1126,7 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
xmax = 0; xmax = 0;
for (s = 0; s < NSVG__SUBSAMPLES; ++s) { for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
// find center of pixel for this scanline // find center of pixel for this scanline
float scany = y*NSVG__SUBSAMPLES + s + 0.5f; float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f;
NSVGactiveEdge **step = &active; NSVGactiveEdge **step = &active;
// update all active edges; // update all active edges;
@ -1323,6 +1329,7 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
if (r->nedges == 0) return; if (r->nedges == 0) return;
FILE* fp = fopen(name, "w"); FILE* fp = fopen(name, "w");
if (fp == NULL) return; if (fp == NULL) return;
xmin = xmax = r->edges[0].x0; xmin = xmax = r->edges[0].x0;
ymin = ymax = r->edges[0].y0; ymin = ymax = r->edges[0].y0;
for (i = 0; i < r->nedges; i++) { for (i = 0; i < r->nedges; i++) {
@ -1336,16 +1343,20 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
ymax = nsvg__maxf(ymax, e->y0); ymax = nsvg__maxf(ymax, e->y0);
ymax = nsvg__maxf(ymax, e->y1); ymax = nsvg__maxf(ymax, e->y1);
} }
fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin)); fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin));
for (i = 0; i < r->nedges; i++) { for (i = 0; i < r->nedges; i++) {
e = &r->edges[i]; e = &r->edges[i];
fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1); fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1);
} }
for (i = 0; i < r->npoints; i++) { for (i = 0; i < r->npoints; i++) {
if (i+1 < r->npoints) if (i+1 < r->npoints)
fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y);
fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0");
} }
fprintf(fp, "</svg>"); fprintf(fp, "</svg>");
fclose(fp); fclose(fp);
} }