nanosvg.h
author Ozkan Sezer <sezeroz@gmail.com>
Thu, 18 Oct 2018 11:57:19 +0300
changeset 612 ca95d0e31aec
parent 583 5fa46fcf9499
child 645 93332afa1831
permissions -rw-r--r--
use less ancient versions of autofoo scripts
slouken@524
     1
/*
slouken@524
     2
 * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
slouken@524
     3
 *
slouken@524
     4
 * This software is provided 'as-is', without any express or implied
slouken@524
     5
 * warranty.  In no event will the authors be held liable for any damages
slouken@524
     6
 * arising from the use of this software.
slouken@524
     7
 *
slouken@524
     8
 * Permission is granted to anyone to use this software for any purpose,
slouken@524
     9
 * including commercial applications, and to alter it and redistribute it
slouken@524
    10
 * freely, subject to the following restrictions:
slouken@524
    11
 *
slouken@524
    12
 * 1. The origin of this software must not be misrepresented; you must not
slouken@524
    13
 * claim that you wrote the original software. If you use this software
slouken@524
    14
 * in a product, an acknowledgment in the product documentation would be
slouken@524
    15
 * appreciated but is not required.
slouken@524
    16
 * 2. Altered source versions must be plainly marked as such, and must not be
slouken@524
    17
 * misrepresented as being the original software.
slouken@524
    18
 * 3. This notice may not be removed or altered from any source distribution.
slouken@524
    19
 *
slouken@524
    20
 * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
slouken@524
    21
 * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
slouken@524
    22
 *
slouken@524
    23
 * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
slouken@524
    24
 *
slouken@524
    25
 * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
slouken@524
    26
 *
slouken@524
    27
 */
slouken@524
    28
slouken@524
    29
#ifndef NANOSVG_H
slouken@524
    30
#define NANOSVG_H
slouken@524
    31
slouken@524
    32
#ifdef __cplusplus
slouken@524
    33
extern "C" {
slouken@524
    34
#endif
slouken@524
    35
slouken@524
    36
// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
slouken@524
    37
//
slouken@524
    38
// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
slouken@524
    39
//
slouken@524
    40
// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
slouken@524
    41
//
slouken@524
    42
// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
slouken@524
    43
// That is, you should get the same looking data as your designed in your favorite app.
slouken@524
    44
//
slouken@524
    45
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
slouken@524
    46
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
slouken@524
    47
//
slouken@524
    48
// The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
slouken@524
    49
// DPI (dots-per-inch) controls how the unit conversion is done.
slouken@524
    50
//
slouken@524
    51
// If you don't know or care about the units stuff, "px" and 96 should get you going.
slouken@524
    52
slouken@524
    53
slouken@524
    54
/* Example Usage:
slouken@524
    55
	// Load
slouken@524
    56
	NSVGImage* image;
slouken@524
    57
	image = nsvgParseFromFile("test.svg", "px", 96);
slouken@524
    58
	printf("size: %f x %f\n", image->width, image->height);
slouken@524
    59
	// Use...
slouken@524
    60
	for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
slouken@524
    61
		for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
slouken@524
    62
			for (int i = 0; i < path->npts-1; i += 3) {
slouken@524
    63
				float* p = &path->pts[i*2];
slouken@524
    64
				drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
slouken@524
    65
			}
slouken@524
    66
		}
slouken@524
    67
	}
slouken@524
    68
	// Delete
slouken@524
    69
	nsvgDelete(image);
slouken@524
    70
*/
slouken@524
    71
slouken@524
    72
enum NSVGpaintType {
slouken@524
    73
	NSVG_PAINT_NONE = 0,
slouken@524
    74
	NSVG_PAINT_COLOR = 1,
slouken@524
    75
	NSVG_PAINT_LINEAR_GRADIENT = 2,
slouken@524
    76
	NSVG_PAINT_RADIAL_GRADIENT = 3
slouken@524
    77
};
slouken@524
    78
slouken@524
    79
enum NSVGspreadType {
slouken@524
    80
	NSVG_SPREAD_PAD = 0,
slouken@524
    81
	NSVG_SPREAD_REFLECT = 1,
slouken@524
    82
	NSVG_SPREAD_REPEAT = 2
slouken@524
    83
};
slouken@524
    84
slouken@524
    85
enum NSVGlineJoin {
slouken@524
    86
	NSVG_JOIN_MITER = 0,
slouken@524
    87
	NSVG_JOIN_ROUND = 1,
slouken@524
    88
	NSVG_JOIN_BEVEL = 2
slouken@524
    89
};
slouken@524
    90
slouken@524
    91
enum NSVGlineCap {
slouken@524
    92
	NSVG_CAP_BUTT = 0,
slouken@524
    93
	NSVG_CAP_ROUND = 1,
slouken@524
    94
	NSVG_CAP_SQUARE = 2
slouken@524
    95
};
slouken@524
    96
slouken@524
    97
enum NSVGfillRule {
slouken@524
    98
	NSVG_FILLRULE_NONZERO = 0,
slouken@524
    99
	NSVG_FILLRULE_EVENODD = 1
slouken@524
   100
};
slouken@524
   101
slouken@524
   102
enum NSVGflags {
slouken@524
   103
	NSVG_FLAGS_VISIBLE = 0x01
slouken@524
   104
};
slouken@524
   105
slouken@524
   106
typedef struct NSVGgradientStop {
slouken@524
   107
	unsigned int color;
slouken@524
   108
	float offset;
slouken@524
   109
} NSVGgradientStop;
slouken@524
   110
slouken@524
   111
typedef struct NSVGgradient {
slouken@524
   112
	float xform[6];
slouken@524
   113
	char spread;
slouken@524
   114
	float fx, fy;
slouken@524
   115
	int nstops;
slouken@524
   116
	NSVGgradientStop stops[1];
slouken@524
   117
} NSVGgradient;
slouken@524
   118
slouken@524
   119
typedef struct NSVGpaint {
slouken@524
   120
	char type;
slouken@524
   121
	union {
slouken@524
   122
		unsigned int color;
slouken@524
   123
		NSVGgradient* gradient;
slouken@524
   124
	};
slouken@524
   125
} NSVGpaint;
slouken@524
   126
slouken@524
   127
typedef struct NSVGpath
slouken@524
   128
{
slouken@524
   129
	float* pts;					// Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
slouken@524
   130
	int npts;					// Total number of bezier points.
slouken@524
   131
	char closed;				// Flag indicating if shapes should be treated as closed.
slouken@524
   132
	float bounds[4];			// Tight bounding box of the shape [minx,miny,maxx,maxy].
slouken@524
   133
	struct NSVGpath* next;		// Pointer to next path, or NULL if last element.
slouken@524
   134
} NSVGpath;
slouken@524
   135
slouken@524
   136
typedef struct NSVGshape
slouken@524
   137
{
slouken@524
   138
	char id[64];				// Optional 'id' attr of the shape or its group
slouken@524
   139
	NSVGpaint fill;				// Fill paint
slouken@524
   140
	NSVGpaint stroke;			// Stroke paint
slouken@524
   141
	float opacity;				// Opacity of the shape.
slouken@524
   142
	float strokeWidth;			// Stroke width (scaled).
slouken@524
   143
	float strokeDashOffset;		// Stroke dash offset (scaled).
slouken@524
   144
	float strokeDashArray[8];			// Stroke dash array (scaled).
slouken@524
   145
	char strokeDashCount;				// Number of dash values in dash array.
slouken@524
   146
	char strokeLineJoin;		// Stroke join type.
slouken@524
   147
	char strokeLineCap;			// Stroke cap type.
slouken@524
   148
	float miterLimit;			// Miter limit
slouken@524
   149
	char fillRule;				// Fill rule, see NSVGfillRule.
slouken@524
   150
	unsigned char flags;		// Logical or of NSVG_FLAGS_* flags
slouken@524
   151
	float bounds[4];			// Tight bounding box of the shape [minx,miny,maxx,maxy].
slouken@524
   152
	NSVGpath* paths;			// Linked list of paths in the image.
slouken@524
   153
	struct NSVGshape* next;		// Pointer to next shape, or NULL if last element.
slouken@524
   154
} NSVGshape;
slouken@524
   155
slouken@524
   156
typedef struct NSVGimage
slouken@524
   157
{
slouken@524
   158
	float width;				// Width of the image.
slouken@524
   159
	float height;				// Height of the image.
slouken@524
   160
	NSVGshape* shapes;			// Linked list of shapes in the image.
slouken@524
   161
} NSVGimage;
slouken@524
   162
slouken@524
   163
// Parses SVG file from a file, returns SVG image as paths.
slouken@524
   164
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
slouken@524
   165
slouken@524
   166
// Parses SVG file from a null terminated string, returns SVG image as paths.
slouken@524
   167
// Important note: changes the string.
slouken@524
   168
NSVGimage* nsvgParse(char* input, const char* units, float dpi);
slouken@524
   169
slouken@524
   170
// Deletes list of paths.
slouken@524
   171
void nsvgDelete(NSVGimage* image);
slouken@524
   172
slouken@524
   173
#ifdef __cplusplus
slouken@524
   174
}
slouken@524
   175
#endif
slouken@524
   176
slouken@524
   177
#endif // NANOSVG_H
slouken@524
   178
slouken@524
   179
#ifdef NANOSVG_IMPLEMENTATION
slouken@524
   180
slouken@545
   181
/*
slouken@524
   182
#include <string.h>
slouken@524
   183
#include <stdlib.h>
slouken@524
   184
#include <math.h>
slouken@545
   185
*/
slouken@524
   186
slouken@524
   187
#define NSVG_PI (3.14159265358979323846264338327f)
slouken@524
   188
#define NSVG_KAPPA90 (0.5522847493f)	// Length proportional to radius of a cubic bezier handle for 90deg arcs.
slouken@524
   189
slouken@524
   190
#define NSVG_ALIGN_MIN 0
slouken@524
   191
#define NSVG_ALIGN_MID 1
slouken@524
   192
#define NSVG_ALIGN_MAX 2
slouken@524
   193
#define NSVG_ALIGN_NONE 0
slouken@524
   194
#define NSVG_ALIGN_MEET 1
slouken@524
   195
#define NSVG_ALIGN_SLICE 2
slouken@524
   196
slouken@524
   197
#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
slouken@524
   198
#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
slouken@524
   199
slouken@524
   200
#ifdef _MSC_VER
slouken@524
   201
	#pragma warning (disable: 4996) // Switch off security warnings
slouken@524
   202
	#pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
slouken@524
   203
	#ifdef __cplusplus
slouken@524
   204
	#define NSVG_INLINE inline
slouken@524
   205
	#else
slouken@524
   206
	#define NSVG_INLINE
slouken@524
   207
	#endif
slouken@524
   208
#else
slouken@524
   209
	#define NSVG_INLINE inline
slouken@524
   210
#endif
slouken@524
   211
slouken@524
   212
slouken@524
   213
static int nsvg__isspace(char c)
slouken@524
   214
{
slouken@524
   215
	return strchr(" \t\n\v\f\r", c) != 0;
slouken@524
   216
}
slouken@524
   217
slouken@524
   218
static int nsvg__isdigit(char c)
slouken@524
   219
{
slouken@524
   220
	return c >= '0' && c <= '9';
slouken@524
   221
}
slouken@524
   222
slouken@524
   223
static int nsvg__isnum(char c)
slouken@524
   224
{
slouken@524
   225
	return strchr("0123456789+-.eE", c) != 0;
slouken@524
   226
}
slouken@524
   227
slouken@524
   228
static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
slouken@524
   229
static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
slouken@524
   230
slouken@524
   231
slouken@524
   232
// Simple XML parser
slouken@524
   233
slouken@524
   234
#define NSVG_XML_TAG 1
slouken@524
   235
#define NSVG_XML_CONTENT 2
slouken@524
   236
#define NSVG_XML_MAX_ATTRIBS 256
slouken@524
   237
slouken@524
   238
static void nsvg__parseContent(char* s,
slouken@524
   239
							   void (*contentCb)(void* ud, const char* s),
slouken@524
   240
							   void* ud)
slouken@524
   241
{
slouken@524
   242
	// Trim start white spaces
slouken@524
   243
	while (*s && nsvg__isspace(*s)) s++;
slouken@524
   244
	if (!*s) return;
slouken@524
   245
slouken@524
   246
	if (contentCb)
slouken@524
   247
		(*contentCb)(ud, s);
slouken@524
   248
}
slouken@524
   249
slouken@524
   250
static void nsvg__parseElement(char* s,
slouken@524
   251
							   void (*startelCb)(void* ud, const char* el, const char** attr),
slouken@524
   252
							   void (*endelCb)(void* ud, const char* el),
slouken@524
   253
							   void* ud)
slouken@524
   254
{
slouken@524
   255
	const char* attr[NSVG_XML_MAX_ATTRIBS];
slouken@524
   256
	int nattr = 0;
slouken@524
   257
	char* name;
slouken@524
   258
	int start = 0;
slouken@524
   259
	int end = 0;
slouken@524
   260
	char quote;
slouken@524
   261
slouken@524
   262
	// Skip white space after the '<'
slouken@524
   263
	while (*s && nsvg__isspace(*s)) s++;
slouken@524
   264
slouken@524
   265
	// Check if the tag is end tag
slouken@524
   266
	if (*s == '/') {
slouken@524
   267
		s++;
slouken@524
   268
		end = 1;
slouken@524
   269
	} else {
slouken@524
   270
		start = 1;
slouken@524
   271
	}
slouken@524
   272
slouken@524
   273
	// Skip comments, data and preprocessor stuff.
slouken@524
   274
	if (!*s || *s == '?' || *s == '!')
slouken@524
   275
		return;
slouken@524
   276
slouken@524
   277
	// Get tag name
slouken@524
   278
	name = s;
slouken@524
   279
	while (*s && !nsvg__isspace(*s)) s++;
slouken@524
   280
	if (*s) { *s++ = '\0'; }
slouken@524
   281
slouken@524
   282
	// Get attribs
slouken@524
   283
	while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
slouken@524
   284
		char* name = NULL;
slouken@524
   285
		char* value = NULL;
slouken@524
   286
slouken@524
   287
		// Skip white space before the attrib name
slouken@524
   288
		while (*s && nsvg__isspace(*s)) s++;
slouken@524
   289
		if (!*s) break;
slouken@524
   290
		if (*s == '/') {
slouken@524
   291
			end = 1;
slouken@524
   292
			break;
slouken@524
   293
		}
slouken@524
   294
		name = s;
slouken@524
   295
		// Find end of the attrib name.
slouken@524
   296
		while (*s && !nsvg__isspace(*s) && *s != '=') s++;
slouken@524
   297
		if (*s) { *s++ = '\0'; }
slouken@524
   298
		// Skip until the beginning of the value.
slouken@524
   299
		while (*s && *s != '\"' && *s != '\'') s++;
slouken@524
   300
		if (!*s) break;
slouken@524
   301
		quote = *s;
slouken@524
   302
		s++;
slouken@524
   303
		// Store value and find the end of it.
slouken@524
   304
		value = s;
slouken@524
   305
		while (*s && *s != quote) s++;
slouken@524
   306
		if (*s) { *s++ = '\0'; }
slouken@524
   307
slouken@524
   308
		// Store only well formed attributes
slouken@524
   309
		if (name && value) {
slouken@524
   310
			attr[nattr++] = name;
slouken@524
   311
			attr[nattr++] = value;
slouken@524
   312
		}
slouken@524
   313
	}
slouken@524
   314
slouken@524
   315
	// List terminator
slouken@524
   316
	attr[nattr++] = 0;
slouken@524
   317
	attr[nattr++] = 0;
slouken@524
   318
slouken@524
   319
	// Call callbacks.
slouken@524
   320
	if (start && startelCb)
slouken@524
   321
		(*startelCb)(ud, name, attr);
slouken@524
   322
	if (end && endelCb)
slouken@524
   323
		(*endelCb)(ud, name);
slouken@524
   324
}
slouken@524
   325
slouken@524
   326
int nsvg__parseXML(char* input,
slouken@524
   327
				   void (*startelCb)(void* ud, const char* el, const char** attr),
slouken@524
   328
				   void (*endelCb)(void* ud, const char* el),
slouken@524
   329
				   void (*contentCb)(void* ud, const char* s),
slouken@524
   330
				   void* ud)
slouken@524
   331
{
slouken@524
   332
	char* s = input;
slouken@524
   333
	char* mark = s;
slouken@524
   334
	int state = NSVG_XML_CONTENT;
slouken@524
   335
	while (*s) {
slouken@524
   336
		if (*s == '<' && state == NSVG_XML_CONTENT) {
slouken@524
   337
			// Start of a tag
slouken@524
   338
			*s++ = '\0';
slouken@524
   339
			nsvg__parseContent(mark, contentCb, ud);
slouken@524
   340
			mark = s;
slouken@524
   341
			state = NSVG_XML_TAG;
slouken@524
   342
		} else if (*s == '>' && state == NSVG_XML_TAG) {
slouken@524
   343
			// Start of a content or new tag.
slouken@524
   344
			*s++ = '\0';
slouken@524
   345
			nsvg__parseElement(mark, startelCb, endelCb, ud);
slouken@524
   346
			mark = s;
slouken@524
   347
			state = NSVG_XML_CONTENT;
slouken@524
   348
		} else {
slouken@524
   349
			s++;
slouken@524
   350
		}
slouken@524
   351
	}
slouken@524
   352
slouken@524
   353
	return 1;
slouken@524
   354
}
slouken@524
   355
slouken@524
   356
slouken@524
   357
/* Simple SVG parser. */
slouken@524
   358
slouken@524
   359
#define NSVG_MAX_ATTR 128
slouken@524
   360
slouken@524
   361
enum NSVGgradientUnits {
slouken@524
   362
	NSVG_USER_SPACE = 0,
slouken@524
   363
	NSVG_OBJECT_SPACE = 1
slouken@524
   364
};
slouken@524
   365
slouken@524
   366
#define NSVG_MAX_DASHES 8
slouken@524
   367
slouken@524
   368
enum NSVGunits {
slouken@524
   369
	NSVG_UNITS_USER,
slouken@524
   370
	NSVG_UNITS_PX,
slouken@524
   371
	NSVG_UNITS_PT,
slouken@524
   372
	NSVG_UNITS_PC,
slouken@524
   373
	NSVG_UNITS_MM,
slouken@524
   374
	NSVG_UNITS_CM,
slouken@524
   375
	NSVG_UNITS_IN,
slouken@524
   376
	NSVG_UNITS_PERCENT,
slouken@524
   377
	NSVG_UNITS_EM,
slouken@524
   378
	NSVG_UNITS_EX
slouken@524
   379
};
slouken@524
   380
slouken@524
   381
typedef struct NSVGcoordinate {
slouken@524
   382
	float value;
slouken@524
   383
	int units;
slouken@524
   384
} NSVGcoordinate;
slouken@524
   385
slouken@524
   386
typedef struct NSVGlinearData {
slouken@524
   387
	NSVGcoordinate x1, y1, x2, y2;
slouken@524
   388
} NSVGlinearData;
slouken@524
   389
slouken@524
   390
typedef struct NSVGradialData {
slouken@524
   391
	NSVGcoordinate cx, cy, r, fx, fy;
slouken@524
   392
} NSVGradialData;
slouken@524
   393
slouken@524
   394
typedef struct NSVGgradientData
slouken@524
   395
{
slouken@524
   396
	char id[64];
slouken@524
   397
	char ref[64];
slouken@524
   398
	char type;
slouken@524
   399
	union {
slouken@524
   400
		NSVGlinearData linear;
slouken@524
   401
		NSVGradialData radial;
slouken@524
   402
	};
slouken@524
   403
	char spread;
slouken@524
   404
	char units;
slouken@524
   405
	float xform[6];
slouken@524
   406
	int nstops;
slouken@524
   407
	NSVGgradientStop* stops;
slouken@524
   408
	struct NSVGgradientData* next;
slouken@524
   409
} NSVGgradientData;
slouken@524
   410
slouken@524
   411
typedef struct NSVGattrib
slouken@524
   412
{
slouken@524
   413
	char id[64];
slouken@524
   414
	float xform[6];
slouken@524
   415
	unsigned int fillColor;
slouken@524
   416
	unsigned int strokeColor;
slouken@524
   417
	float opacity;
slouken@524
   418
	float fillOpacity;
slouken@524
   419
	float strokeOpacity;
slouken@524
   420
	char fillGradient[64];
slouken@524
   421
	char strokeGradient[64];
slouken@524
   422
	float strokeWidth;
slouken@524
   423
	float strokeDashOffset;
slouken@524
   424
	float strokeDashArray[NSVG_MAX_DASHES];
slouken@524
   425
	int strokeDashCount;
slouken@524
   426
	char strokeLineJoin;
slouken@524
   427
	char strokeLineCap;
slouken@524
   428
	float miterLimit;
slouken@524
   429
	char fillRule;
slouken@524
   430
	float fontSize;
slouken@524
   431
	unsigned int stopColor;
slouken@524
   432
	float stopOpacity;
slouken@524
   433
	float stopOffset;
slouken@524
   434
	char hasFill;
slouken@524
   435
	char hasStroke;
slouken@524
   436
	char visible;
slouken@524
   437
} NSVGattrib;
slouken@524
   438
slouken@525
   439
typedef struct NSVGstyles
slouken@525
   440
{
slouken@525
   441
	char*	name;
slouken@525
   442
	char* description;
slouken@525
   443
	struct NSVGstyles* next;
slouken@525
   444
} NSVGstyles;
slouken@525
   445
slouken@524
   446
typedef struct NSVGparser
slouken@524
   447
{
slouken@524
   448
	NSVGattrib attr[NSVG_MAX_ATTR];
slouken@524
   449
	int attrHead;
slouken@524
   450
	float* pts;
slouken@524
   451
	int npts;
slouken@524
   452
	int cpts;
slouken@524
   453
	NSVGpath* plist;
slouken@524
   454
	NSVGimage* image;
slouken@525
   455
	NSVGstyles* styles;
slouken@524
   456
	NSVGgradientData* gradients;
slouken@524
   457
	NSVGshape* shapesTail;
slouken@524
   458
	float viewMinx, viewMiny, viewWidth, viewHeight;
slouken@524
   459
	int alignX, alignY, alignType;
slouken@524
   460
	float dpi;
slouken@524
   461
	char pathFlag;
slouken@524
   462
	char defsFlag;
slouken@525
   463
	char styleFlag;
slouken@524
   464
} NSVGparser;
slouken@524
   465
slouken@524
   466
static void nsvg__xformIdentity(float* t)
slouken@524
   467
{
slouken@524
   468
	t[0] = 1.0f; t[1] = 0.0f;
slouken@524
   469
	t[2] = 0.0f; t[3] = 1.0f;
slouken@524
   470
	t[4] = 0.0f; t[5] = 0.0f;
slouken@524
   471
}
slouken@524
   472
slouken@524
   473
static void nsvg__xformSetTranslation(float* t, float tx, float ty)
slouken@524
   474
{
slouken@524
   475
	t[0] = 1.0f; t[1] = 0.0f;
slouken@524
   476
	t[2] = 0.0f; t[3] = 1.0f;
slouken@524
   477
	t[4] = tx; t[5] = ty;
slouken@524
   478
}
slouken@524
   479
slouken@524
   480
static void nsvg__xformSetScale(float* t, float sx, float sy)
slouken@524
   481
{
slouken@524
   482
	t[0] = sx; t[1] = 0.0f;
slouken@524
   483
	t[2] = 0.0f; t[3] = sy;
slouken@524
   484
	t[4] = 0.0f; t[5] = 0.0f;
slouken@524
   485
}
slouken@524
   486
slouken@524
   487
static void nsvg__xformSetSkewX(float* t, float a)
slouken@524
   488
{
slouken@524
   489
	t[0] = 1.0f; t[1] = 0.0f;
slouken@524
   490
	t[2] = tanf(a); t[3] = 1.0f;
slouken@524
   491
	t[4] = 0.0f; t[5] = 0.0f;
slouken@524
   492
}
slouken@524
   493
slouken@524
   494
static void nsvg__xformSetSkewY(float* t, float a)
slouken@524
   495
{
slouken@524
   496
	t[0] = 1.0f; t[1] = tanf(a);
slouken@524
   497
	t[2] = 0.0f; t[3] = 1.0f;
slouken@524
   498
	t[4] = 0.0f; t[5] = 0.0f;
slouken@524
   499
}
slouken@524
   500
slouken@524
   501
static void nsvg__xformSetRotation(float* t, float a)
slouken@524
   502
{
slouken@524
   503
	float cs = cosf(a), sn = sinf(a);
slouken@524
   504
	t[0] = cs; t[1] = sn;
slouken@524
   505
	t[2] = -sn; t[3] = cs;
slouken@524
   506
	t[4] = 0.0f; t[5] = 0.0f;
slouken@524
   507
}
slouken@524
   508
slouken@524
   509
static void nsvg__xformMultiply(float* t, float* s)
slouken@524
   510
{
slouken@524
   511
	float t0 = t[0] * s[0] + t[1] * s[2];
slouken@524
   512
	float t2 = t[2] * s[0] + t[3] * s[2];
slouken@524
   513
	float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
slouken@524
   514
	t[1] = t[0] * s[1] + t[1] * s[3];
slouken@524
   515
	t[3] = t[2] * s[1] + t[3] * s[3];
slouken@524
   516
	t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
slouken@524
   517
	t[0] = t0;
slouken@524
   518
	t[2] = t2;
slouken@524
   519
	t[4] = t4;
slouken@524
   520
}
slouken@524
   521
slouken@524
   522
static void nsvg__xformInverse(float* inv, float* t)
slouken@524
   523
{
slouken@524
   524
	double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
slouken@524
   525
	if (det > -1e-6 && det < 1e-6) {
slouken@524
   526
		nsvg__xformIdentity(t);
slouken@524
   527
		return;
slouken@524
   528
	}
slouken@524
   529
	invdet = 1.0 / det;
slouken@524
   530
	inv[0] = (float)(t[3] * invdet);
slouken@524
   531
	inv[2] = (float)(-t[2] * invdet);
slouken@524
   532
	inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
slouken@524
   533
	inv[1] = (float)(-t[1] * invdet);
slouken@524
   534
	inv[3] = (float)(t[0] * invdet);
slouken@524
   535
	inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
slouken@524
   536
}
slouken@524
   537
slouken@524
   538
static void nsvg__xformPremultiply(float* t, float* s)
slouken@524
   539
{
slouken@524
   540
	float s2[6];
slouken@524
   541
	memcpy(s2, s, sizeof(float)*6);
slouken@524
   542
	nsvg__xformMultiply(s2, t);
slouken@524
   543
	memcpy(t, s2, sizeof(float)*6);
slouken@524
   544
}
slouken@524
   545
slouken@524
   546
static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
slouken@524
   547
{
slouken@524
   548
	*dx = x*t[0] + y*t[2] + t[4];
slouken@524
   549
	*dy = x*t[1] + y*t[3] + t[5];
slouken@524
   550
}
slouken@524
   551
slouken@524
   552
static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
slouken@524
   553
{
slouken@524
   554
	*dx = x*t[0] + y*t[2];
slouken@524
   555
	*dy = x*t[1] + y*t[3];
slouken@524
   556
}
slouken@524
   557
slouken@524
   558
#define NSVG_EPSILON (1e-12)
slouken@524
   559
slouken@524
   560
static int nsvg__ptInBounds(float* pt, float* bounds)
slouken@524
   561
{
slouken@524
   562
	return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
slouken@524
   563
}
slouken@524
   564
slouken@524
   565
slouken@524
   566
static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
slouken@524
   567
{
slouken@524
   568
	double it = 1.0-t;
slouken@524
   569
	return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
slouken@524
   570
}
slouken@524
   571
slouken@524
   572
static void nsvg__curveBounds(float* bounds, float* curve)
slouken@524
   573
{
slouken@524
   574
	int i, j, count;
slouken@524
   575
	double roots[2], a, b, c, b2ac, t, v;
slouken@524
   576
	float* v0 = &curve[0];
slouken@524
   577
	float* v1 = &curve[2];
slouken@524
   578
	float* v2 = &curve[4];
slouken@524
   579
	float* v3 = &curve[6];
slouken@524
   580
slouken@524
   581
	// Start the bounding box by end points
slouken@524
   582
	bounds[0] = nsvg__minf(v0[0], v3[0]);
slouken@524
   583
	bounds[1] = nsvg__minf(v0[1], v3[1]);
slouken@524
   584
	bounds[2] = nsvg__maxf(v0[0], v3[0]);
slouken@524
   585
	bounds[3] = nsvg__maxf(v0[1], v3[1]);
slouken@524
   586
slouken@524
   587
	// Bezier curve fits inside the convex hull of it's control points.
slouken@524
   588
	// If control points are inside the bounds, we're done.
slouken@524
   589
	if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
slouken@524
   590
		return;
slouken@524
   591
slouken@524
   592
	// Add bezier curve inflection points in X and Y.
slouken@524
   593
	for (i = 0; i < 2; i++) {
slouken@524
   594
		a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
slouken@524
   595
		b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
slouken@524
   596
		c = 3.0 * v1[i] - 3.0 * v0[i];
slouken@524
   597
		count = 0;
slouken@524
   598
		if (fabs(a) < NSVG_EPSILON) {
slouken@524
   599
			if (fabs(b) > NSVG_EPSILON) {
slouken@524
   600
				t = -c / b;
slouken@524
   601
				if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
slouken@524
   602
					roots[count++] = t;
slouken@524
   603
			}
slouken@524
   604
		} else {
slouken@524
   605
			b2ac = b*b - 4.0*c*a;
slouken@524
   606
			if (b2ac > NSVG_EPSILON) {
slouken@524
   607
				t = (-b + sqrt(b2ac)) / (2.0 * a);
slouken@524
   608
				if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
slouken@524
   609
					roots[count++] = t;
slouken@524
   610
				t = (-b - sqrt(b2ac)) / (2.0 * a);
slouken@524
   611
				if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
slouken@524
   612
					roots[count++] = t;
slouken@524
   613
			}
slouken@524
   614
		}
slouken@524
   615
		for (j = 0; j < count; j++) {
slouken@524
   616
			v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
slouken@524
   617
			bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
slouken@524
   618
			bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
slouken@524
   619
		}
slouken@524
   620
	}
slouken@524
   621
}
slouken@524
   622
slouken@524
   623
static NSVGparser* nsvg__createParser()
slouken@524
   624
{
slouken@524
   625
	NSVGparser* p;
slouken@524
   626
	p = (NSVGparser*)malloc(sizeof(NSVGparser));
slouken@524
   627
	if (p == NULL) goto error;
slouken@524
   628
	memset(p, 0, sizeof(NSVGparser));
slouken@524
   629
slouken@524
   630
	p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
slouken@524
   631
	if (p->image == NULL) goto error;
slouken@524
   632
	memset(p->image, 0, sizeof(NSVGimage));
slouken@524
   633
slouken@524
   634
	// Init style
slouken@524
   635
	nsvg__xformIdentity(p->attr[0].xform);
slouken@524
   636
	memset(p->attr[0].id, 0, sizeof p->attr[0].id);
slouken@524
   637
	p->attr[0].fillColor = NSVG_RGB(0,0,0);
slouken@524
   638
	p->attr[0].strokeColor = NSVG_RGB(0,0,0);
slouken@524
   639
	p->attr[0].opacity = 1;
slouken@524
   640
	p->attr[0].fillOpacity = 1;
slouken@524
   641
	p->attr[0].strokeOpacity = 1;
slouken@524
   642
	p->attr[0].stopOpacity = 1;
slouken@524
   643
	p->attr[0].strokeWidth = 1;
slouken@524
   644
	p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
slouken@524
   645
	p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
slouken@524
   646
	p->attr[0].miterLimit = 4;
slouken@524
   647
	p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
slouken@524
   648
	p->attr[0].hasFill = 1;
slouken@524
   649
	p->attr[0].visible = 1;
slouken@524
   650
slouken@524
   651
	return p;
slouken@524
   652
slouken@524
   653
error:
slouken@524
   654
	if (p) {
slouken@524
   655
		if (p->image) free(p->image);
slouken@524
   656
		free(p);
slouken@524
   657
	}
slouken@524
   658
	return NULL;
slouken@524
   659
}
slouken@525
   660
static void nsvg__deleteStyles(NSVGstyles* style) {
slouken@525
   661
	while (style) {
slouken@525
   662
		NSVGstyles *next = style->next;
slouken@525
   663
		if (style->name!= NULL)
slouken@525
   664
			free(style->name);
slouken@525
   665
		if (style->description != NULL)
slouken@525
   666
			free(style->description);
slouken@525
   667
		free(style);
slouken@525
   668
		style = next;
slouken@525
   669
	}
slouken@525
   670
}
slouken@524
   671
slouken@524
   672
static void nsvg__deletePaths(NSVGpath* path)
slouken@524
   673
{
slouken@524
   674
	while (path) {
slouken@524
   675
		NSVGpath *next = path->next;
slouken@524
   676
		if (path->pts != NULL)
slouken@524
   677
			free(path->pts);
slouken@524
   678
		free(path);
slouken@524
   679
		path = next;
slouken@524
   680
	}
slouken@524
   681
}
slouken@524
   682
slouken@524
   683
static void nsvg__deletePaint(NSVGpaint* paint)
slouken@524
   684
{
slouken@524
   685
	if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
slouken@524
   686
		free(paint->gradient);
slouken@524
   687
}
slouken@524
   688
slouken@524
   689
static void nsvg__deleteGradientData(NSVGgradientData* grad)
slouken@524
   690
{
slouken@524
   691
	NSVGgradientData* next;
slouken@524
   692
	while (grad != NULL) {
slouken@524
   693
		next = grad->next;
slouken@524
   694
		free(grad->stops);
slouken@524
   695
		free(grad);
slouken@524
   696
		grad = next;
slouken@524
   697
	}
slouken@524
   698
}
slouken@524
   699
slouken@524
   700
static void nsvg__deleteParser(NSVGparser* p)
slouken@524
   701
{
slouken@524
   702
	if (p != NULL) {
slouken@525
   703
		nsvg__deleteStyles(p->styles);
slouken@524
   704
		nsvg__deletePaths(p->plist);
slouken@524
   705
		nsvg__deleteGradientData(p->gradients);
slouken@524
   706
		nsvgDelete(p->image);
slouken@524
   707
		free(p->pts);
slouken@524
   708
		free(p);
slouken@524
   709
	}
slouken@524
   710
}
slouken@524
   711
slouken@524
   712
static void nsvg__resetPath(NSVGparser* p)
slouken@524
   713
{
slouken@524
   714
	p->npts = 0;
slouken@524
   715
}
slouken@524
   716
slouken@524
   717
static void nsvg__addPoint(NSVGparser* p, float x, float y)
slouken@524
   718
{
slouken@524
   719
	if (p->npts+1 > p->cpts) {
slouken@524
   720
		p->cpts = p->cpts ? p->cpts*2 : 8;
slouken@524
   721
		p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
slouken@524
   722
		if (!p->pts) return;
slouken@524
   723
	}
slouken@524
   724
	p->pts[p->npts*2+0] = x;
slouken@524
   725
	p->pts[p->npts*2+1] = y;
slouken@524
   726
	p->npts++;
slouken@524
   727
}
slouken@524
   728
slouken@524
   729
static void nsvg__moveTo(NSVGparser* p, float x, float y)
slouken@524
   730
{
slouken@524
   731
	if (p->npts > 0) {
slouken@524
   732
		p->pts[(p->npts-1)*2+0] = x;
slouken@524
   733
		p->pts[(p->npts-1)*2+1] = y;
slouken@524
   734
	} else {
slouken@524
   735
		nsvg__addPoint(p, x, y);
slouken@524
   736
	}
slouken@524
   737
}
slouken@524
   738
slouken@524
   739
static void nsvg__lineTo(NSVGparser* p, float x, float y)
slouken@524
   740
{
slouken@524
   741
	float px,py, dx,dy;
slouken@524
   742
	if (p->npts > 0) {
slouken@524
   743
		px = p->pts[(p->npts-1)*2+0];
slouken@524
   744
		py = p->pts[(p->npts-1)*2+1];
slouken@524
   745
		dx = x - px;
slouken@524
   746
		dy = y - py;
slouken@524
   747
		nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
slouken@524
   748
		nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
slouken@524
   749
		nsvg__addPoint(p, x, y);
slouken@524
   750
	}
slouken@524
   751
}
slouken@524
   752
slouken@524
   753
static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
slouken@524
   754
{
slouken@524
   755
	nsvg__addPoint(p, cpx1, cpy1);
slouken@524
   756
	nsvg__addPoint(p, cpx2, cpy2);
slouken@524
   757
	nsvg__addPoint(p, x, y);
slouken@524
   758
}
slouken@524
   759
slouken@524
   760
static NSVGattrib* nsvg__getAttr(NSVGparser* p)
slouken@524
   761
{
slouken@524
   762
	return &p->attr[p->attrHead];
slouken@524
   763
}
slouken@524
   764
slouken@524
   765
static void nsvg__pushAttr(NSVGparser* p)
slouken@524
   766
{
slouken@524
   767
	if (p->attrHead < NSVG_MAX_ATTR-1) {
slouken@524
   768
		p->attrHead++;
slouken@524
   769
		memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
slouken@524
   770
	}
slouken@524
   771
}
slouken@524
   772
slouken@524
   773
static void nsvg__popAttr(NSVGparser* p)
slouken@524
   774
{
slouken@524
   775
	if (p->attrHead > 0)
slouken@524
   776
		p->attrHead--;
slouken@524
   777
}
slouken@524
   778
slouken@524
   779
static float nsvg__actualOrigX(NSVGparser* p)
slouken@524
   780
{
slouken@524
   781
	return p->viewMinx;
slouken@524
   782
}
slouken@524
   783
slouken@524
   784
static float nsvg__actualOrigY(NSVGparser* p)
slouken@524
   785
{
slouken@524
   786
	return p->viewMiny;
slouken@524
   787
}
slouken@524
   788
slouken@524
   789
static float nsvg__actualWidth(NSVGparser* p)
slouken@524
   790
{
slouken@524
   791
	return p->viewWidth;
slouken@524
   792
}
slouken@524
   793
slouken@524
   794
static float nsvg__actualHeight(NSVGparser* p)
slouken@524
   795
{
slouken@524
   796
	return p->viewHeight;
slouken@524
   797
}
slouken@524
   798
slouken@524
   799
static float nsvg__actualLength(NSVGparser* p)
slouken@524
   800
{
slouken@524
   801
	float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
slouken@524
   802
	return sqrtf(w*w + h*h) / sqrtf(2.0f);
slouken@524
   803
}
slouken@524
   804
slouken@524
   805
static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
slouken@524
   806
{
slouken@524
   807
	NSVGattrib* attr = nsvg__getAttr(p);
slouken@524
   808
	switch (c.units) {
slouken@524
   809
		case NSVG_UNITS_USER:		return c.value;
slouken@524
   810
		case NSVG_UNITS_PX:			return c.value;
slouken@524
   811
		case NSVG_UNITS_PT:			return c.value / 72.0f * p->dpi;
slouken@524
   812
		case NSVG_UNITS_PC:			return c.value / 6.0f * p->dpi;
slouken@524
   813
		case NSVG_UNITS_MM:			return c.value / 25.4f * p->dpi;
slouken@524
   814
		case NSVG_UNITS_CM:			return c.value / 2.54f * p->dpi;
slouken@524
   815
		case NSVG_UNITS_IN:			return c.value * p->dpi;
slouken@524
   816
		case NSVG_UNITS_EM:			return c.value * attr->fontSize;
slouken@524
   817
		case NSVG_UNITS_EX:			return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
slouken@524
   818
		case NSVG_UNITS_PERCENT:	return orig + c.value / 100.0f * length;
slouken@524
   819
		default:					return c.value;
slouken@524
   820
	}
slouken@524
   821
	return c.value;
slouken@524
   822
}
slouken@524
   823
slouken@524
   824
static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
slouken@524
   825
{
slouken@524
   826
	NSVGgradientData* grad = p->gradients;
slouken@524
   827
	while (grad) {
slouken@524
   828
		if (strcmp(grad->id, id) == 0)
slouken@524
   829
			return grad;
slouken@524
   830
		grad = grad->next;
slouken@524
   831
	}
slouken@524
   832
	return NULL;
slouken@524
   833
}
slouken@524
   834
slouken@524
   835
static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
slouken@524
   836
{
slouken@524
   837
	NSVGattrib* attr = nsvg__getAttr(p);
slouken@524
   838
	NSVGgradientData* data = NULL;
slouken@524
   839
	NSVGgradientData* ref = NULL;
slouken@524
   840
	NSVGgradientStop* stops = NULL;
slouken@524
   841
	NSVGgradient* grad;
slouken@524
   842
	float ox, oy, sw, sh, sl;
slouken@524
   843
	int nstops = 0;
slouken@524
   844
slouken@524
   845
	data = nsvg__findGradientData(p, id);
slouken@524
   846
	if (data == NULL) return NULL;
slouken@524
   847
slouken@524
   848
	// TODO: use ref to fill in all unset values too.
slouken@524
   849
	ref = data;
slouken@524
   850
	while (ref != NULL) {
slouken@524
   851
		if (stops == NULL && ref->stops != NULL) {
slouken@524
   852
			stops = ref->stops;
slouken@524
   853
			nstops = ref->nstops;
slouken@524
   854
			break;
slouken@524
   855
		}
slouken@524
   856
		ref = nsvg__findGradientData(p, ref->ref);
slouken@524
   857
	}
slouken@524
   858
	if (stops == NULL) return NULL;
slouken@524
   859
slouken@524
   860
	grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
slouken@524
   861
	if (grad == NULL) return NULL;
slouken@524
   862
slouken@524
   863
	// The shape width and height.
slouken@524
   864
	if (data->units == NSVG_OBJECT_SPACE) {
slouken@524
   865
		ox = localBounds[0];
slouken@524
   866
		oy = localBounds[1];
slouken@524
   867
		sw = localBounds[2] - localBounds[0];
slouken@524
   868
		sh = localBounds[3] - localBounds[1];
slouken@524
   869
	} else {
slouken@524
   870
		ox = nsvg__actualOrigX(p);
slouken@524
   871
		oy = nsvg__actualOrigY(p);
slouken@524
   872
		sw = nsvg__actualWidth(p);
slouken@524
   873
		sh = nsvg__actualHeight(p);
slouken@524
   874
	}
slouken@524
   875
	sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);
slouken@524
   876
slouken@524
   877
	if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
slouken@524
   878
		float x1, y1, x2, y2, dx, dy;
slouken@524
   879
		x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
slouken@524
   880
		y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
slouken@524
   881
		x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
slouken@524
   882
		y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
slouken@524
   883
		// Calculate transform aligned to the line
slouken@524
   884
		dx = x2 - x1;
slouken@524
   885
		dy = y2 - y1;
slouken@524
   886
		grad->xform[0] = dy; grad->xform[1] = -dx;
slouken@524
   887
		grad->xform[2] = dx; grad->xform[3] = dy;
slouken@524
   888
		grad->xform[4] = x1; grad->xform[5] = y1;
slouken@524
   889
	} else {
slouken@524
   890
		float cx, cy, fx, fy, r;
slouken@524
   891
		cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
slouken@524
   892
		cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
slouken@524
   893
		fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
slouken@524
   894
		fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
slouken@524
   895
		r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
slouken@524
   896
		// Calculate transform aligned to the circle
slouken@524
   897
		grad->xform[0] = r; grad->xform[1] = 0;
slouken@524
   898
		grad->xform[2] = 0; grad->xform[3] = r;
slouken@524
   899
		grad->xform[4] = cx; grad->xform[5] = cy;
slouken@524
   900
		grad->fx = fx / r;
slouken@524
   901
		grad->fy = fy / r;
slouken@524
   902
	}
slouken@524
   903
slouken@524
   904
	nsvg__xformMultiply(grad->xform, data->xform);
slouken@524
   905
	nsvg__xformMultiply(grad->xform, attr->xform);
slouken@524
   906
slouken@524
   907
	grad->spread = data->spread;
slouken@524
   908
	memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
slouken@524
   909
	grad->nstops = nstops;
slouken@524
   910
slouken@524
   911
	*paintType = data->type;
slouken@524
   912
slouken@524
   913
	return grad;
slouken@524
   914
}
slouken@524
   915
slouken@524
   916
static float nsvg__getAverageScale(float* t)
slouken@524
   917
{
slouken@524
   918
	float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
slouken@524
   919
	float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
slouken@524
   920
	return (sx + sy) * 0.5f;
slouken@524
   921
}
slouken@524
   922
slouken@524
   923
static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
slouken@524
   924
{
slouken@524
   925
	NSVGpath* path;
slouken@524
   926
	float curve[4*2], curveBounds[4];
slouken@524
   927
	int i, first = 1;
slouken@524
   928
	for (path = shape->paths; path != NULL; path = path->next) {
slouken@524
   929
		nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
slouken@524
   930
		for (i = 0; i < path->npts-1; i += 3) {
slouken@524
   931
			nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
slouken@524
   932
			nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
slouken@524
   933
			nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
slouken@524
   934
			nsvg__curveBounds(curveBounds, curve);
slouken@524
   935
			if (first) {
slouken@524
   936
				bounds[0] = curveBounds[0];
slouken@524
   937
				bounds[1] = curveBounds[1];
slouken@524
   938
				bounds[2] = curveBounds[2];
slouken@524
   939
				bounds[3] = curveBounds[3];
slouken@524
   940
				first = 0;
slouken@524
   941
			} else {
slouken@524
   942
				bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
slouken@524
   943
				bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
slouken@524
   944
				bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
slouken@524
   945
				bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
slouken@524
   946
			}
slouken@524
   947
			curve[0] = curve[6];
slouken@524
   948
			curve[1] = curve[7];
slouken@524
   949
		}
slouken@524
   950
	}
slouken@524
   951
}
slouken@524
   952
slouken@524
   953
static void nsvg__addShape(NSVGparser* p)
slouken@524
   954
{
slouken@524
   955
	NSVGattrib* attr = nsvg__getAttr(p);
slouken@524
   956
	float scale = 1.0f;
slouken@524
   957
	NSVGshape* shape;
slouken@524
   958
	NSVGpath* path;
slouken@524
   959
	int i;
slouken@524
   960
slouken@524
   961
	if (p->plist == NULL)
slouken@524
   962
		return;
slouken@524
   963
slouken@524
   964
	shape = (NSVGshape*)malloc(sizeof(NSVGshape));
slouken@524
   965
	if (shape == NULL) goto error;
slouken@524
   966
	memset(shape, 0, sizeof(NSVGshape));
slouken@524
   967
slouken@524
   968
	memcpy(shape->id, attr->id, sizeof shape->id);
slouken@524
   969
	scale = nsvg__getAverageScale(attr->xform);
slouken@524
   970
	shape->strokeWidth = attr->strokeWidth * scale;
slouken@524
   971
	shape->strokeDashOffset = attr->strokeDashOffset * scale;
slouken@524
   972
	shape->strokeDashCount = (char)attr->strokeDashCount;
slouken@524
   973
	for (i = 0; i < attr->strokeDashCount; i++)
slouken@524
   974
		shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
slouken@524
   975
	shape->strokeLineJoin = attr->strokeLineJoin;
slouken@524
   976
	shape->strokeLineCap = attr->strokeLineCap;
slouken@524
   977
	shape->miterLimit = attr->miterLimit;
slouken@524
   978
	shape->fillRule = attr->fillRule;
slouken@524
   979
	shape->opacity = attr->opacity;
slouken@524
   980
slouken@524
   981
	shape->paths = p->plist;
slouken@524
   982
	p->plist = NULL;
slouken@524
   983
slouken@524
   984
	// Calculate shape bounds
slouken@524
   985
	shape->bounds[0] = shape->paths->bounds[0];
slouken@524
   986
	shape->bounds[1] = shape->paths->bounds[1];
slouken@524
   987
	shape->bounds[2] = shape->paths->bounds[2];
slouken@524
   988
	shape->bounds[3] = shape->paths->bounds[3];
slouken@524
   989
	for (path = shape->paths->next; path != NULL; path = path->next) {
slouken@524
   990
		shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
slouken@524
   991
		shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
slouken@524
   992
		shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
slouken@524
   993
		shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
slouken@524
   994
	}
slouken@524
   995
slouken@524
   996
	// Set fill
slouken@524
   997
	if (attr->hasFill == 0) {
slouken@524
   998
		shape->fill.type = NSVG_PAINT_NONE;
slouken@524
   999
	} else if (attr->hasFill == 1) {
slouken@524
  1000
		shape->fill.type = NSVG_PAINT_COLOR;
slouken@524
  1001
		shape->fill.color = attr->fillColor;
slouken@524
  1002
		shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
slouken@524
  1003
	} else if (attr->hasFill == 2) {
slouken@524
  1004
		float inv[6], localBounds[4];
slouken@524
  1005
		nsvg__xformInverse(inv, attr->xform);
slouken@524
  1006
		nsvg__getLocalBounds(localBounds, shape, inv);
slouken@524
  1007
		shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
slouken@524
  1008
		if (shape->fill.gradient == NULL) {
slouken@524
  1009
			shape->fill.type = NSVG_PAINT_NONE;
slouken@524
  1010
		}
slouken@524
  1011
	}
slouken@524
  1012
slouken@524
  1013
	// Set stroke
slouken@524
  1014
	if (attr->hasStroke == 0) {
slouken@524
  1015
		shape->stroke.type = NSVG_PAINT_NONE;
slouken@524
  1016
	} else if (attr->hasStroke == 1) {
slouken@524
  1017
		shape->stroke.type = NSVG_PAINT_COLOR;
slouken@524
  1018
		shape->stroke.color = attr->strokeColor;
slouken@524
  1019
		shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
slouken@524
  1020
	} else if (attr->hasStroke == 2) {
slouken@524
  1021
		float inv[6], localBounds[4];
slouken@524
  1022
		nsvg__xformInverse(inv, attr->xform);
slouken@524
  1023
		nsvg__getLocalBounds(localBounds, shape, inv);
slouken@524
  1024
		shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
slouken@524
  1025
		if (shape->stroke.gradient == NULL)
slouken@524
  1026
			shape->stroke.type = NSVG_PAINT_NONE;
slouken@524
  1027
	}
slouken@524
  1028
slouken@524
  1029
	// Set flags
slouken@524
  1030
	shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
slouken@524
  1031
slouken@524
  1032
	// Add to tail
slouken@524
  1033
	if (p->image->shapes == NULL)
slouken@524
  1034
		p->image->shapes = shape;
slouken@524
  1035
	else
slouken@524
  1036
		p->shapesTail->next = shape;
slouken@524
  1037
	p->shapesTail = shape;
slouken@524
  1038
slouken@524
  1039
	return;
slouken@524
  1040
slouken@524
  1041
error:
slouken@524
  1042
	if (shape) free(shape);
slouken@524
  1043
}
slouken@524
  1044
slouken@524
  1045
static void nsvg__addPath(NSVGparser* p, char closed)
slouken@524
  1046
{
slouken@524
  1047
	NSVGattrib* attr = nsvg__getAttr(p);
slouken@524
  1048
	NSVGpath* path = NULL;
slouken@524
  1049
	float bounds[4];
slouken@524
  1050
	float* curve;
slouken@524
  1051
	int i;
slouken@524
  1052
slouken@524
  1053
	if (p->npts < 4)
slouken@524
  1054
		return;
slouken@524
  1055
slouken@524
  1056
	if (closed)
slouken@524
  1057
		nsvg__lineTo(p, p->pts[0], p->pts[1]);
slouken@524
  1058
slouken@524
  1059
	path = (NSVGpath*)malloc(sizeof(NSVGpath));
slouken@524
  1060
	if (path == NULL) goto error;
slouken@524
  1061
	memset(path, 0, sizeof(NSVGpath));
slouken@524
  1062
slouken@524
  1063
	path->pts = (float*)malloc(p->npts*2*sizeof(float));
slouken@524
  1064
	if (path->pts == NULL) goto error;
slouken@524
  1065
	path->closed = closed;
slouken@524
  1066
	path->npts = p->npts;
slouken@524
  1067
slouken@524
  1068
	// Transform path.
slouken@524
  1069
	for (i = 0; i < p->npts; ++i)
slouken@524
  1070
		nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);
slouken@524
  1071
slouken@524
  1072
	// Find bounds
slouken@524
  1073
	for (i = 0; i < path->npts-1; i += 3) {
slouken@524
  1074
		curve = &path->pts[i*2];
slouken@524
  1075
		nsvg__curveBounds(bounds, curve);
slouken@524
  1076
		if (i == 0) {
slouken@524
  1077
			path->bounds[0] = bounds[0];
slouken@524
  1078
			path->bounds[1] = bounds[1];
slouken@524
  1079
			path->bounds[2] = bounds[2];
slouken@524
  1080
			path->bounds[3] = bounds[3];
slouken@524
  1081
		} else {
slouken@524
  1082
			path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
slouken@524
  1083
			path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
slouken@524
  1084
			path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
slouken@524
  1085
			path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
slouken@524
  1086
		}
slouken@524
  1087
	}
slouken@524
  1088
slouken@524
  1089
	path->next = p->plist;
slouken@524
  1090
	p->plist = path;
slouken@524
  1091
slouken@524
  1092
	return;
slouken@524
  1093
slouken@524
  1094
error:
slouken@524
  1095
	if (path != NULL) {
slouken@524
  1096
		if (path->pts != NULL) free(path->pts);
slouken@524
  1097
		free(path);
slouken@524
  1098
	}
slouken@524
  1099
}
slouken@524
  1100
slouken@524
  1101
// We roll our own string to float because the std library one uses locale and messes things up.
slouken@524
  1102
static double nsvg__atof(const char* s)
slouken@524
  1103
{
slouken@524
  1104
	char* cur = (char*)s;
slouken@524
  1105
	char* end = NULL;
slouken@524
  1106
	double res = 0.0, sign = 1.0;
slouken@524
  1107
	long long intPart = 0, fracPart = 0;
slouken@524
  1108
	char hasIntPart = 0, hasFracPart = 0;
slouken@524
  1109
slouken@524
  1110
	// Parse optional sign
slouken@524
  1111
	if (*cur == '+') {
slouken@524
  1112
		cur++;
slouken@524
  1113
	} else if (*cur == '-') {
slouken@524
  1114
		sign = -1;
slouken@524
  1115
		cur++;
slouken@524
  1116
	}
slouken@524
  1117
slouken@524
  1118
	// Parse integer part
slouken@524
  1119
	if (nsvg__isdigit(*cur)) {
slouken@524
  1120
		// Parse digit sequence
slouken@527
  1121
		intPart = strtoll(cur, &end, 10);
slouken@524
  1122
		if (cur != end) {
slouken@524
  1123
			res = (double)intPart;
slouken@524
  1124
			hasIntPart = 1;
slouken@524
  1125
			cur = end;
slouken@524
  1126
		}
slouken@524
  1127
	}
slouken@524
  1128
slouken@524
  1129
	// Parse fractional part.
slouken@524
  1130
	if (*cur == '.') {
slouken@524
  1131
		cur++; // Skip '.'
slouken@524
  1132
		if (nsvg__isdigit(*cur)) {
slouken@524
  1133
			// Parse digit sequence
slouken@524
  1134
			fracPart = strtoll(cur, &end, 10);
slouken@524
  1135
			if (cur != end) {
slouken@524
  1136
				res += (double)fracPart / pow(10.0, (double)(end - cur));
slouken@524
  1137
				hasFracPart = 1;
slouken@524
  1138
				cur = end;
slouken@524
  1139
			}
slouken@524
  1140
		}
slouken@524
  1141
	}
slouken@524
  1142
slouken@524
  1143
	// A valid number should have integer or fractional part.
slouken@524
  1144
	if (!hasIntPart && !hasFracPart)
slouken@524
  1145
		return 0.0;
slouken@524
  1146
slouken@524
  1147
	// Parse optional exponent
slouken@524
  1148
	if (*cur == 'e' || *cur == 'E') {
slouken@524
  1149
		int expPart = 0;
slouken@524
  1150
		cur++; // skip 'E'
slouken@551
  1151
		expPart = (int)strtol(cur, &end, 10); // Parse digit sequence with sign
slouken@524
  1152
		if (cur != end) {
slouken@524
  1153
			res *= pow(10.0, (double)expPart);
slouken@524
  1154
		}
slouken@524
  1155
	}
slouken@524
  1156
slouken@524
  1157
	return res * sign;
slouken@524
  1158
}
slouken@524
  1159
slouken@524
  1160
slouken@524
  1161
static const char* nsvg__parseNumber(const char* s, char* it, const int size)
slouken@524
  1162
{
slouken@524
  1163
	const int last = size-1;
slouken@524
  1164
	int i = 0;
slouken@524
  1165
slouken@524
  1166
	// sign
slouken@524
  1167
	if (*s == '-' || *s == '+') {
slouken@524
  1168
		if (i < last) it[i++] = *s;
slouken@524
  1169
		s++;
slouken@524
  1170
	}
slouken@524
  1171
	// integer part
slouken@524
  1172
	while (*s && nsvg__isdigit(*s)) {
slouken@524
  1173
		if (i < last) it[i++] = *s;
slouken@524
  1174
		s++;
slouken@524
  1175
	}
slouken@524
  1176
	if (*s == '.') {
slouken@524
  1177
		// decimal point
slouken@524
  1178
		if (i < last) it[i++] = *s;
slouken@524
  1179
		s++;
slouken@524
  1180
		// fraction part
slouken@524
  1181
		while (*s && nsvg__isdigit(*s)) {
slouken@524
  1182
			if (i < last) it[i++] = *s;
slouken@524
  1183
			s++;
slouken@524
  1184
		}
slouken@524
  1185
	}
slouken@524
  1186
	// exponent
slouken@524
  1187
	if (*s == 'e' || *s == 'E') {
slouken@524
  1188
		if (i < last) it[i++] = *s;
slouken@524
  1189
		s++;
slouken@524
  1190
		if (*s == '-' || *s == '+') {
slouken@524
  1191
			if (i < last) it[i++] = *s;
slouken@524
  1192
			s++;
slouken@524
  1193
		}
slouken@524
  1194
		while (*s && nsvg__isdigit(*s)) {
slouken@524
  1195
			if (i < last) it[i++] = *s;
slouken@524
  1196
			s++;
slouken@524
  1197
		}
slouken@524
  1198
	}
slouken@524
  1199
	it[i] = '\0';
slouken@524
  1200
slouken@524
  1201
	return s;
slouken@524
  1202
}
slouken@524
  1203
slouken@524
  1204
static const char* nsvg__getNextPathItem(const char* s, char* it)
slouken@524
  1205
{
slouken@524
  1206
	it[0] = '\0';
slouken@524
  1207
	// Skip white spaces and commas
slouken@524
  1208
	while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
slouken@524
  1209
	if (!*s) return s;
slouken@524
  1210
	if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
slouken@524
  1211
		s = nsvg__parseNumber(s, it, 64);
slouken@524
  1212
	} else {
slouken@524
  1213
		// Parse command
slouken@524
  1214
		it[0] = *s++;
slouken@524
  1215
		it[1] = '\0';
slouken@524
  1216
		return s;
slouken@524
  1217
	}
slouken@524
  1218
slouken@524
  1219
	return s;
slouken@524
  1220
}
slouken@524
  1221
slouken@524
  1222
static unsigned int nsvg__parseColorHex(const char* str)
slouken@524
  1223
{
slouken@524
  1224
	unsigned int c = 0, r = 0, g = 0, b = 0;
slouken@524
  1225
	int n = 0;
slouken@524
  1226
	str++; // skip #
slouken@524
  1227
	// Calculate number of characters.
slouken@524
  1228
	while(str[n] && !nsvg__isspace(str[n]))
slouken@524
  1229
		n++;
slouken@524
  1230
	if (n == 6) {
slouken@524
  1231
		sscanf(str, "%x", &c);
slouken@524
  1232
	} else if (n == 3) {
slouken@524
  1233
		sscanf(str, "%x", &c);
slouken@524
  1234
		c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
slouken@524
  1235
		c |= c<<4;
slouken@524
  1236
	}
slouken@524
  1237
	r = (c >> 16) & 0xff;
slouken@524
  1238
	g = (c >> 8) & 0xff;
slouken@524
  1239
	b = c & 0xff;
slouken@524
  1240
	return NSVG_RGB(r,g,b);
slouken@524
  1241
}
slouken@524
  1242
slouken@524
  1243
static unsigned int nsvg__parseColorRGB(const char* str)
slouken@524
  1244
{
slouken@524
  1245
	int r = -1, g = -1, b = -1;
slouken@524
  1246
	char s1[32]="", s2[32]="";
slouken@524
  1247
	sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
slouken@524
  1248
	if (strchr(s1, '%')) {
slouken@524
  1249
		return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
slouken@524
  1250
	} else {
slouken@524
  1251
		return NSVG_RGB(r,g,b);
slouken@524
  1252
	}
slouken@524
  1253
}
slouken@524
  1254
slouken@524
  1255
typedef struct NSVGNamedColor {
slouken@524
  1256
	const char* name;
slouken@524
  1257
	unsigned int color;
slouken@524
  1258
} NSVGNamedColor;
slouken@524
  1259
slouken@524
  1260
NSVGNamedColor nsvg__colors[] = {
slouken@524
  1261
slouken@524
  1262
	{ "red", NSVG_RGB(255, 0, 0) },
slouken@524
  1263
	{ "green", NSVG_RGB( 0, 128, 0) },
slouken@524
  1264
	{ "blue", NSVG_RGB( 0, 0, 255) },
slouken@524
  1265
	{ "yellow", NSVG_RGB(255, 255, 0) },
slouken@524
  1266
	{ "cyan", NSVG_RGB( 0, 255, 255) },
slouken@524
  1267
	{ "magenta", NSVG_RGB(255, 0, 255) },
slouken@524
  1268
	{ "black", NSVG_RGB( 0, 0, 0) },
slouken@524
  1269
	{ "grey", NSVG_RGB(128, 128, 128) },
slouken@524
  1270
	{ "gray", NSVG_RGB(128, 128, 128) },
slouken@524
  1271
	{ "white", NSVG_RGB(255, 255, 255) },
slouken@524
  1272
slouken@524
  1273
#ifdef NANOSVG_ALL_COLOR_KEYWORDS
slouken@524
  1274
	{ "aliceblue", NSVG_RGB(240, 248, 255) },
slouken@524
  1275
	{ "antiquewhite", NSVG_RGB(250, 235, 215) },
slouken@524
  1276
	{ "aqua", NSVG_RGB( 0, 255, 255) },
slouken@524
  1277
	{ "aquamarine", NSVG_RGB(127, 255, 212) },
slouken@524
  1278
	{ "azure", NSVG_RGB(240, 255, 255) },
slouken@524
  1279
	{ "beige", NSVG_RGB(245, 245, 220) },
slouken@524
  1280
	{ "bisque", NSVG_RGB(255, 228, 196) },
slouken@524
  1281
	{ "blanchedalmond", NSVG_RGB(255, 235, 205) },
slouken@524
  1282
	{ "blueviolet", NSVG_RGB(138, 43, 226) },
slouken@524
  1283
	{ "brown", NSVG_RGB(165, 42, 42) },
slouken@524
  1284
	{ "burlywood", NSVG_RGB(222, 184, 135) },
slouken@524
  1285
	{ "cadetblue", NSVG_RGB( 95, 158, 160) },
slouken@524
  1286
	{ "chartreuse", NSVG_RGB(127, 255, 0) },
slouken@524
  1287
	{ "chocolate", NSVG_RGB(210, 105, 30) },
slouken@524
  1288
	{ "coral", NSVG_RGB(255, 127, 80) },
slouken@524
  1289
	{ "cornflowerblue", NSVG_RGB(100, 149, 237) },
slouken@524
  1290
	{ "cornsilk", NSVG_RGB(255, 248, 220) },
slouken@524
  1291
	{ "crimson", NSVG_RGB(220, 20, 60) },
slouken@524
  1292
	{ "darkblue", NSVG_RGB( 0, 0, 139) },
slouken@524
  1293
	{ "darkcyan", NSVG_RGB( 0, 139, 139) },
slouken@524
  1294
	{ "darkgoldenrod", NSVG_RGB(184, 134, 11) },
slouken@524
  1295
	{ "darkgray", NSVG_RGB(169, 169, 169) },
slouken@524
  1296
	{ "darkgreen", NSVG_RGB( 0, 100, 0) },
slouken@524
  1297
	{ "darkgrey", NSVG_RGB(169, 169, 169) },
slouken@524
  1298
	{ "darkkhaki", NSVG_RGB(189, 183, 107) },
slouken@524
  1299
	{ "darkmagenta", NSVG_RGB(139, 0, 139) },
slouken@524
  1300
	{ "darkolivegreen", NSVG_RGB( 85, 107, 47) },
slouken@524
  1301
	{ "darkorange", NSVG_RGB(255, 140, 0) },
slouken@524
  1302
	{ "darkorchid", NSVG_RGB(153, 50, 204) },
slouken@524
  1303
	{ "darkred", NSVG_RGB(139, 0, 0) },
slouken@524
  1304
	{ "darksalmon", NSVG_RGB(233, 150, 122) },
slouken@524
  1305
	{ "darkseagreen", NSVG_RGB(143, 188, 143) },
slouken@524
  1306
	{ "darkslateblue", NSVG_RGB( 72, 61, 139) },
slouken@524
  1307
	{ "darkslategray", NSVG_RGB( 47, 79, 79) },
slouken@524
  1308
	{ "darkslategrey", NSVG_RGB( 47, 79, 79) },
slouken@524
  1309
	{ "darkturquoise", NSVG_RGB( 0, 206, 209) },
slouken@524
  1310
	{ "darkviolet", NSVG_RGB(148, 0, 211) },
slouken@524
  1311
	{ "deeppink", NSVG_RGB(255, 20, 147) },
slouken@524
  1312
	{ "deepskyblue", NSVG_RGB( 0, 191, 255) },
slouken@524
  1313
	{ "dimgray", NSVG_RGB(105, 105, 105) },
slouken@524
  1314
	{ "dimgrey", NSVG_RGB(105, 105, 105) },
slouken@524
  1315
	{ "dodgerblue", NSVG_RGB( 30, 144, 255) },
slouken@524
  1316
	{ "firebrick", NSVG_RGB(178, 34, 34) },
slouken@524
  1317
	{ "floralwhite", NSVG_RGB(255, 250, 240) },
slouken@524
  1318
	{ "forestgreen", NSVG_RGB( 34, 139, 34) },
slouken@524
  1319
	{ "fuchsia", NSVG_RGB(255, 0, 255) },
slouken@524
  1320
	{ "gainsboro", NSVG_RGB(220, 220, 220) },
slouken@524
  1321
	{ "ghostwhite", NSVG_RGB(248, 248, 255) },
slouken@524
  1322
	{ "gold", NSVG_RGB(255, 215, 0) },
slouken@524
  1323
	{ "goldenrod", NSVG_RGB(218, 165, 32) },
slouken@524
  1324
	{ "greenyellow", NSVG_RGB(173, 255, 47) },
slouken@524
  1325
	{ "honeydew", NSVG_RGB(240, 255, 240) },
slouken@524
  1326
	{ "hotpink", NSVG_RGB(255, 105, 180) },
slouken@524
  1327
	{ "indianred", NSVG_RGB(205, 92, 92) },
slouken@524
  1328
	{ "indigo", NSVG_RGB( 75, 0, 130) },
slouken@524
  1329
	{ "ivory", NSVG_RGB(255, 255, 240) },
slouken@524
  1330
	{ "khaki", NSVG_RGB(240, 230, 140) },
slouken@524
  1331
	{ "lavender", NSVG_RGB(230, 230, 250) },
slouken@524
  1332
	{ "lavenderblush", NSVG_RGB(255, 240, 245) },
slouken@524
  1333
	{ "lawngreen", NSVG_RGB(124, 252, 0) },
slouken@524
  1334
	{ "lemonchiffon", NSVG_RGB(255, 250, 205) },
slouken@524
  1335
	{ "lightblue", NSVG_RGB(173, 216, 230) },
slouken@524
  1336
	{ "lightcoral", NSVG_RGB(240, 128, 128) },
slouken@524
  1337
	{ "lightcyan", NSVG_RGB(224, 255, 255) },
slouken@524
  1338
	{ "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
slouken@524
  1339
	{ "lightgray", NSVG_RGB(211, 211, 211) },
slouken@524
  1340
	{ "lightgreen", NSVG_RGB(144, 238, 144) },
slouken@524
  1341
	{ "lightgrey", NSVG_RGB(211, 211, 211) },
slouken@524
  1342
	{ "lightpink", NSVG_RGB(255, 182, 193) },
slouken@524
  1343
	{ "lightsalmon", NSVG_RGB(255, 160, 122) },
slouken@524
  1344
	{ "lightseagreen", NSVG_RGB( 32, 178, 170) },
slouken@524
  1345
	{ "lightskyblue", NSVG_RGB(135, 206, 250) },
slouken@524
  1346
	{ "lightslategray", NSVG_RGB(119, 136, 153) },
slouken@524
  1347
	{ "lightslategrey", NSVG_RGB(119, 136, 153) },
slouken@524
  1348
	{ "lightsteelblue", NSVG_RGB(176, 196, 222) },
slouken@524
  1349
	{ "lightyellow", NSVG_RGB(255, 255, 224) },
slouken@524
  1350
	{ "lime", NSVG_RGB( 0, 255, 0) },
slouken@524
  1351
	{ "limegreen", NSVG_RGB( 50, 205, 50) },
slouken@524
  1352
	{ "linen", NSVG_RGB(250, 240, 230) },
slouken@524
  1353
	{ "maroon", NSVG_RGB(128, 0, 0) },
slouken@524
  1354
	{ "mediumaquamarine", NSVG_RGB(102, 205, 170) },
slouken@524
  1355
	{ "mediumblue", NSVG_RGB( 0, 0, 205) },
slouken@524
  1356
	{ "mediumorchid", NSVG_RGB(186, 85, 211) },
slouken@524
  1357
	{ "mediumpurple", NSVG_RGB(147, 112, 219) },
slouken@524
  1358
	{ "mediumseagreen", NSVG_RGB( 60, 179, 113) },
slouken@524
  1359
	{ "mediumslateblue", NSVG_RGB(123, 104, 238) },
slouken@524
  1360
	{ "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
slouken@524
  1361
	{ "mediumturquoise", NSVG_RGB( 72, 209, 204) },
slouken@524
  1362
	{ "mediumvioletred", NSVG_RGB(199, 21, 133) },
slouken@524
  1363
	{ "midnightblue", NSVG_RGB( 25, 25, 112) },
slouken@524
  1364
	{ "mintcream", NSVG_RGB(245, 255, 250) },
slouken@524
  1365
	{ "mistyrose", NSVG_RGB(255, 228, 225) },
slouken@524
  1366
	{ "moccasin", NSVG_RGB(255, 228, 181) },
slouken@524
  1367
	{ "navajowhite", NSVG_RGB(255, 222, 173) },
slouken@524
  1368
	{ "navy", NSVG_RGB( 0, 0, 128) },
slouken@524
  1369
	{ "oldlace", NSVG_RGB(253, 245, 230) },
slouken@524
  1370
	{ "olive", NSVG_RGB(128, 128, 0) },
slouken@524
  1371
	{ "olivedrab", NSVG_RGB(107, 142, 35) },
slouken@524
  1372
	{ "orange", NSVG_RGB(255, 165, 0) },
slouken@524
  1373
	{ "orangered", NSVG_RGB(255, 69, 0) },
slouken@524
  1374
	{ "orchid", NSVG_RGB(218, 112, 214) },
slouken@524
  1375
	{ "palegoldenrod", NSVG_RGB(238, 232, 170) },
slouken@524
  1376
	{ "palegreen", NSVG_RGB(152, 251, 152) },
slouken@524
  1377
	{ "paleturquoise", NSVG_RGB(175, 238, 238) },
slouken@524
  1378
	{ "palevioletred", NSVG_RGB(219, 112, 147) },
slouken@524
  1379
	{ "papayawhip", NSVG_RGB(255, 239, 213) },
slouken@524
  1380
	{ "peachpuff", NSVG_RGB(255, 218, 185) },
slouken@524
  1381
	{ "peru", NSVG_RGB(205, 133, 63) },
slouken@524
  1382
	{ "pink", NSVG_RGB(255, 192, 203) },
slouken@524
  1383
	{ "plum", NSVG_RGB(221, 160, 221) },
slouken@524
  1384
	{ "powderblue", NSVG_RGB(176, 224, 230) },
slouken@524
  1385
	{ "purple", NSVG_RGB(128, 0, 128) },
slouken@524
  1386
	{ "rosybrown", NSVG_RGB(188, 143, 143) },
slouken@524
  1387
	{ "royalblue", NSVG_RGB( 65, 105, 225) },
slouken@524
  1388
	{ "saddlebrown", NSVG_RGB(139, 69, 19) },
slouken@524
  1389
	{ "salmon", NSVG_RGB(250, 128, 114) },
slouken@524
  1390
	{ "sandybrown", NSVG_RGB(244, 164, 96) },
slouken@524
  1391
	{ "seagreen", NSVG_RGB( 46, 139, 87) },
slouken@524
  1392
	{ "seashell", NSVG_RGB(255, 245, 238) },
slouken@524
  1393
	{ "sienna", NSVG_RGB(160, 82, 45) },
slouken@524
  1394
	{ "silver", NSVG_RGB(192, 192, 192) },
slouken@524
  1395
	{ "skyblue", NSVG_RGB(135, 206, 235) },
slouken@524
  1396
	{ "slateblue", NSVG_RGB(106, 90, 205) },
slouken@524
  1397
	{ "slategray", NSVG_RGB(112, 128, 144) },
slouken@524
  1398
	{ "slategrey", NSVG_RGB(112, 128, 144) },
slouken@524
  1399
	{ "snow", NSVG_RGB(255, 250, 250) },
slouken@524
  1400
	{ "springgreen", NSVG_RGB( 0, 255, 127) },
slouken@524
  1401
	{ "steelblue", NSVG_RGB( 70, 130, 180) },
slouken@524
  1402
	{ "tan", NSVG_RGB(210, 180, 140) },
slouken@524
  1403
	{ "teal", NSVG_RGB( 0, 128, 128) },
slouken@524
  1404
	{ "thistle", NSVG_RGB(216, 191, 216) },
slouken@524
  1405
	{ "tomato", NSVG_RGB(255, 99, 71) },
slouken@524
  1406
	{ "turquoise", NSVG_RGB( 64, 224, 208) },
slouken@524
  1407
	{ "violet", NSVG_RGB(238, 130, 238) },
slouken@524
  1408
	{ "wheat", NSVG_RGB(245, 222, 179) },
slouken@524
  1409
	{ "whitesmoke", NSVG_RGB(245, 245, 245) },
slouken@524
  1410
	{ "yellowgreen", NSVG_RGB(154, 205, 50) },
slouken@524
  1411
#endif
slouken@524
  1412
};
slouken@524
  1413
slouken@524
  1414
static unsigned int nsvg__parseColorName(const char* str)
slouken@524
  1415
{
slouken@524
  1416
	int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
slouken@524
  1417
slouken@524
  1418
	for (i = 0; i < ncolors; i++) {
slouken@524
  1419
		if (strcmp(nsvg__colors[i].name, str) == 0) {
slouken@524
  1420
			return nsvg__colors[i].color;
slouken@524
  1421
		}
slouken@524
  1422
	}
slouken@524
  1423
slouken@524
  1424
	return NSVG_RGB(128, 128, 128);
slouken@524
  1425
}
slouken@524
  1426
slouken@524
  1427
static unsigned int nsvg__parseColor(const char* str)
slouken@524
  1428
{
slouken@524
  1429
	size_t len = 0;
slouken@524
  1430
	while(*str == ' ') ++str;
slouken@524
  1431
	len = strlen(str);
slouken@524
  1432
	if (len >= 1 && *str == '#')
slouken@524
  1433
		return nsvg__parseColorHex(str);
slouken@524
  1434
	else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
slouken@524
  1435
		return nsvg__parseColorRGB(str);
slouken@524
  1436
	return nsvg__parseColorName(str);
slouken@524
  1437
}
slouken@524
  1438
slouken@524
  1439
static float nsvg__parseOpacity(const char* str)
slouken@524
  1440
{
slouken@524
  1441
	float val = 0;
slouken@524
  1442
	sscanf(str, "%f", &val);
slouken@524
  1443
	if (val < 0.0f) val = 0.0f;
slouken@524
  1444
	if (val > 1.0f) val = 1.0f;
slouken@524
  1445
	return val;
slouken@524
  1446
}
slouken@524
  1447
slouken@524
  1448
static float nsvg__parseMiterLimit(const char* str)
slouken@524
  1449
{
slouken@524
  1450
	float val = 0;
slouken@524
  1451
	sscanf(str, "%f", &val);
slouken@524
  1452
	if (val < 0.0f) val = 0.0f;
slouken@524
  1453
	return val;
slouken@524
  1454
}
slouken@524
  1455
slouken@524
  1456
static int nsvg__parseUnits(const char* units)
slouken@524
  1457
{
slouken@524
  1458
	if (units[0] == 'p' && units[1] == 'x')
slouken@524
  1459
		return NSVG_UNITS_PX;
slouken@524
  1460
	else if (units[0] == 'p' && units[1] == 't')
slouken@524
  1461
		return NSVG_UNITS_PT;
slouken@524
  1462
	else if (units[0] == 'p' && units[1] == 'c')
slouken@524
  1463
		return NSVG_UNITS_PC;
slouken@524
  1464
	else if (units[0] == 'm' && units[1] == 'm')
slouken@524
  1465
		return NSVG_UNITS_MM;
slouken@524
  1466
	else if (units[0] == 'c' && units[1] == 'm')
slouken@524
  1467
		return NSVG_UNITS_CM;
slouken@524
  1468
	else if (units[0] == 'i' && units[1] == 'n')
slouken@524
  1469
		return NSVG_UNITS_IN;
slouken@524
  1470
	else if (units[0] == '%')
slouken@524
  1471
		return NSVG_UNITS_PERCENT;
slouken@524
  1472
	else if (units[0] == 'e' && units[1] == 'm')
slouken@524
  1473
		return NSVG_UNITS_EM;
slouken@524
  1474
	else if (units[0] == 'e' && units[1] == 'x')
slouken@524
  1475
		return NSVG_UNITS_EX;
slouken@524
  1476
	return NSVG_UNITS_USER;
slouken@524
  1477
}
slouken@524
  1478
slouken@524
  1479
static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
slouken@524
  1480
{
slouken@524
  1481
	NSVGcoordinate coord = {0, NSVG_UNITS_USER};
slouken@524
  1482
	char units[32]="";
slouken@524
  1483
	sscanf(str, "%f%31s", &coord.value, units);
slouken@524
  1484
	coord.units = nsvg__parseUnits(units);
slouken@524
  1485
	return coord;
slouken@524
  1486
}
slouken@524
  1487
slouken@524
  1488
static NSVGcoordinate nsvg__coord(float v, int units)
slouken@524
  1489
{
sezeroz@583
  1490
	NSVGcoordinate coord ;
sezeroz@583
  1491
	coord.value = v;
sezeroz@583
  1492
	coord.units = units;
slouken@524
  1493
	return coord;
slouken@524
  1494
}
slouken@524
  1495
slouken@524
  1496
static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
slouken@524
  1497
{
slouken@524
  1498
	NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
slouken@524
  1499
	return nsvg__convertToPixels(p, coord, orig, length);
slouken@524
  1500
}
slouken@524
  1501
slouken@524
  1502
static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
slouken@524
  1503
{
slouken@524
  1504
	const char* end;
slouken@524
  1505
	const char* ptr;
slouken@524
  1506
	char it[64];
slouken@524
  1507
slouken@524
  1508
	*na = 0;
slouken@524
  1509
	ptr = str;
slouken@524
  1510
	while (*ptr && *ptr != '(') ++ptr;
slouken@524
  1511
	if (*ptr == 0)
slouken@524
  1512
		return 1;
slouken@524
  1513
	end = ptr;
slouken@524
  1514
	while (*end && *end != ')') ++end;
slouken@524
  1515
	if (*end == 0)
slouken@524
  1516
		return 1;
slouken@524
  1517
slouken@524
  1518
	while (ptr < end) {
slouken@524
  1519
		if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
slouken@524
  1520
			if (*na >= maxNa) return 0;
slouken@524
  1521
			ptr = nsvg__parseNumber(ptr, it, 64);
slouken@524
  1522
			args[(*na)++] = (float)nsvg__atof(it);
slouken@524
  1523
		} else {
slouken@524
  1524
			++ptr;
slouken@524
  1525
		}
slouken@524
  1526
	}
slouken@524
  1527
	return (int)(end - str);
slouken@524
  1528
}
slouken@524
  1529
slouken@524
  1530
slouken@524
  1531
static int nsvg__parseMatrix(float* xform, const char* str)
slouken@524
  1532
{
slouken@524
  1533
	float t[6];
slouken@524
  1534
	int na = 0;
slouken@524
  1535
	int len = nsvg__parseTransformArgs(str, t, 6, &na);
slouken@524
  1536
	if (na != 6) return len;
slouken@524
  1537
	memcpy(xform, t, sizeof(float)*6);
slouken@524
  1538
	return len;
slouken@524
  1539
}
slouken@524
  1540
slouken@524
  1541
static int nsvg__parseTranslate(float* xform, const char* str)
slouken@524
  1542
{
slouken@524
  1543
	float args[2];
slouken@524
  1544
	float t[6];
slouken@524
  1545
	int na = 0;
slouken@524
  1546
	int len = nsvg__parseTransformArgs(str, args, 2, &na);
slouken@524
  1547
	if (na == 1) args[1] = 0.0;
slouken@524
  1548
slouken@524
  1549
	nsvg__xformSetTranslation(t, args[0], args[1]);
slouken@524
  1550
	memcpy(xform, t, sizeof(float)*6);
slouken@524
  1551
	return len;
slouken@524
  1552
}
slouken@524
  1553
slouken@524
  1554
static int nsvg__parseScale(float* xform, const char* str)
slouken@524
  1555
{
slouken@524
  1556
	float args[2];
slouken@524
  1557
	int na = 0;
slouken@524
  1558
	float t[6];
slouken@524
  1559
	int len = nsvg__parseTransformArgs(str, args, 2, &na);
slouken@524
  1560
	if (na == 1) args[1] = args[0];
slouken@524
  1561
	nsvg__xformSetScale(t, args[0], args[1]);
slouken@524
  1562
	memcpy(xform, t, sizeof(float)*6);
slouken@524
  1563
	return len;
slouken@524
  1564
}
slouken@524
  1565
slouken@524
  1566
static int nsvg__parseSkewX(float* xform, const char* str)
slouken@524
  1567
{
slouken@524
  1568
	float args[1];
slouken@524
  1569
	int na = 0;
slouken@524
  1570
	float t[6];
slouken@524
  1571
	int len = nsvg__parseTransformArgs(str, args, 1, &na);
slouken@524
  1572
	nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
slouken@524
  1573
	memcpy(xform, t, sizeof(float)*6);
slouken@524
  1574
	return len;
slouken@524
  1575
}
slouken@524
  1576
slouken@524
  1577
static int nsvg__parseSkewY(float* xform, const char* str)
slouken@524
  1578
{
slouken@524
  1579
	float args[1];
slouken@524
  1580
	int na = 0;
slouken@524
  1581
	float t[6];
slouken@524
  1582
	int len = nsvg__parseTransformArgs(str, args, 1, &na);
slouken@524
  1583
	nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
slouken@524
  1584
	memcpy(xform, t, sizeof(float)*6);
slouken@524
  1585
	return len;
slouken@524
  1586
}
slouken@524
  1587
slouken@524
  1588
static int nsvg__parseRotate(float* xform, const char* str)
slouken@524
  1589
{
slouken@524
  1590
	float args[3];
slouken@524
  1591
	int na = 0;
slouken@524
  1592
	float m[6];
slouken@524
  1593
	float t[6];
slouken@524
  1594
	int len = nsvg__parseTransformArgs(str, args, 3, &na);
slouken@524
  1595
	if (na == 1)
slouken@524
  1596
		args[1] = args[2] = 0.0f;
slouken@524
  1597
	nsvg__xformIdentity(m);
slouken@524
  1598
slouken@524
  1599
	if (na > 1) {
slouken@524
  1600
		nsvg__xformSetTranslation(t, -args[1], -args[2]);
slouken@524
  1601
		nsvg__xformMultiply(m, t);
slouken@524
  1602
	}
slouken@524
  1603
slouken@524
  1604
	nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
slouken@524
  1605
	nsvg__xformMultiply(m, t);
slouken@524
  1606
slouken@524
  1607
	if (na > 1) {
slouken@524
  1608
		nsvg__xformSetTranslation(t, args[1], args[2]);
slouken@524
  1609
		nsvg__xformMultiply(m, t);
slouken@524
  1610
	}
slouken@524
  1611
slouken@524
  1612
	memcpy(xform, m, sizeof(float)*6);
slouken@524
  1613
slouken@524
  1614
	return len;
slouken@524
  1615
}
slouken@524
  1616
slouken@524
  1617
static void nsvg__parseTransform(float* xform, const char* str)
slouken@524
  1618
{
slouken@524
  1619
	float t[6];
slouken@524
  1620
	nsvg__xformIdentity(xform);
slouken@524
  1621
	while (*str)
slouken@524
  1622
	{
slouken@524
  1623
		if (strncmp(str, "matrix", 6) == 0)
slouken@524
  1624
			str += nsvg__parseMatrix(t, str);
slouken@524
  1625
		else if (strncmp(str, "translate", 9) == 0)
slouken@524
  1626
			str += nsvg__parseTranslate(t, str);
slouken@524
  1627
		else if (strncmp(str, "scale", 5) == 0)
slouken@524
  1628
			str += nsvg__parseScale(t, str);
slouken@524
  1629
		else if (strncmp(str, "rotate", 6) == 0)
slouken@524
  1630
			str += nsvg__parseRotate(t, str);
slouken@524
  1631
		else if (strncmp(str, "skewX", 5) == 0)
slouken@524
  1632
			str += nsvg__parseSkewX(t, str);
slouken@524
  1633
		else if (strncmp(str, "skewY", 5) == 0)
slouken@524
  1634
			str += nsvg__parseSkewY(t, str);
slouken@524
  1635
		else{
slouken@524
  1636
			++str;
slouken@524
  1637
			continue;
slouken@524
  1638
		}
slouken@524
  1639
slouken@524
  1640
		nsvg__xformPremultiply(xform, t);
slouken@524
  1641
	}
slouken@524
  1642
}
slouken@524
  1643
slouken@524
  1644
static void nsvg__parseUrl(char* id, const char* str)
slouken@524
  1645
{
slouken@524
  1646
	int i = 0;
slouken@524
  1647
	str += 4; // "url(";
slouken@524
  1648
	if (*str == '#')
slouken@524
  1649
		str++;
slouken@524
  1650
	while (i < 63 && *str != ')') {
slouken@524
  1651
		id[i] = *str++;
slouken@524
  1652
		i++;
slouken@524
  1653
	}
slouken@524
  1654
	id[i] = '\0';
slouken@524
  1655
}
slouken@524
  1656
slouken@524
  1657
static char nsvg__parseLineCap(const char* str)
slouken@524
  1658
{
slouken@524
  1659
	if (strcmp(str, "butt") == 0)
slouken@524
  1660
		return NSVG_CAP_BUTT;
slouken@524
  1661
	else if (strcmp(str, "round") == 0)
slouken@524
  1662
		return NSVG_CAP_ROUND;
slouken@524
  1663
	else if (strcmp(str, "square") == 0)
slouken@524
  1664
		return NSVG_CAP_SQUARE;
slouken@524
  1665
	// TODO: handle inherit.
slouken@524
  1666
	return NSVG_CAP_BUTT;
slouken@524
  1667
}
slouken@524
  1668
slouken@524
  1669
static char nsvg__parseLineJoin(const char* str)
slouken@524
  1670
{
slouken@524
  1671
	if (strcmp(str, "miter") == 0)
slouken@524
  1672
		return NSVG_JOIN_MITER;
slouken@524
  1673
	else if (strcmp(str, "round") == 0)
slouken@524
  1674
		return NSVG_JOIN_ROUND;
slouken@524
  1675
	else if (strcmp(str, "bevel") == 0)
slouken@524
  1676
		return NSVG_JOIN_BEVEL;
slouken@524
  1677
	// TODO: handle inherit.
slouken@524
  1678
	return NSVG_CAP_BUTT;
slouken@524
  1679
}
slouken@524
  1680
slouken@524
  1681
static char nsvg__parseFillRule(const char* str)
slouken@524
  1682
{
slouken@524
  1683
	if (strcmp(str, "nonzero") == 0)
slouken@524
  1684
		return NSVG_FILLRULE_NONZERO;
slouken@524
  1685
	else if (strcmp(str, "evenodd") == 0)
slouken@524
  1686
		return NSVG_FILLRULE_EVENODD;
slouken@524
  1687
	// TODO: handle inherit.
slouken@524
  1688
	return NSVG_FILLRULE_NONZERO;
slouken@524
  1689
}
slouken@524
  1690
slouken@524
  1691
static const char* nsvg__getNextDashItem(const char* s, char* it)
slouken@524
  1692
{
slouken@524
  1693
	int n = 0;
slouken@524
  1694
	it[0] = '\0';
slouken@524
  1695
	// Skip white spaces and commas
slouken@524
  1696
	while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
slouken@524
  1697
	// Advance until whitespace, comma or end.
slouken@524
  1698
	while (*s && (!nsvg__isspace(*s) && *s != ',')) {
slouken@524
  1699
		if (n < 63)
slouken@524
  1700
			it[n++] = *s;
slouken@524
  1701
		s++;
slouken@524
  1702
	}
slouken@524
  1703
	it[n++] = '\0';
slouken@524
  1704
	return s;
slouken@524
  1705
}
slouken@524
  1706
slouken@524
  1707
static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
slouken@524
  1708
{
slouken@524
  1709
	char item[64];
slouken@524
  1710
	int count = 0, i;
slouken@524
  1711
	float sum = 0.0f;
slouken@524
  1712
slouken@524
  1713
	// Handle "none"
slouken@524
  1714
	if (str[0] == 'n')
slouken@524
  1715
		return 0;
slouken@524
  1716
slouken@524
  1717
	// Parse dashes
slouken@524
  1718
	while (*str) {
slouken@524
  1719
		str = nsvg__getNextDashItem(str, item);
slouken@524
  1720
		if (!*item) break;
slouken@524
  1721
		if (count < NSVG_MAX_DASHES)
slouken@524
  1722
			strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
slouken@524
  1723
	}
slouken@524
  1724
slouken@524
  1725
	for (i = 0; i < count; i++)
slouken@524
  1726
		sum += strokeDashArray[i];
slouken@524
  1727
	if (sum <= 1e-6f)
slouken@524
  1728
		count = 0;
slouken@524
  1729
slouken@524
  1730
	return count;
slouken@524
  1731
}
slouken@524
  1732
slouken@524
  1733
static void nsvg__parseStyle(NSVGparser* p, const char* str);
slouken@524
  1734
slouken@524
  1735
static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
slouken@524
  1736
{
slouken@524
  1737
	float xform[6];
slouken@524
  1738
	NSVGattrib* attr = nsvg__getAttr(p);
slouken@524
  1739
	if (!attr) return 0;
slouken@524
  1740
slouken@524
  1741
	if (strcmp(name, "style") == 0) {
slouken@524
  1742
		nsvg__parseStyle(p, value);
slouken@524
  1743
	} else if (strcmp(name, "display") == 0) {
slouken@524
  1744
		if (strcmp(value, "none") == 0)
slouken@524
  1745
			attr->visible = 0;
slouken@524
  1746
		// Don't reset ->visible on display:inline, one display:none hides the whole subtree
slouken@524
  1747
slouken@524
  1748
	} else if (strcmp(name, "fill") == 0) {
slouken@524
  1749
		if (strcmp(value, "none") == 0) {
slouken@524
  1750
			attr->hasFill = 0;
slouken@524
  1751
		} else if (strncmp(value, "url(", 4) == 0) {
slouken@524
  1752
			attr->hasFill = 2;
slouken@524
  1753
			nsvg__parseUrl(attr->fillGradient, value);
slouken@524
  1754
		} else {
slouken@524
  1755
			attr->hasFill = 1;
slouken@524
  1756
			attr->fillColor = nsvg__parseColor(value);
slouken@524
  1757
		}
slouken@524
  1758
	} else if (strcmp(name, "opacity") == 0) {
slouken@524
  1759
		attr->opacity = nsvg__parseOpacity(value);
slouken@524
  1760
	} else if (strcmp(name, "fill-opacity") == 0) {
slouken@524
  1761
		attr->fillOpacity = nsvg__parseOpacity(value);
slouken@524
  1762
	} else if (strcmp(name, "stroke") == 0) {
slouken@524
  1763
		if (strcmp(value, "none") == 0) {
slouken@524
  1764
			attr->hasStroke = 0;
slouken@524
  1765
		} else if (strncmp(value, "url(", 4) == 0) {
slouken@524
  1766
			attr->hasStroke = 2;
slouken@524
  1767
			nsvg__parseUrl(attr->strokeGradient, value);
slouken@524
  1768
		} else {
slouken@524
  1769
			attr->hasStroke = 1;
slouken@524
  1770
			attr->strokeColor = nsvg__parseColor(value);
slouken@524
  1771
		}
slouken@524
  1772
	} else if (strcmp(name, "stroke-width") == 0) {
slouken@524
  1773
		attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
slouken@524
  1774
	} else if (strcmp(name, "stroke-dasharray") == 0) {
slouken@524
  1775
		attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
slouken@524
  1776
	} else if (strcmp(name, "stroke-dashoffset") == 0) {
slouken@524
  1777
		attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
slouken@524
  1778
	} else if (strcmp(name, "stroke-opacity") == 0) {
slouken@524
  1779
		attr->strokeOpacity = nsvg__parseOpacity(value);
slouken@524
  1780
	} else if (strcmp(name, "stroke-linecap") == 0) {
slouken@524
  1781
		attr->strokeLineCap = nsvg__parseLineCap(value);
slouken@524
  1782
	} else if (strcmp(name, "stroke-linejoin") == 0) {
slouken@524
  1783
		attr->strokeLineJoin = nsvg__parseLineJoin(value);
slouken@524
  1784
	} else if (strcmp(name, "stroke-miterlimit") == 0) {
slouken@524
  1785
		attr->miterLimit = nsvg__parseMiterLimit(value);
slouken@524
  1786
	} else if (strcmp(name, "fill-rule") == 0) {
slouken@524
  1787
		attr->fillRule = nsvg__parseFillRule(value);
slouken@524
  1788
	} else if (strcmp(name, "font-size") == 0) {
slouken@524
  1789
		attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
slouken@524
  1790
	} else if (strcmp(name, "transform") == 0) {
slouken@524
  1791
		nsvg__parseTransform(xform, value);
slouken@524
  1792
		nsvg__xformPremultiply(attr->xform, xform);
slouken@524
  1793
	} else if (strcmp(name, "stop-color") == 0) {
slouken@524
  1794
		attr->stopColor = nsvg__parseColor(value);
slouken@524
  1795
	} else if (strcmp(name, "stop-opacity") == 0) {
slouken@524
  1796
		attr->stopOpacity = nsvg__parseOpacity(value);
slouken@524
  1797
	} else if (strcmp(name, "offset") == 0) {
slouken@524
  1798
		attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
slouken@524
  1799
	} else if (strcmp(name, "id") == 0) {
slouken@524
  1800
		strncpy(attr->id, value, 63);
slouken@524
  1801
		attr->id[63] = '\0';
slouken@525
  1802
	} else if (strcmp(name, "class") == 0) {
slouken@525
  1803
		NSVGstyles* style = p->styles;
slouken@525
  1804
		while (style) {
slouken@525
  1805
			if (strcmp(style->name + 1, value) == 0) {
slouken@525
  1806
				break;
slouken@525
  1807
			}
slouken@525
  1808
			style = style->next;
slouken@525
  1809
		}
slouken@525
  1810
		if (style) {
slouken@525
  1811
			nsvg__parseStyle(p, style->description);
slouken@525
  1812
		}
slouken@525
  1813
	} 
slouken@525
  1814
	else {
slouken@524
  1815
		return 0;
slouken@525
  1816
	} 
slouken@524
  1817
	return 1;
slouken@524
  1818
}
slouken@524
  1819
slouken@524
  1820
static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
slouken@524
  1821
{
slouken@524
  1822
	const char* str;
slouken@524
  1823
	const char* val;
slouken@524
  1824
	char name[512];
slouken@524
  1825
	char value[512];
slouken@524
  1826
	int n;
slouken@524
  1827
slouken@524
  1828
	str = start;
slouken@524
  1829
	while (str < end && *str != ':') ++str;
slouken@524
  1830
slouken@524
  1831
	val = str;
slouken@524
  1832
slouken@524
  1833
	// Right Trim
slouken@524
  1834
	while (str > start &&  (*str == ':' || nsvg__isspace(*str))) --str;
slouken@524
  1835
	++str;
slouken@524
  1836
slouken@524
  1837
	n = (int)(str - start);
slouken@524
  1838
	if (n > 511) n = 511;
slouken@524
  1839
	if (n) memcpy(name, start, n);
slouken@524
  1840
	name[n] = 0;
slouken@524
  1841
slouken@524
  1842
	while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
slouken@524
  1843
slouken@524
  1844
	n = (int)(end - val);
slouken@524
  1845
	if (n > 511) n = 511;
slouken@524
  1846
	if (n) memcpy(value, val, n);
slouken@524
  1847
	value[n] = 0;
slouken@524
  1848
slouken@524
  1849
	return nsvg__parseAttr(p, name, value);
slouken@524
  1850
}
slouken@524
  1851
slouken@524
  1852
static void nsvg__parseStyle(NSVGparser* p, const char* str)
slouken@524
  1853
{
slouken@524
  1854
	const char* start;
slouken@524
  1855
	const char* end;
slouken@524
  1856
slouken@524
  1857
	while (*str) {
slouken@524
  1858
		// Left Trim
slouken@524
  1859
		while(*str && nsvg__isspace(*str)) ++str;
slouken@524
  1860
		start = str;
slouken@524
  1861
		while(*str && *str != ';') ++str;
slouken@524
  1862
		end = str;
slouken@524
  1863
slouken@524
  1864
		// Right Trim
slouken@524
  1865
		while (end > start &&  (*end == ';' || nsvg__isspace(*end))) --end;
slouken@524
  1866
		++end;
slouken@524
  1867
slouken@524
  1868
		nsvg__parseNameValue(p, start, end);
slouken@524
  1869
		if (*str) ++str;
slouken@524
  1870
	}
slouken@524
  1871
}
slouken@524
  1872
slouken@524
  1873
static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
slouken@524
  1874
{
slouken@524
  1875
	int i;
slouken@524
  1876
	for (i = 0; attr[i]; i += 2)
slouken@524
  1877
	{
slouken@524
  1878
		if (strcmp(attr[i], "style") == 0)
slouken@524
  1879
			nsvg__parseStyle(p, attr[i + 1]);
slouken@524
  1880
		else
slouken@524
  1881
			nsvg__parseAttr(p, attr[i], attr[i + 1]);
slouken@524
  1882
	}
slouken@524
  1883
}
slouken@524
  1884
slouken@524
  1885
static int nsvg__getArgsPerElement(char cmd)
slouken@524
  1886
{
slouken@524
  1887
	switch (cmd) {
slouken@524
  1888
		case 'v':
slouken@524
  1889
		case 'V':
slouken@524
  1890
		case 'h':
slouken@524
  1891
		case 'H':
slouken@524
  1892
			return 1;
slouken@524
  1893
		case 'm':
slouken@524
  1894
		case 'M':
slouken@524
  1895
		case 'l':
slouken@524
  1896
		case 'L':
slouken@524
  1897
		case 't':
slouken@524
  1898
		case 'T':
slouken@524
  1899
			return 2;
slouken@524
  1900
		case 'q':
slouken@524
  1901
		case 'Q':
slouken@524
  1902
		case 's':
slouken@524
  1903
		case 'S':
slouken@524
  1904
			return 4;
slouken@524
  1905
		case 'c':
slouken@524
  1906
		case 'C':
slouken@524
  1907
			return 6;
slouken@524
  1908
		case 'a':
slouken@524
  1909
		case 'A':
slouken@524
  1910
			return 7;
slouken@524
  1911
	}
slouken@524
  1912
	return 0;
slouken@524
  1913
}
slouken@524
  1914
slouken@524
  1915
static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
slouken@524
  1916
{
slouken@524
  1917
	if (rel) {
slouken@524
  1918
		*cpx += args[0];
slouken@524
  1919
		*cpy += args[1];
slouken@524
  1920
	} else {
slouken@524
  1921
		*cpx = args[0];
slouken@524
  1922
		*cpy = args[1];
slouken@524
  1923
	}
slouken@524
  1924
	nsvg__moveTo(p, *cpx, *cpy);
slouken@524
  1925
}
slouken@524
  1926
slouken@524
  1927
static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
slouken@524
  1928
{
slouken@524
  1929
	if (rel) {
slouken@524
  1930
		*cpx += args[0];
slouken@524
  1931
		*cpy += args[1];
slouken@524
  1932
	} else {
slouken@524
  1933
		*cpx = args[0];
slouken@524
  1934
		*cpy = args[1];
slouken@524
  1935
	}
slouken@524
  1936
	nsvg__lineTo(p, *cpx, *cpy);
slouken@524
  1937
}
slouken@524
  1938
slouken@524
  1939
static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
slouken@524
  1940
{
slouken@524
  1941
	if (rel)
slouken@524
  1942
		*cpx += args[0];
slouken@524
  1943
	else
slouken@524
  1944
		*cpx = args[0];
slouken@524
  1945
	nsvg__lineTo(p, *cpx, *cpy);
slouken@524
  1946
}
slouken@524
  1947
slouken@524
  1948
static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
slouken@524
  1949
{
slouken@524
  1950
	if (rel)
slouken@524
  1951
		*cpy += args[0];
slouken@524
  1952
	else
slouken@524
  1953
		*cpy = args[0];
slouken@524
  1954
	nsvg__lineTo(p, *cpx, *cpy);
slouken@524
  1955
}
slouken@524
  1956
slouken@524
  1957
static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
slouken@524
  1958
								 float* cpx2, float* cpy2, float* args, int rel)
slouken@524
  1959
{
slouken@524
  1960
	float x2, y2, cx1, cy1, cx2, cy2;
slouken@524
  1961
slouken@524
  1962
	if (rel) {
slouken@524
  1963
		cx1 = *cpx + args[0];
slouken@524
  1964
		cy1 = *cpy + args[1];
slouken@524
  1965
		cx2 = *cpx + args[2];
slouken@524
  1966
		cy2 = *cpy + args[3];
slouken@524
  1967
		x2 = *cpx + args[4];
slouken@524
  1968
		y2 = *cpy + args[5];
slouken@524
  1969
	} else {
slouken@524
  1970
		cx1 = args[0];
slouken@524
  1971
		cy1 = args[1];
slouken@524
  1972
		cx2 = args[2];
slouken@524
  1973
		cy2 = args[3];
slouken@524
  1974
		x2 = args[4];
slouken@524
  1975
		y2 = args[5];
slouken@524
  1976
	}
slouken@524
  1977
slouken@524
  1978
	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
slouken@524
  1979
slouken@524
  1980
	*cpx2 = cx2;
slouken@524
  1981
	*cpy2 = cy2;
slouken@524
  1982
	*cpx = x2;
slouken@524
  1983
	*cpy = y2;
slouken@524
  1984
}
slouken@524
  1985
slouken@524
  1986
static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
slouken@524
  1987
									  float* cpx2, float* cpy2, float* args, int rel)
slouken@524
  1988
{
slouken@524
  1989
	float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
slouken@524
  1990
slouken@524
  1991
	x1 = *cpx;
slouken@524
  1992
	y1 = *cpy;
slouken@524
  1993
	if (rel) {
slouken@524
  1994
		cx2 = *cpx + args[0];
slouken@524
  1995
		cy2 = *cpy + args[1];
slouken@524
  1996
		x2 = *cpx + args[2];
slouken@524
  1997
		y2 = *cpy + args[3];
slouken@524
  1998
	} else {
slouken@524
  1999
		cx2 = args[0];
slouken@524
  2000
		cy2 = args[1];
slouken@524
  2001
		x2 = args[2];
slouken@524
  2002
		y2 = args[3];
slouken@524
  2003
	}
slouken@524
  2004
slouken@524
  2005
	cx1 = 2*x1 - *cpx2;
slouken@524
  2006
	cy1 = 2*y1 - *cpy2;
slouken@524
  2007
slouken@524
  2008
	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
slouken@524
  2009
slouken@524
  2010
	*cpx2 = cx2;
slouken@524
  2011
	*cpy2 = cy2;
slouken@524
  2012
	*cpx = x2;
slouken@524
  2013
	*cpy = y2;
slouken@524
  2014
}
slouken@524
  2015
slouken@524
  2016
static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
slouken@524
  2017
								float* cpx2, float* cpy2, float* args, int rel)
slouken@524
  2018
{
slouken@524
  2019
	float x1, y1, x2, y2, cx, cy;
slouken@524
  2020
	float cx1, cy1, cx2, cy2;
slouken@524
  2021
slouken@524
  2022
	x1 = *cpx;
slouken@524
  2023
	y1 = *cpy;
slouken@524
  2024
	if (rel) {
slouken@524
  2025
		cx = *cpx + args[0];
slouken@524
  2026
		cy = *cpy + args[1];
slouken@524
  2027
		x2 = *cpx + args[2];
slouken@524
  2028
		y2 = *cpy + args[3];
slouken@524
  2029
	} else {
slouken@524
  2030
		cx = args[0];
slouken@524
  2031
		cy = args[1];
slouken@524
  2032
		x2 = args[2];
slouken@524
  2033
		y2 = args[3];
slouken@524
  2034
	}
slouken@524
  2035
slouken@524
  2036
	// Convert to cubic bezier
slouken@524
  2037
	cx1 = x1 + 2.0f/3.0f*(cx - x1);
slouken@524
  2038
	cy1 = y1 + 2.0f/3.0f*(cy - y1);
slouken@524
  2039
	cx2 = x2 + 2.0f/3.0f*(cx - x2);
slouken@524
  2040
	cy2 = y2 + 2.0f/3.0f*(cy - y2);
slouken@524
  2041
slouken@524
  2042
	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
slouken@524
  2043
slouken@524
  2044
	*cpx2 = cx;
slouken@524
  2045
	*cpy2 = cy;
slouken@524
  2046
	*cpx = x2;
slouken@524
  2047
	*cpy = y2;
slouken@524
  2048
}
slouken@524
  2049
slouken@524
  2050
static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
slouken@524
  2051
									 float* cpx2, float* cpy2, float* args, int rel)
slouken@524
  2052
{
slouken@524
  2053
	float x1, y1, x2, y2, cx, cy;
slouken@524
  2054
	float cx1, cy1, cx2, cy2;
slouken@524
  2055
slouken@524
  2056
	x1 = *cpx;
slouken@524
  2057
	y1 = *cpy;
slouken@524
  2058
	if (rel) {
slouken@524
  2059
		x2 = *cpx + args[0];
slouken@524
  2060
		y2 = *cpy + args[1];
slouken@524
  2061
	} else {
slouken@524
  2062
		x2 = args[0];
slouken@524
  2063
		y2 = args[1];
slouken@524
  2064
	}
slouken@524
  2065
slouken@524
  2066
	cx = 2*x1 - *cpx2;
slouken@524
  2067
	cy = 2*y1 - *cpy2;
slouken@524
  2068
slouken@524
  2069
	// Convert to cubix bezier
slouken@524
  2070
	cx1 = x1 + 2.0f/3.0f*(cx - x1);
slouken@524
  2071
	cy1 = y1 + 2.0f/3.0f*(cy - y1);
slouken@524
  2072
	cx2 = x2 + 2.0f/3.0f*(cx - x2);
slouken@524
  2073
	cy2 = y2 + 2.0f/3.0f*(cy - y2);
slouken@524
  2074
slouken@524
  2075
	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
slouken@524
  2076
slouken@524
  2077
	*cpx2 = cx;
slouken@524
  2078
	*cpy2 = cy;
slouken@524
  2079
	*cpx = x2;
slouken@524
  2080
	*cpy = y2;
slouken@524
  2081
}
slouken@524
  2082
slouken@524
  2083
static float nsvg__sqr(float x) { return x*x; }
slouken@524
  2084
static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
slouken@524
  2085
slouken@524
  2086
static float nsvg__vecrat(float ux, float uy, float vx, float vy)
slouken@524
  2087
{
slouken@524
  2088
	return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
slouken@524
  2089
}
slouken@524
  2090
slouken@524
  2091
static float nsvg__vecang(float ux, float uy, float vx, float vy)
slouken@524
  2092
{
slouken@524
  2093
	float r = nsvg__vecrat(ux,uy, vx,vy);
slouken@524
  2094
	if (r < -1.0f) r = -1.0f;
slouken@524
  2095
	if (r > 1.0f) r = 1.0f;
slouken@524
  2096
	return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
slouken@524
  2097
}
slouken@524
  2098
slouken@524
  2099
static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
slouken@524
  2100
{
slouken@524
  2101
	// Ported from canvg (https://code.google.com/p/canvg/)
slouken@524
  2102
	float rx, ry, rotx;
slouken@524
  2103
	float x1, y1, x2, y2, cx, cy, dx, dy, d;
slouken@524
  2104
	float x1p, y1p, cxp, cyp, s, sa, sb;
slouken@524
  2105
	float ux, uy, vx, vy, a1, da;
slouken@524
  2106
	float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
slouken@524
  2107
	float sinrx, cosrx;
slouken@524
  2108
	int fa, fs;
slouken@524
  2109
	int i, ndivs;
slouken@524
  2110
	float hda, kappa;
slouken@524
  2111
slouken@524
  2112
	rx = fabsf(args[0]);				// y radius
slouken@524
  2113
	ry = fabsf(args[1]);				// x radius
slouken@524
  2114
	rotx = args[2] / 180.0f * NSVG_PI;		// x rotation angle
slouken@524
  2115
	fa = fabsf(args[3]) > 1e-6 ? 1 : 0;	// Large arc
slouken@524
  2116
	fs = fabsf(args[4]) > 1e-6 ? 1 : 0;	// Sweep direction
slouken@524
  2117
	x1 = *cpx;							// start point
slouken@524
  2118
	y1 = *cpy;
slouken@524
  2119
	if (rel) {							// end point
slouken@524
  2120
		x2 = *cpx + args[5];
slouken@524
  2121
		y2 = *cpy + args[6];
slouken@524
  2122
	} else {
slouken@524
  2123
		x2 = args[5];
slouken@524
  2124
		y2 = args[6];
slouken@524
  2125
	}
slouken@524
  2126
slouken@524
  2127
	dx = x1 - x2;
slouken@524
  2128
	dy = y1 - y2;
slouken@524
  2129
	d = sqrtf(dx*dx + dy*dy);
slouken@524
  2130
	if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
slouken@524
  2131
		// The arc degenerates to a line
slouken@524
  2132
		nsvg__lineTo(p, x2, y2);
slouken@524
  2133
		*cpx = x2;
slouken@524
  2134
		*cpy = y2;
slouken@524
  2135
		return;
slouken@524
  2136
	}
slouken@524
  2137
slouken@524
  2138
	sinrx = sinf(rotx);
slouken@524
  2139
	cosrx = cosf(rotx);
slouken@524
  2140
slouken@524
  2141
	// Convert to center point parameterization.
slouken@524
  2142
	// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
slouken@524
  2143
	// 1) Compute x1', y1'
slouken@524
  2144
	x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
slouken@524
  2145
	y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
slouken@524
  2146
	d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
slouken@524
  2147
	if (d > 1) {
slouken@524
  2148
		d = sqrtf(d);
slouken@524
  2149
		rx *= d;
slouken@524
  2150
		ry *= d;
slouken@524
  2151
	}
slouken@524
  2152
	// 2) Compute cx', cy'
slouken@524
  2153
	s = 0.0f;
slouken@524
  2154
	sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
slouken@524
  2155
	sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
slouken@524
  2156
	if (sa < 0.0f) sa = 0.0f;
slouken@524
  2157
	if (sb > 0.0f)
slouken@524
  2158
		s = sqrtf(sa / sb);
slouken@524
  2159
	if (fa == fs)
slouken@524
  2160
		s = -s;
slouken@524
  2161
	cxp = s * rx * y1p / ry;
slouken@524
  2162
	cyp = s * -ry * x1p / rx;
slouken@524
  2163
slouken@524
  2164
	// 3) Compute cx,cy from cx',cy'
slouken@524
  2165
	cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
slouken@524
  2166
	cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
slouken@524
  2167
slouken@524
  2168
	// 4) Calculate theta1, and delta theta.
slouken@524
  2169
	ux = (x1p - cxp) / rx;
slouken@524
  2170
	uy = (y1p - cyp) / ry;
slouken@524
  2171
	vx = (-x1p - cxp) / rx;
slouken@524
  2172
	vy = (-y1p - cyp) / ry;
slouken@524
  2173
	a1 = nsvg__vecang(1.0f,0.0f, ux,uy);	// Initial angle
slouken@524
  2174
	da = nsvg__vecang(ux,uy, vx,vy);		// Delta angle
slouken@524
  2175
slouken@524
  2176
//	if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
slouken@524
  2177
//	if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
slouken@524
  2178
slouken@524
  2179
	if (fs == 0 && da > 0)
slouken@524
  2180
		da -= 2 * NSVG_PI;
slouken@524
  2181
	else if (fs == 1 && da < 0)
slouken@524
  2182
		da += 2 * NSVG_PI;
slouken@524
  2183
slouken@524
  2184
	// Approximate the arc using cubic spline segments.
slouken@524
  2185
	t[0] = cosrx; t[1] = sinrx;
slouken@524
  2186
	t[2] = -sinrx; t[3] = cosrx;
slouken@524
  2187
	t[4] = cx; t[5] = cy;
slouken@524
  2188
slouken@524
  2189
	// Split arc into max 90 degree segments.
slouken@524
  2190
	// The loop assumes an iteration per end point (including start and end), this +1.
slouken@524
  2191
	ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
slouken@524
  2192
	hda = (da / (float)ndivs) / 2.0f;
slouken@524
  2193
	kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
slouken@524
  2194
	if (da < 0.0f)
slouken@524
  2195
		kappa = -kappa;
slouken@524
  2196
slouken@524
  2197
	for (i = 0; i <= ndivs; i++) {
slouken@524
  2198
		a = a1 + da * ((float)i/(float)ndivs);
slouken@524
  2199
		dx = cosf(a);
slouken@524
  2200
		dy = sinf(a);
slouken@524
  2201
		nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
slouken@524
  2202
		nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
slouken@524
  2203
		if (i > 0)
slouken@524
  2204
			nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
slouken@524
  2205
		px = x;
slouken@524
  2206
		py = y;
slouken@524
  2207
		ptanx = tanx;
slouken@524
  2208
		ptany = tany;
slouken@524
  2209
	}
slouken@524
  2210
slouken@524
  2211
	*cpx = x2;
slouken@524
  2212
	*cpy = y2;
slouken@524
  2213
}
slouken@524
  2214
slouken@524
  2215
static void nsvg__parsePath(NSVGparser* p, const char** attr)
slouken@524
  2216
{
slouken@524
  2217
	const char* s = NULL;
slouken@524
  2218
	char cmd = '\0';
slouken@524
  2219
	float args[10];
slouken@524
  2220
	int nargs;
slouken@524
  2221
	int rargs = 0;
slouken@524
  2222
	float cpx, cpy, cpx2, cpy2;
slouken@524
  2223
	const char* tmp[4];
slouken@524
  2224
	char closedFlag;
slouken@524
  2225
	int i;
slouken@524
  2226
	char item[64];
slouken@524
  2227
slouken@524
  2228
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2229
		if (strcmp(attr[i], "d") == 0) {
slouken@524
  2230
			s = attr[i + 1];
slouken@524
  2231
		} else {
slouken@524
  2232
			tmp[0] = attr[i];
slouken@524
  2233
			tmp[1] = attr[i + 1];
slouken@524
  2234
			tmp[2] = 0;
slouken@524
  2235
			tmp[3] = 0;
slouken@524
  2236
			nsvg__parseAttribs(p, tmp);
slouken@524
  2237
		}
slouken@524
  2238
	}
slouken@524
  2239
slouken@524
  2240
	if (s) {
slouken@524
  2241
		nsvg__resetPath(p);
slouken@524
  2242
		cpx = 0; cpy = 0;
slouken@524
  2243
		cpx2 = 0; cpy2 = 0;
slouken@524
  2244
		closedFlag = 0;
slouken@524
  2245
		nargs = 0;
slouken@524
  2246
slouken@524
  2247
		while (*s) {
slouken@524
  2248
			s = nsvg__getNextPathItem(s, item);
slouken@524
  2249
			if (!*item) break;
slouken@524
  2250
			if (nsvg__isnum(item[0])) {
slouken@524
  2251
				if (nargs < 10)
slouken@524
  2252
					args[nargs++] = (float)nsvg__atof(item);
slouken@524
  2253
				if (nargs >= rargs) {
slouken@524
  2254
					switch (cmd) {
slouken@524
  2255
						case 'm':
slouken@524
  2256
						case 'M':
slouken@524
  2257
							nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
slouken@524
  2258
							// Moveto can be followed by multiple coordinate pairs,
slouken@524
  2259
							// which should be treated as linetos.
slouken@524
  2260
							cmd = (cmd == 'm') ? 'l' : 'L';
slouken@524
  2261
							rargs = nsvg__getArgsPerElement(cmd);
slouken@524
  2262
							cpx2 = cpx; cpy2 = cpy;
slouken@524
  2263
							break;
slouken@524
  2264
						case 'l':
slouken@524
  2265
						case 'L':
slouken@524
  2266
							nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
slouken@524
  2267
							cpx2 = cpx; cpy2 = cpy;
slouken@524
  2268
							break;
slouken@524
  2269
						case 'H':
slouken@524
  2270
						case 'h':
slouken@524
  2271
							nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
slouken@524
  2272
							cpx2 = cpx; cpy2 = cpy;
slouken@524
  2273
							break;
slouken@524
  2274
						case 'V':
slouken@524
  2275
						case 'v':
slouken@524
  2276
							nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
slouken@524
  2277
							cpx2 = cpx; cpy2 = cpy;
slouken@524
  2278
							break;
slouken@524
  2279
						case 'C':
slouken@524
  2280
						case 'c':
slouken@524
  2281
							nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
slouken@524
  2282
							break;
slouken@524
  2283
						case 'S':
slouken@524
  2284
						case 's':
slouken@524
  2285
							nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
slouken@524
  2286
							break;
slouken@524
  2287
						case 'Q':
slouken@524
  2288
						case 'q':
slouken@524
  2289
							nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
slouken@524
  2290
							break;
slouken@524
  2291
						case 'T':
slouken@524
  2292
						case 't':
slouken@524
  2293
							nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
slouken@524
  2294
							break;
slouken@524
  2295
						case 'A':
slouken@524
  2296
						case 'a':
slouken@524
  2297
							nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
slouken@524
  2298
							cpx2 = cpx; cpy2 = cpy;
slouken@524
  2299
							break;
slouken@524
  2300
						default:
slouken@524
  2301
							if (nargs >= 2) {
slouken@524
  2302
								cpx = args[nargs-2];
slouken@524
  2303
								cpy = args[nargs-1];
slouken@524
  2304
								cpx2 = cpx; cpy2 = cpy;
slouken@524
  2305
							}
slouken@524
  2306
							break;
slouken@524
  2307
					}
slouken@524
  2308
					nargs = 0;
slouken@524
  2309
				}
slouken@524
  2310
			} else {
slouken@524
  2311
				cmd = item[0];
slouken@524
  2312
				rargs = nsvg__getArgsPerElement(cmd);
slouken@524
  2313
				if (cmd == 'M' || cmd == 'm') {
slouken@524
  2314
					// Commit path.
slouken@524
  2315
					if (p->npts > 0)
slouken@524
  2316
						nsvg__addPath(p, closedFlag);
slouken@524
  2317
					// Start new subpath.
slouken@524
  2318
					nsvg__resetPath(p);
slouken@524
  2319
					closedFlag = 0;
slouken@524
  2320
					nargs = 0;
slouken@524
  2321
				} else if (cmd == 'Z' || cmd == 'z') {
slouken@524
  2322
					closedFlag = 1;
slouken@524
  2323
					// Commit path.
slouken@524
  2324
					if (p->npts > 0) {
slouken@524
  2325
						// Move current point to first point
slouken@524
  2326
						cpx = p->pts[0];
slouken@524
  2327
						cpy = p->pts[1];
slouken@524
  2328
						cpx2 = cpx; cpy2 = cpy;
slouken@524
  2329
						nsvg__addPath(p, closedFlag);
slouken@524
  2330
					}
slouken@524
  2331
					// Start new subpath.
slouken@524
  2332
					nsvg__resetPath(p);
slouken@524
  2333
					nsvg__moveTo(p, cpx, cpy);
slouken@524
  2334
					closedFlag = 0;
slouken@524
  2335
					nargs = 0;
slouken@524
  2336
				}
slouken@524
  2337
			}
slouken@524
  2338
		}
slouken@524
  2339
		// Commit path.
slouken@524
  2340
		if (p->npts)
slouken@524
  2341
			nsvg__addPath(p, closedFlag);
slouken@524
  2342
	}
slouken@524
  2343
slouken@524
  2344
	nsvg__addShape(p);
slouken@524
  2345
}
slouken@524
  2346
slouken@524
  2347
static void nsvg__parseRect(NSVGparser* p, const char** attr)
slouken@524
  2348
{
slouken@524
  2349
	float x = 0.0f;
slouken@524
  2350
	float y = 0.0f;
slouken@524
  2351
	float w = 0.0f;
slouken@524
  2352
	float h = 0.0f;
slouken@524
  2353
	float rx = -1.0f; // marks not set
slouken@524
  2354
	float ry = -1.0f;
slouken@524
  2355
	int i;
slouken@524
  2356
slouken@524
  2357
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2358
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2359
			if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
slouken@524
  2360
			if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
slouken@524
  2361
			if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
slouken@524
  2362
			if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
slouken@524
  2363
			if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
slouken@524
  2364
			if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
slouken@524
  2365
		}
slouken@524
  2366
	}
slouken@524
  2367
slouken@524
  2368
	if (rx < 0.0f && ry > 0.0f) rx = ry;
slouken@524
  2369
	if (ry < 0.0f && rx > 0.0f) ry = rx;
slouken@524
  2370
	if (rx < 0.0f) rx = 0.0f;
slouken@524
  2371
	if (ry < 0.0f) ry = 0.0f;
slouken@524
  2372
	if (rx > w/2.0f) rx = w/2.0f;
slouken@524
  2373
	if (ry > h/2.0f) ry = h/2.0f;
slouken@524
  2374
slouken@524
  2375
	if (w != 0.0f && h != 0.0f) {
slouken@524
  2376
		nsvg__resetPath(p);
slouken@524
  2377
slouken@524
  2378
		if (rx < 0.00001f || ry < 0.0001f) {
slouken@524
  2379
			nsvg__moveTo(p, x, y);
slouken@524
  2380
			nsvg__lineTo(p, x+w, y);
slouken@524
  2381
			nsvg__lineTo(p, x+w, y+h);
slouken@524
  2382
			nsvg__lineTo(p, x, y+h);
slouken@524
  2383
		} else {
slouken@524
  2384
			// Rounded rectangle
slouken@524
  2385
			nsvg__moveTo(p, x+rx, y);
slouken@524
  2386
			nsvg__lineTo(p, x+w-rx, y);
slouken@524
  2387
			nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
slouken@524
  2388
			nsvg__lineTo(p, x+w, y+h-ry);
slouken@524
  2389
			nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
slouken@524
  2390
			nsvg__lineTo(p, x+rx, y+h);
slouken@524
  2391
			nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
slouken@524
  2392
			nsvg__lineTo(p, x, y+ry);
slouken@524
  2393
			nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
slouken@524
  2394
		}
slouken@524
  2395
slouken@524
  2396
		nsvg__addPath(p, 1);
slouken@524
  2397
slouken@524
  2398
		nsvg__addShape(p);
slouken@524
  2399
	}
slouken@524
  2400
}
slouken@524
  2401
slouken@524
  2402
static void nsvg__parseCircle(NSVGparser* p, const char** attr)
slouken@524
  2403
{
slouken@524
  2404
	float cx = 0.0f;
slouken@524
  2405
	float cy = 0.0f;
slouken@524
  2406
	float r = 0.0f;
slouken@524
  2407
	int i;
slouken@524
  2408
slouken@524
  2409
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2410
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2411
			if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
slouken@524
  2412
			if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
slouken@524
  2413
			if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
slouken@524
  2414
		}
slouken@524
  2415
	}
slouken@524
  2416
slouken@524
  2417
	if (r > 0.0f) {
slouken@524
  2418
		nsvg__resetPath(p);
slouken@524
  2419
slouken@524
  2420
		nsvg__moveTo(p, cx+r, cy);
slouken@524
  2421
		nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
slouken@524
  2422
		nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
slouken@524
  2423
		nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
slouken@524
  2424
		nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
slouken@524
  2425
slouken@524
  2426
		nsvg__addPath(p, 1);
slouken@524
  2427
slouken@524
  2428
		nsvg__addShape(p);
slouken@524
  2429
	}
slouken@524
  2430
}
slouken@524
  2431
slouken@524
  2432
static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
slouken@524
  2433
{
slouken@524
  2434
	float cx = 0.0f;
slouken@524
  2435
	float cy = 0.0f;
slouken@524
  2436
	float rx = 0.0f;
slouken@524
  2437
	float ry = 0.0f;
slouken@524
  2438
	int i;
slouken@524
  2439
slouken@524
  2440
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2441
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2442
			if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
slouken@524
  2443
			if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
slouken@524
  2444
			if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
slouken@524
  2445
			if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
slouken@524
  2446
		}
slouken@524
  2447
	}
slouken@524
  2448
slouken@524
  2449
	if (rx > 0.0f && ry > 0.0f) {
slouken@524
  2450
slouken@524
  2451
		nsvg__resetPath(p);
slouken@524
  2452
slouken@524
  2453
		nsvg__moveTo(p, cx+rx, cy);
slouken@524
  2454
		nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
slouken@524
  2455
		nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
slouken@524
  2456
		nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
slouken@524
  2457
		nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
slouken@524
  2458
slouken@524
  2459
		nsvg__addPath(p, 1);
slouken@524
  2460
slouken@524
  2461
		nsvg__addShape(p);
slouken@524
  2462
	}
slouken@524
  2463
}
slouken@524
  2464
slouken@524
  2465
static void nsvg__parseLine(NSVGparser* p, const char** attr)
slouken@524
  2466
{
slouken@524
  2467
	float x1 = 0.0;
slouken@524
  2468
	float y1 = 0.0;
slouken@524
  2469
	float x2 = 0.0;
slouken@524
  2470
	float y2 = 0.0;
slouken@524
  2471
	int i;
slouken@524
  2472
slouken@524
  2473
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2474
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2475
			if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
slouken@524
  2476
			if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
slouken@524
  2477
			if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
slouken@524
  2478
			if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
slouken@524
  2479
		}
slouken@524
  2480
	}
slouken@524
  2481
slouken@524
  2482
	nsvg__resetPath(p);
slouken@524
  2483
slouken@524
  2484
	nsvg__moveTo(p, x1, y1);
slouken@524
  2485
	nsvg__lineTo(p, x2, y2);
slouken@524
  2486
slouken@524
  2487
	nsvg__addPath(p, 0);
slouken@524
  2488
slouken@524
  2489
	nsvg__addShape(p);
slouken@524
  2490
}
slouken@524
  2491
slouken@524
  2492
static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
slouken@524
  2493
{
slouken@524
  2494
	int i;
slouken@524
  2495
	const char* s;
slouken@524
  2496
	float args[2];
slouken@524
  2497
	int nargs, npts = 0;
slouken@524
  2498
	char item[64];
slouken@524
  2499
slouken@524
  2500
	nsvg__resetPath(p);
slouken@524
  2501
slouken@524
  2502
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2503
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2504
			if (strcmp(attr[i], "points") == 0) {
slouken@524
  2505
				s = attr[i + 1];
slouken@524
  2506
				nargs = 0;
slouken@524
  2507
				while (*s) {
slouken@524
  2508
					s = nsvg__getNextPathItem(s, item);
slouken@524
  2509
					args[nargs++] = (float)nsvg__atof(item);
slouken@524
  2510
					if (nargs >= 2) {
slouken@524
  2511
						if (npts == 0)
slouken@524
  2512
							nsvg__moveTo(p, args[0], args[1]);
slouken@524
  2513
						else
slouken@524
  2514
							nsvg__lineTo(p, args[0], args[1]);
slouken@524
  2515
						nargs = 0;
slouken@524
  2516
						npts++;
slouken@524
  2517
					}
slouken@524
  2518
				}
slouken@524
  2519
			}
slouken@524
  2520
		}
slouken@524
  2521
	}
slouken@524
  2522
slouken@524
  2523
	nsvg__addPath(p, (char)closeFlag);
slouken@524
  2524
slouken@524
  2525
	nsvg__addShape(p);
slouken@524
  2526
}
slouken@524
  2527
slouken@524
  2528
static void nsvg__parseSVG(NSVGparser* p, const char** attr)
slouken@524
  2529
{
slouken@524
  2530
	int i;
slouken@524
  2531
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2532
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2533
			if (strcmp(attr[i], "width") == 0) {
slouken@524
  2534
				p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f);
slouken@524
  2535
			} else if (strcmp(attr[i], "height") == 0) {
slouken@524
  2536
				p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f);
slouken@524
  2537
			} else if (strcmp(attr[i], "viewBox") == 0) {
slouken@524
  2538
				sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight);
slouken@524
  2539
			} else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
slouken@524
  2540
				if (strstr(attr[i + 1], "none") != 0) {
slouken@524
  2541
					// No uniform scaling
slouken@524
  2542
					p->alignType = NSVG_ALIGN_NONE;
slouken@524
  2543
				} else {
slouken@524
  2544
					// Parse X align
slouken@524
  2545
					if (strstr(attr[i + 1], "xMin") != 0)
slouken@524
  2546
						p->alignX = NSVG_ALIGN_MIN;
slouken@524
  2547
					else if (strstr(attr[i + 1], "xMid") != 0)
slouken@524
  2548
						p->alignX = NSVG_ALIGN_MID;
slouken@524
  2549
					else if (strstr(attr[i + 1], "xMax") != 0)
slouken@524
  2550
						p->alignX = NSVG_ALIGN_MAX;
slouken@524
  2551
					// Parse X align
slouken@524
  2552
					if (strstr(attr[i + 1], "yMin") != 0)
slouken@524
  2553
						p->alignY = NSVG_ALIGN_MIN;
slouken@524
  2554
					else if (strstr(attr[i + 1], "yMid") != 0)
slouken@524
  2555
						p->alignY = NSVG_ALIGN_MID;
slouken@524
  2556
					else if (strstr(attr[i + 1], "yMax") != 0)
slouken@524
  2557
						p->alignY = NSVG_ALIGN_MAX;
slouken@524
  2558
					// Parse meet/slice
slouken@524
  2559
					p->alignType = NSVG_ALIGN_MEET;
slouken@524
  2560
					if (strstr(attr[i + 1], "slice") != 0)
slouken@524
  2561
						p->alignType = NSVG_ALIGN_SLICE;
slouken@524
  2562
				}
slouken@524
  2563
			}
slouken@524
  2564
		}
slouken@524
  2565
	}
slouken@524
  2566
}
slouken@524
  2567
slouken@524
  2568
static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
slouken@524
  2569
{
slouken@524
  2570
	int i;
slouken@524
  2571
	NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
slouken@524
  2572
	if (grad == NULL) return;
slouken@524
  2573
	memset(grad, 0, sizeof(NSVGgradientData));
slouken@524
  2574
	grad->units = NSVG_OBJECT_SPACE;
slouken@524
  2575
	grad->type = type;
slouken@524
  2576
	if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
slouken@524
  2577
		grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
slouken@524
  2578
		grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
slouken@524
  2579
		grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
slouken@524
  2580
		grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
slouken@524
  2581
	} else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
slouken@524
  2582
		grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
slouken@524
  2583
		grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
slouken@524
  2584
		grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
slouken@524
  2585
	}
slouken@524
  2586
slouken@524
  2587
	nsvg__xformIdentity(grad->xform);
slouken@524
  2588
slouken@524
  2589
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2590
		if (strcmp(attr[i], "id") == 0) {
slouken@524
  2591
			strncpy(grad->id, attr[i+1], 63);
slouken@524
  2592
			grad->id[63] = '\0';
slouken@524
  2593
		} else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
slouken@524
  2594
			if (strcmp(attr[i], "gradientUnits") == 0) {
slouken@524
  2595
				if (strcmp(attr[i+1], "objectBoundingBox") == 0)
slouken@524
  2596
					grad->units = NSVG_OBJECT_SPACE;
slouken@524
  2597
				else
slouken@524
  2598
					grad->units = NSVG_USER_SPACE;
slouken@524
  2599
			} else if (strcmp(attr[i], "gradientTransform") == 0) {
slouken@524
  2600
				nsvg__parseTransform(grad->xform, attr[i + 1]);
slouken@524
  2601
			} else if (strcmp(attr[i], "cx") == 0) {
slouken@524
  2602
				grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2603
			} else if (strcmp(attr[i], "cy") == 0) {
slouken@524
  2604
				grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2605
			} else if (strcmp(attr[i], "r") == 0) {
slouken@524
  2606
				grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2607
			} else if (strcmp(attr[i], "fx") == 0) {
slouken@524
  2608
				grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2609
			} else if (strcmp(attr[i], "fy") == 0) {
slouken@524
  2610
				grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2611
			} else if (strcmp(attr[i], "x1") == 0) {
slouken@524
  2612
				grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2613
			} else if (strcmp(attr[i], "y1") == 0) {
slouken@524
  2614
				grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2615
			} else if (strcmp(attr[i], "x2") == 0) {
slouken@524
  2616
				grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2617
			} else if (strcmp(attr[i], "y2") == 0) {
slouken@524
  2618
				grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
slouken@524
  2619
			} else if (strcmp(attr[i], "spreadMethod") == 0) {
slouken@524
  2620
				if (strcmp(attr[i+1], "pad") == 0)
slouken@524
  2621
					grad->spread = NSVG_SPREAD_PAD;
slouken@524
  2622
				else if (strcmp(attr[i+1], "reflect") == 0)
slouken@524
  2623
					grad->spread = NSVG_SPREAD_REFLECT;
slouken@524
  2624
				else if (strcmp(attr[i+1], "repeat") == 0)
slouken@524
  2625
					grad->spread = NSVG_SPREAD_REPEAT;
slouken@524
  2626
			} else if (strcmp(attr[i], "xlink:href") == 0) {
slouken@524
  2627
				const char *href = attr[i+1];
slouken@524
  2628
				strncpy(grad->ref, href+1, 62);
slouken@524
  2629
				grad->ref[62] = '\0';
slouken@524
  2630
			}
slouken@524
  2631
		}
slouken@524
  2632
	}
slouken@524
  2633
slouken@524
  2634
	grad->next = p->gradients;
slouken@524
  2635
	p->gradients = grad;
slouken@524
  2636
}
slouken@524
  2637
slouken@524
  2638
static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
slouken@524
  2639
{
slouken@524
  2640
	NSVGattrib* curAttr = nsvg__getAttr(p);
slouken@524
  2641
	NSVGgradientData* grad;
slouken@524
  2642
	NSVGgradientStop* stop;
slouken@524
  2643
	int i, idx;
slouken@524
  2644
slouken@524
  2645
	curAttr->stopOffset = 0;
slouken@524
  2646
	curAttr->stopColor = 0;
slouken@524
  2647
	curAttr->stopOpacity = 1.0f;
slouken@524
  2648
slouken@524
  2649
	for (i = 0; attr[i]; i += 2) {
slouken@524
  2650
		nsvg__parseAttr(p, attr[i], attr[i + 1]);
slouken@524
  2651
	}
slouken@524
  2652
slouken@524
  2653
	// Add stop to the last gradient.
slouken@524
  2654
	grad = p->gradients;
slouken@524
  2655
	if (grad == NULL) return;
slouken@524
  2656
slouken@524
  2657
	grad->nstops++;
slouken@524
  2658
	grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
slouken@524
  2659
	if (grad->stops == NULL) return;
slouken@524
  2660
slouken@524
  2661
	// Insert
slouken@524
  2662
	idx = grad->nstops-1;
slouken@524
  2663
	for (i = 0; i < grad->nstops-1; i++) {
slouken@524
  2664
		if (curAttr->stopOffset < grad->stops[i].offset) {
slouken@524
  2665
			idx = i;
slouken@524
  2666
			break;
slouken@524
  2667
		}
slouken@524
  2668
	}
slouken@524
  2669
	if (idx != grad->nstops-1) {
slouken@524
  2670
		for (i = grad->nstops-1; i > idx; i--)
slouken@524
  2671
			grad->stops[i] = grad->stops[i-1];
slouken@524
  2672
	}
slouken@524
  2673
slouken@524
  2674
	stop = &grad->stops[idx];
slouken@524
  2675
	stop->color = curAttr->stopColor;
slouken@524
  2676
	stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
slouken@524
  2677
	stop->offset = curAttr->stopOffset;
slouken@524
  2678
}
slouken@524
  2679
slouken@524
  2680
static void nsvg__startElement(void* ud, const char* el, const char** attr)
slouken@524
  2681
{
slouken@524
  2682
	NSVGparser* p = (NSVGparser*)ud;
slouken@524
  2683
slouken@524
  2684
	if (p->defsFlag) {
slouken@524
  2685
		// Skip everything but gradients in defs
slouken@524
  2686
		if (strcmp(el, "linearGradient") == 0) {
slouken@524
  2687
			nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
slouken@524
  2688
		} else if (strcmp(el, "radialGradient") == 0) {
slouken@524
  2689
			nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
slouken@524
  2690
		} else if (strcmp(el, "stop") == 0) {
slouken@524
  2691
			nsvg__parseGradientStop(p, attr);
slouken@524
  2692
		}
slouken@524
  2693
		return;
slouken@524
  2694
	}
slouken@524
  2695
slouken@524
  2696
	if (strcmp(el, "g") == 0) {
slouken@524
  2697
		nsvg__pushAttr(p);
slouken@524
  2698
		nsvg__parseAttribs(p, attr);
slouken@524
  2699
	} else if (strcmp(el, "path") == 0) {
slouken@524
  2700
		if (p->pathFlag)	// Do not allow nested paths.
slouken@524
  2701
			return;
slouken@524
  2702
		nsvg__pushAttr(p);
slouken@524
  2703
		nsvg__parsePath(p, attr);
slouken@524
  2704
		nsvg__popAttr(p);
slouken@524
  2705
	} else if (strcmp(el, "rect") == 0) {
slouken@524
  2706
		nsvg__pushAttr(p);
slouken@524
  2707
		nsvg__parseRect(p, attr);
slouken@524
  2708
		nsvg__popAttr(p);
slouken@524
  2709
	} else if (strcmp(el, "circle") == 0) {
slouken@524
  2710
		nsvg__pushAttr(p);
slouken@524
  2711
		nsvg__parseCircle(p, attr);
slouken@524
  2712
		nsvg__popAttr(p);
slouken@524
  2713
	} else if (strcmp(el, "ellipse") == 0) {
slouken@524
  2714
		nsvg__pushAttr(p);
slouken@524
  2715
		nsvg__parseEllipse(p, attr);
slouken@524
  2716
		nsvg__popAttr(p);
slouken@524
  2717
	} else if (strcmp(el, "line") == 0)  {
slouken@524
  2718
		nsvg__pushAttr(p);
slouken@524
  2719
		nsvg__parseLine(p, attr);
slouken@524
  2720
		nsvg__popAttr(p);
slouken@524
  2721
	} else if (strcmp(el, "polyline") == 0)  {
slouken@524
  2722
		nsvg__pushAttr(p);
slouken@524
  2723
		nsvg__parsePoly(p, attr, 0);
slouken@524
  2724
		nsvg__popAttr(p);
slouken@524
  2725
	} else if (strcmp(el, "polygon") == 0)  {
slouken@524
  2726
		nsvg__pushAttr(p);
slouken@524
  2727
		nsvg__parsePoly(p, attr, 1);
slouken@524
  2728
		nsvg__popAttr(p);
slouken@524
  2729
	} else  if (strcmp(el, "linearGradient") == 0) {
slouken@524
  2730
		nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
slouken@524
  2731
	} else if (strcmp(el, "radialGradient") == 0) {
slouken@524
  2732
		nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
slouken@524
  2733
	} else if (strcmp(el, "stop") == 0) {
slouken@524
  2734
		nsvg__parseGradientStop(p, attr);
slouken@524
  2735
	} else if (strcmp(el, "defs") == 0) {
slouken@524
  2736
		p->defsFlag = 1;
slouken@524
  2737
	} else if (strcmp(el, "svg") == 0) {
slouken@524
  2738
		nsvg__parseSVG(p, attr);
slouken@525
  2739
	} else if (strcmp(el, "style") == 0) {
slouken@525
  2740
		p->styleFlag = 1;
slouken@524
  2741
	}
slouken@524
  2742
}
slouken@524
  2743
slouken@524
  2744
static void nsvg__endElement(void* ud, const char* el)
slouken@524
  2745
{
slouken@524
  2746
	NSVGparser* p = (NSVGparser*)ud;
slouken@524
  2747
slouken@524
  2748
	if (strcmp(el, "g") == 0) {
slouken@524
  2749
		nsvg__popAttr(p);
slouken@524
  2750
	} else if (strcmp(el, "path") == 0) {
slouken@524
  2751
		p->pathFlag = 0;
slouken@524
  2752
	} else if (strcmp(el, "defs") == 0) {
slouken@524
  2753
		p->defsFlag = 0;
slouken@525
  2754
	} else if (strcmp(el, "style") == 0) {
slouken@525
  2755
		p->styleFlag = 0;
slouken@524
  2756
	}
slouken@524
  2757
}
slouken@524
  2758
slouken@525
  2759
static char *nsvg__strndup(const char *s, size_t n)
slouken@525
  2760
{
slouken@525
  2761
	char *result;
slouken@525
  2762
	size_t len = strlen(s);
slouken@525