Handle fill rule properly
- added parser for fill rule - added even-odd fill rule support for rasterisermaster
parent
46e8b7864c
commit
5966d6e77a
|
@ -32,7 +32,7 @@ int main()
|
|||
NSVGrasterizer *rast = NULL;
|
||||
unsigned char* img = NULL;
|
||||
int w, h;
|
||||
const char* filename = "../example/23.svg";
|
||||
const char* filename = "../example/_test.svg";
|
||||
|
||||
printf("parsing %s\n", filename);
|
||||
image = nsvgParseFromFile(filename, "px", 96.0f);
|
||||
|
|
151
src/nanosvg.h
151
src/nanosvg.h
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -183,12 +183,12 @@ static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
|
|||
if (cur != NULL && cur->next != NULL) {
|
||||
return cur->next;
|
||||
}
|
||||
|
||||
|
||||
// Alloc new page
|
||||
newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage));
|
||||
if (newp == NULL) return NULL;
|
||||
memset(newp, 0, sizeof(NSVGmemPage));
|
||||
|
||||
|
||||
// Add to linked list
|
||||
if (cur != NULL)
|
||||
cur->next = newp;
|
||||
|
@ -297,14 +297,14 @@ static float nsvg__normalize(float *x, float* y)
|
|||
|
||||
static float nsvg__absf(float x) { return x < 0 ? -x : x; }
|
||||
|
||||
static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
||||
static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
||||
float x1, float y1, float x2, float y2,
|
||||
float x3, float y3, float x4, float y4,
|
||||
int level, int type)
|
||||
{
|
||||
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
|
||||
float dx,dy,d2,d3;
|
||||
|
||||
|
||||
if (level > 10) return;
|
||||
|
||||
x12 = (x1+x2)*0.5f;
|
||||
|
@ -331,8 +331,8 @@ static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
|||
x1234 = (x123+x234)*0.5f;
|
||||
y1234 = (y123+y234)*0.5f;
|
||||
|
||||
nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
|
||||
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
|
||||
nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
|
||||
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
|
||||
}
|
||||
|
||||
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
||||
|
@ -768,48 +768,67 @@ static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)
|
|||
r->freelist = z;
|
||||
}
|
||||
|
||||
static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax)
|
||||
{
|
||||
int i = x0 >> NSVG__FIXSHIFT;
|
||||
int j = x1 >> NSVG__FIXSHIFT;
|
||||
if (i < *xmin) *xmin = i;
|
||||
if (j > *xmax) *xmax = j;
|
||||
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);
|
||||
} else {
|
||||
if (i >= 0) // add antialiasing for x0
|
||||
scanline[i] += (unsigned char)(((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);
|
||||
else
|
||||
j = len; // clip
|
||||
|
||||
for (++i; i < j; ++i) // fill pixels between x0 and x1
|
||||
scanline[i] += (unsigned char)maxWeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: this routine clips fills that extend off the edges... ideally this
|
||||
// wouldn't happen, but it could happen if the truetype glyph bounding boxes
|
||||
// are wrong, or if the user supplies a too-small bitmap
|
||||
static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax)
|
||||
static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)
|
||||
{
|
||||
// non-zero winding fill
|
||||
int x0 = 0, w = 0;
|
||||
|
||||
while (e != NULL) {
|
||||
if (w == 0) {
|
||||
// if we're currently at zero, we need to record the edge start point
|
||||
x0 = e->x; w += e->dir;
|
||||
} else {
|
||||
int x1 = e->x; w += e->dir;
|
||||
// if we went to zero, we need to draw
|
||||
if (fillRule == NSVG_FILLRULE_NONZERO) {
|
||||
// Non-zero
|
||||
while (e != NULL) {
|
||||
if (w == 0) {
|
||||
int i = x0 >> NSVG__FIXSHIFT;
|
||||
int j = x1 >> NSVG__FIXSHIFT;
|
||||
if (i < *xmin) *xmin = i;
|
||||
if (j > *xmax) *xmax = j;
|
||||
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);
|
||||
} else {
|
||||
if (i >= 0) // add antialiasing for x0
|
||||
scanline[i] += (unsigned char)(((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);
|
||||
else
|
||||
j = len; // clip
|
||||
|
||||
for (++i; i < j; ++i) // fill pixels between x0 and x1
|
||||
scanline[i] += (unsigned char)maxWeight;
|
||||
}
|
||||
}
|
||||
// if we're currently at zero, we need to record the edge start point
|
||||
x0 = e->x; w += e->dir;
|
||||
} else {
|
||||
int x1 = e->x; w += e->dir;
|
||||
// if we went to zero, we need to draw
|
||||
if (w == 0)
|
||||
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
} else if (fillRule == NSVG_FILLRULE_EVENODD) {
|
||||
// Even-odd
|
||||
while (e != NULL) {
|
||||
if (w == 0) {
|
||||
// if we're currently at zero, we need to record the edge start point
|
||||
x0 = e->x; w = 1;
|
||||
} else {
|
||||
int x1 = e->x; w = 0;
|
||||
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -968,7 +987,7 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
|
|||
}
|
||||
}
|
||||
|
||||
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache)
|
||||
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
|
||||
{
|
||||
NSVGactiveEdge *active = NULL;
|
||||
int y, s;
|
||||
|
@ -1044,13 +1063,13 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
|
|||
|
||||
// now process all active edges in non-zero fashion
|
||||
if (active != NULL)
|
||||
nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax);
|
||||
nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);
|
||||
}
|
||||
// Blit
|
||||
if (xmin < 0) xmin = 0;
|
||||
if (xmax > r->width-1) xmax = r->width-1;
|
||||
if (xmin <= xmax) {
|
||||
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty,scale,cache);
|
||||
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1226,7 +1245,7 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
NSVGedge *e = NULL;
|
||||
NSVGcachedPaint cache;
|
||||
int i;
|
||||
|
||||
|
||||
r->bitmap = dst;
|
||||
r->width = w;
|
||||
r->height = h;
|
||||
|
@ -1263,8 +1282,8 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
|
||||
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
||||
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
|
||||
|
||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
|
||||
|
||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
|
||||
}
|
||||
if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
|
||||
nsvg__resetPool(r);
|
||||
|
@ -1289,8 +1308,8 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
|
||||
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
||||
nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
|
||||
|
||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
|
||||
|
||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue