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