Implemented dashed stroke rendering
This commit is contained in:
parent
7efce85328
commit
dc75508682
@ -140,6 +140,9 @@ typedef struct NSVGshape
|
|||||||
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).
|
||||||
|
float strokeDashOffset; // Stroke dash offset (scaled).
|
||||||
|
float strokeDashArray[8]; // Stroke dash array (scaled).
|
||||||
|
char strokeDashCount; // Number of dash values in dash array.
|
||||||
char strokeLineJoin; // Stroke join type.
|
char strokeLineJoin; // Stroke join type.
|
||||||
char strokeLineCap; // Stroke cap type.
|
char strokeLineCap; // Stroke cap type.
|
||||||
char fillRule; // Fill rule, see NSVGfillRule.
|
char fillRule; // Fill rule, see NSVGfillRule.
|
||||||
@ -348,6 +351,8 @@ enum NSVGgradientUnits {
|
|||||||
NSVG_OBJECT_SPACE = 1,
|
NSVG_OBJECT_SPACE = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define NSVG_MAX_DASHES 8
|
||||||
|
|
||||||
enum NSVGunits {
|
enum NSVGunits {
|
||||||
NSVG_UNITS_USER,
|
NSVG_UNITS_USER,
|
||||||
NSVG_UNITS_PX,
|
NSVG_UNITS_PX,
|
||||||
@ -403,6 +408,9 @@ typedef struct NSVGattrib
|
|||||||
char fillGradient[64];
|
char fillGradient[64];
|
||||||
char strokeGradient[64];
|
char strokeGradient[64];
|
||||||
float strokeWidth;
|
float strokeWidth;
|
||||||
|
float strokeDashOffset;
|
||||||
|
float strokeDashArray[NSVG_MAX_DASHES];
|
||||||
|
int strokeDashCount;
|
||||||
char strokeLineJoin;
|
char strokeLineJoin;
|
||||||
char strokeLineCap;
|
char strokeLineCap;
|
||||||
char fillRule;
|
char fillRule;
|
||||||
@ -614,7 +622,6 @@ static NSVGparser* nsvg__createParser()
|
|||||||
p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
|
p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
|
||||||
p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
|
p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
|
||||||
p->attr[0].hasFill = 1;
|
p->attr[0].hasFill = 1;
|
||||||
p->attr[0].hasStroke = 0;
|
|
||||||
p->attr[0].visible = 1;
|
p->attr[0].visible = 1;
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
@ -913,6 +920,7 @@ static void nsvg__addShape(NSVGparser* p)
|
|||||||
float scale = 1.0f;
|
float scale = 1.0f;
|
||||||
NSVGshape *shape, *cur, *prev;
|
NSVGshape *shape, *cur, *prev;
|
||||||
NSVGpath* path;
|
NSVGpath* path;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (p->plist == NULL)
|
if (p->plist == NULL)
|
||||||
return;
|
return;
|
||||||
@ -924,6 +932,10 @@ static void nsvg__addShape(NSVGparser* p)
|
|||||||
memcpy(shape->id, attr->id, sizeof shape->id);
|
memcpy(shape->id, attr->id, sizeof shape->id);
|
||||||
scale = nsvg__getAverageScale(attr->xform);
|
scale = nsvg__getAverageScale(attr->xform);
|
||||||
shape->strokeWidth = attr->strokeWidth * scale;
|
shape->strokeWidth = attr->strokeWidth * scale;
|
||||||
|
shape->strokeDashOffset = attr->strokeDashOffset * scale;
|
||||||
|
shape->strokeDashCount = attr->strokeDashCount;
|
||||||
|
for (i = 0; i < attr->strokeDashCount; i++)
|
||||||
|
shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
|
||||||
shape->strokeLineJoin = attr->strokeLineJoin;
|
shape->strokeLineJoin = attr->strokeLineJoin;
|
||||||
shape->strokeLineCap = attr->strokeLineCap;
|
shape->strokeLineCap = attr->strokeLineCap;
|
||||||
shape->fillRule = attr->fillRule;
|
shape->fillRule = attr->fillRule;
|
||||||
@ -1574,6 +1586,48 @@ static char nsvg__parseFillRule(const char* str)
|
|||||||
return NSVG_FILLRULE_NONZERO;
|
return NSVG_FILLRULE_NONZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* nsvg__getNextDashItem(const char* s, char* it)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
it[0] = '\0';
|
||||||
|
// Skip white spaces and commas
|
||||||
|
while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
|
||||||
|
// Advance until whitespace, comma or end.
|
||||||
|
while (*s && (!nsvg__isspace(*s) && *s != ',')) {
|
||||||
|
if (n < 63)
|
||||||
|
it[n++] = *s;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
it[n++] = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
|
||||||
|
{
|
||||||
|
char item[64];
|
||||||
|
int count = 0, i;
|
||||||
|
float sum = 0.0f;
|
||||||
|
|
||||||
|
// Handle "none"
|
||||||
|
if (str[0] == 'n')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Parse dashes
|
||||||
|
while (*str) {
|
||||||
|
str = nsvg__getNextDashItem(str, item);
|
||||||
|
if (!*item) break;
|
||||||
|
if (count < NSVG_MAX_DASHES)
|
||||||
|
strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
sum += strokeDashArray[i];
|
||||||
|
if (sum <= 1e-6f)
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@ -1615,6 +1669,10 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
|
|||||||
}
|
}
|
||||||
} else if (strcmp(name, "stroke-width") == 0) {
|
} else if (strcmp(name, "stroke-width") == 0) {
|
||||||
attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
|
attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
|
||||||
|
} else if (strcmp(name, "stroke-dasharray") == 0) {
|
||||||
|
attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
|
||||||
|
} else if (strcmp(name, "stroke-dashoffset") == 0) {
|
||||||
|
attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
|
||||||
} else if (strcmp(name, "stroke-opacity") == 0) {
|
} else if (strcmp(name, "stroke-opacity") == 0) {
|
||||||
attr->strokeOpacity = nsvg__parseOpacity(value);
|
attr->strokeOpacity = nsvg__parseOpacity(value);
|
||||||
} else if (strcmp(name, "stroke-linecap") == 0) {
|
} else if (strcmp(name, "stroke-linecap") == 0) {
|
||||||
@ -2632,7 +2690,7 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
|
|||||||
{
|
{
|
||||||
NSVGshape* shape;
|
NSVGshape* shape;
|
||||||
NSVGpath* path;
|
NSVGpath* path;
|
||||||
float tx, ty, sx, sy, us, bounds[4], t[6];
|
float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
|
||||||
int i;
|
int i;
|
||||||
float* pt;
|
float* pt;
|
||||||
|
|
||||||
@ -2683,6 +2741,7 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
|
|||||||
// Transform
|
// Transform
|
||||||
sx *= us;
|
sx *= us;
|
||||||
sy *= us;
|
sy *= us;
|
||||||
|
avgs = (sx+sy) / 2.0f;
|
||||||
for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
|
for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
|
||||||
shape->bounds[0] = (shape->bounds[0] + tx) * sx;
|
shape->bounds[0] = (shape->bounds[0] + tx) * sx;
|
||||||
shape->bounds[1] = (shape->bounds[1] + ty) * sy;
|
shape->bounds[1] = (shape->bounds[1] + ty) * sy;
|
||||||
@ -2711,6 +2770,10 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
|
|||||||
nsvg__xformInverse(shape->stroke.gradient->xform, t);
|
nsvg__xformInverse(shape->stroke.gradient->xform, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shape->strokeWidth *= avgs;
|
||||||
|
shape->strokeDashOffset *= avgs;
|
||||||
|
for (i = 0; i < shape->strokeDashCount; i++)
|
||||||
|
shape->strokeDashArray[i] *= avgs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +128,10 @@ struct NSVGrasterizer
|
|||||||
int npoints;
|
int npoints;
|
||||||
int cpoints;
|
int cpoints;
|
||||||
|
|
||||||
|
NSVGpoint* points2;
|
||||||
|
int npoints2;
|
||||||
|
int cpoints2;
|
||||||
|
|
||||||
NSVGactiveEdge* freelist;
|
NSVGactiveEdge* freelist;
|
||||||
NSVGmemPage* pages;
|
NSVGmemPage* pages;
|
||||||
NSVGmemPage* curpage;
|
NSVGmemPage* curpage;
|
||||||
@ -170,6 +174,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->points) free(r->points);
|
||||||
|
if (r->points2) free(r->points2);
|
||||||
if (r->scanline) free(r->scanline);
|
if (r->scanline) free(r->scanline);
|
||||||
|
|
||||||
free(r);
|
free(r);
|
||||||
@ -252,6 +257,29 @@ static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
|
|||||||
r->npoints++;
|
r->npoints++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
r->points[r->npoints] = pt;
|
||||||
|
r->npoints++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nsvg__duplicatePoints(NSVGrasterizer* r)
|
||||||
|
{
|
||||||
|
if (r->npoints > r->cpoints2) {
|
||||||
|
r->cpoints2 = r->npoints;
|
||||||
|
r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);
|
||||||
|
if (r->points2 == NULL) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);
|
||||||
|
r->npoints2 = 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;
|
||||||
@ -571,41 +599,84 @@ static int nsvg__curveDivs(float r, float arc, float tol)
|
|||||||
return divs;
|
return divs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth)
|
||||||
{
|
{
|
||||||
int i, j, closed;
|
|
||||||
int s, e;
|
|
||||||
NSVGpath* path;
|
|
||||||
NSVGpoint* p0, *p1;
|
|
||||||
float miterLimit = 4;
|
|
||||||
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.
|
int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle.
|
||||||
NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0};
|
NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0};
|
||||||
|
NSVGpoint* p0, *p1;
|
||||||
|
int j, s, e;
|
||||||
|
|
||||||
for (path = shape->paths; path != NULL; path = path->next) {
|
// Build stroke edges
|
||||||
r->npoints = 0;
|
if (closed) {
|
||||||
// Flatten path
|
// Looping
|
||||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
|
p0 = &points[npoints-1];
|
||||||
for (i = 0; i < path->npts-1; i += 3) {
|
p1 = &points[0];
|
||||||
float* p = &path->pts[i*2];
|
s = 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, NSVG_PT_CORNER);
|
e = npoints;
|
||||||
|
} else {
|
||||||
|
// Add cap
|
||||||
|
p0 = &points[0];
|
||||||
|
p1 = &points[1];
|
||||||
|
s = 1;
|
||||||
|
e = npoints-1;
|
||||||
}
|
}
|
||||||
if (r->npoints < 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
closed = path->closed;
|
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_CORNER) {
|
||||||
|
if (lineJoin == NSVG_JOIN_ROUND)
|
||||||
|
nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
|
||||||
|
else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL))
|
||||||
|
nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
|
||||||
|
else
|
||||||
|
nsvg__miterJoin(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
NSVGpoint* p0, *p1;
|
||||||
|
|
||||||
// If the first and last points are the same, remove the last, mark as closed path.
|
|
||||||
p0 = &r->points[r->npoints-1];
|
p0 = &r->points[r->npoints-1];
|
||||||
p1 = &r->points[0];
|
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++) {
|
for (i = 0; i < r->npoints; i++) {
|
||||||
// Calculate segment direction and length
|
// Calculate segment direction and length
|
||||||
p0->dx = p1->x - p0->x;
|
p0->dx = p1->x - p0->x;
|
||||||
@ -654,69 +725,113 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
|||||||
|
|
||||||
p0 = p1++;
|
p0 = p1++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build stroke edges
|
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
||||||
if (closed) {
|
{
|
||||||
// Looping
|
int i, j, closed;
|
||||||
|
NSVGpath* path;
|
||||||
|
NSVGpoint* p0, *p1;
|
||||||
|
float miterLimit = 4;
|
||||||
|
int lineJoin = shape->strokeLineJoin;
|
||||||
|
int lineCap = shape->strokeLineCap;
|
||||||
|
float lineWidth = shape->strokeWidth * scale;
|
||||||
|
|
||||||
|
for (path = shape->paths; path != NULL; path = path->next) {
|
||||||
|
// Flatten path
|
||||||
|
r->npoints = 0;
|
||||||
|
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];
|
p0 = &r->points[r->npoints-1];
|
||||||
p1 = &r->points[0];
|
p1 = &r->points[0];
|
||||||
s = 0;
|
if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) {
|
||||||
e = r->npoints;
|
r->npoints--;
|
||||||
} else {
|
p0 = &r->points[r->npoints-1];
|
||||||
// Add cap
|
closed = 1;
|
||||||
p0 = &r->points[0];
|
|
||||||
p1 = &r->points[1];
|
|
||||||
s = 1;
|
|
||||||
e = r->npoints-1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closed) {
|
if (shape->strokeDashCount > 0) {
|
||||||
nsvg__initClosed(&left, &right, p0, p1, lineWidth);
|
int idash = 0, dashState = 1;
|
||||||
firstLeft = left;
|
float totalDist = 0, dashLen, allDashLen, dashOffset;
|
||||||
firstRight = right;
|
NSVGpoint cur;
|
||||||
} 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 (closed)
|
||||||
// if (p1->flags & NSVG_PT_BEVEL) {
|
nsvg__appendPathPoint(r, r->points[0]);
|
||||||
if (p1->flags & NSVG_PT_CORNER) {
|
|
||||||
if (lineJoin == NSVG_JOIN_ROUND)
|
|
||||||
nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
|
|
||||||
else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL))
|
|
||||||
nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
|
|
||||||
else
|
|
||||||
nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth);
|
|
||||||
} else {
|
|
||||||
nsvg__straightJoin(r, &left, &right, p1, lineWidth);
|
|
||||||
}
|
|
||||||
p0 = p1++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closed) {
|
// Duplicate points -> points2.
|
||||||
// Loop it
|
nsvg__duplicatePoints(r);
|
||||||
nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y);
|
|
||||||
nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y);
|
r->npoints = 0;
|
||||||
|
cur = r->points2[0];
|
||||||
|
nsvg__appendPathPoint(r, cur);
|
||||||
|
|
||||||
|
// Figure out dash offset.
|
||||||
|
allDashLen = 0;
|
||||||
|
for (j = 0; j < shape->strokeDashCount; j++)
|
||||||
|
allDashLen += shape->strokeDashArray[j];
|
||||||
|
if (shape->strokeDashCount & 1)
|
||||||
|
allDashLen *= 2.0f;
|
||||||
|
// Find location inside pattern
|
||||||
|
dashOffset = fmodf(shape->strokeDashOffset, allDashLen);
|
||||||
|
if (dashOffset < 0.0f)
|
||||||
|
dashOffset += allDashLen;
|
||||||
|
|
||||||
|
while (dashOffset > shape->strokeDashArray[idash]) {
|
||||||
|
dashOffset -= shape->strokeDashArray[idash];
|
||||||
|
idash = (idash + 1) % shape->strokeDashCount;
|
||||||
|
}
|
||||||
|
dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
|
||||||
|
|
||||||
|
for (j = 1; j < r->npoints2; ) {
|
||||||
|
float dx = r->points2[j].x - cur.x;
|
||||||
|
float dy = r->points2[j].y - cur.y;
|
||||||
|
float dist = sqrtf(dx*dx + dy*dy);
|
||||||
|
|
||||||
|
if ((totalDist + dist) > dashLen) {
|
||||||
|
// Calculate intermediate point
|
||||||
|
float d = (dashLen - totalDist) / dist;
|
||||||
|
float x = cur.x + dx * d;
|
||||||
|
float y = cur.y + dy * d;
|
||||||
|
nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER);
|
||||||
|
|
||||||
|
// Stroke
|
||||||
|
if (r->npoints > 1 && dashState) {
|
||||||
|
nsvg__prepareStroke(r, miterLimit, lineJoin);
|
||||||
|
nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
|
||||||
|
}
|
||||||
|
// Advance dash pattern
|
||||||
|
dashState = !dashState;
|
||||||
|
idash = (idash+1) % shape->strokeDashCount;
|
||||||
|
dashLen = shape->strokeDashArray[idash] * scale;
|
||||||
|
// Restart
|
||||||
|
cur.x = x;
|
||||||
|
cur.y = y;
|
||||||
|
cur.flags = NSVG_PT_CORNER;
|
||||||
|
totalDist = 0.0f;
|
||||||
|
r->npoints = 0;
|
||||||
|
nsvg__appendPathPoint(r, cur);
|
||||||
} else {
|
} else {
|
||||||
// Add cap
|
totalDist += dist;
|
||||||
float dx = p1->x - p0->x;
|
cur = r->points2[j];
|
||||||
float dy = p1->y - p0->y;
|
nsvg__appendPathPoint(r, cur);
|
||||||
nsvg__normalize(&dx, &dy);
|
j++;
|
||||||
if (lineCap == NSVG_CAP_BUTT)
|
}
|
||||||
nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
|
}
|
||||||
else if (lineCap == NSVG_CAP_SQUARE)
|
// Stroke any leftover path
|
||||||
nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
|
if (r->npoints > 1 && dashState)
|
||||||
else if (lineCap == NSVG_CAP_ROUND)
|
nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
|
||||||
nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1);
|
} else {
|
||||||
|
nsvg__prepareStroke(r, miterLimit, lineJoin);
|
||||||
|
nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user