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