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