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"
|
id="layer1"
|
||||||
transform="translate(0,-52.362183)">
|
transform="translate(0,-52.362183)">
|
||||||
<path
|
<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"
|
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"
|
id="path2985"
|
||||||
inkscape:connector-curvature="0" />
|
inkscape:connector-curvature="0" />
|
||||||
<rect
|
<rect
|
||||||
style="fill:#00ffff"
|
style="fill:#00ffff;stroke:#000000;stroke-width:10.62107277px;"
|
||||||
id="rect2987"
|
id="rect2987"
|
||||||
width="390.01697"
|
width="390.01697"
|
||||||
height="200.70551"
|
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);
|
nsvgDelete(image);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define NSVG_PAINT_NONE 0
|
enum NSVGpaintType {
|
||||||
#define NSVG_PAINT_COLOR 1
|
NSVG_PAINT_NONE = 0,
|
||||||
#define NSVG_PAINT_LINEAR_GRADIENT 2
|
NSVG_PAINT_COLOR = 1,
|
||||||
#define NSVG_PAINT_RADIAL_GRADIENT 3
|
NSVG_PAINT_LINEAR_GRADIENT = 2,
|
||||||
|
NSVG_PAINT_RADIAL_GRADIENT = 3,
|
||||||
|
};
|
||||||
|
|
||||||
#define NSVG_SPREAD_PAD 0
|
enum NSVGspreadType {
|
||||||
#define NSVG_SPREAD_REFLECT 1
|
NSVG_SPREAD_PAD = 0,
|
||||||
#define NSVG_SPREAD_REPEAT 2
|
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 {
|
typedef struct NSVGgradientStop {
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
@ -113,7 +129,9 @@ typedef struct NSVGshape
|
|||||||
NSVGpaint fill; // Fill paint
|
NSVGpaint fill; // Fill paint
|
||||||
NSVGpaint stroke; // Stroke paint
|
NSVGpaint stroke; // Stroke paint
|
||||||
float opacity; // Opacity of the shape.
|
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].
|
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
||||||
NSVGpath* paths; // Linked list of paths in the image.
|
NSVGpath* paths; // Linked list of paths in the image.
|
||||||
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
||||||
@ -351,6 +369,8 @@ typedef struct NSVGattrib
|
|||||||
char fillGradient[64];
|
char fillGradient[64];
|
||||||
char strokeGradient[64];
|
char strokeGradient[64];
|
||||||
float strokeWidth;
|
float strokeWidth;
|
||||||
|
char strokeLineJoin;
|
||||||
|
char strokeLineCap;
|
||||||
float fontSize;
|
float fontSize;
|
||||||
unsigned int stopColor;
|
unsigned int stopColor;
|
||||||
float stopOpacity;
|
float stopOpacity;
|
||||||
@ -554,6 +574,8 @@ static NSVGparser* nsvg__createParser()
|
|||||||
p->attr[0].strokeOpacity = 1;
|
p->attr[0].strokeOpacity = 1;
|
||||||
p->attr[0].stopOpacity = 1;
|
p->attr[0].stopOpacity = 1;
|
||||||
p->attr[0].strokeWidth = 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].hasFill = 1;
|
||||||
p->attr[0].hasStroke = 0;
|
p->attr[0].hasStroke = 0;
|
||||||
p->attr[0].visible = 1;
|
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]));
|
scale = nsvg__maxf(fabsf(attr->xform[0]), fabsf(attr->xform[3]));
|
||||||
shape->strokeWidth = attr->strokeWidth * scale;
|
shape->strokeWidth = attr->strokeWidth * scale;
|
||||||
|
shape->strokeLineJoin = attr->strokeLineJoin;
|
||||||
|
shape->strokeLineCap = attr->strokeLineCap;
|
||||||
shape->opacity = attr->opacity;
|
shape->opacity = attr->opacity;
|
||||||
|
|
||||||
shape->paths = p->plist;
|
shape->paths = p->plist;
|
||||||
@ -1382,6 +1406,30 @@ static void nsvg__parseUrl(char* id, const char* str)
|
|||||||
id[i] = '\0';
|
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 void nsvg__parseStyle(NSVGparser* p, const char* str);
|
||||||
|
|
||||||
static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
|
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);
|
attr->strokeWidth = nsvg__parseFloat(p, value, 2);
|
||||||
} else if (strcmp(name, "stroke-opacity") == 0) {
|
} else if (strcmp(name, "stroke-opacity") == 0) {
|
||||||
attr->strokeOpacity = nsvg__parseFloat(NULL, value, 2);
|
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) {
|
} else if (strcmp(name, "font-size") == 0) {
|
||||||
attr->fontSize = nsvg__parseFloat(p, value, 2);
|
attr->fontSize = nsvg__parseFloat(p, value, 2);
|
||||||
} else if (strcmp(name, "transform") == 0) {
|
} else if (strcmp(name, "transform") == 0) {
|
||||||
|
@ -80,11 +80,19 @@ void nsvgDeleteRasterizer(NSVGrasterizer*);
|
|||||||
#define NSVG__MEMPAGE_SIZE 1024
|
#define NSVG__MEMPAGE_SIZE 1024
|
||||||
|
|
||||||
typedef struct NSVGedge {
|
typedef struct NSVGedge {
|
||||||
float x0,y0, x1,y1;
|
float x0,y0, x1,y1;
|
||||||
int dir;
|
int dir;
|
||||||
struct NSVGedge* next;
|
struct NSVGedge* next;
|
||||||
} NSVGedge;
|
} NSVGedge;
|
||||||
|
|
||||||
|
typedef struct NSVGpoint {
|
||||||
|
float x, y;
|
||||||
|
float dx, dy;
|
||||||
|
float len;
|
||||||
|
float dmx, dmy;
|
||||||
|
unsigned char flags;
|
||||||
|
} NSVGpoint;
|
||||||
|
|
||||||
typedef struct NSVGactiveEdge {
|
typedef struct NSVGactiveEdge {
|
||||||
int x,dx;
|
int x,dx;
|
||||||
float ey;
|
float ey;
|
||||||
@ -109,10 +117,17 @@ struct NSVGrasterizer
|
|||||||
{
|
{
|
||||||
float px, py;
|
float px, py;
|
||||||
|
|
||||||
struct NSVGedge* edges;
|
float tessTol;
|
||||||
|
float distTol;
|
||||||
|
|
||||||
|
NSVGedge* edges;
|
||||||
int nedges;
|
int nedges;
|
||||||
int cedges;
|
int cedges;
|
||||||
|
|
||||||
|
NSVGpoint* points;
|
||||||
|
int npoints;
|
||||||
|
int cpoints;
|
||||||
|
|
||||||
NSVGactiveEdge* freelist;
|
NSVGactiveEdge* freelist;
|
||||||
NSVGmemPage* pages;
|
NSVGmemPage* pages;
|
||||||
NSVGmemPage* curpage;
|
NSVGmemPage* curpage;
|
||||||
@ -130,6 +145,9 @@ NSVGrasterizer* nsvgCreateRasterizer()
|
|||||||
if (r == NULL) goto error;
|
if (r == NULL) goto error;
|
||||||
memset(r, 0, sizeof(NSVGrasterizer));
|
memset(r, 0, sizeof(NSVGrasterizer));
|
||||||
|
|
||||||
|
r->tessTol = 0.25f;
|
||||||
|
r->distTol = 0.01f;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -151,6 +169,7 @@ void nsvgDeleteRasterizer(NSVGrasterizer* r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (r->edges) free(r->edges);
|
if (r->edges) free(r->edges);
|
||||||
|
if (r->points) free(r->points);
|
||||||
if (r->scanline) free(r->scanline);
|
if (r->scanline) free(r->scanline);
|
||||||
|
|
||||||
free(r);
|
free(r);
|
||||||
@ -201,6 +220,38 @@ static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)
|
|||||||
return buf;
|
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)
|
static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)
|
||||||
{
|
{
|
||||||
NSVGedge* e;
|
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 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 x1, float y1, float x2, float y2,
|
||||||
float x3, float y3, float x4, float y4,
|
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 x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
|
||||||
|
float dx,dy,d2,d3;
|
||||||
|
|
||||||
if (level > 10) return;
|
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;
|
x12 = (x1+x2)*0.5f;
|
||||||
y12 = (y1+y2)*0.5f;
|
y12 = (y1+y2)*0.5f;
|
||||||
x23 = (x2+x3)*0.5f;
|
x23 = (x2+x3)*0.5f;
|
||||||
@ -259,31 +315,364 @@ static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
|||||||
y34 = (y3+y4)*0.5f;
|
y34 = (y3+y4)*0.5f;
|
||||||
x123 = (x12+x23)*0.5f;
|
x123 = (x12+x23)*0.5f;
|
||||||
y123 = (y12+y23)*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;
|
x234 = (x23+x34)*0.5f;
|
||||||
y234 = (y23+y34)*0.5f;
|
y234 = (y23+y34)*0.5f;
|
||||||
x1234 = (x123+x234)*0.5f;
|
x1234 = (x123+x234)*0.5f;
|
||||||
y1234 = (y123+y234)*0.5f;
|
y1234 = (y123+y234)*0.5f;
|
||||||
|
|
||||||
nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, 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, tol, level+1);
|
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
||||||
{
|
{
|
||||||
|
int i, j;
|
||||||
NSVGpath* path;
|
NSVGpath* path;
|
||||||
float tol = 0.25f * 4.0f / scale;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (path = shape->paths; path != NULL; path = path->next) {
|
for (path = shape->paths; path != NULL; path = path->next) {
|
||||||
|
r->npoints = 0;
|
||||||
// Flatten path
|
// Flatten path
|
||||||
r->px = path->pts[0];
|
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
|
||||||
r->py = path->pts[1];
|
|
||||||
for (i = 0; i < path->npts-1; i += 3) {
|
for (i = 0; i < path->npts-1; i += 3) {
|
||||||
float* p = &path->pts[i*2];
|
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
|
// 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);
|
memset(&dst[i*stride], 0, w*4);
|
||||||
|
|
||||||
for (shape = image->shapes; shape != NULL; shape = shape->next) {
|
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)
|
nsvg__flattenShape(r, shape, scale);
|
||||||
continue;
|
|
||||||
|
|
||||||
nsvg__resetPool(r);
|
// Scale and translate edges
|
||||||
r->freelist = NULL;
|
for (i = 0; i < r->nedges; i++) {
|
||||||
r->nedges = 0;
|
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
|
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
||||||
for (i = 0; i < r->nedges; i++) {
|
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
|
||||||
e = &r->edges[i];
|
|
||||||
e->x0 = tx + e->x0 * scale;
|
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
|
||||||
e->y0 = (ty + e->y0 * scale) * NSVG__SUBSAMPLES;
|
|
||||||
e->x1 = tx + e->x1 * scale;
|
|
||||||
e->y1 = (ty + e->y1 * scale) * NSVG__SUBSAMPLES;
|
|
||||||
}
|
}
|
||||||
|
if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
|
||||||
|
nsvg__resetPool(r);
|
||||||
|
r->freelist = NULL;
|
||||||
|
r->nedges = 0;
|
||||||
|
|
||||||
// Rasterize edges
|
nsvg__flattenShapeStroke(r, shape, scale);
|
||||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
|
||||||
|
|
||||||
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
// Scale and translate edges
|
||||||
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
|
for (i = 0; i < r->nedges; i++) {
|
||||||
|
e = &r->edges[i];
|
||||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
|
e->x0 = tx + e->x0;
|
||||||
|
e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
|
||||||
|
e->x1 = tx + e->x1;
|
||||||
|
e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
nsvg__unpremultiplyAlpha(dst, w, h, stride);
|
||||||
|
Loading…
Reference in New Issue
Block a user