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