nanosvgrast.h
author Sam Lantinga <slouken@libsdl.org>
Sun, 22 Oct 2017 00:46:52 -0700
changeset 527 a26235cc1970
parent 524 b4b49635cbd8
child 551 21559520f1be
permissions -rwxr-xr-x
Fixed compiling with Visual Studio
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 polygon rasterization is heavily based on stb_truetype rasterizer
slouken@524
    21
 * by Sean Barrett - http://nothings.org/
slouken@524
    22
 *
slouken@524
    23
 */
slouken@524
    24
slouken@524
    25
#ifndef NANOSVGRAST_H
slouken@524
    26
#define NANOSVGRAST_H
slouken@524
    27
slouken@524
    28
#ifdef __cplusplus
slouken@524
    29
extern "C" {
slouken@524
    30
#endif
slouken@524
    31
slouken@524
    32
typedef struct NSVGrasterizer NSVGrasterizer;
slouken@524
    33
slouken@524
    34
/* Example Usage:
slouken@524
    35
	// Load SVG
slouken@524
    36
	struct SNVGImage* image = nsvgParseFromFile("test.svg.");
slouken@524
    37
slouken@524
    38
	// Create rasterizer (can be used to render multiple images).
slouken@524
    39
	struct NSVGrasterizer* rast = nsvgCreateRasterizer();
slouken@524
    40
	// Allocate memory for image
slouken@524
    41
	unsigned char* img = malloc(w*h*4);
slouken@524
    42
	// Rasterize
slouken@524
    43
	nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
slouken@524
    44
*/
slouken@524
    45
slouken@524
    46
// Allocated rasterizer context.
slouken@524
    47
NSVGrasterizer* nsvgCreateRasterizer();
slouken@524
    48
slouken@524
    49
// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
slouken@524
    50
//   r - pointer to rasterizer context
slouken@524
    51
//   image - pointer to image to rasterize
slouken@524
    52
//   tx,ty - image offset (applied after scaling)
slouken@524
    53
//   scale - image scale
slouken@524
    54
//   dst - pointer to destination image data, 4 bytes per pixel (RGBA)
slouken@524
    55
//   w - width of the image to render
slouken@524
    56
//   h - height of the image to render
slouken@524
    57
//   stride - number of bytes per scaleline in the destination buffer
slouken@524
    58
void nsvgRasterize(NSVGrasterizer* r,
slouken@524
    59
				   NSVGimage* image, float tx, float ty, float scale,
slouken@524
    60
				   unsigned char* dst, int w, int h, int stride);
slouken@524
    61
slouken@524
    62
// Deletes rasterizer context.
slouken@524
    63
void nsvgDeleteRasterizer(NSVGrasterizer*);
slouken@524
    64
slouken@524
    65
slouken@524
    66
#ifdef __cplusplus
slouken@524
    67
}
slouken@524
    68
#endif
slouken@524
    69
slouken@524
    70
#endif // NANOSVGRAST_H
slouken@524
    71
slouken@524
    72
#ifdef NANOSVGRAST_IMPLEMENTATION
slouken@524
    73
slouken@524
    74
#include <math.h>
slouken@524
    75
slouken@524
    76
#define NSVG__SUBSAMPLES	5
slouken@524
    77
#define NSVG__FIXSHIFT		10
slouken@524
    78
#define NSVG__FIX			(1 << NSVG__FIXSHIFT)
slouken@524
    79
#define NSVG__FIXMASK		(NSVG__FIX-1)
slouken@524
    80
#define NSVG__MEMPAGE_SIZE	1024
slouken@524
    81
slouken@524
    82
typedef struct NSVGedge {
slouken@524
    83
	float x0,y0, x1,y1;
slouken@524
    84
	int dir;
slouken@524
    85
	struct NSVGedge* next;
slouken@524
    86
} NSVGedge;
slouken@524
    87
slouken@524
    88
typedef struct NSVGpoint {
slouken@524
    89
	float x, y;
slouken@524
    90
	float dx, dy;
slouken@524
    91
	float len;
slouken@524
    92
	float dmx, dmy;
slouken@524
    93
	unsigned char flags;
slouken@524
    94
} NSVGpoint;
slouken@524
    95
slouken@524
    96
typedef struct NSVGactiveEdge {
slouken@524
    97
	int x,dx;
slouken@524
    98
	float ey;
slouken@524
    99
	int dir;
slouken@524
   100
	struct NSVGactiveEdge *next;
slouken@524
   101
} NSVGactiveEdge;
slouken@524
   102
slouken@524
   103
typedef struct NSVGmemPage {
slouken@524
   104
	unsigned char mem[NSVG__MEMPAGE_SIZE];
slouken@524
   105
	int size;
slouken@524
   106
	struct NSVGmemPage* next;
slouken@524
   107
} NSVGmemPage;
slouken@524
   108
slouken@524
   109
typedef struct NSVGcachedPaint {
slouken@524
   110
	char type;
slouken@524
   111
	char spread;
slouken@524
   112
	float xform[6];
slouken@524
   113
	unsigned int colors[256];
slouken@524
   114
} NSVGcachedPaint;
slouken@524
   115
slouken@524
   116
struct NSVGrasterizer
slouken@524
   117
{
slouken@524
   118
	float px, py;
slouken@524
   119
slouken@524
   120
	float tessTol;
slouken@524
   121
	float distTol;
slouken@524
   122
slouken@524
   123
	NSVGedge* edges;
slouken@524
   124
	int nedges;
slouken@524
   125
	int cedges;
slouken@524
   126
slouken@524
   127
	NSVGpoint* points;
slouken@524
   128
	int npoints;
slouken@524
   129
	int cpoints;
slouken@524
   130
slouken@524
   131
	NSVGpoint* points2;
slouken@524
   132
	int npoints2;
slouken@524
   133
	int cpoints2;
slouken@524
   134
slouken@524
   135
	NSVGactiveEdge* freelist;
slouken@524
   136
	NSVGmemPage* pages;
slouken@524
   137
	NSVGmemPage* curpage;
slouken@524
   138
slouken@524
   139
	unsigned char* scanline;
slouken@524
   140
	int cscanline;
slouken@524
   141
slouken@524
   142
	unsigned char* bitmap;
slouken@524
   143
	int width, height, stride;
slouken@524
   144
};
slouken@524
   145
slouken@524
   146
NSVGrasterizer* nsvgCreateRasterizer()
slouken@524
   147
{
slouken@524
   148
	NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
slouken@524
   149
	if (r == NULL) goto error;
slouken@524
   150
	memset(r, 0, sizeof(NSVGrasterizer));
slouken@524
   151
slouken@524
   152
	r->tessTol = 0.25f;
slouken@524
   153
	r->distTol = 0.01f;
slouken@524
   154
slouken@524
   155
	return r;
slouken@524
   156
slouken@524
   157
error:
slouken@524
   158
	nsvgDeleteRasterizer(r);
slouken@524
   159
	return NULL;
slouken@524
   160
}
slouken@524
   161
slouken@524
   162
void nsvgDeleteRasterizer(NSVGrasterizer* r)
slouken@524
   163
{
slouken@524
   164
	NSVGmemPage* p;
slouken@524
   165
slouken@524
   166
	if (r == NULL) return;
slouken@524
   167
slouken@524
   168
	p = r->pages;
slouken@524
   169
	while (p != NULL) {
slouken@524
   170
		NSVGmemPage* next = p->next;
slouken@524
   171
		free(p);
slouken@524
   172
		p = next;
slouken@524
   173
	}
slouken@524
   174
slouken@524
   175
	if (r->edges) free(r->edges);
slouken@524
   176
	if (r->points) free(r->points);
slouken@524
   177
	if (r->points2) free(r->points2);
slouken@524
   178
	if (r->scanline) free(r->scanline);
slouken@524
   179
slouken@524
   180
	free(r);
slouken@524
   181
}
slouken@524
   182
slouken@524
   183
static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
slouken@524
   184
{
slouken@524
   185
	NSVGmemPage *newp;
slouken@524
   186
slouken@524
   187
	// If using existing chain, return the next page in chain
slouken@524
   188
	if (cur != NULL && cur->next != NULL) {
slouken@524
   189
		return cur->next;
slouken@524
   190
	}
slouken@524
   191
slouken@524
   192
	// Alloc new page
slouken@524
   193
	newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage));
slouken@524
   194
	if (newp == NULL) return NULL;
slouken@524
   195
	memset(newp, 0, sizeof(NSVGmemPage));
slouken@524
   196
slouken@524
   197
	// Add to linked list
slouken@524
   198
	if (cur != NULL)
slouken@524
   199
		cur->next = newp;
slouken@524
   200
	else
slouken@524
   201
		r->pages = newp;
slouken@524
   202
slouken@524
   203
	return newp;
slouken@524
   204
}
slouken@524
   205
slouken@524
   206
static void nsvg__resetPool(NSVGrasterizer* r)
slouken@524
   207
{
slouken@524
   208
	NSVGmemPage* p = r->pages;
slouken@524
   209
	while (p != NULL) {
slouken@524
   210
		p->size = 0;
slouken@524
   211
		p = p->next;
slouken@524
   212
	}
slouken@524
   213
	r->curpage = r->pages;
slouken@524
   214
}
slouken@524
   215
slouken@524
   216
static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)
slouken@524
   217
{
slouken@524
   218
	unsigned char* buf;
slouken@524
   219
	if (size > NSVG__MEMPAGE_SIZE) return NULL;
slouken@524
   220
	if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) {
slouken@524
   221
		r->curpage = nsvg__nextPage(r, r->curpage);
slouken@524
   222
	}
slouken@524
   223
	buf = &r->curpage->mem[r->curpage->size];
slouken@524
   224
	r->curpage->size += size;
slouken@524
   225
	return buf;
slouken@524
   226
}
slouken@524
   227
slouken@524
   228
static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
slouken@524
   229
{
slouken@524
   230
	float dx = x2 - x1;
slouken@524
   231
	float dy = y2 - y1;
slouken@524
   232
	return dx*dx + dy*dy < tol*tol;
slouken@524
   233
}
slouken@524
   234
slouken@524
   235
static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
slouken@524
   236
{
slouken@524
   237
	NSVGpoint* pt;
slouken@524
   238
slouken@524
   239
	if (r->npoints > 0) {
slouken@524
   240
		pt = &r->points[r->npoints-1];
slouken@524
   241
		if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
slouken@524
   242
			pt->flags = (unsigned char)(pt->flags | flags);
slouken@524
   243
			return;
slouken@524
   244
		}
slouken@524
   245
	}
slouken@524
   246
slouken@524
   247
	if (r->npoints+1 > r->cpoints) {
slouken@524
   248
		r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
slouken@524
   249
		r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
slouken@524
   250
		if (r->points == NULL) return;
slouken@524
   251
	}
slouken@524
   252
slouken@524
   253
	pt = &r->points[r->npoints];
slouken@524
   254
	pt->x = x;
slouken@524
   255
	pt->y = y;
slouken@524
   256
	pt->flags = (unsigned char)flags;
slouken@524
   257
	r->npoints++;
slouken@524
   258
}
slouken@524
   259
slouken@524
   260
static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
slouken@524
   261
{
slouken@524
   262
	if (r->npoints+1 > r->cpoints) {
slouken@524
   263
		r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
slouken@524
   264
		r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
slouken@524
   265
		if (r->points == NULL) return;
slouken@524
   266
	}
slouken@524
   267
	r->points[r->npoints] = pt;
slouken@524
   268
	r->npoints++;
slouken@524
   269
}
slouken@524
   270
slouken@524
   271
static void nsvg__duplicatePoints(NSVGrasterizer* r)
slouken@524
   272
{
slouken@524
   273
	if (r->npoints > r->cpoints2) {
slouken@524
   274
		r->cpoints2 = r->npoints;
slouken@524
   275
		r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);
slouken@524
   276
		if (r->points2 == NULL) return;
slouken@524
   277
	}
slouken@524
   278
slouken@524
   279
	memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);
slouken@524
   280
	r->npoints2 = r->npoints;
slouken@524
   281
}
slouken@524
   282
slouken@524
   283
static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)
slouken@524
   284
{
slouken@524
   285
	NSVGedge* e;
slouken@524
   286
slouken@524
   287
	// Skip horizontal edges
slouken@524
   288
	if (y0 == y1)
slouken@524
   289
		return;
slouken@524
   290
slouken@524
   291
	if (r->nedges+1 > r->cedges) {
slouken@524
   292
		r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
slouken@524
   293
		r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges);
slouken@524
   294
		if (r->edges == NULL) return;
slouken@524
   295
	}
slouken@524
   296
slouken@524
   297
	e = &r->edges[r->nedges];
slouken@524
   298
	r->nedges++;
slouken@524
   299
slouken@524
   300
	if (y0 < y1) {
slouken@524
   301
		e->x0 = x0;
slouken@524
   302
		e->y0 = y0;
slouken@524
   303
		e->x1 = x1;
slouken@524
   304
		e->y1 = y1;
slouken@524
   305
		e->dir = 1;
slouken@524
   306
	} else {
slouken@524
   307
		e->x0 = x1;
slouken@524
   308
		e->y0 = y1;
slouken@524
   309
		e->x1 = x0;
slouken@524
   310
		e->y1 = y0;
slouken@524
   311
		e->dir = -1;
slouken@524
   312
	}
slouken@524
   313
}
slouken@524
   314
slouken@524
   315
static float nsvg__normalize(float *x, float* y)
slouken@524
   316
{
slouken@524
   317
	float d = sqrtf((*x)*(*x) + (*y)*(*y));
slouken@524
   318
	if (d > 1e-6f) {
slouken@524
   319
		float id = 1.0f / d;
slouken@524
   320
		*x *= id;
slouken@524
   321
		*y *= id;
slouken@524
   322
	}
slouken@524
   323
	return d;
slouken@524
   324
}
slouken@524
   325
slouken@524
   326
static float nsvg__absf(float x) { return x < 0 ? -x : x; }
slouken@524
   327
slouken@524
   328
static void nsvg__flattenCubicBez(NSVGrasterizer* r,
slouken@524
   329
								  float x1, float y1, float x2, float y2,
slouken@524
   330
								  float x3, float y3, float x4, float y4,
slouken@524
   331
								  int level, int type)
slouken@524
   332
{
slouken@524
   333
	float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
slouken@524
   334
	float dx,dy,d2,d3;
slouken@524
   335
slouken@524
   336
	if (level > 10) return;
slouken@524
   337
slouken@524
   338
	x12 = (x1+x2)*0.5f;
slouken@524
   339
	y12 = (y1+y2)*0.5f;
slouken@524
   340
	x23 = (x2+x3)*0.5f;
slouken@524
   341
	y23 = (y2+y3)*0.5f;
slouken@524
   342
	x34 = (x3+x4)*0.5f;
slouken@524
   343
	y34 = (y3+y4)*0.5f;
slouken@524
   344
	x123 = (x12+x23)*0.5f;
slouken@524
   345
	y123 = (y12+y23)*0.5f;
slouken@524
   346
slouken@524
   347
	dx = x4 - x1;
slouken@524
   348
	dy = y4 - y1;
slouken@524
   349
	d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx));
slouken@524
   350
	d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx));
slouken@524
   351
slouken@524
   352
	if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) {
slouken@524
   353
		nsvg__addPathPoint(r, x4, y4, type);
slouken@524
   354
		return;
slouken@524
   355
	}
slouken@524
   356
slouken@524
   357
	x234 = (x23+x34)*0.5f;
slouken@524
   358
	y234 = (y23+y34)*0.5f;
slouken@524
   359
	x1234 = (x123+x234)*0.5f;
slouken@524
   360
	y1234 = (y123+y234)*0.5f;
slouken@524
   361
slouken@524
   362
	nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
slouken@524
   363
	nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
slouken@524
   364
}
slouken@524
   365
slouken@524
   366
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
slouken@524
   367
{
slouken@524
   368
	int i, j;
slouken@524
   369
	NSVGpath* path;
slouken@524
   370
slouken@524
   371
	for (path = shape->paths; path != NULL; path = path->next) {
slouken@524
   372
		r->npoints = 0;
slouken@524
   373
		// Flatten path
slouken@524
   374
		nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
slouken@524
   375
		for (i = 0; i < path->npts-1; i += 3) {
slouken@524
   376
			float* p = &path->pts[i*2];
slouken@524
   377
			nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
slouken@524
   378
		}
slouken@524
   379
		// Close path
slouken@524
   380
		nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
slouken@524
   381
		// Build edges
slouken@524
   382
		for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
slouken@524
   383
			nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
slouken@524
   384
	}
slouken@524
   385
}
slouken@524
   386
slouken@524
   387
enum NSVGpointFlags
slouken@524
   388
{
slouken@524
   389
	NSVG_PT_CORNER = 0x01,
slouken@524
   390
	NSVG_PT_BEVEL = 0x02,
slouken@524
   391
	NSVG_PT_LEFT = 0x04
slouken@524
   392
};
slouken@524
   393
slouken@524
   394
static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
slouken@524
   395
{
slouken@524
   396
	float w = lineWidth * 0.5f;
slouken@524
   397
	float dx = p1->x - p0->x;
slouken@524
   398
	float dy = p1->y - p0->y;
slouken@524
   399
	float len = nsvg__normalize(&dx, &dy);
slouken@524
   400
	float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f;
slouken@524
   401
	float dlx = dy, dly = -dx;
slouken@524
   402
	float lx = px - dlx*w, ly = py - dly*w;
slouken@524
   403
	float rx = px + dlx*w, ry = py + dly*w;
slouken@524
   404
	left->x = lx; left->y = ly;
slouken@524
   405
	right->x = rx; right->y = ry;
slouken@524
   406
}
slouken@524
   407
slouken@524
   408
static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
slouken@524
   409
{
slouken@524
   410
	float w = lineWidth * 0.5f;
slouken@524
   411
	float px = p->x, py = p->y;
slouken@524
   412
	float dlx = dy, dly = -dx;
slouken@524
   413
	float lx = px - dlx*w, ly = py - dly*w;
slouken@524
   414
	float rx = px + dlx*w, ry = py + dly*w;
slouken@524
   415
slouken@524
   416
	nsvg__addEdge(r, lx, ly, rx, ry);
slouken@524
   417
slouken@524
   418
	if (connect) {
slouken@524
   419
		nsvg__addEdge(r, left->x, left->y, lx, ly);
slouken@524
   420
		nsvg__addEdge(r, rx, ry, right->x, right->y);
slouken@524
   421
	}
slouken@524
   422
	left->x = lx; left->y = ly;
slouken@524
   423
	right->x = rx; right->y = ry;
slouken@524
   424
}
slouken@524
   425
slouken@524
   426
static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
slouken@524
   427
{
slouken@524
   428
	float w = lineWidth * 0.5f;
slouken@524
   429
	float px = p->x - dx*w, py = p->y - dy*w;
slouken@524
   430
	float dlx = dy, dly = -dx;
slouken@524
   431
	float lx = px - dlx*w, ly = py - dly*w;
slouken@524
   432
	float rx = px + dlx*w, ry = py + dly*w;
slouken@524
   433
slouken@524
   434
	nsvg__addEdge(r, lx, ly, rx, ry);
slouken@524
   435
slouken@524
   436
	if (connect) {
slouken@524
   437
		nsvg__addEdge(r, left->x, left->y, lx, ly);
slouken@524
   438
		nsvg__addEdge(r, rx, ry, right->x, right->y);
slouken@524
   439
	}
slouken@524
   440
	left->x = lx; left->y = ly;
slouken@524
   441
	right->x = rx; right->y = ry;
slouken@524
   442
}
slouken@524
   443
slouken@524
   444
#ifndef NSVG_PI
slouken@524
   445
#define NSVG_PI (3.14159265358979323846264338327f)
slouken@524
   446
#endif
slouken@524
   447
slouken@524
   448
static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect)
slouken@524
   449
{
slouken@524
   450
	int i;
slouken@524
   451
	float w = lineWidth * 0.5f;
slouken@524
   452
	float px = p->x, py = p->y;
slouken@524
   453
	float dlx = dy, dly = -dx;
slouken@524
   454
	float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0;
slouken@524
   455
slouken@524
   456
	for (i = 0; i < ncap; i++) {
slouken@524
   457
		float a = (float)i/(float)(ncap-1)*NSVG_PI;
slouken@524
   458
		float ax = cosf(a) * w, ay = sinf(a) * w;
slouken@524
   459
		float x = px - dlx*ax - dx*ay;
slouken@524
   460
		float y = py - dly*ax - dy*ay;
slouken@524
   461
slouken@524
   462
		if (i > 0)
slouken@524
   463
			nsvg__addEdge(r, prevx, prevy, x, y);
slouken@524
   464
slouken@524
   465
		prevx = x;
slouken@524
   466
		prevy = y;
slouken@524
   467
slouken@524
   468
		if (i == 0) {
slouken@524
   469
			lx = x; ly = y;
slouken@524
   470
		} else if (i == ncap-1) {
slouken@524
   471
			rx = x; ry = y;
slouken@524
   472
		}
slouken@524
   473
	}
slouken@524
   474
slouken@524
   475
	if (connect) {
slouken@524
   476
		nsvg__addEdge(r, left->x, left->y, lx, ly);
slouken@524
   477
		nsvg__addEdge(r, rx, ry, right->x, right->y);
slouken@524
   478
	}
slouken@524
   479
slouken@524
   480
	left->x = lx; left->y = ly;
slouken@524
   481
	right->x = rx; right->y = ry;
slouken@524
   482
}
slouken@524
   483
slouken@524
   484
static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
slouken@524
   485
{
slouken@524
   486
	float w = lineWidth * 0.5f;
slouken@524
   487
	float dlx0 = p0->dy, dly0 = -p0->dx;
slouken@524
   488
	float dlx1 = p1->dy, dly1 = -p1->dx;
slouken@524
   489
	float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w);
slouken@524
   490
	float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w);
slouken@524
   491
	float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w);
slouken@524
   492
	float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w);
slouken@524
   493
slouken@524
   494
	nsvg__addEdge(r, lx0, ly0, left->x, left->y);
slouken@524
   495
	nsvg__addEdge(r, lx1, ly1, lx0, ly0);
slouken@524
   496
slouken@524
   497
	nsvg__addEdge(r, right->x, right->y, rx0, ry0);
slouken@524
   498
	nsvg__addEdge(r, rx0, ry0, rx1, ry1);
slouken@524
   499
slouken@524
   500
	left->x = lx1; left->y = ly1;
slouken@524
   501
	right->x = rx1; right->y = ry1;
slouken@524
   502
}
slouken@524
   503
slouken@524
   504
static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
slouken@524
   505
{
slouken@524
   506
	float w = lineWidth * 0.5f;
slouken@524
   507
	float dlx0 = p0->dy, dly0 = -p0->dx;
slouken@524
   508
	float dlx1 = p1->dy, dly1 = -p1->dx;
slouken@524
   509
	float lx0, rx0, lx1, rx1;
slouken@524
   510
	float ly0, ry0, ly1, ry1;
slouken@524
   511
slouken@524
   512
	if (p1->flags & NSVG_PT_LEFT) {
slouken@524
   513
		lx0 = lx1 = p1->x - p1->dmx * w;
slouken@524
   514
		ly0 = ly1 = p1->y - p1->dmy * w;
slouken@524
   515
		nsvg__addEdge(r, lx1, ly1, left->x, left->y);
slouken@524
   516
slouken@524
   517
		rx0 = p1->x + (dlx0 * w);
slouken@524
   518
		ry0 = p1->y + (dly0 * w);
slouken@524
   519
		rx1 = p1->x + (dlx1 * w);
slouken@524
   520
		ry1 = p1->y + (dly1 * w);
slouken@524
   521
		nsvg__addEdge(r, right->x, right->y, rx0, ry0);
slouken@524
   522
		nsvg__addEdge(r, rx0, ry0, rx1, ry1);
slouken@524
   523
	} else {
slouken@524
   524
		lx0 = p1->x - (dlx0 * w);
slouken@524
   525
		ly0 = p1->y - (dly0 * w);
slouken@524
   526
		lx1 = p1->x - (dlx1 * w);
slouken@524
   527
		ly1 = p1->y - (dly1 * w);
slouken@524
   528
		nsvg__addEdge(r, lx0, ly0, left->x, left->y);
slouken@524
   529
		nsvg__addEdge(r, lx1, ly1, lx0, ly0);
slouken@524
   530
slouken@524
   531
		rx0 = rx1 = p1->x + p1->dmx * w;
slouken@524
   532
		ry0 = ry1 = p1->y + p1->dmy * w;
slouken@524
   533
		nsvg__addEdge(r, right->x, right->y, rx1, ry1);
slouken@524
   534
	}
slouken@524
   535
slouken@524
   536
	left->x = lx1; left->y = ly1;
slouken@524
   537
	right->x = rx1; right->y = ry1;
slouken@524
   538
}
slouken@524
   539
slouken@524
   540
static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap)
slouken@524
   541
{
slouken@524
   542
	int i, n;
slouken@524
   543
	float w = lineWidth * 0.5f;
slouken@524
   544
	float dlx0 = p0->dy, dly0 = -p0->dx;
slouken@524
   545
	float dlx1 = p1->dy, dly1 = -p1->dx;
slouken@524
   546
	float a0 = atan2f(dly0, dlx0);
slouken@524
   547
	float a1 = atan2f(dly1, dlx1);
slouken@524
   548
	float da = a1 - a0;
slouken@524
   549
	float lx, ly, rx, ry;
slouken@524
   550
slouken@524
   551
	if (da < NSVG_PI) da += NSVG_PI*2;
slouken@524
   552
	if (da > NSVG_PI) da -= NSVG_PI*2;
slouken@524
   553
slouken@524
   554
	n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap);
slouken@524
   555
	if (n < 2) n = 2;
slouken@524
   556
	if (n > ncap) n = ncap;
slouken@524
   557
slouken@524
   558
	lx = left->x;
slouken@524
   559
	ly = left->y;
slouken@524
   560
	rx = right->x;
slouken@524
   561
	ry = right->y;
slouken@524
   562
slouken@524
   563
	for (i = 0; i < n; i++) {
slouken@524
   564
		float u = (float)i/(float)(n-1);
slouken@524
   565
		float a = a0 + u*da;
slouken@524
   566
		float ax = cosf(a) * w, ay = sinf(a) * w;
slouken@524
   567
		float lx1 = p1->x - ax, ly1 = p1->y - ay;
slouken@524
   568
		float rx1 = p1->x + ax, ry1 = p1->y + ay;
slouken@524
   569
slouken@524
   570
		nsvg__addEdge(r, lx1, ly1, lx, ly);
slouken@524
   571
		nsvg__addEdge(r, rx, ry, rx1, ry1);
slouken@524
   572
slouken@524
   573
		lx = lx1; ly = ly1;
slouken@524
   574
		rx = rx1; ry = ry1;
slouken@524
   575
	}
slouken@524
   576
slouken@524
   577
	left->x = lx; left->y = ly;
slouken@524
   578
	right->x = rx; right->y = ry;
slouken@524
   579
}
slouken@524
   580
slouken@524
   581
static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth)
slouken@524
   582
{
slouken@524
   583
	float w = lineWidth * 0.5f;
slouken@524
   584
	float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w);
slouken@524
   585
	float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w);
slouken@524
   586
slouken@524
   587
	nsvg__addEdge(r, lx, ly, left->x, left->y);
slouken@524
   588
	nsvg__addEdge(r, right->x, right->y, rx, ry);
slouken@524
   589
slouken@524
   590
	left->x = lx; left->y = ly;
slouken@524
   591
	right->x = rx; right->y = ry;
slouken@524
   592
}
slouken@524
   593
slouken@524
   594
static int nsvg__curveDivs(float r, float arc, float tol)
slouken@524
   595
{
slouken@524
   596
	float da = acosf(r / (r + tol)) * 2.0f;
slouken@524
   597
	int divs = (int)ceilf(arc / da);
slouken@524
   598
	if (divs < 2) divs = 2;
slouken@524
   599
	return divs;
slouken@524
   600
}
slouken@524
   601
slouken@524
   602
static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth)
slouken@524
   603
{
slouken@524
   604
	int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol);	// Calculate divisions per half circle.
slouken@524
   605
	NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0};
slouken@524
   606
	NSVGpoint* p0, *p1;
slouken@524
   607
	int j, s, e;
slouken@524
   608
slouken@524
   609
	// Build stroke edges
slouken@524
   610
	if (closed) {
slouken@524
   611
		// Looping
slouken@524
   612
		p0 = &points[npoints-1];
slouken@524
   613
		p1 = &points[0];
slouken@524
   614
		s = 0;
slouken@524
   615
		e = npoints;
slouken@524
   616
	} else {
slouken@524
   617
		// Add cap
slouken@524
   618
		p0 = &points[0];
slouken@524
   619
		p1 = &points[1];
slouken@524
   620
		s = 1;
slouken@524
   621
		e = npoints-1;
slouken@524
   622
	}
slouken@524
   623
slouken@524
   624
	if (closed) {
slouken@524
   625
		nsvg__initClosed(&left, &right, p0, p1, lineWidth);
slouken@524
   626
		firstLeft = left;
slouken@524
   627
		firstRight = right;
slouken@524
   628
	} else {
slouken@524
   629
		// Add cap
slouken@524
   630
		float dx = p1->x - p0->x;
slouken@524
   631
		float dy = p1->y - p0->y;
slouken@524
   632
		nsvg__normalize(&dx, &dy);
slouken@524
   633
		if (lineCap == NSVG_CAP_BUTT)
slouken@524
   634
			nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
slouken@524
   635
		else if (lineCap == NSVG_CAP_SQUARE)
slouken@524
   636
			nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
slouken@524
   637
		else if (lineCap == NSVG_CAP_ROUND)
slouken@524
   638
			nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0);
slouken@524
   639
	}
slouken@524
   640
slouken@524
   641
	for (j = s; j < e; ++j) {
slouken@524
   642
		if (p1->flags & NSVG_PT_CORNER) {
slouken@524
   643
			if (lineJoin == NSVG_JOIN_ROUND)
slouken@524
   644
				nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
slouken@524
   645
			else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL))
slouken@524
   646
				nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
slouken@524
   647
			else
slouken@524
   648
				nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth);
slouken@524
   649
		} else {
slouken@524
   650
			nsvg__straightJoin(r, &left, &right, p1, lineWidth);
slouken@524
   651
		}
slouken@524
   652
		p0 = p1++;
slouken@524
   653
	}
slouken@524
   654
slouken@524
   655
	if (closed) {
slouken@524
   656
		// Loop it
slouken@524
   657
		nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y);
slouken@524
   658
		nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y);
slouken@524
   659
	} else {
slouken@524
   660
		// Add cap
slouken@524
   661
		float dx = p1->x - p0->x;
slouken@524
   662
		float dy = p1->y - p0->y;
slouken@524
   663
		nsvg__normalize(&dx, &dy);
slouken@524
   664
		if (lineCap == NSVG_CAP_BUTT)
slouken@524
   665
			nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
slouken@524
   666
		else if (lineCap == NSVG_CAP_SQUARE)
slouken@524
   667
			nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
slouken@524
   668
		else if (lineCap == NSVG_CAP_ROUND)
slouken@524
   669
			nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1);
slouken@524
   670
	}
slouken@524
   671
}
slouken@524
   672
slouken@524
   673
static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin)
slouken@524
   674
{
slouken@524
   675
	int i, j;
slouken@524
   676
	NSVGpoint* p0, *p1;
slouken@524
   677
slouken@524
   678
	p0 = &r->points[r->npoints-1];
slouken@524
   679
	p1 = &r->points[0];
slouken@524
   680
	for (i = 0; i < r->npoints; i++) {
slouken@524
   681
		// Calculate segment direction and length
slouken@524
   682
		p0->dx = p1->x - p0->x;
slouken@524
   683
		p0->dy = p1->y - p0->y;
slouken@524
   684
		p0->len = nsvg__normalize(&p0->dx, &p0->dy);
slouken@524
   685
		// Advance
slouken@524
   686
		p0 = p1++;
slouken@524
   687
	}
slouken@524
   688
slouken@524
   689
	// calculate joins
slouken@524
   690
	p0 = &r->points[r->npoints-1];
slouken@524
   691
	p1 = &r->points[0];
slouken@524
   692
	for (j = 0; j < r->npoints; j++) {
slouken@524
   693
		float dlx0, dly0, dlx1, dly1, dmr2, cross;
slouken@524
   694
		dlx0 = p0->dy;
slouken@524
   695
		dly0 = -p0->dx;
slouken@524
   696
		dlx1 = p1->dy;
slouken@524
   697
		dly1 = -p1->dx;
slouken@524
   698
		// Calculate extrusions
slouken@524
   699
		p1->dmx = (dlx0 + dlx1) * 0.5f;
slouken@524
   700
		p1->dmy = (dly0 + dly1) * 0.5f;
slouken@524
   701
		dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
slouken@524
   702
		if (dmr2 > 0.000001f) {
slouken@524
   703
			float s2 = 1.0f / dmr2;
slouken@524
   704
			if (s2 > 600.0f) {
slouken@524
   705
				s2 = 600.0f;
slouken@524
   706
			}
slouken@524
   707
			p1->dmx *= s2;
slouken@524
   708
			p1->dmy *= s2;
slouken@524
   709
		}
slouken@524
   710
slouken@524
   711
		// Clear flags, but keep the corner.
slouken@524
   712
		p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0;
slouken@524
   713
slouken@524
   714
		// Keep track of left turns.
slouken@524
   715
		cross = p1->dx * p0->dy - p0->dx * p1->dy;
slouken@524
   716
		if (cross > 0.0f)
slouken@524
   717
			p1->flags |= NSVG_PT_LEFT;
slouken@524
   718
slouken@524
   719
		// Check to see if the corner needs to be beveled.
slouken@524
   720
		if (p1->flags & NSVG_PT_CORNER) {
slouken@524
   721
			if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
slouken@524
   722
				p1->flags |= NSVG_PT_BEVEL;
slouken@524
   723
			}
slouken@524
   724
		}
slouken@524
   725
slouken@524
   726
		p0 = p1++;
slouken@524
   727
	}
slouken@524
   728
}
slouken@524
   729
slouken@524
   730
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
slouken@524
   731
{
slouken@524
   732
	int i, j, closed;
slouken@524
   733
	NSVGpath* path;
slouken@524
   734
	NSVGpoint* p0, *p1;
slouken@524
   735
	float miterLimit = shape->miterLimit;
slouken@524
   736
	int lineJoin = shape->strokeLineJoin;
slouken@524
   737
	int lineCap = shape->strokeLineCap;
slouken@524
   738
	float lineWidth = shape->strokeWidth * scale;
slouken@524
   739
slouken@524
   740
	for (path = shape->paths; path != NULL; path = path->next) {
slouken@524
   741
		// Flatten path
slouken@524
   742
		r->npoints = 0;
slouken@524
   743
		nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
slouken@524
   744
		for (i = 0; i < path->npts-1; i += 3) {
slouken@524
   745
			float* p = &path->pts[i*2];
slouken@524
   746
			nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
slouken@524
   747
		}
slouken@524
   748
		if (r->npoints < 2)
slouken@524
   749
			continue;
slouken@524
   750
slouken@524
   751
		closed = path->closed;
slouken@524
   752
slouken@524
   753
		// If the first and last points are the same, remove the last, mark as closed path.
slouken@524
   754
		p0 = &r->points[r->npoints-1];
slouken@524
   755
		p1 = &r->points[0];
slouken@524
   756
		if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) {
slouken@524
   757
			r->npoints--;
slouken@524
   758
			p0 = &r->points[r->npoints-1];
slouken@524
   759
			closed = 1;
slouken@524
   760
		}
slouken@524
   761
slouken@524
   762
		if (shape->strokeDashCount > 0) {
slouken@524
   763
			int idash = 0, dashState = 1;
slouken@524
   764
			float totalDist = 0, dashLen, allDashLen, dashOffset;
slouken@524
   765
			NSVGpoint cur;
slouken@524
   766
slouken@524
   767
			if (closed)
slouken@524
   768
				nsvg__appendPathPoint(r, r->points[0]);
slouken@524
   769
slouken@524
   770
			// Duplicate points -> points2.
slouken@524
   771
			nsvg__duplicatePoints(r);
slouken@524
   772
slouken@524
   773
			r->npoints = 0;
slouken@524
   774
 			cur = r->points2[0];
slouken@524
   775
			nsvg__appendPathPoint(r, cur);
slouken@524
   776
slouken@524
   777
			// Figure out dash offset.
slouken@524
   778
			allDashLen = 0;
slouken@524
   779
			for (j = 0; j < shape->strokeDashCount; j++)
slouken@524
   780
				allDashLen += shape->strokeDashArray[j];
slouken@524
   781
			if (shape->strokeDashCount & 1)
slouken@524
   782
				allDashLen *= 2.0f;
slouken@524
   783
			// Find location inside pattern
slouken@524
   784
			dashOffset = fmodf(shape->strokeDashOffset, allDashLen);
slouken@524
   785
			if (dashOffset < 0.0f)
slouken@524
   786
				dashOffset += allDashLen;
slouken@524
   787
slouken@524
   788
			while (dashOffset > shape->strokeDashArray[idash]) {
slouken@524
   789
				dashOffset -= shape->strokeDashArray[idash];
slouken@524
   790
				idash = (idash + 1) % shape->strokeDashCount;
slouken@524
   791
			}
slouken@524
   792
			dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
slouken@524
   793
slouken@524
   794
			for (j = 1; j < r->npoints2; ) {
slouken@524
   795
				float dx = r->points2[j].x - cur.x;
slouken@524
   796
				float dy = r->points2[j].y - cur.y;
slouken@524
   797
				float dist = sqrtf(dx*dx + dy*dy);
slouken@524
   798
slouken@524
   799
				if ((totalDist + dist) > dashLen) {
slouken@524
   800
					// Calculate intermediate point
slouken@524
   801
					float d = (dashLen - totalDist) / dist;
slouken@524
   802
					float x = cur.x + dx * d;
slouken@524
   803
					float y = cur.y + dy * d;
slouken@524
   804
					nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER);
slouken@524
   805
slouken@524
   806
					// Stroke
slouken@524
   807
					if (r->npoints > 1 && dashState) {
slouken@524
   808
						nsvg__prepareStroke(r, miterLimit, lineJoin);
slouken@524
   809
						nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
slouken@524
   810
					}
slouken@524
   811
					// Advance dash pattern
slouken@524
   812
					dashState = !dashState;
slouken@524
   813
					idash = (idash+1) % shape->strokeDashCount;
slouken@524
   814
					dashLen = shape->strokeDashArray[idash] * scale;
slouken@524
   815
					// Restart
slouken@524
   816
					cur.x = x;
slouken@524
   817
					cur.y = y;
slouken@524
   818
					cur.flags = NSVG_PT_CORNER;
slouken@524
   819
					totalDist = 0.0f;
slouken@524
   820
					r->npoints = 0;
slouken@524
   821
					nsvg__appendPathPoint(r, cur);
slouken@524
   822
				} else {
slouken@524
   823
					totalDist += dist;
slouken@524
   824
					cur = r->points2[j];
slouken@524
   825
					nsvg__appendPathPoint(r, cur);
slouken@524
   826
					j++;
slouken@524
   827
				}
slouken@524
   828
			}
slouken@524
   829
			// Stroke any leftover path
slouken@524
   830
			if (r->npoints > 1 && dashState)
slouken@524
   831
				nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
slouken@524
   832
		} else {
slouken@524
   833
			nsvg__prepareStroke(r, miterLimit, lineJoin);
slouken@524
   834
			nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth);
slouken@524
   835
		}
slouken@524
   836
	}
slouken@524
   837
}
slouken@524
   838
slouken@524
   839
static int nsvg__cmpEdge(const void *p, const void *q)
slouken@524
   840
{
slouken@524
   841
	const NSVGedge* a = (const NSVGedge*)p;
slouken@524
   842
	const NSVGedge* b = (const NSVGedge*)q;
slouken@524
   843
slouken@524
   844
	if (a->y0 < b->y0) return -1;
slouken@524
   845
	if (a->y0 > b->y0) return  1;
slouken@524
   846
	return 0;
slouken@524
   847
}
slouken@524
   848
slouken@524
   849
slouken@524
   850
static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint)
slouken@524
   851
{
slouken@524
   852
	 NSVGactiveEdge* z;
slouken@527
   853
	 float dxdy;
slouken@524
   854
slouken@524
   855
	if (r->freelist != NULL) {
slouken@524
   856
		// Restore from freelist.
slouken@524
   857
		z = r->freelist;
slouken@524
   858
		r->freelist = z->next;
slouken@524
   859
	} else {
slouken@524
   860
		// Alloc new edge.
slouken@524
   861
		z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge));
slouken@524
   862
		if (z == NULL) return NULL;
slouken@524
   863
	}
slouken@524
   864
slouken@527
   865
	dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
slouken@524
   866
//	STBTT_assert(e->y0 <= start_point);
slouken@524
   867
	// round dx down to avoid going too far
slouken@524
   868
	if (dxdy < 0)
slouken@524
   869
		z->dx = (int)(-floorf(NSVG__FIX * -dxdy));
slouken@524
   870
	else
slouken@524
   871
		z->dx = (int)floorf(NSVG__FIX * dxdy);
slouken@524
   872
	z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0)));
slouken@524
   873
//	z->x -= off_x * FIX;
slouken@524
   874
	z->ey = e->y1;
slouken@524
   875
	z->next = 0;
slouken@524
   876
	z->dir = e->dir;
slouken@524
   877
slouken@524
   878
	return z;
slouken@524
   879
}
slouken@524
   880
slouken@524
   881
static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)
slouken@524
   882
{
slouken@524
   883
	z->next = r->freelist;
slouken@524
   884
	r->freelist = z;
slouken@524
   885
}
slouken@524
   886
slouken@524
   887
static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax)
slouken@524
   888
{
slouken@524
   889
	int i = x0 >> NSVG__FIXSHIFT;
slouken@524
   890
	int j = x1 >> NSVG__FIXSHIFT;
slouken@524
   891
	if (i < *xmin) *xmin = i;
slouken@524
   892
	if (j > *xmax) *xmax = j;
slouken@524
   893
	if (i < len && j >= 0) {
slouken@524
   894
		if (i == j) {
slouken@524
   895
			// x0,x1 are the same pixel, so compute combined coverage
slouken@524
   896
			scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));
slouken@524
   897
		} else {
slouken@524
   898
			if (i >= 0) // add antialiasing for x0
slouken@524
   899
				scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT));
slouken@524
   900
			else
slouken@524
   901
				i = -1; // clip
slouken@524
   902
slouken@524
   903
			if (j < len) // add antialiasing for x1
slouken@524
   904
				scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT));
slouken@524
   905
			else
slouken@524
   906
				j = len; // clip
slouken@524
   907
slouken@524
   908
			for (++i; i < j; ++i) // fill pixels between x0 and x1
slouken@524
   909
				scanline[i] = (unsigned char)(scanline[i] + maxWeight);
slouken@524
   910
		}
slouken@524
   911
	}
slouken@524
   912
}
slouken@524
   913
slouken@524
   914
// note: this routine clips fills that extend off the edges... ideally this
slouken@524
   915
// wouldn't happen, but it could happen if the truetype glyph bounding boxes
slouken@524
   916
// are wrong, or if the user supplies a too-small bitmap
slouken@524
   917
static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)
slouken@524
   918
{
slouken@524
   919
	// non-zero winding fill
slouken@524
   920
	int x0 = 0, w = 0;
slouken@524
   921
slouken@524
   922
	if (fillRule == NSVG_FILLRULE_NONZERO) {
slouken@524
   923
		// Non-zero
slouken@524
   924
		while (e != NULL) {
slouken@524
   925
			if (w == 0) {
slouken@524
   926
				// if we're currently at zero, we need to record the edge start point
slouken@524
   927
				x0 = e->x; w += e->dir;
slouken@524
   928
			} else {
slouken@524
   929
				int x1 = e->x; w += e->dir;
slouken@524
   930
				// if we went to zero, we need to draw
slouken@524
   931
				if (w == 0)
slouken@524
   932
					nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
slouken@524
   933
			}
slouken@524
   934
			e = e->next;
slouken@524
   935
		}
slouken@524
   936
	} else if (fillRule == NSVG_FILLRULE_EVENODD) {
slouken@524
   937
		// Even-odd
slouken@524
   938
		while (e != NULL) {
slouken@524
   939
			if (w == 0) {
slouken@524
   940
				// if we're currently at zero, we need to record the edge start point
slouken@524
   941
				x0 = e->x; w = 1;
slouken@524
   942
			} else {
slouken@524
   943
				int x1 = e->x; w = 0;
slouken@524
   944
				nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
slouken@524
   945
			}
slouken@524
   946
			e = e->next;
slouken@524
   947
		}
slouken@524
   948
	}
slouken@524
   949
}
slouken@524
   950
slouken@524
   951
static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
slouken@524
   952
slouken@524
   953
static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
slouken@524
   954
{
slouken@524
   955
	return (r) | (g << 8) | (b << 16) | (a << 24);
slouken@524
   956
}
slouken@524
   957
slouken@524
   958
static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
slouken@524
   959
{
slouken@524
   960
	int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
slouken@524
   961
	int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8;
slouken@524
   962
	int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8;
slouken@524
   963
	int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8;
slouken@524
   964
	int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8;
slouken@524
   965
	return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
slouken@524
   966
}
slouken@524
   967
slouken@524
   968
static unsigned int nsvg__applyOpacity(unsigned int c, float u)
slouken@524
   969
{
slouken@524
   970
	int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
slouken@524
   971
	int r = (c) & 0xff;
slouken@524
   972
	int g = (c>>8) & 0xff;
slouken@524
   973
	int b = (c>>16) & 0xff;
slouken@524
   974
	int a = (((c>>24) & 0xff)*iu) >> 8;
slouken@524
   975
	return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
slouken@524
   976
}
slouken@524
   977
slouken@527
   978
static int nsvg__div255(int x)
slouken@524
   979
{
slouken@524
   980
    return ((x+1) * 257) >> 16;
slouken@524
   981
}
slouken@524
   982
slouken@524
   983
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
slouken@524
   984
								float tx, float ty, float scale, NSVGcachedPaint* cache)
slouken@524
   985
{
slouken@524
   986
slouken@524
   987
	if (cache->type == NSVG_PAINT_COLOR) {
slouken@524
   988
		int i, cr, cg, cb, ca;
slouken@524
   989
		cr = cache->colors[0] & 0xff;
slouken@524
   990
		cg = (cache->colors[0] >> 8) & 0xff;
slouken@524
   991
		cb = (cache->colors[0] >> 16) & 0xff;
slouken@524
   992
		ca = (cache->colors[0] >> 24) & 0xff;
slouken@524
   993
slouken@524
   994
		for (i = 0; i < count; i++) {
slouken@524
   995
			int r,g,b;
slouken@524
   996
			int a = nsvg__div255((int)cover[0] * ca);
slouken@524
   997
			int ia = 255 - a;
slouken@524
   998
			// Premultiply
slouken@524
   999
			r = nsvg__div255(cr * a);
slouken@524
  1000
			g = nsvg__div255(cg * a);
slouken@524
  1001
			b = nsvg__div255(cb * a);
slouken@524
  1002
slouken@524
  1003
			// Blend over
slouken@524
  1004
			r += nsvg__div255(ia * (int)dst[0]);
slouken@524
  1005
			g += nsvg__div255(ia * (int)dst[1]);
slouken@524
  1006
			b += nsvg__div255(ia * (int)dst[2]);
slouken@524
  1007
			a += nsvg__div255(ia * (int)dst[3]);
slouken@524
  1008
slouken@524
  1009
			dst[0] = (unsigned char)r;
slouken@524
  1010
			dst[1] = (unsigned char)g;
slouken@524
  1011
			dst[2] = (unsigned char)b;
slouken@524
  1012
			dst[3] = (unsigned char)a;
slouken@524
  1013
slouken@524
  1014
			cover++;
slouken@524
  1015
			dst += 4;
slouken@524
  1016
		}
slouken@524
  1017
	} else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) {
slouken@524
  1018
		// TODO: spread modes.
slouken@524
  1019
		// TODO: plenty of opportunities to optimize.
slouken@524
  1020
		float fx, fy, dx, gy;
slouken@524
  1021
		float* t = cache->xform;
slouken@524
  1022
		int i, cr, cg, cb, ca;
slouken@524
  1023
		unsigned int c;
slouken@524
  1024
slouken@524
  1025
		fx = ((float)x - tx) / scale;
slouken@524
  1026
		fy = ((float)y - ty) / scale;
slouken@524
  1027
		dx = 1.0f / scale;
slouken@524
  1028
slouken@524
  1029
		for (i = 0; i < count; i++) {
slouken@524
  1030
			int r,g,b,a,ia;
slouken@524
  1031
			gy = fx*t[1] + fy*t[3] + t[5];
slouken@524
  1032
			c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)];
slouken@524
  1033
			cr = (c) & 0xff;
slouken@524
  1034
			cg = (c >> 8) & 0xff;
slouken@524
  1035
			cb = (c >> 16) & 0xff;
slouken@524
  1036
			ca = (c >> 24) & 0xff;
slouken@524
  1037
slouken@524
  1038
			a = nsvg__div255((int)cover[0] * ca);
slouken@524
  1039
			ia = 255 - a;
slouken@524
  1040
slouken@524
  1041
			// Premultiply
slouken@524
  1042
			r = nsvg__div255(cr * a);
slouken@524
  1043
			g = nsvg__div255(cg * a);
slouken@524
  1044
			b = nsvg__div255(cb * a);
slouken@524
  1045
slouken@524
  1046
			// Blend over
slouken@524
  1047
			r += nsvg__div255(ia * (int)dst[0]);
slouken@524
  1048
			g += nsvg__div255(ia * (int)dst[1]);
slouken@524
  1049
			b += nsvg__div255(ia * (int)dst[2]);
slouken@524
  1050
			a += nsvg__div255(ia * (int)dst[3]);
slouken@524
  1051
slouken@524
  1052
			dst[0] = (unsigned char)r;
slouken@524
  1053
			dst[1] = (unsigned char)g;
slouken@524
  1054
			dst[2] = (unsigned char)b;
slouken@524
  1055
			dst[3] = (unsigned char)a;
slouken@524
  1056
slouken@524
  1057
			cover++;
slouken@524
  1058
			dst += 4;
slouken@524
  1059
			fx += dx;
slouken@524
  1060
		}
slouken@524
  1061
	} else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) {
slouken@524
  1062
		// TODO: spread modes.
slouken@524
  1063
		// TODO: plenty of opportunities to optimize.
slouken@524
  1064
		// TODO: focus (fx,fy)
slouken@524
  1065
		float fx, fy, dx, gx, gy, gd;
slouken@524
  1066
		float* t = cache->xform;
slouken@524
  1067
		int i, cr, cg, cb, ca;
slouken@524
  1068
		unsigned int c;
slouken@524
  1069
slouken@524
  1070
		fx = ((float)x - tx) / scale;
slouken@524
  1071
		fy = ((float)y - ty) / scale;
slouken@524
  1072
		dx = 1.0f / scale;
slouken@524
  1073
slouken@524
  1074
		for (i = 0; i < count; i++) {
slouken@524
  1075
			int r,g,b,a,ia;
slouken@524
  1076
			gx = fx*t[0] + fy*t[2] + t[4];
slouken@524
  1077
			gy = fx*t[1] + fy*t[3] + t[5];
slouken@524
  1078
			gd = sqrtf(gx*gx + gy*gy);
slouken@524
  1079
			c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)];
slouken@524
  1080
			cr = (c) & 0xff;
slouken@524
  1081
			cg = (c >> 8) & 0xff;
slouken@524
  1082
			cb = (c >> 16) & 0xff;
slouken@524
  1083
			ca = (c >> 24) & 0xff;
slouken@524
  1084
slouken@524
  1085
			a = nsvg__div255((int)cover[0] * ca);
slouken@524
  1086
			ia = 255 - a;
slouken@524
  1087
slouken@524
  1088
			// Premultiply
slouken@524
  1089
			r = nsvg__div255(cr * a);
slouken@524
  1090
			g = nsvg__div255(cg * a);
slouken@524
  1091
			b = nsvg__div255(cb * a);
slouken@524
  1092
slouken@524
  1093
			// Blend over
slouken@524
  1094
			r += nsvg__div255(ia * (int)dst[0]);
slouken@524
  1095
			g += nsvg__div255(ia * (int)dst[1]);
slouken@524
  1096
			b += nsvg__div255(ia * (int)dst[2]);
slouken@524
  1097
			a += nsvg__div255(ia * (int)dst[3]);
slouken@524
  1098
slouken@524
  1099
			dst[0] = (unsigned char)r;
slouken@524
  1100
			dst[1] = (unsigned char)g;
slouken@524
  1101
			dst[2] = (unsigned char)b;
slouken@524
  1102
			dst[3] = (unsigned char)a;
slouken@524
  1103
slouken@524
  1104
			cover++;
slouken@524
  1105
			dst += 4;
slouken@524
  1106
			fx += dx;
slouken@524
  1107
		}
slouken@524
  1108
	}
slouken@524
  1109
}
slouken@524
  1110
slouken@524
  1111
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
slouken@524
  1112
{
slouken@524
  1113
	NSVGactiveEdge *active = NULL;
slouken@524
  1114
	int y, s;
slouken@524
  1115
	int e = 0;
slouken@524
  1116
	int maxWeight = (255 / NSVG__SUBSAMPLES);  // weight per vertical scanline
slouken@524
  1117
	int xmin, xmax;
slouken@524
  1118
slouken@524
  1119
	for (y = 0; y < r->height; y++) {
slouken@524
  1120
		memset(r->scanline, 0, r->width);
slouken@524
  1121
		xmin = r->width;
slouken@524
  1122
		xmax = 0;
slouken@524
  1123
		for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
slouken@524
  1124
			// find center of pixel for this scanline
slouken@524
  1125
			float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f;
slouken@524
  1126
			NSVGactiveEdge **step = &active;
slouken@524
  1127
slouken@524
  1128
			// update all active edges;
slouken@524
  1129
			// remove all active edges that terminate before the center of this scanline
slouken@524
  1130
			while (*step) {
slouken@524
  1131
				NSVGactiveEdge *z = *step;
slouken@524
  1132
				if (z->ey <= scany) {
slouken@524
  1133
					*step = z->next; // delete from list
slouken@524
  1134
//					NSVG__assert(z->valid);
slouken@524
  1135
					nsvg__freeActive(r, z);
slouken@524
  1136
				} else {
slouken@524
  1137
					z->x += z->dx; // advance to position for current scanline
slouken@524
  1138
					step = &((*step)->next); // advance through list
slouken@524
  1139
				}
slouken@524
  1140
			}
slouken@524
  1141
slouken@524
  1142
			// resort the list if needed
slouken@524
  1143
			for (;;) {
slouken@524
  1144
				int changed = 0;
slouken@524
  1145
				step = &active;
slouken@524
  1146
				while (*step && (*step)->next) {
slouken@524
  1147
					if ((*step)->x > (*step)->next->x) {
slouken@524
  1148
						NSVGactiveEdge* t = *step;
slouken@524
  1149
						NSVGactiveEdge* q = t->next;
slouken@524
  1150
						t->next = q->next;
slouken@524
  1151
						q->next = t;
slouken@524
  1152
						*step = q;
slouken@524
  1153
						changed = 1;
slouken@524
  1154
					}
slouken@524
  1155
					step = &(*step)->next;
slouken@524
  1156
				}
slouken@524
  1157
				if (!changed) break;
slouken@524
  1158
			}
slouken@524
  1159
slouken@524
  1160
			// insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
slouken@524
  1161
			while (e < r->nedges && r->edges[e].y0 <= scany) {
slouken@524
  1162
				if (r->edges[e].y1 > scany) {
slouken@524
  1163
					NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany);
slouken@524
  1164
					if (z == NULL) break;
slouken@524
  1165
					// find insertion point
slouken@524
  1166
					if (active == NULL) {
slouken@524
  1167
						active = z;
slouken@524
  1168
					} else if (z->x < active->x) {
slouken@524
  1169
						// insert at front
slouken@524
  1170
						z->next = active;
slouken@524
  1171
						active = z;
slouken@524
  1172
					} else {
slouken@524
  1173
						// find thing to insert AFTER
slouken@524
  1174
						NSVGactiveEdge* p = active;
slouken@524
  1175
						while (p->next && p->next->x < z->x)
slouken@524
  1176
							p = p->next;
slouken@524
  1177
						// at this point, p->next->x is NOT < z->x
slouken@524
  1178
						z->next = p->next;
slouken@524
  1179
						p->next = z;
slouken@524
  1180
					}
slouken@524
  1181
				}
slouken@524
  1182
				e++;
slouken@524
  1183
			}
slouken@524
  1184
slouken@524
  1185
			// now process all active edges in non-zero fashion
slouken@524
  1186
			if (active != NULL)
slouken@524
  1187
				nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);
slouken@524
  1188
		}
slouken@524
  1189
		// Blit
slouken@524
  1190
		if (xmin < 0) xmin = 0;
slouken@524
  1191
		if (xmax > r->width-1) xmax = r->width-1;
slouken@524
  1192
		if (xmin <= xmax) {
slouken@524
  1193
			nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
slouken@524
  1194
		}
slouken@524
  1195
	}
slouken@524
  1196
slouken@524
  1197
}
slouken@524
  1198
slouken@524
  1199
static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)
slouken@524
  1200
{
slouken@524
  1201
	int x,y;
slouken@524
  1202
slouken@524
  1203
	// Unpremultiply
slouken@524
  1204
	for (y = 0; y < h; y++) {
slouken@524
  1205
		unsigned char *row = &image[y*stride];
slouken@524
  1206
		for (x = 0; x < w; x++) {
slouken@524
  1207
			int r = row[0], g = row[1], b = row[2], a = row[3];
slouken@524
  1208
			if (a != 0) {
slouken@524
  1209
				row[0] = (unsigned char)(r*255/a);
slouken@524
  1210
				row[1] = (unsigned char)(g*255/a);
slouken@524
  1211
				row[2] = (unsigned char)(b*255/a);
slouken@524
  1212
			}
slouken@524
  1213
			row += 4;
slouken@524
  1214
		}
slouken@524
  1215
	}
slouken@524
  1216
slouken@524
  1217
	// Defringe
slouken@524
  1218
	for (y = 0; y < h; y++) {
slouken@524
  1219
		unsigned char *row = &image[y*stride];
slouken@524
  1220
		for (x = 0; x < w; x++) {
slouken@524
  1221
			int r = 0, g = 0, b = 0, a = row[3], n = 0;
slouken@524
  1222
			if (a == 0) {
slouken@524
  1223
				if (x-1 > 0 && row[-1] != 0) {
slouken@524
  1224
					r += row[-4];
slouken@524
  1225
					g += row[-3];
slouken@524
  1226
					b += row[-2];
slouken@524
  1227
					n++;
slouken@524
  1228
				}
slouken@524
  1229
				if (x+1 < w && row[7] != 0) {
slouken@524
  1230
					r += row[4];
slouken@524
  1231
					g += row[5];
slouken@524
  1232
					b += row[6];
slouken@524
  1233
					n++;
slouken@524
  1234
				}
slouken@524
  1235
				if (y-1 > 0 && row[-stride+3] != 0) {
slouken@524
  1236
					r += row[-stride];
slouken@524
  1237
					g += row[-stride+1];
slouken@524
  1238
					b += row[-stride+2];
slouken@524
  1239
					n++;
slouken@524
  1240
				}
slouken@524
  1241
				if (y+1 < h && row[stride+3] != 0) {
slouken@524
  1242
					r += row[stride];
slouken@524
  1243
					g += row[stride+1];
slouken@524
  1244
					b += row[stride+2];
slouken@524
  1245
					n++;
slouken@524
  1246
				}
slouken@524
  1247
				if (n > 0) {
slouken@524
  1248
					row[0] = (unsigned char)(r/n);
slouken@524
  1249
					row[1] = (unsigned char)(g/n);
slouken@524
  1250
					row[2] = (unsigned char)(b/n);
slouken@524
  1251
				}
slouken@524
  1252
			}
slouken@524
  1253
			row += 4;
slouken@524
  1254
		}
slouken@524
  1255
	}
slouken@524
  1256
}
slouken@524
  1257
slouken@524
  1258
slouken@524
  1259
static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity)
slouken@524
  1260
{
slouken@524
  1261
	int i, j;
slouken@524
  1262
	NSVGgradient* grad;
slouken@524
  1263
slouken@524
  1264
	cache->type = paint->type;
slouken@524
  1265
slouken@524
  1266
	if (paint->type == NSVG_PAINT_COLOR) {
slouken@524
  1267
		cache->colors[0] = nsvg__applyOpacity(paint->color, opacity);
slouken@524
  1268
		return;
slouken@524
  1269
	}
slouken@524
  1270
slouken@524
  1271
	grad = paint->gradient;
slouken@524
  1272
slouken@524
  1273
	cache->spread = grad->spread;
slouken@524
  1274
	memcpy(cache->xform, grad->xform, sizeof(float)*6);
slouken@524
  1275
slouken@524
  1276
	if (grad->nstops == 0) {
slouken@524
  1277
		for (i = 0; i < 256; i++)
slouken@524
  1278
			cache->colors[i] = 0;
slouken@524
  1279
	} if (grad->nstops == 1) {
slouken@524
  1280
		for (i = 0; i < 256; i++)
slouken@524
  1281
			cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity);
slouken@524
  1282
	} else {
slouken@524
  1283
		unsigned int ca, cb = 0;
slouken@524
  1284
		float ua, ub, du, u;
slouken@524
  1285
		int ia, ib, count;
slouken@524
  1286
slouken@524
  1287
		ca = nsvg__applyOpacity(grad->stops[0].color, opacity);
slouken@524
  1288
		ua = nsvg__clampf(grad->stops[0].offset, 0, 1);
slouken@524
  1289
		ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1);
slouken@524
  1290
		ia = (int)(ua * 255.0f);
slouken@524
  1291
		ib = (int)(ub * 255.0f);
slouken@524
  1292
		for (i = 0; i < ia; i++) {
slouken@524
  1293
			cache->colors[i] = ca;
slouken@524
  1294
		}
slouken@524
  1295
slouken@524
  1296
		for (i = 0; i < grad->nstops-1; i++) {
slouken@524
  1297
			ca = nsvg__applyOpacity(grad->stops[i].color, opacity);
slouken@524
  1298
			cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity);
slouken@524
  1299
			ua = nsvg__clampf(grad->stops[i].offset, 0, 1);
slouken@524
  1300
			ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1);
slouken@524
  1301
			ia = (int)(ua * 255.0f);
slouken@524
  1302
			ib = (int)(ub * 255.0f);
slouken@524
  1303
			count = ib - ia;
slouken@524
  1304
			if (count <= 0) continue;
slouken@524
  1305
			u = 0;
slouken@524
  1306
			du = 1.0f / (float)count;
slouken@524
  1307
			for (j = 0; j < count; j++) {
slouken@524
  1308
				cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u);
slouken@524
  1309
				u += du;
slouken@524
  1310
			}
slouken@524
  1311
		}
slouken@524
  1312
slouken@524
  1313
		for (i = ib; i < 256; i++)
slouken@524
  1314
			cache->colors[i] = cb;
slouken@524
  1315
	}
slouken@524
  1316
slouken@524
  1317
}
slouken@524
  1318
slouken@524
  1319
/*
slouken@524
  1320
static void dumpEdges(NSVGrasterizer* r, const char* name)
slouken@524
  1321
{
slouken@524
  1322
	float xmin = 0, xmax = 0, ymin = 0, ymax = 0;
slouken@524
  1323
	NSVGedge *e = NULL;
slouken@524
  1324
	int i;
slouken@524
  1325
	if (r->nedges == 0) return;
slouken@524
  1326
	FILE* fp = fopen(name, "w");
slouken@524
  1327
	if (fp == NULL) return;
slouken@524
  1328
slouken@524
  1329
	xmin = xmax = r->edges[0].x0;
slouken@524
  1330
	ymin = ymax = r->edges[0].y0;
slouken@524
  1331
	for (i = 0; i < r->nedges; i++) {
slouken@524
  1332
		e = &r->edges[i];
slouken@524
  1333
		xmin = nsvg__minf(xmin, e->x0);
slouken@524
  1334
		xmin = nsvg__minf(xmin, e->x1);
slouken@524
  1335
		xmax = nsvg__maxf(xmax, e->x0);
slouken@524
  1336
		xmax = nsvg__maxf(xmax, e->x1);
slouken@524
  1337
		ymin = nsvg__minf(ymin, e->y0);
slouken@524
  1338
		ymin = nsvg__minf(ymin, e->y1);
slouken@524
  1339
		ymax = nsvg__maxf(ymax, e->y0);
slouken@524
  1340
		ymax = nsvg__maxf(ymax, e->y1);
slouken@524
  1341
	}
slouken@524
  1342
slouken@524
  1343
	fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin));
slouken@524
  1344
slouken@524
  1345
	for (i = 0; i < r->nedges; i++) {
slouken@524
  1346
		e = &r->edges[i];
slouken@524
  1347
		fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1);
slouken@524
  1348
	}
slouken@524
  1349
slouken@524
  1350
	for (i = 0; i < r->npoints; i++) {
slouken@524
  1351
		if (i+1 < r->npoints)
slouken@524
  1352
			fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y);
slouken@524
  1353
		fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0");
slouken@524
  1354
	}
slouken@524
  1355
slouken@524
  1356
	fprintf(fp, "</svg>");
slouken@524
  1357
	fclose(fp);
slouken@524
  1358
}
slouken@524
  1359
*/
slouken@524
  1360
slouken@524
  1361
void nsvgRasterize(NSVGrasterizer* r,
slouken@524
  1362
				   NSVGimage* image, float tx, float ty, float scale,
slouken@524
  1363
				   unsigned char* dst, int w, int h, int stride)
slouken@524
  1364
{
slouken@524
  1365
	NSVGshape *shape = NULL;
slouken@524
  1366
	NSVGedge *e = NULL;
slouken@524
  1367
	NSVGcachedPaint cache;
slouken@524
  1368
	int i;
slouken@524
  1369
slouken@524
  1370
	r->bitmap = dst;
slouken@524
  1371
	r->width = w;
slouken@524
  1372
	r->height = h;
slouken@524
  1373
	r->stride = stride;
slouken@524
  1374
slouken@524
  1375
	if (w > r->cscanline) {
slouken@524
  1376
		r->cscanline = w;
slouken@524
  1377
		r->scanline = (unsigned char*)realloc(r->scanline, w);
slouken@524
  1378
		if (r->scanline == NULL) return;
slouken@524
  1379
	}
slouken@524
  1380
slouken@524
  1381
	for (i = 0; i < h; i++)
slouken@524
  1382
		memset(&dst[i*stride], 0, w*4);
slouken@524
  1383
slouken@524
  1384
	for (shape = image->shapes; shape != NULL; shape = shape->next) {
slouken@524
  1385
		if (!(shape->flags & NSVG_FLAGS_VISIBLE))
slouken@524
  1386
			continue;
slouken@524
  1387
slouken@524
  1388
		if (shape->fill.type != NSVG_PAINT_NONE) {
slouken@524
  1389
			nsvg__resetPool(r);
slouken@524
  1390
			r->freelist = NULL;
slouken@524
  1391
			r->nedges = 0;
slouken@524
  1392
slouken@524
  1393
			nsvg__flattenShape(r, shape, scale);
slouken@524
  1394
slouken@524
  1395
			// Scale and translate edges
slouken@524
  1396
			for (i = 0; i < r->nedges; i++) {
slouken@524
  1397
				e = &r->edges[i];
slouken@524
  1398
				e->x0 = tx + e->x0;
slouken@524
  1399
				e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
slouken@524
  1400
				e->x1 = tx + e->x1;
slouken@524
  1401
				e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
slouken@524
  1402
			}
slouken@524
  1403
slouken@524
  1404
			// Rasterize edges
slouken@524
  1405
			qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
slouken@524
  1406
slouken@524
  1407
			// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
slouken@524
  1408
			nsvg__initPaint(&cache, &shape->fill, shape->opacity);
slouken@524
  1409
slouken@524
  1410
			nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
slouken@524
  1411
		}
slouken@524
  1412
		if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
slouken@524
  1413
			nsvg__resetPool(r);
slouken@524
  1414
			r->freelist = NULL;
slouken@524
  1415
			r->nedges = 0;
slouken@524
  1416
slouken@524
  1417
			nsvg__flattenShapeStroke(r, shape, scale);
slouken@524
  1418
slouken@524
  1419
//			dumpEdges(r, "edge.svg");
slouken@524
  1420
slouken@524
  1421
			// Scale and translate edges
slouken@524
  1422
			for (i = 0; i < r->nedges; i++) {
slouken@524
  1423
				e = &r->edges[i];
slouken@524
  1424
				e->x0 = tx + e->x0;
slouken@524
  1425
				e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
slouken@524
  1426
				e->x1 = tx + e->x1;
slouken@524
  1427
				e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
slouken@524
  1428
			}
slouken@524
  1429
slouken@524
  1430
			// Rasterize edges
slouken@524
  1431
			qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
slouken@524
  1432
slouken@524
  1433
			// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
slouken@524
  1434
			nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
slouken@524
  1435
slouken@524
  1436
			nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
slouken@524
  1437
		}
slouken@524
  1438
	}
slouken@524
  1439
slouken@524
  1440
	nsvg__unpremultiplyAlpha(dst, w, h, stride);
slouken@524
  1441
slouken@524
  1442
	r->bitmap = NULL;
slouken@524
  1443
	r->width = 0;
slouken@524
  1444
	r->height = 0;
slouken@524
  1445
	r->stride = 0;
slouken@524
  1446
}
slouken@524
  1447
slouken@524
  1448
#endif