First stab at line rendering
- added tessellation and rendering of lines - added parsing for line joins and caps
This commit is contained in:
parent
268503755e
commit
cd0a2029fa
@ -53,12 +53,12 @@
|
||||
id="layer1"
|
||||
transform="translate(0,-52.362183)">
|
||||
<path
|
||||
style="fill:#ff5555;stroke:#000000;stroke-width:1.62107277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="fill:#ff5555;stroke:#000000;stroke-width:10.62107277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 131.73911,422.01626 c 0,146.85769 43.82213,215.39128 201.5818,141.96244 157.75968,-73.42885 188.43518,-107.69564 354.95926,78.32409 166.5241,186.01973 210.34624,244.76282 162.1419,-122.3814 -48.20435,-367.1442 -4.38221,34.26679 -131.46641,-24.47627 C 591.87149,436.70204 732.10231,191.93923 543.66715,187.04398 355.23198,182.14871 574.34264,265.36807 534.90271,368.16845 495.4628,470.96883 355.23198,627.61702 311.40985,475.8641 267.58772,324.11115 193.09009,333.90166 131.73911,422.01626 z"
|
||||
id="path2985"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="fill:#00ffff"
|
||||
style="fill:#00ffff;stroke:#000000;stroke-width:10.62107277px;"
|
||||
id="rect2987"
|
||||
width="390.01697"
|
||||
height="200.70551"
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.4 KiB |
@ -69,14 +69,30 @@ extern "C" {
|
||||
nsvgDelete(image);
|
||||
*/
|
||||
|
||||
#define NSVG_PAINT_NONE 0
|
||||
#define NSVG_PAINT_COLOR 1
|
||||
#define NSVG_PAINT_LINEAR_GRADIENT 2
|
||||
#define NSVG_PAINT_RADIAL_GRADIENT 3
|
||||
enum NSVGpaintType {
|
||||
NSVG_PAINT_NONE = 0,
|
||||
NSVG_PAINT_COLOR = 1,
|
||||
NSVG_PAINT_LINEAR_GRADIENT = 2,
|
||||
NSVG_PAINT_RADIAL_GRADIENT = 3,
|
||||
};
|
||||
|
||||
#define NSVG_SPREAD_PAD 0
|
||||
#define NSVG_SPREAD_REFLECT 1
|
||||
#define NSVG_SPREAD_REPEAT 2
|
||||
enum NSVGspreadType {
|
||||
NSVG_SPREAD_PAD = 0,
|
||||
NSVG_SPREAD_REFLECT = 1,
|
||||
NSVG_SPREAD_REPEAT = 2,
|
||||
};
|
||||
|
||||
enum NSVGlineJoin {
|
||||
NSVG_JOIN_MITER = 0,
|
||||
NSVG_JOIN_ROUND = 1,
|
||||
NSVG_JOIN_BEVEL = 2,
|
||||
};
|
||||
|
||||
enum NSVGlineCap {
|
||||
NSVG_CAP_BUTT = 0,
|
||||
NSVG_CAP_ROUND = 1,
|
||||
NSVG_CAP_SQUARE = 2,
|
||||
};
|
||||
|
||||
typedef struct NSVGgradientStop {
|
||||
unsigned int color;
|
||||
@ -113,7 +129,9 @@ typedef struct NSVGshape
|
||||
NSVGpaint fill; // Fill paint
|
||||
NSVGpaint stroke; // Stroke paint
|
||||
float opacity; // Opacity of the shape.
|
||||
float strokeWidth; // Stroke width (scaled)
|
||||
float strokeWidth; // Stroke width (scaled).
|
||||
char strokeLineJoin; // Stroke join type.
|
||||
char strokeLineCap; // Stroke cap type.
|
||||
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.
|
||||
@ -351,6 +369,8 @@ typedef struct NSVGattrib
|
||||
char fillGradient[64];
|
||||
char strokeGradient[64];
|
||||
float strokeWidth;
|
||||
char strokeLineJoin;
|
||||
char strokeLineCap;
|
||||
float fontSize;
|
||||
unsigned int stopColor;
|
||||
float stopOpacity;
|
||||
@ -554,6 +574,8 @@ static NSVGparser* nsvg__createParser()
|
||||
p->attr[0].strokeOpacity = 1;
|
||||
p->attr[0].stopOpacity = 1;
|
||||
p->attr[0].strokeWidth = 1;
|
||||
p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
|
||||
p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
|
||||
p->attr[0].hasFill = 1;
|
||||
p->attr[0].hasStroke = 0;
|
||||
p->attr[0].visible = 1;
|
||||
@ -761,6 +783,8 @@ static void nsvg__addShape(NSVGparser* p)
|
||||
|
||||
scale = nsvg__maxf(fabsf(attr->xform[0]), fabsf(attr->xform[3]));
|
||||
shape->strokeWidth = attr->strokeWidth * scale;
|
||||
shape->strokeLineJoin = attr->strokeLineJoin;
|
||||
shape->strokeLineCap = attr->strokeLineCap;
|
||||
shape->opacity = attr->opacity;
|
||||
|
||||
shape->paths = p->plist;
|
||||
@ -1382,6 +1406,30 @@ static void nsvg__parseUrl(char* id, const char* str)
|
||||
id[i] = '\0';
|
||||
}
|
||||
|
||||
static char nsvg__parseLineCap(const char* str)
|
||||
{
|
||||
if (strcmp(str, "butt") == 0)
|
||||
return NSVG_CAP_BUTT;
|
||||
else if (strcmp(str, "round") == 0)
|
||||
return NSVG_CAP_ROUND;
|
||||
else if (strcmp(str, "square") == 0)
|
||||
return NSVG_CAP_SQUARE;
|
||||
// TODO: handle inherit.
|
||||
return NSVG_CAP_BUTT;
|
||||
}
|
||||
|
||||
static char nsvg__parseLineJoin(const char* str)
|
||||
{
|
||||
if (strcmp(str, "miter") == 0)
|
||||
return NSVG_JOIN_MITER;
|
||||
else if (strcmp(str, "round") == 0)
|
||||
return NSVG_JOIN_ROUND;
|
||||
else if (strcmp(str, "bevel") == 0)
|
||||
return NSVG_JOIN_BEVEL;
|
||||
// TODO: handle inherit.
|
||||
return NSVG_CAP_BUTT;
|
||||
}
|
||||
|
||||
static void nsvg__parseStyle(NSVGparser* p, const char* str);
|
||||
|
||||
static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
|
||||
@ -1425,6 +1473,10 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
|
||||
attr->strokeWidth = nsvg__parseFloat(p, value, 2);
|
||||
} else if (strcmp(name, "stroke-opacity") == 0) {
|
||||
attr->strokeOpacity = nsvg__parseFloat(NULL, value, 2);
|
||||
} else if (strcmp(name, "stroke-linecap") == 0) {
|
||||
attr->strokeLineCap = nsvg__parseLineCap(value);
|
||||
} else if (strcmp(name, "stroke-linejoin") == 0) {
|
||||
attr->strokeLineJoin = nsvg__parseLineJoin(value);
|
||||
} else if (strcmp(name, "font-size") == 0) {
|
||||
attr->fontSize = nsvg__parseFloat(p, value, 2);
|
||||
} else if (strcmp(name, "transform") == 0) {
|
||||
|
@ -80,11 +80,19 @@ void nsvgDeleteRasterizer(NSVGrasterizer*);
|
||||
#define NSVG__MEMPAGE_SIZE 1024
|
||||
|
||||
typedef struct NSVGedge {
|
||||
float x0,y0, x1,y1;
|
||||
int dir;
|
||||
struct NSVGedge* next;
|
||||
float x0,y0, x1,y1;
|
||||
int dir;
|
||||
struct NSVGedge* next;
|
||||
} NSVGedge;
|
||||
|
||||
typedef struct NSVGpoint {
|
||||
float x, y;
|
||||
float dx, dy;
|
||||
float len;
|
||||
float dmx, dmy;
|
||||
unsigned char flags;
|
||||
} NSVGpoint;
|
||||
|
||||
typedef struct NSVGactiveEdge {
|
||||
int x,dx;
|
||||
float ey;
|
||||
@ -109,10 +117,17 @@ struct NSVGrasterizer
|
||||
{
|
||||
float px, py;
|
||||
|
||||
struct NSVGedge* edges;
|
||||
float tessTol;
|
||||
float distTol;
|
||||
|
||||
NSVGedge* edges;
|
||||
int nedges;
|
||||
int cedges;
|
||||
|
||||
NSVGpoint* points;
|
||||
int npoints;
|
||||
int cpoints;
|
||||
|
||||
NSVGactiveEdge* freelist;
|
||||
NSVGmemPage* pages;
|
||||
NSVGmemPage* curpage;
|
||||
@ -130,6 +145,9 @@ NSVGrasterizer* nsvgCreateRasterizer()
|
||||
if (r == NULL) goto error;
|
||||
memset(r, 0, sizeof(NSVGrasterizer));
|
||||
|
||||
r->tessTol = 0.25f;
|
||||
r->distTol = 0.01f;
|
||||
|
||||
return r;
|
||||
|
||||
error:
|
||||
@ -151,6 +169,7 @@ void nsvgDeleteRasterizer(NSVGrasterizer* r)
|
||||
}
|
||||
|
||||
if (r->edges) free(r->edges);
|
||||
if (r->points) free(r->points);
|
||||
if (r->scanline) free(r->scanline);
|
||||
|
||||
free(r);
|
||||
@ -201,6 +220,38 @@ static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
|
||||
{
|
||||
float dx = x2 - x1;
|
||||
float dy = y2 - y1;
|
||||
return dx*dx + dy*dy < tol*tol;
|
||||
}
|
||||
|
||||
static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
|
||||
{
|
||||
NSVGpoint* pt;
|
||||
|
||||
if (r->npoints > 0) {
|
||||
pt = &r->points[r->npoints-1];
|
||||
if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
|
||||
pt->flags |= flags;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->npoints+1 > r->cpoints) {
|
||||
r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
|
||||
r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
|
||||
if (r->points == NULL) return;
|
||||
}
|
||||
|
||||
pt = &r->points[r->npoints];
|
||||
pt->x = x;
|
||||
pt->y = y;
|
||||
pt->flags = flags;
|
||||
r->npoints++;
|
||||
}
|
||||
|
||||
static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)
|
||||
{
|
||||
NSVGedge* e;
|
||||
@ -233,24 +284,29 @@ static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float
|
||||
}
|
||||
}
|
||||
|
||||
static float nsvg__normalize(float *x, float* y)
|
||||
{
|
||||
float d = sqrtf((*x)*(*x) + (*y)*(*y));
|
||||
if (d > 1e-6f) {
|
||||
float id = 1.0f / d;
|
||||
*x *= id;
|
||||
*y *= id;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static float nsvg__absf(float x) { return x < 0 ? -x : x; }
|
||||
|
||||
static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
||||
float x1, float y1, float x2, float y2,
|
||||
float x3, float y3, float x4, float y4,
|
||||
float tol, int level)
|
||||
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;
|
||||
|
||||
if (nsvg__absf(x1+x3-x2-x2) + nsvg__absf(y1+y3-y2-y2) + nsvg__absf(x2+x4-x3-x3) + nsvg__absf(y2+y4-y3-y3) < tol) {
|
||||
nsvg__addEdge(r, r->px, r->py, x4, y4);
|
||||
r->px = x4;
|
||||
r->py = y4;
|
||||
return;
|
||||
}
|
||||
|
||||
x12 = (x1+x2)*0.5f;
|
||||
y12 = (y1+y2)*0.5f;
|
||||
x23 = (x2+x3)*0.5f;
|
||||
@ -259,31 +315,364 @@ static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
||||
y34 = (y3+y4)*0.5f;
|
||||
x123 = (x12+x23)*0.5f;
|
||||
y123 = (y12+y23)*0.5f;
|
||||
|
||||
dx = x4 - x1;
|
||||
dy = y4 - y1;
|
||||
d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx));
|
||||
d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx));
|
||||
|
||||
if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) {
|
||||
nsvg__addPathPoint(r, x4, y4, type);
|
||||
return;
|
||||
}
|
||||
|
||||
x234 = (x23+x34)*0.5f;
|
||||
y234 = (y23+y34)*0.5f;
|
||||
x1234 = (x123+x234)*0.5f;
|
||||
y1234 = (y123+y234)*0.5f;
|
||||
|
||||
nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, tol, level+1);
|
||||
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1);
|
||||
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)
|
||||
{
|
||||
int i, j;
|
||||
NSVGpath* path;
|
||||
float tol = 0.25f * 4.0f / scale;
|
||||
int i;
|
||||
|
||||
for (path = shape->paths; path != NULL; path = path->next) {
|
||||
r->npoints = 0;
|
||||
// Flatten path
|
||||
r->px = path->pts[0];
|
||||
r->py = path->pts[1];
|
||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
|
||||
for (i = 0; i < path->npts-1; i += 3) {
|
||||
float* p = &path->pts[i*2];
|
||||
nsvg__flattenCubicBez(r, p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7], tol, 0);
|
||||
nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
|
||||
}
|
||||
// Close path
|
||||
nsvg__addEdge(r, r->px,r->py, path->pts[0],path->pts[1]);
|
||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
|
||||
// Build edges
|
||||
for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
|
||||
nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
|
||||
}
|
||||
}
|
||||
|
||||
enum NSVGpointFlags
|
||||
{
|
||||
NSVG_PT_CORNER = 0x01,
|
||||
NSVG_PT_BEVEL = 0x02,
|
||||
};
|
||||
|
||||
static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
|
||||
{
|
||||
float w = lineWidth * 0.5f;
|
||||
float dx = p1->x - p0->x;
|
||||
float dy = p1->y - p0->y;
|
||||
float len = nsvg__normalize(&dx, &dy);
|
||||
float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f;
|
||||
float dlx = dy, dly = -dx;
|
||||
float lx = px - dlx*w, ly = py - dly*w;
|
||||
float rx = px + dlx*w, ry = py + dly*w;
|
||||
left->x = lx; left->y = ly;
|
||||
right->x = rx; right->y = ry;
|
||||
}
|
||||
|
||||
static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
|
||||
{
|
||||
float w = lineWidth * 0.5f;
|
||||
float px = p->x, py = p->y;
|
||||
float dlx = dy, dly = -dx;
|
||||
float lx = px - dlx*w, ly = py - dly*w;
|
||||
float rx = px + dlx*w, ry = py + dly*w;
|
||||
|
||||
nsvg__addEdge(r, lx, ly, rx, ry);
|
||||
|
||||
if (connect) {
|
||||
nsvg__addEdge(r, left->x, left->y, lx, ly);
|
||||
nsvg__addEdge(r, rx, ry, right->x, right->y);
|
||||
}
|
||||
left->x = lx; left->y = ly;
|
||||
right->x = rx; right->y = ry;
|
||||
}
|
||||
|
||||
static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
|
||||
{
|
||||
float w = lineWidth * 0.5f;
|
||||
float px = p->x - dx*w, py = p->y - dy*w;
|
||||
float dlx = dy, dly = -dx;
|
||||
float lx = px - dlx*w, ly = py - dly*w;
|
||||
float rx = px + dlx*w, ry = py + dly*w;
|
||||
|
||||
nsvg__addEdge(r, lx, ly, rx, ry);
|
||||
|
||||
if (connect) {
|
||||
nsvg__addEdge(r, left->x, left->y, lx, ly);
|
||||
nsvg__addEdge(r, rx, ry, right->x, right->y);
|
||||
}
|
||||
left->x = lx; left->y = ly;
|
||||
right->x = rx; right->y = ry;
|
||||
}
|
||||
|
||||
#ifndef NSVG_PI
|
||||
#define NSVG_PI (3.14159265358979323846264338327f)
|
||||
#endif
|
||||
|
||||
static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect)
|
||||
{
|
||||
int i;
|
||||
float w = lineWidth * 0.5f;
|
||||
float px = p->x, py = p->y;
|
||||
float dlx = dy, dly = -dx;
|
||||
float lx, ly, rx, ry, prevx, prevy;
|
||||
|
||||
for (i = 0; i < ncap; i++) {
|
||||
float a = 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;
|
||||
|
||||
if (i > 0)
|
||||
nsvg__addEdge(r, prevx, prevy, x, y);
|
||||
|
||||
prevx = x;
|
||||
prevy = y;
|
||||
|
||||
if (i == 0) {
|
||||
lx = x; ly = y;
|
||||
} else if (i == ncap-1) {
|
||||
rx = x; ry = y;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect) {
|
||||
nsvg__addEdge(r, left->x, left->y, lx, ly);
|
||||
nsvg__addEdge(r, rx, ry, right->x, right->y);
|
||||
}
|
||||
|
||||
left->x = lx; left->y = ly;
|
||||
right->x = rx; right->y = ry;
|
||||
}
|
||||
|
||||
static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
|
||||
{
|
||||
float w = lineWidth * 0.5f;
|
||||
float dlx0 = p0->dy, dly0 = -p0->dx;
|
||||
float dlx1 = p1->dy, dly1 = -p1->dx;
|
||||
float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w);
|
||||
float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w);
|
||||
float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w);
|
||||
float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w);
|
||||
|
||||
nsvg__addEdge(r, lx0, ly0, left->x, left->y);
|
||||
nsvg__addEdge(r, lx1, ly1, lx0, ly0);
|
||||
|
||||
nsvg__addEdge(r, right->x, right->y, rx0, ry0);
|
||||
nsvg__addEdge(r, rx0, ry0, rx1, ry1);
|
||||
|
||||
left->x = lx1; left->y = ly1;
|
||||
right->x = rx1; right->y = ry1;
|
||||
}
|
||||
|
||||
static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap)
|
||||
{
|
||||
int i, n;
|
||||
float w = lineWidth * 0.5f;
|
||||
float dlx0 = p0->dy, dly0 = -p0->dx;
|
||||
float dlx1 = p1->dy, dly1 = -p1->dx;
|
||||
float a0 = atan2f(dly0, dlx0);
|
||||
float a1 = atan2f(dly1, dlx1);
|
||||
float da = a1 - a0;
|
||||
float lx, ly, rx, ry;
|
||||
|
||||
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);
|
||||
if (n < 2) n = 2;
|
||||
if (n > ncap) n = ncap;
|
||||
|
||||
lx = left->x;
|
||||
ly = left->y;
|
||||
rx = right->x;
|
||||
ry = right->y;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
float u = 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;
|
||||
float rx1 = p1->x + ax, ry1 = p1->y + ay;
|
||||
|
||||
nsvg__addEdge(r, lx1, ly1, lx, ly);
|
||||
nsvg__addEdge(r, rx, ry, rx1, ry1);
|
||||
|
||||
lx = lx1; ly = ly1;
|
||||
rx = rx1; ry = ry1;
|
||||
}
|
||||
|
||||
left->x = lx; left->y = ly;
|
||||
right->x = rx; right->y = ry;
|
||||
}
|
||||
|
||||
static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth)
|
||||
{
|
||||
float w = lineWidth * 0.5f;
|
||||
float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w);
|
||||
float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w);
|
||||
|
||||
nsvg__addEdge(r, lx, ly, left->x, left->y);
|
||||
nsvg__addEdge(r, right->x, right->y, rx, ry);
|
||||
|
||||
left->x = lx; left->y = ly;
|
||||
right->x = rx; right->y = ry;
|
||||
}
|
||||
|
||||
static int nsvg__curveDivs(float r, float arc, float tol)
|
||||
{
|
||||
float da = acosf(r / (r + tol)) * 2.0f;
|
||||
int divs = (int)ceilf(arc / da);
|
||||
if (divs < 2) divs = 2;
|
||||
return divs;
|
||||
}
|
||||
|
||||
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
||||
{
|
||||
int i, j, closed;
|
||||
int s, e;
|
||||
NSVGpath* path;
|
||||
NSVGpoint* p0, *p1;
|
||||
float miterLimit = 10;
|
||||
int lineJoin = shape->strokeLineJoin;
|
||||
int lineCap = shape->strokeLineCap;
|
||||
float lineWidth = shape->strokeWidth * scale;
|
||||
int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle.
|
||||
NSVGpoint left, right, firstLeft, firstRight;
|
||||
|
||||
for (path = shape->paths; path != NULL; path = path->next) {
|
||||
r->npoints = 0;
|
||||
// Flatten path
|
||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
|
||||
for (i = 0; i < path->npts-1; i += 3) {
|
||||
float* p = &path->pts[i*2];
|
||||
nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
|
||||
}
|
||||
if (r->npoints < 2)
|
||||
continue;
|
||||
|
||||
closed = path->closed;
|
||||
|
||||
// If the first and last points are the same, remove the last, mark as closed path.
|
||||
p0 = &r->points[r->npoints-1];
|
||||
p1 = &r->points[0];
|
||||
if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) {
|
||||
r->npoints--;
|
||||
p0 = &r->points[r->npoints-1];
|
||||
closed = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < r->npoints; i++) {
|
||||
// Calculate segment direction and length
|
||||
p0->dx = p1->x - p0->x;
|
||||
p0->dy = p1->y - p0->y;
|
||||
p0->len = nsvg__normalize(&p0->dx, &p0->dy);
|
||||
// Advance
|
||||
p0 = p1++;
|
||||
}
|
||||
|
||||
// calculate joins
|
||||
p0 = &r->points[r->npoints-1];
|
||||
p1 = &r->points[0];
|
||||
for (j = 0; j < r->npoints; j++) {
|
||||
float dlx0, dly0, dlx1, dly1, dmr2;
|
||||
dlx0 = p0->dy;
|
||||
dly0 = -p0->dx;
|
||||
dlx1 = p1->dy;
|
||||
dly1 = -p1->dx;
|
||||
// Calculate extrusions
|
||||
p1->dmx = (dlx0 + dlx1) * 0.5f;
|
||||
p1->dmy = (dly0 + dly1) * 0.5f;
|
||||
dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
|
||||
if (dmr2 > 0.000001f) {
|
||||
float s = 1.0f / dmr2;
|
||||
if (s > 600.0f) {
|
||||
s = 600.0f;
|
||||
}
|
||||
p1->dmx *= s;
|
||||
p1->dmy *= s;
|
||||
}
|
||||
|
||||
// Clear flags, but keep the corner.
|
||||
p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0;
|
||||
|
||||
// Check to see if the corner needs to be beveled.
|
||||
if (p1->flags & NSVG_PT_CORNER) {
|
||||
if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
|
||||
p1->flags |= NSVG_PT_BEVEL;
|
||||
}
|
||||
}
|
||||
|
||||
p0 = p1++;
|
||||
}
|
||||
|
||||
// Build stroke edges
|
||||
if (closed) {
|
||||
// Looping
|
||||
p0 = &r->points[r->npoints-1];
|
||||
p1 = &r->points[0];
|
||||
s = 0;
|
||||
e = r->npoints;
|
||||
} else {
|
||||
// Add cap
|
||||
p0 = &r->points[0];
|
||||
p1 = &r->points[1];
|
||||
s = 1;
|
||||
e = r->npoints-1;
|
||||
}
|
||||
|
||||
if (closed) {
|
||||
nsvg__initClosed(&left, &right, p0, p1, lineWidth);
|
||||
firstLeft = left;
|
||||
firstRight = right;
|
||||
} else {
|
||||
// Add cap
|
||||
float dx = p1->x - p0->x;
|
||||
float dy = p1->y - p0->y;
|
||||
nsvg__normalize(&dx, &dy);
|
||||
if (lineCap == NSVG_CAP_BUTT)
|
||||
nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
|
||||
else if (lineCap == NSVG_CAP_SQUARE)
|
||||
nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
|
||||
else if (lineCap == NSVG_CAP_ROUND)
|
||||
nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0);
|
||||
}
|
||||
|
||||
for (j = s; j < e; ++j) {
|
||||
if (p1->flags & NSVG_PT_BEVEL) {
|
||||
if (lineJoin == NSVG_JOIN_ROUND)
|
||||
nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
|
||||
else
|
||||
nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
|
||||
} else {
|
||||
nsvg__straightJoin(r, &left, &right, p1, lineWidth);
|
||||
}
|
||||
p0 = p1++;
|
||||
}
|
||||
|
||||
if (closed) {
|
||||
// Loop it
|
||||
nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y);
|
||||
nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y);
|
||||
} else {
|
||||
// Add cap
|
||||
float dx = p1->x - p0->x;
|
||||
float dy = p1->y - p0->y;
|
||||
nsvg__normalize(&dx, &dy);
|
||||
if (lineCap == NSVG_CAP_BUTT)
|
||||
nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
|
||||
else if (lineCap == NSVG_CAP_SQUARE)
|
||||
nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
|
||||
else if (lineCap == NSVG_CAP_ROUND)
|
||||
nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -766,32 +1155,54 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||
memset(&dst[i*stride], 0, w*4);
|
||||
|
||||
for (shape = image->shapes; shape != NULL; shape = shape->next) {
|
||||
if (shape->fill.type != NSVG_PAINT_NONE) {
|
||||
nsvg__resetPool(r);
|
||||
r->freelist = NULL;
|
||||
r->nedges = 0;
|
||||
|
||||
if (shape->fill.type == NSVG_PAINT_NONE)
|
||||
continue;
|
||||
nsvg__flattenShape(r, shape, scale);
|
||||
|
||||
nsvg__resetPool(r);
|
||||
r->freelist = NULL;
|
||||
r->nedges = 0;
|
||||
// Scale and translate edges
|
||||
for (i = 0; i < r->nedges; i++) {
|
||||
e = &r->edges[i];
|
||||
e->x0 = tx + e->x0;
|
||||
e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
|
||||
e->x1 = tx + e->x1;
|
||||
e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
|
||||
}
|
||||
|
||||
nsvg__flattenShape(r, shape, scale);
|
||||
// Rasterize edges
|
||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
||||
|
||||
// Scale and translate edges
|
||||
for (i = 0; i < r->nedges; i++) {
|
||||
e = &r->edges[i];
|
||||
e->x0 = tx + e->x0 * scale;
|
||||
e->y0 = (ty + e->y0 * scale) * NSVG__SUBSAMPLES;
|
||||
e->x1 = tx + e->x1 * scale;
|
||||
e->y1 = (ty + e->y1 * scale) * NSVG__SUBSAMPLES;
|
||||
// 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);
|
||||
}
|
||||
if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
|
||||
nsvg__resetPool(r);
|
||||
r->freelist = NULL;
|
||||
r->nedges = 0;
|
||||
|
||||
// Rasterize edges
|
||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
||||
nsvg__flattenShapeStroke(r, shape, scale);
|
||||
|
||||
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
||||
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
|
||||
// Scale and translate edges
|
||||
for (i = 0; i < r->nedges; i++) {
|
||||
e = &r->edges[i];
|
||||
e->x0 = tx + e->x0;
|
||||
e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
|
||||
e->x1 = tx + e->x1;
|
||||
e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
|
||||
}
|
||||
|
||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
|
||||
// Rasterize edges
|
||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
||||
|
||||
// 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__unpremultiplyAlpha(dst, w, h, stride);
|
||||
|
Loading…
Reference in New Issue
Block a user