Handle fill rule properly

- added parser for fill rule
- added even-odd fill rule support for rasteriser
master
Mikko Mononen 2015-06-12 07:45:34 +03:00
parent 46e8b7864c
commit 5966d6e77a
3 changed files with 155 additions and 115 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}