Path: blob/main/cad/PrusaSlicer/files/patch-src_libnanosvg_nanosvgrast.h
18157 views
--- src/libnanosvg/nanosvgrast.h.orig 2024-10-16 10:58:04 UTC1+++ src/libnanosvg/nanosvgrast.h2@@ -0,0 +1,1482 @@3+/*4+ * Copyright (c) 2013-14 Mikko Mononen [email protected]5+ *6+ * This software is provided 'as-is', without any express or implied7+ * warranty. In no event will the authors be held liable for any damages8+ * arising from the use of this software.9+ *10+ * Permission is granted to anyone to use this software for any purpose,11+ * including commercial applications, and to alter it and redistribute it12+ * freely, subject to the following restrictions:13+ *14+ * 1. The origin of this software must not be misrepresented; you must not15+ * claim that you wrote the original software. If you use this software16+ * in a product, an acknowledgment in the product documentation would be17+ * appreciated but is not required.18+ * 2. Altered source versions must be plainly marked as such, and must not be19+ * misrepresented as being the original software.20+ * 3. This notice may not be removed or altered from any source distribution.21+ *22+ * The polygon rasterization is heavily based on stb_truetype rasterizer23+ * by Sean Barrett - http://nothings.org/24+ *25+ */26+27+/* Modified by FLTK to support non-square X,Y axes scaling.28+ *29+ * Added: nsvgRasterizeXY()30+*/31+32+33+#ifndef NANOSVGRAST_H34+#define NANOSVGRAST_H35+36+#include "nanosvg.h"37+38+#ifndef NANOSVGRAST_CPLUSPLUS39+#ifdef __cplusplus40+extern "C" {41+#endif42+#endif43+44+typedef struct NSVGrasterizer NSVGrasterizer;45+46+/* Example Usage:47+ // Load SVG48+ NSVGimage* image;49+ image = nsvgParseFromFile("test.svg", "px", 96);50+51+ // Create rasterizer (can be used to render multiple images).52+ struct NSVGrasterizer* rast = nsvgCreateRasterizer();53+ // Allocate memory for image54+ unsigned char* img = malloc(w*h*4);55+ // Rasterize56+ nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);57+58+ // For non-square X,Y scaling, use59+ nsvgRasterizeXY(rast, image, 0,0,1,1, img, w, h, w*4);60+*/61+62+// Allocated rasterizer context.63+static NSVGrasterizer* nsvgCreateRasterizer(void);64+65+// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)66+// r - pointer to rasterizer context67+// image - pointer to image to rasterize68+// tx,ty - image offset (applied after scaling)69+// scale - image scale (assumes square aspect ratio)70+// dst - pointer to destination image data, 4 bytes per pixel (RGBA)71+// w - width of the image to render72+// h - height of the image to render73+// stride - number of bytes per scaleline in the destination buffer74+static void nsvgRasterize(NSVGrasterizer* r,75+ NSVGimage* image, float tx, float ty, float scale,76+ unsigned char* dst, int w, int h, int stride);77+78+// As above, but allow X and Y axes to scale independently for non-square aspects79+static void nsvgRasterizeXY(NSVGrasterizer* r,80+ NSVGimage* image, float tx, float ty,81+ float sx, float sy,82+ unsigned char* dst, int w, int h, int stride);83+84+// Deletes rasterizer context.85+static void nsvgDeleteRasterizer(NSVGrasterizer*);86+87+88+#ifndef NANOSVGRAST_CPLUSPLUS89+#ifdef __cplusplus90+}91+#endif92+#endif93+94+#if 1 || defined(NANOSVGRAST_IMPLEMENTATION)95+96+#include <math.h>97+#include <stdlib.h>98+#include <string.h>99+100+#define NSVG__SUBSAMPLES 5101+#define NSVG__FIXSHIFT 10102+#define NSVG__FIX (1 << NSVG__FIXSHIFT)103+#define NSVG__FIXMASK (NSVG__FIX-1)104+#define NSVG__MEMPAGE_SIZE 1024105+106+typedef struct NSVGedge {107+ float x0,y0, x1,y1;108+ int dir;109+ struct NSVGedge* next;110+} NSVGedge;111+112+typedef struct NSVGpoint {113+ float x, y;114+ float dx, dy;115+ float len;116+ float dmx, dmy;117+ unsigned char flags;118+} NSVGpoint;119+120+typedef struct NSVGactiveEdge {121+ int x,dx;122+ float ey;123+ int dir;124+ struct NSVGactiveEdge *next;125+} NSVGactiveEdge;126+127+typedef struct NSVGmemPage {128+ unsigned char mem[NSVG__MEMPAGE_SIZE];129+ int size;130+ struct NSVGmemPage* next;131+} NSVGmemPage;132+133+typedef struct NSVGcachedPaint {134+ signed char type;135+ char spread;136+ float xform[6];137+ unsigned int colors[256];138+} NSVGcachedPaint;139+140+struct NSVGrasterizer141+{142+ float px, py;143+144+ float tessTol;145+ float distTol;146+147+ NSVGedge* edges;148+ int nedges;149+ int cedges;150+151+ NSVGpoint* points;152+ int npoints;153+ int cpoints;154+155+ NSVGpoint* points2;156+ int npoints2;157+ int cpoints2;158+159+ NSVGactiveEdge* freelist;160+ NSVGmemPage* pages;161+ NSVGmemPage* curpage;162+163+ unsigned char* scanline;164+ int cscanline;165+166+ unsigned char* bitmap;167+ int width, height, stride;168+};169+170+static NSVGrasterizer* nsvgCreateRasterizer(void)171+{172+ NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));173+ if (r == NULL) goto error;174+ memset(r, 0, sizeof(NSVGrasterizer));175+176+ r->tessTol = 0.25f;177+ r->distTol = 0.01f;178+179+ return r;180+181+error:182+ nsvgDeleteRasterizer(r);183+ return NULL;184+}185+186+static void nsvgDeleteRasterizer(NSVGrasterizer* r)187+{188+ NSVGmemPage* p;189+190+ if (r == NULL) return;191+192+ p = r->pages;193+ while (p != NULL) {194+ NSVGmemPage* next = p->next;195+ free(p);196+ p = next;197+ }198+199+ if (r->edges) free(r->edges);200+ if (r->points) free(r->points);201+ if (r->points2) free(r->points2);202+ if (r->scanline) free(r->scanline);203+204+ free(r);205+}206+207+static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)208+{209+ NSVGmemPage *newp;210+211+ // If using existing chain, return the next page in chain212+ if (cur != NULL && cur->next != NULL) {213+ return cur->next;214+ }215+216+ // Alloc new page217+ newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage));218+ if (newp == NULL) return NULL;219+ memset(newp, 0, sizeof(NSVGmemPage));220+221+ // Add to linked list222+ if (cur != NULL)223+ cur->next = newp;224+ else225+ r->pages = newp;226+227+ return newp;228+}229+230+static void nsvg__resetPool(NSVGrasterizer* r)231+{232+ NSVGmemPage* p = r->pages;233+ while (p != NULL) {234+ p->size = 0;235+ p = p->next;236+ }237+ r->curpage = r->pages;238+}239+240+static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)241+{242+ unsigned char* buf;243+ if (size > NSVG__MEMPAGE_SIZE) return NULL;244+ if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) {245+ r->curpage = nsvg__nextPage(r, r->curpage);246+ }247+ buf = &r->curpage->mem[r->curpage->size];248+ r->curpage->size += size;249+ return buf;250+}251+252+static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol)253+{254+ float dx = x2 - x1;255+ float dy = y2 - y1;256+ return dx*dx + dy*dy < tol*tol;257+}258+259+static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)260+{261+ NSVGpoint* pt;262+263+ if (r->npoints > 0) {264+ pt = &r->points[r->npoints-1];265+ if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {266+ pt->flags = (unsigned char)(pt->flags | flags);267+ return;268+ }269+ }270+271+ if (r->npoints+1 > r->cpoints) {272+ r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;273+ r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);274+ if (r->points == NULL) return;275+ }276+277+ pt = &r->points[r->npoints];278+ pt->x = x;279+ pt->y = y;280+ pt->flags = (unsigned char)flags;281+ r->npoints++;282+}283+284+static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)285+{286+ if (r->npoints+1 > r->cpoints) {287+ r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;288+ r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);289+ if (r->points == NULL) return;290+ }291+ r->points[r->npoints] = pt;292+ r->npoints++;293+}294+295+static void nsvg__duplicatePoints(NSVGrasterizer* r)296+{297+ if (r->npoints > r->cpoints2) {298+ r->cpoints2 = r->npoints;299+ r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);300+ if (r->points2 == NULL) return;301+ }302+303+ memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);304+ r->npoints2 = r->npoints;305+}306+307+static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)308+{309+ NSVGedge* e;310+311+ // Skip horizontal edges312+ if (y0 == y1)313+ return;314+315+ if (r->nedges+1 > r->cedges) {316+ r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;317+ r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges);318+ if (r->edges == NULL) return;319+ }320+321+ e = &r->edges[r->nedges];322+ r->nedges++;323+324+ if (y0 < y1) {325+ e->x0 = x0;326+ e->y0 = y0;327+ e->x1 = x1;328+ e->y1 = y1;329+ e->dir = 1;330+ } else {331+ e->x0 = x1;332+ e->y0 = y1;333+ e->x1 = x0;334+ e->y1 = y0;335+ e->dir = -1;336+ }337+}338+339+static float nsvg__normalize(float *x, float* y)340+{341+ float d = sqrtf((*x)*(*x) + (*y)*(*y));342+ if (d > 1e-6f) {343+ float id = 1.0f / d;344+ *x *= id;345+ *y *= id;346+ }347+ return d;348+}349+350+static float nsvg__absf(float x) { return x < 0 ? -x : x; }351+352+static 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+390+static 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 path398+ 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 path404+ nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0);405+ // Build edges406+ 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+411+enum NSVGpointFlags412+{413+ NSVG_PT_CORNER = 0x01,414+ NSVG_PT_BEVEL = 0x02,415+ NSVG_PT_LEFT = 0x04416+};417+418+static 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+432+static 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+450+static 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_PI469+#define NSVG_PI (3.14159265358979323846264338327f)470+#endif471+472+static 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+508+static 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+528+static 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+564+static 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+605+static 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+618+static 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+626+static 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 edges634+ if (closed) {635+ // Looping636+ p0 = &points[npoints-1];637+ p1 = &points[0];638+ s = 0;639+ e = npoints;640+ } else {641+ // Add cap642+ 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 cap654+ 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+ else672+ 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 it681+ 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 cap685+ 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+697+static 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 length706+ p0->dx = p1->x - p0->x;707+ p0->dy = p1->y - p0->y;708+ p0->len = nsvg__normalize(&p0->dx, &p0->dy);709+ // Advance710+ p0 = p1++;711+ }712+713+ // calculate joins714+ 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 extrusions723+ 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+754+static 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 factor763+ const float lineWidth = shape->strokeWidth * sw; // FIXME (?)764+765+ for (path = shape->paths; path != NULL; path = path->next) {766+ // Flatten path767+ 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 pattern809+ 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 point826+ 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+ // Stroke832+ 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 pattern837+ dashState = !dashState;838+ idash = (idash+1) % shape->strokeDashCount;839+ dashLen = shape->strokeDashArray[idash] * sw;840+ // Restart841+ 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 path855+ 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+864+static 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+875+static 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 far892+ if (dxdy < 0)893+ z->dx = (int)(-floorf(NSVG__FIX * -dxdy));894+ else895+ z->dx = (int)floorf(NSVG__FIX * dxdy);896+ z->x = (int)floorf(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+905+static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)906+{907+ z->next = r->freelist;908+ r->freelist = z;909+}910+911+static 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 coverage920+ scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));921+ } else {922+ if (i >= 0) // add antialiasing for x0923+ scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT));924+ else925+ i = -1; // clip926+927+ if (j < len) // add antialiasing for x1928+ scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT));929+ else930+ j = len; // clip931+932+ for (++i; i < j; ++i) // fill pixels between x0 and x1933+ scanline[i] = (unsigned char)(scanline[i] + maxWeight);934+ }935+ }936+}937+938+// note: this routine clips fills that extend off the edges... ideally this939+// wouldn't happen, but it could happen if the truetype glyph bounding boxes940+// are wrong, or if the user supplies a too-small bitmap941+static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)942+{943+ // non-zero winding fill944+ int x0 = 0, w = 0;945+946+ if (fillRule == NSVG_FILLRULE_NONZERO) {947+ // Non-zero948+ while (e != NULL) {949+ if (w == 0) {950+ // if we're currently at zero, we need to record the edge start point951+ 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 draw955+ 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-odd962+ while (e != NULL) {963+ if (w == 0) {964+ // if we're currently at zero, we need to record the edge start point965+ 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+975+static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }976+977+static 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+982+static 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+992+static 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+1002+static inline int nsvg__div255(int x)1003+{1004+ return ((x+1) * 257) >> 16;1005+}1006+1007+static 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+ // Premultiply1023+ r = nsvg__div255(cr * a);1024+ g = nsvg__div255(cg * a);1025+ b = nsvg__div255(cb * a);1026+1027+ // Blend over1028+ 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+ // Premultiply1066+ r = nsvg__div255(cr * a);1067+ g = nsvg__div255(cg * a);1068+ b = nsvg__div255(cb * a);1069+1070+ // Blend over1071+ 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+ // Premultiply1113+ r = nsvg__div255(cr * a);1114+ g = nsvg__div255(cg * a);1115+ b = nsvg__div255(cb * a);1116+1117+ // Blend over1118+ 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+1135+static 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 scanline1141+ 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 scanline1149+ 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 scanline1154+ while (*step) {1155+ NSVGactiveEdge *z = *step;1156+ if (z->ey <= scany) {1157+ *step = z->next; // delete from list1158+// NSVG__assert(z->valid);1159+ nsvg__freeActive(r, z);1160+ } else {1161+ z->x += z->dx; // advance to position for current scanline1162+ step = &((*step)->next); // advance through list1163+ }1164+ }1165+1166+ // resort the list if needed1167+ 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 scanline1185+ 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 point1190+ if (active == NULL) {1191+ active = z;1192+ } else if (z->x < active->x) {1193+ // insert at front1194+ z->next = active;1195+ active = z;1196+ } else {1197+ // find thing to insert AFTER1198+ 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->x1202+ z->next = p->next;1203+ p->next = z;1204+ }1205+ }1206+ e++;1207+ }1208+1209+ // now process all active edges in non-zero fashion1210+ if (active != NULL)1211+ nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);1212+ }1213+ // Blit1214+ 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+1223+static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)1224+{1225+ int x,y;1226+1227+ // Unpremultiply1228+ 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+ // Defringe1242+ 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+1283+static 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->color, opacity);1292+ return;1293+ }1294+1295+ grad = paint->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+ } 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+/*1344+static 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+1385+static void nsvgRasterizeXY(NSVGrasterizer* r,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))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 edges1421+ 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 edges1430+ 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 rule1434+ 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 edges1448+ 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 edges1457+ 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 rule1461+ 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+1475+static void nsvgRasterize(NSVGrasterizer* r,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_IMPLEMENTATION1483+1484+#endif // NANOSVGRAST_H148514861487