Improved gradient support

Fixed/added the following things:
- The right initial values for linear (x2=1.0f) and radial gradients
(cx=cy=r=0.5f). See:
http://www.w3.org/TR/SVG/pservers.html#LinearGradients
- Fixed matrix multiplication order in "nsvg__createGradient" to
properly support gradientUnits="userSpaceOnUse"
- Added support for gradientUnits="objectBoundingBox" with percentage
and floating point values (tested with
http://www.w3.org/TR/SVG/images/pservers/lingrad01.svg)
- Added patch from https://github.com/memononen/nanosvg/issues/29
- Fixed removing the hash in front of "link:href" references. Currently
this only means that the first char is skipped. Not quite optimal (can
there be leading whitespace chars?), but it at least works.
This commit is contained in:
Christoph Neuhauser 2015-05-27 14:27:31 +02:00
parent 0874d4917d
commit 7f38f873bc

View File

@ -606,7 +606,7 @@ static void nsvg__deletePaths(NSVGpath* path)
static void nsvg__deletePaint(NSVGpaint* paint) static void nsvg__deletePaint(NSVGpaint* paint)
{ {
if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_LINEAR_GRADIENT) if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
free(paint->gradient); free(paint->gradient);
} }
@ -727,7 +727,7 @@ static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const f
// TODO: use ref to fill in all unset values too. // TODO: use ref to fill in all unset values too.
ref = data; ref = data;
while (ref != NULL) { while (ref != NULL) {
if (ref->stops != NULL) { if (!stops && ref->stops != NULL) {
stops = ref->stops; stops = ref->stops;
nstops = ref->nstops; nstops = ref->nstops;
break; break;
@ -739,8 +739,6 @@ static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const f
grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
if (grad == NULL) return NULL; if (grad == NULL) return NULL;
// TODO: handle data->units == NSVG_OBJECT_SPACE.
if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
// Calculate transform aligned to the line // Calculate transform aligned to the line
dx = data->linear.x2 - data->linear.x1; dx = data->linear.x2 - data->linear.x1;
@ -757,8 +755,20 @@ static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const f
grad->fy = data->radial.fy / data->radial.r; grad->fy = data->radial.fy / data->radial.r;
} }
nsvg__xformMultiply(grad->xform, attr->xform); if (data->units == NSVG_OBJECT_SPACE) {
float boundingBox[6];
dx = bounds[2] - bounds[0];
dy = bounds[3] - bounds[1];
boundingBox[0] = dx; boundingBox[1] = 0.0f;
boundingBox[2] = 0.0f; boundingBox[3] = dy;
boundingBox[4] = bounds[0]; boundingBox[5] = bounds[1];
nsvg__xformMultiply(grad->xform, boundingBox);
nsvg__xformMultiply(grad->xform, data->xform); nsvg__xformMultiply(grad->xform, data->xform);
nsvg__xformMultiply(grad->xform, attr->xform);
}
nsvg__xformMultiply(grad->xform, data->xform);
nsvg__xformMultiply(grad->xform, attr->xform);
grad->spread = data->spread; grad->spread = data->spread;
memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
@ -2275,11 +2285,16 @@ static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
if (grad == NULL) return; if (grad == NULL) return;
memset(grad, 0, sizeof(NSVGgradientData)); memset(grad, 0, sizeof(NSVGgradientData));
grad->units = NSVG_OBJECT_SPACE;
grad->type = type; grad->type = type;
if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
grad->linear.x1 = 0.0f; grad->linear.y1 = 0.0f; grad->linear.x2 = 1.0f; grad->linear.y2 = 0.0f;
} else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
grad->radial.cx = grad->radial.cy = grad->radial.r = 0.5f;
}
nsvg__xformIdentity(grad->xform); nsvg__xformIdentity(grad->xform);
// TODO: does not handle percent and objectBoundingBox correctly yet.
for (i = 0; attr[i]; i += 2) { for (i = 0; attr[i]; i += 2) {
if (strcmp(attr[i], "id") == 0) { if (strcmp(attr[i], "id") == 0) {
strncpy(grad->id, attr[i+1], 63); strncpy(grad->id, attr[i+1], 63);
@ -2318,8 +2333,9 @@ static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
else if (strcmp(attr[i+1], "repeat") == 0) else if (strcmp(attr[i+1], "repeat") == 0)
grad->spread = NSVG_SPREAD_REPEAT; grad->spread = NSVG_SPREAD_REPEAT;
} else if (strcmp(attr[i], "xlink:href") == 0) { } else if (strcmp(attr[i], "xlink:href") == 0) {
strncpy(grad->ref, attr[i+1], 63); const char *href = attr[i+1];
grad->ref[63] = '\0'; strncpy(grad->ref, href+1, 62);
grad->ref[62] = '\0';
} }
} }
} }
@ -2506,13 +2522,13 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
if (p->image->width > 0) if (p->image->width > 0)
p->viewWidth = p->image->width; p->viewWidth = p->image->width;
else else
p->viewWidth = bounds[2]; p->viewWidth = bounds[2] - bounds[0]; // patch https://github.com/memononen/nanosvg/issues/29
} }
if (p->viewHeight == 0) { if (p->viewHeight == 0) {
if (p->image->height > 0) if (p->image->height > 0)
p->viewHeight = p->image->height; p->viewHeight = p->image->height;
else else
p->viewHeight = bounds[3]; p->viewHeight = bounds[3] - bounds[1]; // patch https://github.com/memononen/nanosvg/issues/29
} }
if (p->image->width == 0) if (p->image->width == 0)
p->image->width = p->viewWidth; p->image->width = p->viewWidth;