|
|
@ -343,15 +343,35 @@ int nsvg__parseXML(char* input, |
|
|
|
|
|
|
|
#define NSVG_MAX_ATTR 128 |
|
|
|
|
|
|
|
#define NSVG_USER_SPACE 0 |
|
|
|
#define NSVG_OBJECT_SPACE 1 |
|
|
|
enum NSVGgradientUnits { |
|
|
|
NSVG_USER_SPACE = 0, |
|
|
|
NSVG_OBJECT_SPACE = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
enum NSVGunits { |
|
|
|
NSVG_UNITS_USER, |
|
|
|
NSVG_UNITS_PX, |
|
|
|
NSVG_UNITS_PT, |
|
|
|
NSVG_UNITS_PC, |
|
|
|
NSVG_UNITS_MM, |
|
|
|
NSVG_UNITS_CM, |
|
|
|
NSVG_UNITS_IN, |
|
|
|
NSVG_UNITS_PERCENT, |
|
|
|
NSVG_UNITS_EM, |
|
|
|
NSVG_UNITS_EX, |
|
|
|
}; |
|
|
|
|
|
|
|
typedef struct NSVGcoordinate { |
|
|
|
float value; |
|
|
|
int units; |
|
|
|
} NSVGcoordinate; |
|
|
|
|
|
|
|
typedef struct NSVGlinearData { |
|
|
|
float x1, y1, x2, y2; |
|
|
|
NSVGcoordinate x1, y1, x2, y2; |
|
|
|
} NSVGlinearData; |
|
|
|
|
|
|
|
typedef struct NSVGradialData { |
|
|
|
float cx, cy, r, fx, fy; |
|
|
|
NSVGcoordinate cx, cy, r, fx, fy; |
|
|
|
} NSVGradialData; |
|
|
|
|
|
|
|
typedef struct NSVGgradientData |
|
|
@ -470,12 +490,12 @@ static void nsvg__xformMultiply(float* t, float* s) |
|
|
|
|
|
|
|
static void nsvg__xformInverse(float* inv, float* t) |
|
|
|
{ |
|
|
|
double det = (double)t[0] * t[3] - (double)t[2] * t[1]; |
|
|
|
double invdet = 1.0 / det; |
|
|
|
if (det > -1e-6 && det < -1e-6) { |
|
|
|
double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; |
|
|
|
if (det > -1e-6 && det < 1e-6) { |
|
|
|
nsvg__xformIdentity(t); |
|
|
|
return; |
|
|
|
} |
|
|
|
invdet = 1.0 / det; |
|
|
|
inv[0] = (float)(t[3] * invdet); |
|
|
|
inv[2] = (float)(-t[2] * invdet); |
|
|
|
inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); |
|
|
@ -713,6 +733,51 @@ static void nsvg__popAttr(NSVGparser* p) |
|
|
|
p->attrHead--; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualOrigX(NSVGparser* p) |
|
|
|
{ |
|
|
|
return p->viewMinx; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualOrigY(NSVGparser* p) |
|
|
|
{ |
|
|
|
return p->viewMiny; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualWidth(NSVGparser* p) |
|
|
|
{ |
|
|
|
return p->viewWidth; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualHeight(NSVGparser* p) |
|
|
|
{ |
|
|
|
return p->viewHeight; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualLength(NSVGparser* p) |
|
|
|
{ |
|
|
|
float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); |
|
|
|
return sqrtf(w*w + h*h) / sqrtf(2.0f); |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) |
|
|
|
{ |
|
|
|
NSVGattrib* attr = nsvg__getAttr(p); |
|
|
|
switch (c.units) { |
|
|
|
case NSVG_UNITS_USER: return c.value; |
|
|
|
case NSVG_UNITS_PX: return c.value; |
|
|
|
case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; |
|
|
|
case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; |
|
|
|
case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; |
|
|
|
case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; |
|
|
|
case NSVG_UNITS_IN: return c.value * p->dpi; |
|
|
|
case NSVG_UNITS_EM: return c.value * attr->fontSize; |
|
|
|
case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
|
|
|
|
case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; |
|
|
|
default: return c.value; |
|
|
|
} |
|
|
|
return c.value; |
|
|
|
} |
|
|
|
|
|
|
|
static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) |
|
|
|
{ |
|
|
|
NSVGgradientData* grad = p->gradients; |
|
|
@ -724,16 +789,15 @@ static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* bounds, char* paintType) |
|
|
|
static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) |
|
|
|
{ |
|
|
|
NSVGattrib* attr = nsvg__getAttr(p); |
|
|
|
NSVGgradientData* data = NULL; |
|
|
|
NSVGgradientData* ref = NULL; |
|
|
|
NSVGgradientStop* stops = NULL; |
|
|
|
NSVGgradient* grad; |
|
|
|
float dx, dy; |
|
|
|
float ox, oy, sw, sh, sl; |
|
|
|
int nstops = 0; |
|
|
|
NSVG_NOTUSED(bounds); |
|
|
|
|
|
|
|
data = nsvg__findGradientData(p, id); |
|
|
|
if (data == NULL) return NULL; |
|
|
@ -753,34 +817,47 @@ static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const f |
|
|
|
grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); |
|
|
|
if (grad == NULL) return NULL; |
|
|
|
|
|
|
|
// The shape width and height.
|
|
|
|
if (data->units == NSVG_OBJECT_SPACE) { |
|
|
|
ox = localBounds[0]; |
|
|
|
oy = localBounds[1]; |
|
|
|
sw = localBounds[2] - localBounds[0]; |
|
|
|
sh = localBounds[3] - localBounds[1]; |
|
|
|
} else { |
|
|
|
ox = nsvg__actualOrigX(p); |
|
|
|
oy = nsvg__actualOrigY(p); |
|
|
|
sw = nsvg__actualWidth(p); |
|
|
|
sh = nsvg__actualHeight(p); |
|
|
|
} |
|
|
|
sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); |
|
|
|
|
|
|
|
if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { |
|
|
|
float x1, y1, x2, y2, dx, dy; |
|
|
|
x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); |
|
|
|
y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); |
|
|
|
x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); |
|
|
|
y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); |
|
|
|
// Calculate transform aligned to the line
|
|
|
|
dx = data->linear.x2 - data->linear.x1; |
|
|
|
dy = data->linear.y2 - data->linear.y1; |
|
|
|
dx = x2 - x1; |
|
|
|
dy = y2 - y1; |
|
|
|
grad->xform[0] = dy; grad->xform[1] = -dx; |
|
|
|
grad->xform[2] = dx; grad->xform[3] = dy; |
|
|
|
grad->xform[4] = data->linear.x1; grad->xform[5] = data->linear.y1; |
|
|
|
grad->xform[4] = x1; grad->xform[5] = y1; |
|
|
|
} else { |
|
|
|
float cx, cy, fx, fy, r; |
|
|
|
cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); |
|
|
|
cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); |
|
|
|
fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); |
|
|
|
fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); |
|
|
|
r = nsvg__convertToPixels(p, data->radial.r, 0, sl); |
|
|
|
// Calculate transform aligned to the circle
|
|
|
|
grad->xform[0] = data->radial.r; grad->xform[1] = 0; |
|
|
|
grad->xform[2] = 0; grad->xform[3] = data->radial.r; |
|
|
|
grad->xform[4] = data->radial.cx; grad->xform[5] = data->radial.cy; |
|
|
|
grad->fx = data->radial.fx / data->radial.r; |
|
|
|
grad->fy = data->radial.fy / data->radial.r; |
|
|
|
grad->xform[0] = r; grad->xform[1] = 0; |
|
|
|
grad->xform[2] = 0; grad->xform[3] = r; |
|
|
|
grad->xform[4] = cx; grad->xform[5] = cy; |
|
|
|
grad->fx = fx / r; |
|
|
|
grad->fy = fy / r; |
|
|
|
} |
|
|
|
|
|
|
|
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, attr->xform); |
|
|
|
} |
|
|
|
nsvg__xformMultiply(grad->xform, data->xform); |
|
|
|
nsvg__xformMultiply(grad->xform, attr->xform); |
|
|
|
|
|
|
@ -800,6 +877,36 @@ static float nsvg__getAverageScale(float* t) |
|
|
|
return (sx + sy) * 0.5f; |
|
|
|
} |
|
|
|
|
|
|
|
static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) |
|
|
|
{ |
|
|
|
NSVGpath* path; |
|
|
|
float curve[4*2], curveBounds[4]; |
|
|
|
int i, first = 1; |
|
|
|
for (path = shape->paths; path != NULL; path = path->next) { |
|
|
|
nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); |
|
|
|
for (i = 0; i < path->npts-1; i += 3) { |
|
|
|
nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); |
|
|
|
nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); |
|
|
|
nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); |
|
|
|
nsvg__curveBounds(curveBounds, curve); |
|
|
|
if (first) { |
|
|
|
bounds[0] = curveBounds[0]; |
|
|
|
bounds[1] = curveBounds[1]; |
|
|
|
bounds[2] = curveBounds[2]; |
|
|
|
bounds[3] = curveBounds[3]; |
|
|
|
first = 0; |
|
|
|
} else { |
|
|
|
bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); |
|
|
|
bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); |
|
|
|
bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); |
|
|
|
bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); |
|
|
|
} |
|
|
|
curve[0] = curve[6]; |
|
|
|
curve[1] = curve[7]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void nsvg__addShape(NSVGparser* p) |
|
|
|
{ |
|
|
|
NSVGattrib* attr = nsvg__getAttr(p); |
|
|
@ -845,7 +952,10 @@ static void nsvg__addShape(NSVGparser* p) |
|
|
|
shape->fill.color = attr->fillColor; |
|
|
|
shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; |
|
|
|
} else if (attr->hasFill == 2) { |
|
|
|
shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, shape->bounds, &shape->fill.type); |
|
|
|
float inv[6], localBounds[4]; |
|
|
|
nsvg__xformInverse(inv, attr->xform); |
|
|
|
nsvg__getLocalBounds(localBounds, shape, inv); |
|
|
|
shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); |
|
|
|
if (shape->fill.gradient == NULL) { |
|
|
|
shape->fill.type = NSVG_PAINT_NONE; |
|
|
|
} |
|
|
@ -859,7 +969,10 @@ static void nsvg__addShape(NSVGparser* p) |
|
|
|
shape->stroke.color = attr->strokeColor; |
|
|
|
shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; |
|
|
|
} else if (attr->hasStroke == 2) { |
|
|
|
shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, shape->bounds, &shape->stroke.type); |
|
|
|
float inv[6], localBounds[4]; |
|
|
|
nsvg__xformInverse(inv, attr->xform); |
|
|
|
nsvg__getLocalBounds(localBounds, shape, inv); |
|
|
|
shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); |
|
|
|
if (shape->stroke.gradient == NULL) |
|
|
|
shape->stroke.type = NSVG_PAINT_NONE; |
|
|
|
} |
|
|
@ -1002,23 +1115,6 @@ static const char* nsvg__getNextPathItem(const char* s, char* it) |
|
|
|
return s; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualWidth(NSVGparser* p) |
|
|
|
{ |
|
|
|
return p->viewWidth; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualHeight(NSVGparser* p) |
|
|
|
{ |
|
|
|
return p->viewHeight; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__actualLength(NSVGparser* p) |
|
|
|
{ |
|
|
|
float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); |
|
|
|
return sqrtf(w*w + h*h) / sqrtf(2.0f); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static unsigned int nsvg__parseColorHex(const char* str) |
|
|
|
{ |
|
|
|
unsigned int c = 0, r = 0, g = 0, b = 0; |
|
|
@ -1236,68 +1332,57 @@ static unsigned int nsvg__parseColor(const char* str) |
|
|
|
return nsvg__parseColorName(str); |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__convertToPixels(NSVGparser* p, float val, const char* units, int dir) |
|
|
|
static float nsvg__parseOpacity(const char* str) |
|
|
|
{ |
|
|
|
NSVGattrib* attr; |
|
|
|
|
|
|
|
if (p != NULL) { |
|
|
|
// Convert units to pixels.
|
|
|
|
if (units[0] == '\0') { |
|
|
|
return val; |
|
|
|
} else if (units[0] == 'p' && units[1] == 'x') { |
|
|
|
return val; |
|
|
|
} else if (units[0] == 'p' && units[1] == 't') { |
|
|
|
return val / 72.0f * p->dpi; |
|
|
|
} else if (units[0] == 'p' && units[1] == 'c') { |
|
|
|
return val / 6.0f * p->dpi; |
|
|
|
} else if (units[0] == 'm' && units[1] == 'm') { |
|
|
|
return val / 25.4f * p->dpi; |
|
|
|
} else if (units[0] == 'c' && units[1] == 'm') { |
|
|
|
return val / 2.54f * p->dpi; |
|
|
|
} else if (units[0] == 'i' && units[1] == 'n') { |
|
|
|
return val * p->dpi; |
|
|
|
} else if (units[0] == '%') { |
|
|
|
if (p != NULL) { |
|
|
|
attr = nsvg__getAttr(p); |
|
|
|
if (dir == 0) |
|
|
|
return (val/100.0f) * nsvg__actualWidth(p); |
|
|
|
else if (dir == 1) |
|
|
|
return (val/100.0f) * nsvg__actualHeight(p); |
|
|
|
else if (dir == 2) |
|
|
|
return (val/100.0f) * nsvg__actualLength(p); |
|
|
|
} else { |
|
|
|
return (val/100.0f); |
|
|
|
} |
|
|
|
} else if (units[0] == 'e' && units[1] == 'm') { |
|
|
|
if (p != NULL) { |
|
|
|
attr = nsvg__getAttr(p); |
|
|
|
return val * attr->fontSize; |
|
|
|
} |
|
|
|
} else if (units[0] == 'e' && units[1] == 'x') { |
|
|
|
if (p != NULL) { |
|
|
|
attr = nsvg__getAttr(p); |
|
|
|
return val * attr->fontSize * 0.52f; // x-height of Helvetica.
|
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// Convert units to pixels.
|
|
|
|
if (units[0] == '\0') { |
|
|
|
return val; |
|
|
|
} else if (units[0] == 'p' && units[1] == 'x') { |
|
|
|
return val; |
|
|
|
} else if (units[0] == '%') { |
|
|
|
return (val/100.0f); |
|
|
|
} |
|
|
|
} |
|
|
|
float val = 0; |
|
|
|
sscanf(str, "%f", &val); |
|
|
|
if (val < 0.0f) val = 0.0f; |
|
|
|
if (val > 1.0f) val = 1.0f; |
|
|
|
return val; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__parseFloat(NSVGparser* p, const char* str, int dir) |
|
|
|
{ |
|
|
|
float val = 0; |
|
|
|
static int nsvg__parseUnits(const char* units) |
|
|
|
{ |
|
|
|
if (units[0] == 'p' && units[1] == 'x') |
|
|
|
return NSVG_UNITS_PX; |
|
|
|
else if (units[0] == 'p' && units[1] == 't') |
|
|
|
return NSVG_UNITS_PT; |
|
|
|
else if (units[0] == 'p' && units[1] == 'c') |
|
|
|
return NSVG_UNITS_PC; |
|
|
|
else if (units[0] == 'm' && units[1] == 'm') |
|
|
|
return NSVG_UNITS_MM; |
|
|
|
else if (units[0] == 'c' && units[1] == 'm') |
|
|
|
return NSVG_UNITS_CM; |
|
|
|
else if (units[0] == 'i' && units[1] == 'n') |
|
|
|
return NSVG_UNITS_IN; |
|
|
|
else if (units[0] == '%') |
|
|
|
return NSVG_UNITS_PERCENT; |
|
|
|
else if (units[0] == 'e' && units[1] == 'm') |
|
|
|
return NSVG_UNITS_EM; |
|
|
|
else if (units[0] == 'e' && units[1] == 'x') |
|
|
|
return NSVG_UNITS_EX; |
|
|
|
return NSVG_UNITS_USER; |
|
|
|
} |
|
|
|
|
|
|
|
static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) |
|
|
|
{ |
|
|
|
NSVGcoordinate coord = {0, NSVG_UNITS_USER}; |
|
|
|
char units[32]=""; |
|
|
|
sscanf(str, "%f%s", &val, units); |
|
|
|
return nsvg__convertToPixels(p, val, units, dir); |
|
|
|
sscanf(str, "%f%s", &coord.value, units); |
|
|
|
coord.units = nsvg__parseUnits(units); |
|
|
|
return coord; |
|
|
|
} |
|
|
|
|
|
|
|
static NSVGcoordinate nsvg__coord(float v, int units) |
|
|
|
{ |
|
|
|
NSVGcoordinate coord = {v, units}; |
|
|
|
return coord; |
|
|
|
} |
|
|
|
|
|
|
|
static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) |
|
|
|
{ |
|
|
|
NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); |
|
|
|
return nsvg__convertToPixels(p, coord, orig, length); |
|
|
|
} |
|
|
|
|
|
|
|
static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) |
|
|
@ -1515,9 +1600,9 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) |
|
|
|
attr->fillColor = nsvg__parseColor(value); |
|
|
|
} |
|
|
|
} else if (strcmp(name, "opacity") == 0) { |
|
|
|
attr->opacity = nsvg__parseFloat(p, value, 2); |
|
|
|
attr->opacity = nsvg__parseOpacity(value); |
|
|
|
} else if (strcmp(name, "fill-opacity") == 0) { |
|
|
|
attr->fillOpacity = nsvg__parseFloat(p, value, 2); |
|
|
|
attr->fillOpacity = nsvg__parseOpacity(value); |
|
|
|
} else if (strcmp(name, "stroke") == 0) { |
|
|
|
if (strcmp(value, "none") == 0) { |
|
|
|
attr->hasStroke = 0; |
|
|
@ -1529,9 +1614,9 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) |
|
|
|
attr->strokeColor = nsvg__parseColor(value); |
|
|
|
} |
|
|
|
} else if (strcmp(name, "stroke-width") == 0) { |
|
|
|
attr->strokeWidth = nsvg__parseFloat(p, value, 2); |
|
|
|
attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); |
|
|
|
} else if (strcmp(name, "stroke-opacity") == 0) { |
|
|
|
attr->strokeOpacity = nsvg__parseFloat(NULL, value, 2); |
|
|
|
attr->strokeOpacity = nsvg__parseOpacity(value); |
|
|
|
} else if (strcmp(name, "stroke-linecap") == 0) { |
|
|
|
attr->strokeLineCap = nsvg__parseLineCap(value); |
|
|
|
} else if (strcmp(name, "stroke-linejoin") == 0) { |
|
|
@ -1539,16 +1624,16 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* 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); |
|
|
|
attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); |
|
|
|
} else if (strcmp(name, "transform") == 0) { |
|
|
|
nsvg__parseTransform(xform, value); |
|
|
|
nsvg__xformPremultiply(attr->xform, xform); |
|
|
|
} else if (strcmp(name, "stop-color") == 0) { |
|
|
|
attr->stopColor = nsvg__parseColor(value); |
|
|
|
} else if (strcmp(name, "stop-opacity") == 0) { |
|
|
|
attr->stopOpacity = nsvg__parseFloat(NULL, value, 2); |
|
|
|
attr->stopOpacity = nsvg__parseOpacity(value); |
|
|
|
} else if (strcmp(name, "offset") == 0) { |
|
|
|
attr->stopOffset = nsvg__parseFloat(NULL, value, 2); |
|
|
|
attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); |
|
|
|
} else if (strcmp(name, "id") == 0) { |
|
|
|
strncpy(attr->id, value, 63); |
|
|
|
attr->id[63] = '\0'; |
|
|
@ -2100,12 +2185,12 @@ static void nsvg__parseRect(NSVGparser* p, const char** attr) |
|
|
|
|
|
|
|
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); |
|
|
|
if (strcmp(attr[i], "y") == 0) y = nsvg__parseFloat(p, attr[i+1], 1); |
|
|
|
if (strcmp(attr[i], "width") == 0) w = nsvg__parseFloat(p, attr[i+1], 0); |
|
|
|
if (strcmp(attr[i], "height") == 0) h = nsvg__parseFloat(p, attr[i+1], 1); |
|
|
|
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(p, attr[i+1], 0)); |
|
|
|
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1)); |
|
|
|
if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); |
|
|
|
if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); |
|
|
|
if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); |
|
|
|
if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); |
|
|
|
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); |
|
|
|
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2152,9 +2237,9 @@ static void nsvg__parseCircle(NSVGparser* p, const char** attr) |
|
|
|
|
|
|
|
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); |
|
|
|
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(p, attr[i+1], 1); |
|
|
|
if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseFloat(p, attr[i+1], 2)); |
|
|
|
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); |
|
|
|
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); |
|
|
|
if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2183,10 +2268,10 @@ static void nsvg__parseEllipse(NSVGparser* p, const char** attr) |
|
|
|
|
|
|
|
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); |
|
|
|
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(p, attr[i+1], 1); |
|
|
|
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(p, attr[i+1], 0)); |
|
|
|
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1)); |
|
|
|
if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); |
|
|
|
if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); |
|
|
|
if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); |
|
|
|
if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2216,10 +2301,10 @@ static void nsvg__parseLine(NSVGparser* p, const char** attr) |
|
|
|
|
|
|
|
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); |
|
|
|
if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
|
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); |
|
|
|
if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); |
|
|
|
if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); |
|
|
|
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2275,9 +2360,9 @@ static void nsvg__parseSVG(NSVGparser* p, const char** attr) |
|
|
|
for (i = 0; attr[i]; i += 2) { |
|
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { |
|
|
|
if (strcmp(attr[i], "width") == 0) { |
|
|
|
p->image->width = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
|
p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); |
|
|
|
} else if (strcmp(attr[i], "height") == 0) { |
|
|
|
p->image->height = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); |
|
|
|
} else if (strcmp(attr[i], "viewBox") == 0) { |
|
|
|
sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight); |
|
|
|
} else if (strcmp(attr[i], "preserveAspectRatio") == 0) { |
|
|
@ -2318,9 +2403,14 @@ static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) |
|
|
|
grad->units = NSVG_OBJECT_SPACE; |
|
|
|
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; |
|
|
|
grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); |
|
|
|
grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); |
|
|
|
grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); |
|
|
|
grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); |
|
|
|
} else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { |
|
|
|
grad->radial.cx = grad->radial.cy = grad->radial.r = 0.5f; |
|
|
|
grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); |
|
|
|
grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); |
|
|
|
grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); |
|
|
|
} |
|
|
|
|
|
|
|
nsvg__xformIdentity(grad->xform); |
|
|
@ -2338,23 +2428,23 @@ static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) |
|
|
|
} else if (strcmp(attr[i], "gradientTransform") == 0) { |
|
|
|
nsvg__parseTransform(grad->xform, attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "cx") == 0) { |
|
|
|
grad->radial.cx = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
|
grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "cy") == 0) { |
|
|
|
grad->radial.cy = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "r") == 0) { |
|
|
|
grad->radial.r = nsvg__parseFloat(p, attr[i + 1], 2); |
|
|
|
grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "fx") == 0) { |
|
|
|
grad->radial.fx = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
|
grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "fy") == 0) { |
|
|
|
grad->radial.fy = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "x1") == 0) { |
|
|
|
grad->linear.x1 = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
|
grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "y1") == 0) { |
|
|
|
grad->linear.y1 = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "x2") == 0) { |
|
|
|
grad->linear.x2 = nsvg__parseFloat(p, attr[i + 1], 0); |
|
|
|
grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "y2") == 0) { |
|
|
|
grad->linear.y2 = nsvg__parseFloat(p, attr[i + 1], 1); |
|
|
|
grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); |
|
|
|
} else if (strcmp(attr[i], "spreadMethod") == 0) { |
|
|
|
if (strcmp(attr[i+1], "pad") == 0) |
|
|
|
grad->spread = NSVG_SPREAD_PAD; |
|
|
@ -2548,17 +2638,22 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) |
|
|
|
|
|
|
|
// Guess image size if not set completely.
|
|
|
|
nsvg__imageBounds(p, bounds); |
|
|
|
|
|
|
|
if (p->viewWidth == 0) { |
|
|
|
if (p->image->width > 0) |
|
|
|
if (p->image->width > 0) { |
|
|
|
p->viewWidth = p->image->width; |
|
|
|
else |
|
|
|
p->viewWidth = bounds[2] - bounds[0]; // patch https://github.com/memononen/nanosvg/issues/29
|
|
|
|
} else { |
|
|
|
p->viewMinx = bounds[0]; |
|
|
|
p->viewWidth = bounds[2] - bounds[0]; |
|
|
|
} |
|
|
|
} |
|
|
|
if (p->viewHeight == 0) { |
|
|
|
if (p->image->height > 0) |
|
|
|
if (p->image->height > 0) { |
|
|
|
p->viewHeight = p->image->height; |
|
|
|
else |
|
|
|
p->viewHeight = bounds[3] - bounds[1]; // patch https://github.com/memononen/nanosvg/issues/29
|
|
|
|
} else { |
|
|
|
p->viewMiny = bounds[1]; |
|
|
|
p->viewHeight = bounds[3] - bounds[1]; |
|
|
|
} |
|
|
|
} |
|
|
|
if (p->image->width == 0) |
|
|
|
p->image->width = p->viewWidth; |
|
|
@ -2569,7 +2664,8 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) |
|
|
|
ty = -p->viewMiny; |
|
|
|
sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; |
|
|
|
sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; |
|
|
|
us = 1.0f / nsvg__convertToPixels(p, 1.0f, units, 0); |
|
|
|
// Unit scaling
|
|
|
|
us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); |
|
|
|
|
|
|
|
// Fix aspect ratio
|
|
|
|
if (p->alignType == NSVG_ALIGN_MEET) { |
|
|
@ -2616,9 +2712,6 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
sx *= us; |
|
|
|
sy *= us; |
|
|
|
} |
|
|
|
|
|
|
|
NSVGimage* nsvgParse(char* input, const char* units, float dpi) |
|
|
|