3rdparty/nanosvg: Re-base on latest upstream.

Now based on upstream cc6c08d3a80f1a305021af3d6394cdf1535d02a2.

Among other things, this version is supposed to be less sensitive to the
global locale.
This commit is contained in:
Vas Crabb 2020-10-05 22:18:22 +11:00
parent a89f9b274e
commit f390ea1004
7 changed files with 288 additions and 121 deletions

View file

@ -1,9 +1,11 @@
*This project is not actively maintained.*
Nano SVG
==========
## Parser
![screenshot of some splines rendered witht the sample program](/example/screenshot-1.png?raw=true)
![screenshot of some splines rendered with the sample program](/example/screenshot-1.png?raw=true)
NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
@ -17,7 +19,7 @@ That is, you should get the same looking data as your designed in your favorite
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.
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.
If you don't know or care about the units stuff, "px" and 96 should get you going.
@ -97,4 +99,4 @@ $ ./example
# License
The library is licensed under [zlib license](LICENSE.txt)
The library is licensed under [zlib license](LICENSE.txt)

View file

@ -32,7 +32,7 @@ int main()
NSVGrasterizer *rast = NULL;
unsigned char* img = NULL;
int w, h;
const char* filename = "../example/_test.svg";
const char* filename = "../example/23.svg";
printf("parsing %s\n", filename);
image = nsvgParseFromFile(filename, "px", 96.0f);

View file

@ -14,7 +14,7 @@ solution "nanosvg"
targetdir("build")
configuration { "linux" }
links { "X11","Xrandr", "rt", "GL", "GLU", "pthread" }
links { "X11","Xrandr", "rt", "GL", "GLU", "pthread", "glfw" }
configuration { "windows" }
links { "glu32","opengl32", "gdi32", "winmm", "user32" }

View file

@ -29,9 +29,11 @@
#ifndef NANOSVG_H
#define NANOSVG_H
#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus
extern "C" {
#endif
#endif
// 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
// 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.
//
// If you don't know or care about the units stuff, "px" and 96 should get you going.
/* Example Usage:
// Load
NSVGImage* image;
// Load SVG
NSVGimage* image;
image = nsvgParseFromFile("test.svg", "px", 96);
printf("size: %f x %f\n", image->width, image->height);
// Use...
@ -73,30 +75,30 @@ enum NSVGpaintType {
NSVG_PAINT_NONE = 0,
NSVG_PAINT_COLOR = 1,
NSVG_PAINT_LINEAR_GRADIENT = 2,
NSVG_PAINT_RADIAL_GRADIENT = 3,
NSVG_PAINT_RADIAL_GRADIENT = 3
};
enum NSVGspreadType {
NSVG_SPREAD_PAD = 0,
NSVG_SPREAD_REFLECT = 1,
NSVG_SPREAD_REPEAT = 2,
NSVG_SPREAD_REPEAT = 2
};
enum NSVGlineJoin {
NSVG_JOIN_MITER = 0,
NSVG_JOIN_ROUND = 1,
NSVG_JOIN_BEVEL = 2,
NSVG_JOIN_BEVEL = 2
};
enum NSVGlineCap {
NSVG_CAP_BUTT = 0,
NSVG_CAP_ROUND = 1,
NSVG_CAP_SQUARE = 2,
NSVG_CAP_SQUARE = 2
};
enum NSVGfillRule {
NSVG_FILLRULE_NONZERO = 0,
NSVG_FILLRULE_EVENODD = 1,
NSVG_FILLRULE_EVENODD = 1
};
enum NSVGflags {
@ -146,6 +148,7 @@ typedef struct NSVGshape
char strokeDashCount; // Number of dash values in dash array.
char strokeLineJoin; // Stroke join type.
char strokeLineCap; // Stroke cap type.
float miterLimit; // Miter limit
char fillRule; // Fill rule, see NSVGfillRule.
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
@ -167,24 +170,28 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
// Important note: changes the string.
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);
#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus
};
}
#endif
#endif
#endif // NANOSVG_H
#ifdef NANOSVG_IMPLEMENTATION
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#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_MID 1
@ -216,12 +223,7 @@ static int nsvg__isspace(char c)
static int nsvg__isdigit(char c)
{
return strchr("0123456789", c) != 0;
}
static int nsvg__isnum(char c)
{
return strchr("0123456789+-.eE", c) != 0;
return c >= '0' && c <= '9';
}
static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
@ -280,6 +282,9 @@ static void nsvg__parseElement(char* s,
// Get attribs
while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
char* name = NULL;
char* value = NULL;
// Skip white space before the attrib name
while (*s && nsvg__isspace(*s)) s++;
if (!*s) break;
@ -287,7 +292,7 @@ static void nsvg__parseElement(char* s,
end = 1;
break;
}
attr[nattr++] = s;
name = s;
// Find end of the attrib name.
while (*s && !nsvg__isspace(*s) && *s != '=') s++;
if (*s) { *s++ = '\0'; }
@ -297,9 +302,15 @@ static void nsvg__parseElement(char* s,
quote = *s;
s++;
// Store value and find the end of it.
attr[nattr++] = s;
value = s;
while (*s && *s != quote) s++;
if (*s) { *s++ = '\0'; }
// Store only well formed attributes
if (name && value) {
attr[nattr++] = name;
attr[nattr++] = value;
}
}
// List terminator
@ -350,7 +361,7 @@ int nsvg__parseXML(char* input,
enum NSVGgradientUnits {
NSVG_USER_SPACE = 0,
NSVG_OBJECT_SPACE = 1,
NSVG_OBJECT_SPACE = 1
};
#define NSVG_MAX_DASHES 8
@ -365,7 +376,7 @@ enum NSVGunits {
NSVG_UNITS_IN,
NSVG_UNITS_PERCENT,
NSVG_UNITS_EM,
NSVG_UNITS_EX,
NSVG_UNITS_EX
};
typedef struct NSVGcoordinate {
@ -416,6 +427,7 @@ typedef struct NSVGattrib
int strokeDashCount;
char strokeLineJoin;
char strokeLineCap;
float miterLimit;
char fillRule;
float fontSize;
unsigned int stopColor;
@ -436,6 +448,7 @@ typedef struct NSVGparser
NSVGpath* plist;
NSVGimage* image;
NSVGgradientData* gradients;
NSVGshape* shapesTail;
float viewMinx, viewMiny, viewWidth, viewHeight;
int alignX, alignY, alignType;
float dpi;
@ -625,6 +638,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].miterLimit = 4;
p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
p->attr[0].hasFill = 1;
p->attr[0].visible = 1;
@ -721,9 +735,11 @@ static void nsvg__lineTo(NSVGparser* p, float x, float y)
static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
{
nsvg__addPoint(p, cpx1, cpy1);
nsvg__addPoint(p, cpx2, cpy2);
nsvg__addPoint(p, x, y);
if (p->npts > 0) {
nsvg__addPoint(p, cpx1, cpy1);
nsvg__addPoint(p, cpx2, cpy2);
nsvg__addPoint(p, x, y);
}
}
static NSVGattrib* nsvg__getAttr(NSVGparser* p)
@ -793,7 +809,9 @@ static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig,
static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
{
NSVGgradientData* grad = p->gradients;
while (grad) {
if (id == NULL || *id == '\0')
return NULL;
while (grad != NULL) {
if (strcmp(grad->id, id) == 0)
return grad;
grad = grad->next;
@ -810,19 +828,26 @@ static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const f
NSVGgradient* grad;
float ox, oy, sw, sh, sl;
int nstops = 0;
int refIter;
data = nsvg__findGradientData(p, id);
if (data == NULL) return NULL;
// TODO: use ref to fill in all unset values too.
ref = data;
refIter = 0;
while (ref != NULL) {
NSVGgradientData* nextRef = NULL;
if (stops == NULL && ref->stops != NULL) {
stops = ref->stops;
nstops = ref->nstops;
break;
}
ref = nsvg__findGradientData(p, ref->ref);
nextRef = nsvg__findGradientData(p, ref->ref);
if (nextRef == ref) break; // prevent infite loops on malformed data
ref = nextRef;
refIter++;
if (refIter > 32) break; // prevent infite loops on malformed data
}
if (stops == NULL) return NULL;
@ -923,7 +948,7 @@ static void nsvg__addShape(NSVGparser* p)
{
NSVGattrib* attr = nsvg__getAttr(p);
float scale = 1.0f;
NSVGshape *shape, *cur, *prev;
NSVGshape* shape;
NSVGpath* path;
int i;
@ -939,11 +964,12 @@ static void nsvg__addShape(NSVGparser* p)
scale = nsvg__getAverageScale(attr->xform);
shape->strokeWidth = attr->strokeWidth * scale;
shape->strokeDashOffset = attr->strokeDashOffset * scale;
shape->strokeDashCount = attr->strokeDashCount;
shape->strokeDashCount = (char)attr->strokeDashCount;
for (i = 0; i < attr->strokeDashCount; i++)
shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
shape->strokeLineJoin = attr->strokeLineJoin;
shape->strokeLineCap = attr->strokeLineCap;
shape->miterLimit = attr->miterLimit;
shape->fillRule = attr->fillRule;
shape->opacity = attr->opacity;
@ -999,16 +1025,11 @@ static void nsvg__addShape(NSVGparser* p)
shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
// Add to tail
prev = NULL;
cur = p->image->shapes;
while (cur != NULL) {
prev = cur;
cur = cur->next;
}
if (prev == NULL)
if (p->image->shapes == NULL)
p->image->shapes = shape;
else
prev->next = shape;
p->shapesTail->next = shape;
p->shapesTail = shape;
return;
@ -1030,6 +1051,10 @@ static void nsvg__addPath(NSVGparser* p, char closed)
if (closed)
nsvg__lineTo(p, p->pts[0], p->pts[1]);
// Expect 1 + N*3 points (N = number of cubic bezier segments).
if ((p->npts % 3) != 1)
return;
path = (NSVGpath*)malloc(sizeof(NSVGpath));
if (path == NULL) goto error;
memset(path, 0, sizeof(NSVGpath));
@ -1072,6 +1097,66 @@ error:
}
}
// 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)
{
const int last = size-1;
@ -1098,7 +1183,7 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
}
}
// exponent
if (*s == 'e' || *s == 'E') {
if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
if (i < last) it[i++] = *s;
s++;
if (*s == '-' || *s == '+') {
@ -1352,13 +1437,19 @@ static unsigned int nsvg__parseColor(const char* str)
static float nsvg__parseOpacity(const char* str)
{
float val = 0;
sscanf(str, "%f", &val);
float val = nsvg__atof(str);
if (val < 0.0f) val = 0.0f;
if (val > 1.0f) val = 1.0f;
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)
{
if (units[0] == 'p' && units[1] == 'x')
@ -1382,12 +1473,21 @@ static int nsvg__parseUnits(const char* units)
return NSVG_UNITS_USER;
}
static int nsvg__isCoordinate(const char* s)
{
// optional sign
if (*s == '-' || *s == '+')
s++;
// must have at least one digit
return nsvg__isdigit(*s);
}
static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
{
NSVGcoordinate coord = {0, NSVG_UNITS_USER};
char units[32]="";
sscanf(str, "%f%s", &coord.value, units);
coord.units = nsvg__parseUnits(units);
char buf[64];
coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64));
coord.value = nsvg__atof(buf);
return coord;
}
@ -1423,7 +1523,7 @@ static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int
if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
if (*na >= maxNa) return 0;
ptr = nsvg__parseNumber(ptr, it, 64);
args[(*na)++] = (float)atof(it);
args[(*na)++] = (float)nsvg__atof(it);
} else {
++ptr;
}
@ -1521,25 +1621,32 @@ static int nsvg__parseRotate(float* xform, const char* str)
static void nsvg__parseTransform(float* xform, const char* str)
{
float t[6];
int len;
nsvg__xformIdentity(xform);
while (*str)
{
if (strncmp(str, "matrix", 6) == 0)
str += nsvg__parseMatrix(t, str);
len = nsvg__parseMatrix(t, str);
else if (strncmp(str, "translate", 9) == 0)
str += nsvg__parseTranslate(t, str);
len = nsvg__parseTranslate(t, str);
else if (strncmp(str, "scale", 5) == 0)
str += nsvg__parseScale(t, str);
len = nsvg__parseScale(t, str);
else if (strncmp(str, "rotate", 6) == 0)
str += nsvg__parseRotate(t, str);
len = nsvg__parseRotate(t, str);
else if (strncmp(str, "skewX", 5) == 0)
str += nsvg__parseSkewX(t, str);
len = nsvg__parseSkewX(t, str);
else if (strncmp(str, "skewY", 5) == 0)
str += nsvg__parseSkewY(t, str);
len = nsvg__parseSkewY(t, str);
else{
++str;
continue;
}
if (len != 0) {
str += len;
} else {
++str;
continue;
}
nsvg__xformPremultiply(xform, t);
}
@ -1579,7 +1686,7 @@ static char nsvg__parseLineJoin(const char* str)
else if (strcmp(str, "bevel") == 0)
return NSVG_JOIN_BEVEL;
// TODO: handle inherit.
return NSVG_CAP_BUTT;
return NSVG_JOIN_MITER;
}
static char nsvg__parseFillRule(const char* str)
@ -1685,6 +1792,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, "stroke-miterlimit") == 0) {
attr->miterLimit = nsvg__parseMiterLimit(value);
} else if (strcmp(name, "fill-rule") == 0) {
attr->fillRule = nsvg__parseFillRule(value);
} else if (strcmp(name, "font-size") == 0) {
@ -1798,8 +1907,11 @@ static int nsvg__getArgsPerElement(char cmd)
case 'a':
case 'A':
return 7;
case 'z':
case 'Z':
return 0;
}
return 0;
return -1;
}
static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
@ -2001,7 +2113,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args,
rx = fabsf(args[0]); // y 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
fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
x1 = *cpx; // start point
@ -2066,13 +2178,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 = 0;
if (fa) {
// Choose large arc
if (da > 0.0f)
da = da - 2*NSVG_PI;
else
da = 2*NSVG_PI + da;
}
if (fs == 0 && da > 0)
da -= 2 * NSVG_PI;
else if (fs == 1 && da < 0)
da += 2 * NSVG_PI;
// Approximate the arc using cubic spline segments.
t[0] = cosrx; t[1] = sinrx;
@ -2088,7 +2197,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args,
kappa = -kappa;
for (i = 0; i <= ndivs; i++) {
a = a1 + da * (i/(float)ndivs);
a = a1 + da * ((float)i/(float)ndivs);
dx = cosf(a);
dy = sinf(a);
nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
@ -2112,6 +2221,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
float args[10];
int nargs;
int rargs = 0;
char initPoint;
float cpx, cpy, cpx2, cpy2;
const char* tmp[4];
char closedFlag;
@ -2134,15 +2244,16 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
nsvg__resetPath(p);
cpx = 0; cpy = 0;
cpx2 = 0; cpy2 = 0;
initPoint = 0;
closedFlag = 0;
nargs = 0;
while (*s) {
s = nsvg__getNextPathItem(s, item);
if (!*item) break;
if (nsvg__isnum(item[0])) {
if (cmd != '\0' && nsvg__isCoordinate(item)) {
if (nargs < 10)
args[nargs++] = (float)atof(item);
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= rargs) {
switch (cmd) {
case 'm':
@ -2151,23 +2262,24 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
// Moveto can be followed by multiple coordinate pairs,
// which should be treated as linetos.
cmd = (cmd == 'm') ? 'l' : 'L';
rargs = nsvg__getArgsPerElement(cmd);
cpx2 = cpx; cpy2 = cpy;
rargs = nsvg__getArgsPerElement(cmd);
cpx2 = cpx; cpy2 = cpy;
initPoint = 1;
break;
case 'l':
case 'L':
nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
cpx2 = cpx; cpy2 = cpy;
break;
case 'H':
case 'h':
nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
cpx2 = cpx; cpy2 = cpy;
break;
case 'V':
case 'v':
nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
cpx2 = cpx; cpy2 = cpy;
break;
case 'C':
case 'c':
@ -2188,13 +2300,13 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
case 'A':
case 'a':
nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
cpx2 = cpx; cpy2 = cpy;
break;
default:
if (nargs >= 2) {
cpx = args[nargs-2];
cpy = args[nargs-1];
cpx2 = cpx; cpy2 = cpy;
cpx2 = cpx; cpy2 = cpy;
}
break;
}
@ -2202,7 +2314,6 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
}
} else {
cmd = item[0];
rargs = nsvg__getArgsPerElement(cmd);
if (cmd == 'M' || cmd == 'm') {
// Commit path.
if (p->npts > 0)
@ -2211,7 +2322,11 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
nsvg__resetPath(p);
closedFlag = 0;
nargs = 0;
} else if (cmd == 'Z' || cmd == 'z') {
} else if (initPoint == 0) {
// Do not allow other commands until initial point has been set (moveTo called once).
cmd = '\0';
}
if (cmd == 'Z' || cmd == 'z') {
closedFlag = 1;
// Commit path.
if (p->npts > 0) {
@ -2227,6 +2342,12 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
closedFlag = 0;
nargs = 0;
}
rargs = nsvg__getArgsPerElement(cmd);
if (rargs == -1) {
// Command not recognized
cmd = '\0';
rargs = 0;
}
}
}
// Commit path.
@ -2399,7 +2520,7 @@ static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
nargs = 0;
while (*s) {
s = nsvg__getNextPathItem(s, item);
args[nargs++] = (float)atof(item);
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= 2) {
if (npts == 0)
nsvg__moveTo(p, args[0], args[1]);
@ -2424,11 +2545,26 @@ static void nsvg__parseSVG(NSVGparser* p, const char** attr)
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
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) {
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) {
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) {
if (strstr(attr[i + 1], "none") != 0) {
// No uniform scaling
@ -2593,7 +2729,6 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr)
if (p->pathFlag) // Do not allow nested paths.
return;
nsvg__pushAttr(p);
p->pathFlag = 1;
p->shapeFlag = 1;
nsvg__parsePath(p, attr);
nsvg__popAttr(p);
@ -2721,12 +2856,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)
{
grad->xform[0] *= sx;
grad->xform[1] *= sx;
grad->xform[2] *= sy;
grad->xform[3] *= sy;
grad->xform[4] += tx*sx;
grad->xform[5] += ty*sx;
float t[6];
nsvg__xformSetTranslation(t, tx, ty);
nsvg__xformMultiply (grad->xform, t);
nsvg__xformSetScale(t, sx, sy);
nsvg__xformMultiply (grad->xform, t);
}
static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
@ -2873,6 +3008,36 @@ error:
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)
{
NSVGshape *snext, *shape;

View file

@ -25,15 +25,18 @@
#ifndef NANOSVGRAST_H
#define NANOSVGRAST_H
#ifndef NANOSVGRAST_CPLUSPLUS
#ifdef __cplusplus
extern "C" {
#endif
#endif
typedef struct NSVGrasterizer NSVGrasterizer;
/* Example Usage:
// 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).
struct NSVGrasterizer* rast = nsvgCreateRasterizer();
@ -63,8 +66,10 @@ void nsvgRasterize(NSVGrasterizer* r,
void nsvgDeleteRasterizer(NSVGrasterizer*);
#ifndef NANOSVGRAST_CPLUSPLUS
#ifdef __cplusplus
};
}
#endif
#endif
#endif // NANOSVGRAST_H
@ -239,7 +244,7 @@ static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
if (r->npoints > 0) {
pt = &r->points[r->npoints-1];
if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
pt->flags |= flags;
pt->flags = (unsigned char)(pt->flags | flags);
return;
}
}
@ -388,7 +393,7 @@ enum NSVGpointFlags
{
NSVG_PT_CORNER = 0x01,
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)
@ -454,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;
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 x = px - dlx*ax - dx*ay;
float y = py - dly*ax - dy*ay;
@ -551,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;
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 > ncap) n = ncap;
@ -561,7 +566,7 @@ static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
ry = right->y;
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 ax = cosf(a) * w, ay = sinf(a) * w;
float lx1 = p1->x - ax, ly1 = p1->y - ay;
@ -732,7 +737,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
int i, j, closed;
NSVGpath* path;
NSVGpoint* p0, *p1;
float miterLimit = 4;
float miterLimit = shape->miterLimit;
int lineJoin = shape->strokeLineJoin;
int lineCap = shape->strokeLineCap;
float lineWidth = shape->strokeWidth * scale;
@ -838,8 +843,8 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
static int nsvg__cmpEdge(const void *p, const void *q)
{
NSVGedge* a = (NSVGedge*)p;
NSVGedge* b = (NSVGedge*)q;
const NSVGedge* a = (const NSVGedge*)p;
const NSVGedge* b = (const NSVGedge*)q;
if (a->y0 < b->y0) return -1;
if (a->y0 > b->y0) return 1;
@ -892,20 +897,20 @@ static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1,
if (i < len && j >= 0) {
if (i == j) {
// 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 {
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
i = -1; // clip
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
j = len; // clip
for (++i; i < j; ++i) // fill pixels between x0 and x1
scanline[i] += (unsigned char)maxWeight;
scanline[i] = (unsigned char)(scanline[i] + maxWeight);
}
}
}
@ -1021,8 +1026,8 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
int i, cr, cg, cb, ca;
unsigned int c;
fx = (x - tx) / scale;
fy = (y - ty) / scale;
fx = ((float)x - tx) / scale;
fy = ((float)y - ty) / scale;
dx = 1.0f / scale;
for (i = 0; i < count; i++) {
@ -1066,8 +1071,8 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
int i, cr, cg, cb, ca;
unsigned int c;
fx = (x - tx) / scale;
fy = (y - ty) / scale;
fx = ((float)x - tx) / scale;
fy = ((float)y - ty) / scale;
dx = 1.0f / scale;
for (i = 0; i < count; i++) {
@ -1121,7 +1126,7 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
xmax = 0;
for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
// 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;
// update all active edges;

View file

@ -94,19 +94,10 @@ private:
screen_device::svg_renderer::svg_renderer(memory_region *region)
{
// nanosvg makes assumptions about the global locale
{
const std::unique_ptr<char []> s(new char[region->bytes() + 1]);
memcpy(s.get(), region->base(), region->bytes());
s[region->bytes()] = 0;
const std::string lcctype(std::setlocale(LC_CTYPE, nullptr));
const std::string lcnumeric(std::setlocale(LC_NUMERIC, nullptr));
std::setlocale(LC_CTYPE, "C");
std::setlocale(LC_NUMERIC, "C");
m_image = nsvgParse(s.get(), "px", 72);
std::setlocale(LC_CTYPE, lcctype.c_str());
std::setlocale(LC_NUMERIC, lcnumeric.c_str());
}
const std::unique_ptr<char []> s(new char[region->bytes() + 1]);
memcpy(s.get(), region->base(), region->bytes());
s[region->bytes()] = 0;
m_image = nsvgParse(s.get(), "px", 72);
m_rasterizer = nsvgCreateRasterizer();
m_key_count = 0;

View file

@ -1,6 +1,10 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_ALL_COLOR_KEYWORDS
#define NANOSVG_IMPLEMENTATION
#define NANOSVGRAST_IMPLEMENTATION
#define NANOSVG_ALL_COLOR_KEYWORDS
#include <nanosvg/src/nanosvg.h>
#include <nanosvg/src/nanosvgrast.h>