123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461 |
- /*
- * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- *
- * The polygon rasterization is heavily based on stb_truetype rasterizer
- * by Sean Barrett - http://nothings.org/
- *
- */
- #ifndef NANOSVGRAST_H
- #define NANOSVGRAST_H
- #include "nanosvg.h"
- #ifndef NANOSVGRAST_CPLUSPLUS
- #ifdef __cplusplus
- extern "C" {
- #endif
- #endif
- typedef struct NSVGrasterizer NSVGrasterizer;
- /* Example Usage:
- // Load SVG
- NSVGimage* image;
- image = nsvgParseFromFile("test.svg", "px", 96);
- // Create rasterizer (can be used to render multiple images).
- struct NSVGrasterizer* rast = nsvgCreateRasterizer();
- // Allocate memory for image
- unsigned char* img = malloc(w*h*4);
- // Rasterize
- nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
- */
- // Allocated rasterizer context.
- NSVGrasterizer* nsvgCreateRasterizer(void);
- // Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
- // r - pointer to rasterizer context
- // image - pointer to image to rasterize
- // tx,ty - image offset (applied after scaling)
- // scale - image scale
- // dst - pointer to destination image data, 4 bytes per pixel (RGBA)
- // w - width of the image to render
- // h - height of the image to render
- // stride - number of bytes per scaleline in the destination buffer
- void nsvgRasterize(NSVGrasterizer* r,
- NSVGimage* image, float tx, float ty, float scale,
- unsigned char* dst, int w, int h, int stride);
- // Deletes rasterizer context.
- void nsvgDeleteRasterizer(NSVGrasterizer*);
- #ifndef NANOSVGRAST_CPLUSPLUS
- #ifdef __cplusplus
- }
- #endif
- #endif
- #ifdef NANOSVGRAST_IMPLEMENTATION
- #include <math.h>
- #include <stdlib.h>
- #include <string.h>
- #define NSVG__SUBSAMPLES 5
- #define NSVG__FIXSHIFT 10
- #define NSVG__FIX (1 << NSVG__FIXSHIFT)
- #define NSVG__FIXMASK (NSVG__FIX-1)
- #define NSVG__MEMPAGE_SIZE 1024
- typedef struct NSVGedge {
- float x0,y0, x1,y1;
- int dir;
- struct NSVGedge* next;
- } NSVGedge;
- typedef struct NSVGpoint {
- float x, y;
- float dx, dy;
- float len;
- float dmx, dmy;
- unsigned char flags;
- } NSVGpoint;
- typedef struct NSVGactiveEdge {
- int x,dx;
- float ey;
- int dir;
- struct NSVGactiveEdge *next;
- } NSVGactiveEdge;
- typedef struct NSVGmemPage {
- unsigned char mem[NSVG__MEMPAGE_SIZE];
- int size;
- struct NSVGmemPage* next;
- } NSVGmemPage;
- typedef struct NSVGcachedPaint {
- signed char type;
- char spread;
- float xform[6];
- unsigned int colors[256];
- } NSVGcachedPaint;
- struct NSVGrasterizer
- {
- float px, py;
- float tessTol;
- float distTol;
- NSVGedge* edges;
- int nedges;
- int cedges;
- NSVGpoint* points;
- int npoints;
- int cpoints;
- NSVGpoint* points2;
- int npoints2;
- int cpoints2;
- NSVGactiveEdge* freelist;
- NSVGmemPage* pages;
- NSVGmemPage* curpage;
- unsigned char* scanline;
- int cscanline;
- unsigned char* bitmap;
- int width, height, stride;
- };
- NSVGrasterizer* nsvgCreateRasterizer(void)
- {
- NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
- if (r == NULL) goto error;
- memset(r, 0, sizeof(NSVGrasterizer));
- r->tessTol = 0.25f;
- r->distTol = 0.01f;
- return r;
- error:
- nsvgDeleteRasterizer(r);
- return NULL;
- }
- void nsvgDeleteRasterizer(NSVGrasterizer* r)
- {
- NSVGmemPage* p;
- if (r == NULL) return;
- p = r->pages;
- while (p != NULL) {
- NSVGmemPage* next = p->next;
- free(p);
- p = next;
- }
- if (r->edges) free(r->edges);
- if (r->points) free(r->points);
- if (r->points2) free(r->points2);
- if (r->scanline) free(r->scanline);
- free(r);
- }
- static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
- {
- NSVGmemPage *newp;
- // If using existing chain, return the next page in chain
- if (cur != NULL && cur->next != NULL) {
- return cur->next;
- }
- // Alloc new page
- newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage));
- if (newp == NULL) return NULL;
- memset(newp, 0, sizeof(NSVGmemPage));
- // Add to linked list
- if (cur != NULL)
- cur->next = newp;
- else
- r->pages = newp;
- return newp;
- }
- static void nsvg__resetPool(NSVGrasterizer* r)
- {
- NSVGmemPage* p = r->pages;
- while (p != NULL) {
- p->size = 0;
- p = p->next;
- }
- r->curpage = r->pages;
- }
- static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)
- {
- unsigned char* buf;
- if (size > NSVG__MEMPAGE_SIZE) return NULL;
- if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) {
- r->curpage = nsvg__nextPage(r, r->curpage);
- }
- buf = &r->curpage->mem[r->curpage->size];
- r->curpage->size += size;
- return buf;
- }
- static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
- {
- float dx = x2 - x1;
- float dy = y2 - y1;
- return dx*dx + dy*dy < tol*tol;
- }
- static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
- {
- NSVGpoint* pt;
- if (r->npoints > 0) {
- pt = &r->points[r->npoints-1];
- if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
- pt->flags = (unsigned char)(pt->flags | flags);
- return;
- }
- }
- if (r->npoints+1 > r->cpoints) {
- r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
- r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
- if (r->points == NULL) return;
- }
- pt = &r->points[r->npoints];
- pt->x = x;
- pt->y = y;
- pt->flags = (unsigned char)flags;
- r->npoints++;
- }
- static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
- {
- if (r->npoints+1 > r->cpoints) {
- r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
- r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
- if (r->points == NULL) return;
- }
- r->points[r->npoints] = pt;
- r->npoints++;
- }
- static void nsvg__duplicatePoints(NSVGrasterizer* r)
- {
- if (r->npoints > r->cpoints2) {
- r->cpoints2 = r->npoints;
- r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);
- if (r->points2 == NULL) return;
- }
- memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);
- r->npoints2 = r->npoints;
- }
- static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)
- {
- NSVGedge* e;
- // Skip horizontal edges
- if (y0 == y1)
- return;
- if (r->nedges+1 > r->cedges) {
- r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
- r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges);
- if (r->edges == NULL) return;
- }
- e = &r->edges[r->nedges];
- r->nedges++;
- if (y0 < y1) {
- e->x0 = x0;
- e->y0 = y0;
- e->x1 = x1;
- e->y1 = y1;
- e->dir = 1;
- } else {
- e->x0 = x1;
- e->y0 = y1;
- e->x1 = x0;
- e->y1 = y0;
- e->dir = -1;
- }
- }
- static float nsvg__normalize(float *x, float* y)
- {
- float d = sqrtf((*x)*(*x) + (*y)*(*y));
- if (d > 1e-6f) {
- float id = 1.0f / d;
- *x *= id;
- *y *= id;
- }
- return d;
- }
- static float nsvg__absf(float x) { return x < 0 ? -x : x; }
- static float nsvg__roundf(float x) { return (x >= 0) ? floorf(x + 0.5) : ceilf(x - 0.5); }
- static void nsvg__flattenCubicBez(NSVGrasterizer* r,
- float x1, float y1, float x2, float y2,
- float x3, float y3, float x4, float y4,
- int level, int type)
- {
- float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
- float dx,dy,d2,d3;
- if (level > 10) return;
- x12 = (x1+x2)*0.5f;
- y12 = (y1+y2)*0.5f;
- x23 = (x2+x3)*0.5f;
- y23 = (y2+y3)*0.5f;
- x34 = (x3+x4)*0.5f;
- y34 = (y3+y4)*0.5f;
- x123 = (x12+x23)*0.5f;
- y123 = (y12+y23)*0.5f;
- dx = x4 - x1;
- dy = y4 - y1;
- d2 = nsvg__absf((x2 - x4) * dy - (y2 - y4) * dx);
- d3 = nsvg__absf((x3 - x4) * dy - (y3 - y4) * dx);
- if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) {
- nsvg__addPathPoint(r, x4, y4, type);
- return;
- }
- x234 = (x23+x34)*0.5f;
- y234 = (y23+y34)*0.5f;
- x1234 = (x123+x234)*0.5f;
- y1234 = (y123+y234)*0.5f;
- nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
- nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
- }
- static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
- {
- int i, j;
- NSVGpath* path;
- for (path = shape->paths; path != NULL; path = path->next) {
- r->npoints = 0;
- // Flatten path
- nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
- for (i = 0; i < path->npts-1; i += 3) {
- float* p = &path->pts[i*2];
- 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);
- }
- // Close path
- nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
- // Build edges
- for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
- nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
- }
- }
- enum NSVGpointFlags
- {
- NSVG_PT_CORNER = 0x01,
- NSVG_PT_BEVEL = 0x02,
- NSVG_PT_LEFT = 0x04
- };
- static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
- {
- float w = lineWidth * 0.5f;
- float dx = p1->x - p0->x;
- float dy = p1->y - p0->y;
- float len = nsvg__normalize(&dx, &dy);
- float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f;
- float dlx = dy, dly = -dx;
- float lx = px - dlx*w, ly = py - dly*w;
- float rx = px + dlx*w, ry = py + dly*w;
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
- }
- static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
- {
- float w = lineWidth * 0.5f;
- float px = p->x, py = p->y;
- float dlx = dy, dly = -dx;
- float lx = px - dlx*w, ly = py - dly*w;
- float rx = px + dlx*w, ry = py + dly*w;
- nsvg__addEdge(r, lx, ly, rx, ry);
- if (connect) {
- nsvg__addEdge(r, left->x, left->y, lx, ly);
- nsvg__addEdge(r, rx, ry, right->x, right->y);
- }
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
- }
- static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
- {
- float w = lineWidth * 0.5f;
- float px = p->x - dx*w, py = p->y - dy*w;
- float dlx = dy, dly = -dx;
- float lx = px - dlx*w, ly = py - dly*w;
- float rx = px + dlx*w, ry = py + dly*w;
- nsvg__addEdge(r, lx, ly, rx, ry);
- if (connect) {
- nsvg__addEdge(r, left->x, left->y, lx, ly);
- nsvg__addEdge(r, rx, ry, right->x, right->y);
- }
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
- }
- #ifndef NSVG_PI
- #define NSVG_PI (3.14159265358979323846264338327f)
- #endif
- static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect)
- {
- int i;
- float w = lineWidth * 0.5f;
- float px = p->x, py = p->y;
- float dlx = dy, dly = -dx;
- float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0;
- for (i = 0; i < ncap; i++) {
- float a = (float)i/(float)(ncap-1)*NSVG_PI;
- float ax = cosf(a) * w, ay = sinf(a) * w;
- float x = px - dlx*ax - dx*ay;
- float y = py - dly*ax - dy*ay;
- if (i > 0)
- nsvg__addEdge(r, prevx, prevy, x, y);
- prevx = x;
- prevy = y;
- if (i == 0) {
- lx = x; ly = y;
- } else if (i == ncap-1) {
- rx = x; ry = y;
- }
- }
- if (connect) {
- nsvg__addEdge(r, left->x, left->y, lx, ly);
- nsvg__addEdge(r, rx, ry, right->x, right->y);
- }
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
- }
- static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
- {
- float w = lineWidth * 0.5f;
- float dlx0 = p0->dy, dly0 = -p0->dx;
- float dlx1 = p1->dy, dly1 = -p1->dx;
- float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w);
- float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w);
- float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w);
- float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w);
- nsvg__addEdge(r, lx0, ly0, left->x, left->y);
- nsvg__addEdge(r, lx1, ly1, lx0, ly0);
- nsvg__addEdge(r, right->x, right->y, rx0, ry0);
- nsvg__addEdge(r, rx0, ry0, rx1, ry1);
- left->x = lx1; left->y = ly1;
- right->x = rx1; right->y = ry1;
- }
- static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
- {
- float w = lineWidth * 0.5f;
- float dlx0 = p0->dy, dly0 = -p0->dx;
- float dlx1 = p1->dy, dly1 = -p1->dx;
- float lx0, rx0, lx1, rx1;
- float ly0, ry0, ly1, ry1;
- if (p1->flags & NSVG_PT_LEFT) {
- lx0 = lx1 = p1->x - p1->dmx * w;
- ly0 = ly1 = p1->y - p1->dmy * w;
- nsvg__addEdge(r, lx1, ly1, left->x, left->y);
- rx0 = p1->x + (dlx0 * w);
- ry0 = p1->y + (dly0 * w);
- rx1 = p1->x + (dlx1 * w);
- ry1 = p1->y + (dly1 * w);
- nsvg__addEdge(r, right->x, right->y, rx0, ry0);
- nsvg__addEdge(r, rx0, ry0, rx1, ry1);
- } else {
- lx0 = p1->x - (dlx0 * w);
- ly0 = p1->y - (dly0 * w);
- lx1 = p1->x - (dlx1 * w);
- ly1 = p1->y - (dly1 * w);
- nsvg__addEdge(r, lx0, ly0, left->x, left->y);
- nsvg__addEdge(r, lx1, ly1, lx0, ly0);
- rx0 = rx1 = p1->x + p1->dmx * w;
- ry0 = ry1 = p1->y + p1->dmy * w;
- nsvg__addEdge(r, right->x, right->y, rx1, ry1);
- }
- left->x = lx1; left->y = ly1;
- right->x = rx1; right->y = ry1;
- }
- static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap)
- {
- int i, n;
- float w = lineWidth * 0.5f;
- float dlx0 = p0->dy, dly0 = -p0->dx;
- float dlx1 = p1->dy, dly1 = -p1->dx;
- float a0 = atan2f(dly0, dlx0);
- float a1 = atan2f(dly1, dlx1);
- float da = a1 - a0;
- float lx, ly, rx, ry;
- if (da < NSVG_PI) da += NSVG_PI*2;
- if (da > NSVG_PI) da -= NSVG_PI*2;
- n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap);
- if (n < 2) n = 2;
- if (n > ncap) n = ncap;
- lx = left->x;
- ly = left->y;
- rx = right->x;
- ry = right->y;
- for (i = 0; i < n; i++) {
- float u = (float)i/(float)(n-1);
- float a = a0 + u*da;
- float ax = cosf(a) * w, ay = sinf(a) * w;
- float lx1 = p1->x - ax, ly1 = p1->y - ay;
- float rx1 = p1->x + ax, ry1 = p1->y + ay;
- nsvg__addEdge(r, lx1, ly1, lx, ly);
- nsvg__addEdge(r, rx, ry, rx1, ry1);
- lx = lx1; ly = ly1;
- rx = rx1; ry = ry1;
- }
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
- }
- static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth)
- {
- float w = lineWidth * 0.5f;
- float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w);
- float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w);
- nsvg__addEdge(r, lx, ly, left->x, left->y);
- nsvg__addEdge(r, right->x, right->y, rx, ry);
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
- }
- static int nsvg__curveDivs(float r, float arc, float tol)
- {
- float da = acosf(r / (r + tol)) * 2.0f;
- int divs = (int)ceilf(arc / da);
- if (divs < 2) divs = 2;
- return divs;
- }
- static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth)
- {
- int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle.
- 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};
- NSVGpoint* p0, *p1;
- int j, s, e;
- // Build stroke edges
- if (closed) {
- // Looping
- p0 = &points[npoints-1];
- p1 = &points[0];
- s = 0;
- e = npoints;
- } else {
- // Add cap
- p0 = &points[0];
- p1 = &points[1];
- s = 1;
- e = npoints-1;
- }
- if (closed) {
- nsvg__initClosed(&left, &right, p0, p1, lineWidth);
- firstLeft = left;
- firstRight = right;
- } else {
- // Add cap
- float dx = p1->x - p0->x;
- float dy = p1->y - p0->y;
- nsvg__normalize(&dx, &dy);
- if (lineCap == NSVG_CAP_BUTT)
- nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
- else if (lineCap == NSVG_CAP_SQUARE)
- nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
- else if (lineCap == NSVG_CAP_ROUND)
- nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0);
- }
- for (j = s; j < e; ++j) {
- if (p1->flags & NSVG_PT_CORNER) {
- if (lineJoin == NSVG_JOIN_ROUND)
- nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
- else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL))
- nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
- else
- nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth);
- } else {
- nsvg__straightJoin(r, &left, &right, p1, lineWidth);
- }
- p0 = p1++;
- }
- if (closed) {
- // Loop it
- nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y);
- nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y);
- } else {
- // Add cap
- float dx = p1->x - p0->x;
- float dy = p1->y - p0->y;
- nsvg__normalize(&dx, &dy);
- if (lineCap == NSVG_CAP_BUTT)
- nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
- else if (lineCap == NSVG_CAP_SQUARE)
- nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
- else if (lineCap == NSVG_CAP_ROUND)
- nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1);
- }
- }
- static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin)
- {
- int i, j;
- NSVGpoint* p0, *p1;
- p0 = &r->points[r->npoints-1];
- p1 = &r->points[0];
- for (i = 0; i < r->npoints; i++) {
- // Calculate segment direction and length
- p0->dx = p1->x - p0->x;
- p0->dy = p1->y - p0->y;
- p0->len = nsvg__normalize(&p0->dx, &p0->dy);
- // Advance
- p0 = p1++;
- }
- // calculate joins
- p0 = &r->points[r->npoints-1];
- p1 = &r->points[0];
- for (j = 0; j < r->npoints; j++) {
- float dlx0, dly0, dlx1, dly1, dmr2, cross;
- dlx0 = p0->dy;
- dly0 = -p0->dx;
- dlx1 = p1->dy;
- dly1 = -p1->dx;
- // Calculate extrusions
- p1->dmx = (dlx0 + dlx1) * 0.5f;
- p1->dmy = (dly0 + dly1) * 0.5f;
- dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
- if (dmr2 > 0.000001f) {
- float s2 = 1.0f / dmr2;
- if (s2 > 600.0f) {
- s2 = 600.0f;
- }
- p1->dmx *= s2;
- p1->dmy *= s2;
- }
- // Clear flags, but keep the corner.
- p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0;
- // Keep track of left turns.
- cross = p1->dx * p0->dy - p0->dx * p1->dy;
- if (cross > 0.0f)
- p1->flags |= NSVG_PT_LEFT;
- // Check to see if the corner needs to be beveled.
- if (p1->flags & NSVG_PT_CORNER) {
- if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
- p1->flags |= NSVG_PT_BEVEL;
- }
- }
- p0 = p1++;
- }
- }
- static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
- {
- int i, j, closed;
- NSVGpath* path;
- NSVGpoint* p0, *p1;
- float miterLimit = shape->miterLimit;
- int lineJoin = shape->strokeLineJoin;
- int lineCap = shape->strokeLineCap;
- float lineWidth = shape->strokeWidth * scale;
- for (path = shape->paths; path != NULL; path = path->next) {
- // Flatten path
- r->npoints = 0;
- nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
- for (i = 0; i < path->npts-1; i += 3) {
- float* p = &path->pts[i*2];
- 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);
- }
- if (r->npoints < 2)
- continue;
- closed = path->closed;
- // If the first and last points are the same, remove the last, mark as closed path.
- p0 = &r->points[r->npoints-1];
- p1 = &r->points[0];
- if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) {
- r->npoints--;
- p0 = &r->points[r->npoints-1];
- closed = 1;
- }
- if (shape->strokeDashCount > 0) {
- int idash = 0, dashState = 1;
- float totalDist = 0, dashLen, allDashLen, dashOffset;
- NSVGpoint cur;
- if (closed)
- nsvg__appendPathPoint(r, r->points[0]);
- // Duplicate points -> points2.
- nsvg__duplicatePoints(r);
- r->npoints = 0;
- cur = r->points2[0];
- nsvg__appendPathPoint(r, cur);
- // Figure out dash offset.
- allDashLen = 0;
- for (j = 0; j < shape->strokeDashCount; j++)
- allDashLen += shape->strokeDashArray[j];
- if (shape->strokeDashCount & 1)
- allDashLen *= 2.0f;
- // Find location inside pattern
- dashOffset = fmodf(shape->strokeDashOffset, allDashLen);
- if (dashOffset < 0.0f)
- dashOffset += allDashLen;
- while (dashOffset > shape->strokeDashArray[idash]) {
- dashOffset -= shape->strokeDashArray[idash];
- idash = (idash + 1) % shape->strokeDashCount;
- }
- dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
- for (j = 1; j < r->npoints2; ) {
- float dx = r->points2[j].x - cur.x;
- float dy = r->points2[j].y - cur.y;
- float dist = sqrtf(dx*dx + dy*dy);
- if ((totalDist + dist) > dashLen) {
- // Calculate intermediate point
- float d = (dashLen - totalDist) / dist;
- float x = cur.x + dx * d;
- float y = cur.y + dy * d;
- nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER);
- // Stroke
- if (r->npoints > 1 && dashState) {
- nsvg__prepareStroke(r, miterLimit, lineJoin);
- nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
- }
- // Advance dash pattern
- dashState = !dashState;
- idash = (idash+1) % shape->strokeDashCount;
- dashLen = shape->strokeDashArray[idash] * scale;
- // Restart
- cur.x = x;
- cur.y = y;
- cur.flags = NSVG_PT_CORNER;
- totalDist = 0.0f;
- r->npoints = 0;
- nsvg__appendPathPoint(r, cur);
- } else {
- totalDist += dist;
- cur = r->points2[j];
- nsvg__appendPathPoint(r, cur);
- j++;
- }
- }
- // Stroke any leftover path
- if (r->npoints > 1 && dashState)
- nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
- } else {
- nsvg__prepareStroke(r, miterLimit, lineJoin);
- nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth);
- }
- }
- }
- static int nsvg__cmpEdge(const void *p, const void *q)
- {
- const NSVGedge* a = (const NSVGedge*)p;
- const NSVGedge* b = (const NSVGedge*)q;
- if (a->y0 < b->y0) return -1;
- if (a->y0 > b->y0) return 1;
- return 0;
- }
- static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint)
- {
- NSVGactiveEdge* z;
- if (r->freelist != NULL) {
- // Restore from freelist.
- z = r->freelist;
- r->freelist = z->next;
- } else {
- // Alloc new edge.
- z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge));
- if (z == NULL) return NULL;
- }
- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
- // STBTT_assert(e->y0 <= start_point);
- // round dx down to avoid going too far
- if (dxdy < 0)
- z->dx = (int)(-nsvg__roundf(NSVG__FIX * -dxdy));
- else
- z->dx = (int)nsvg__roundf(NSVG__FIX * dxdy);
- z->x = (int)nsvg__roundf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0)));
- // z->x -= off_x * FIX;
- z->ey = e->y1;
- z->next = 0;
- z->dir = e->dir;
- return z;
- }
- static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)
- {
- z->next = r->freelist;
- r->freelist = z;
- }
- static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax)
- {
- int i = x0 >> NSVG__FIXSHIFT;
- int j = x1 >> NSVG__FIXSHIFT;
- if (i < *xmin) *xmin = i;
- if (j > *xmax) *xmax = j;
- if (i < len && j >= 0) {
- if (i == j) {
- // x0,x1 are the same pixel, so compute combined coverage
- scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));
- } else {
- if (i >= 0) // add antialiasing for x0
- scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT));
- else
- i = -1; // clip
- if (j < len) // add antialiasing for x1
- scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT));
- else
- j = len; // clip
- for (++i; i < j; ++i) // fill pixels between x0 and x1
- scanline[i] = (unsigned char)(scanline[i] + maxWeight);
- }
- }
- }
- // note: this routine clips fills that extend off the edges... ideally this
- // wouldn't happen, but it could happen if the truetype glyph bounding boxes
- // are wrong, or if the user supplies a too-small bitmap
- static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)
- {
- // non-zero winding fill
- int x0 = 0, w = 0;
- if (fillRule == NSVG_FILLRULE_NONZERO) {
- // Non-zero
- while (e != NULL) {
- if (w == 0) {
- // if we're currently at zero, we need to record the edge start point
- x0 = e->x; w += e->dir;
- } else {
- int x1 = e->x; w += e->dir;
- // if we went to zero, we need to draw
- if (w == 0)
- nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
- }
- e = e->next;
- }
- } else if (fillRule == NSVG_FILLRULE_EVENODD) {
- // Even-odd
- while (e != NULL) {
- if (w == 0) {
- // if we're currently at zero, we need to record the edge start point
- x0 = e->x; w = 1;
- } else {
- int x1 = e->x; w = 0;
- nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
- }
- e = e->next;
- }
- }
- }
- static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
- static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
- {
- return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24);
- }
- static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
- {
- int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
- int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8;
- int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8;
- int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8;
- int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8;
- return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
- }
- static unsigned int nsvg__applyOpacity(unsigned int c, float u)
- {
- int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
- int r = (c) & 0xff;
- int g = (c>>8) & 0xff;
- int b = (c>>16) & 0xff;
- int a = (((c>>24) & 0xff)*iu) >> 8;
- return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
- }
- static inline int nsvg__div255(int x)
- {
- return ((x+1) * 257) >> 16;
- }
- static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
- float tx, float ty, float scale, NSVGcachedPaint* cache)
- {
- if (cache->type == NSVG_PAINT_COLOR) {
- int i, cr, cg, cb, ca;
- cr = cache->colors[0] & 0xff;
- cg = (cache->colors[0] >> 8) & 0xff;
- cb = (cache->colors[0] >> 16) & 0xff;
- ca = (cache->colors[0] >> 24) & 0xff;
- for (i = 0; i < count; i++) {
- int r,g,b;
- int a = nsvg__div255((int)cover[0] * ca);
- int ia = 255 - a;
- // Premultiply
- r = nsvg__div255(cr * a);
- g = nsvg__div255(cg * a);
- b = nsvg__div255(cb * a);
- // Blend over
- r += nsvg__div255(ia * (int)dst[0]);
- g += nsvg__div255(ia * (int)dst[1]);
- b += nsvg__div255(ia * (int)dst[2]);
- a += nsvg__div255(ia * (int)dst[3]);
- dst[0] = (unsigned char)r;
- dst[1] = (unsigned char)g;
- dst[2] = (unsigned char)b;
- dst[3] = (unsigned char)a;
- cover++;
- dst += 4;
- }
- } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) {
- // TODO: spread modes.
- // TODO: plenty of opportunities to optimize.
- float fx, fy, dx, gy;
- float* t = cache->xform;
- int i, cr, cg, cb, ca;
- unsigned int c;
- fx = ((float)x - tx) / scale;
- fy = ((float)y - ty) / scale;
- dx = 1.0f / scale;
- for (i = 0; i < count; i++) {
- int r,g,b,a,ia;
- gy = fx*t[1] + fy*t[3] + t[5];
- c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)];
- cr = (c) & 0xff;
- cg = (c >> 8) & 0xff;
- cb = (c >> 16) & 0xff;
- ca = (c >> 24) & 0xff;
- a = nsvg__div255((int)cover[0] * ca);
- ia = 255 - a;
- // Premultiply
- r = nsvg__div255(cr * a);
- g = nsvg__div255(cg * a);
- b = nsvg__div255(cb * a);
- // Blend over
- r += nsvg__div255(ia * (int)dst[0]);
- g += nsvg__div255(ia * (int)dst[1]);
- b += nsvg__div255(ia * (int)dst[2]);
- a += nsvg__div255(ia * (int)dst[3]);
- dst[0] = (unsigned char)r;
- dst[1] = (unsigned char)g;
- dst[2] = (unsigned char)b;
- dst[3] = (unsigned char)a;
- cover++;
- dst += 4;
- fx += dx;
- }
- } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) {
- // TODO: spread modes.
- // TODO: plenty of opportunities to optimize.
- // TODO: focus (fx,fy)
- float fx, fy, dx, gx, gy, gd;
- float* t = cache->xform;
- int i, cr, cg, cb, ca;
- unsigned int c;
- fx = ((float)x - tx) / scale;
- fy = ((float)y - ty) / scale;
- dx = 1.0f / scale;
- for (i = 0; i < count; i++) {
- int r,g,b,a,ia;
- gx = fx*t[0] + fy*t[2] + t[4];
- gy = fx*t[1] + fy*t[3] + t[5];
- gd = sqrtf(gx*gx + gy*gy);
- c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)];
- cr = (c) & 0xff;
- cg = (c >> 8) & 0xff;
- cb = (c >> 16) & 0xff;
- ca = (c >> 24) & 0xff;
- a = nsvg__div255((int)cover[0] * ca);
- ia = 255 - a;
- // Premultiply
- r = nsvg__div255(cr * a);
- g = nsvg__div255(cg * a);
- b = nsvg__div255(cb * a);
- // Blend over
- r += nsvg__div255(ia * (int)dst[0]);
- g += nsvg__div255(ia * (int)dst[1]);
- b += nsvg__div255(ia * (int)dst[2]);
- a += nsvg__div255(ia * (int)dst[3]);
- dst[0] = (unsigned char)r;
- dst[1] = (unsigned char)g;
- dst[2] = (unsigned char)b;
- dst[3] = (unsigned char)a;
- cover++;
- dst += 4;
- fx += dx;
- }
- }
- }
- static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
- {
- NSVGactiveEdge *active = NULL;
- int y, s;
- int e = 0;
- int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline
- int xmin, xmax;
- for (y = 0; y < r->height; y++) {
- memset(r->scanline, 0, r->width);
- xmin = r->width;
- xmax = 0;
- for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
- // find center of pixel for this scanline
- float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f;
- NSVGactiveEdge **step = &active;
- // update all active edges;
- // remove all active edges that terminate before the center of this scanline
- while (*step) {
- NSVGactiveEdge *z = *step;
- if (z->ey <= scany) {
- *step = z->next; // delete from list
- // NSVG__assert(z->valid);
- nsvg__freeActive(r, z);
- } else {
- z->x += z->dx; // advance to position for current scanline
- step = &((*step)->next); // advance through list
- }
- }
- // resort the list if needed
- for (;;) {
- int changed = 0;
- step = &active;
- while (*step && (*step)->next) {
- if ((*step)->x > (*step)->next->x) {
- NSVGactiveEdge* t = *step;
- NSVGactiveEdge* q = t->next;
- t->next = q->next;
- q->next = t;
- *step = q;
- changed = 1;
- }
- step = &(*step)->next;
- }
- if (!changed) break;
- }
- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
- while (e < r->nedges && r->edges[e].y0 <= scany) {
- if (r->edges[e].y1 > scany) {
- NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany);
- if (z == NULL) break;
- // find insertion point
- if (active == NULL) {
- active = z;
- } else if (z->x < active->x) {
- // insert at front
- z->next = active;
- active = z;
- } else {
- // find thing to insert AFTER
- NSVGactiveEdge* p = active;
- while (p->next && p->next->x < z->x)
- p = p->next;
- // at this point, p->next->x is NOT < z->x
- z->next = p->next;
- p->next = z;
- }
- }
- e++;
- }
- // now process all active edges in non-zero fashion
- if (active != NULL)
- nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);
- }
- // Blit
- if (xmin < 0) xmin = 0;
- if (xmax > r->width-1) xmax = r->width-1;
- if (xmin <= xmax) {
- nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
- }
- }
- }
- static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)
- {
- int x,y;
- // Unpremultiply
- for (y = 0; y < h; y++) {
- unsigned char *row = &image[y*stride];
- for (x = 0; x < w; x++) {
- int r = row[0], g = row[1], b = row[2], a = row[3];
- if (a != 0) {
- row[0] = (unsigned char)(r*255/a);
- row[1] = (unsigned char)(g*255/a);
- row[2] = (unsigned char)(b*255/a);
- }
- row += 4;
- }
- }
- // Defringe
- for (y = 0; y < h; y++) {
- unsigned char *row = &image[y*stride];
- for (x = 0; x < w; x++) {
- int r = 0, g = 0, b = 0, a = row[3], n = 0;
- if (a == 0) {
- if (x-1 > 0 && row[-1] != 0) {
- r += row[-4];
- g += row[-3];
- b += row[-2];
- n++;
- }
- if (x+1 < w && row[7] != 0) {
- r += row[4];
- g += row[5];
- b += row[6];
- n++;
- }
- if (y-1 > 0 && row[-stride+3] != 0) {
- r += row[-stride];
- g += row[-stride+1];
- b += row[-stride+2];
- n++;
- }
- if (y+1 < h && row[stride+3] != 0) {
- r += row[stride];
- g += row[stride+1];
- b += row[stride+2];
- n++;
- }
- if (n > 0) {
- row[0] = (unsigned char)(r/n);
- row[1] = (unsigned char)(g/n);
- row[2] = (unsigned char)(b/n);
- }
- }
- row += 4;
- }
- }
- }
- static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity)
- {
- int i, j;
- NSVGgradient* grad;
- cache->type = paint->type;
- if (paint->type == NSVG_PAINT_COLOR) {
- cache->colors[0] = nsvg__applyOpacity(paint->color, opacity);
- return;
- }
- grad = paint->gradient;
- cache->spread = grad->spread;
- memcpy(cache->xform, grad->xform, sizeof(float)*6);
- if (grad->nstops == 0) {
- for (i = 0; i < 256; i++)
- cache->colors[i] = 0;
- } else if (grad->nstops == 1) {
- unsigned int color = nsvg__applyOpacity(grad->stops[0].color, opacity);
- for (i = 0; i < 256; i++)
- cache->colors[i] = color;
- } else {
- unsigned int ca, cb = 0;
- float ua, ub, du, u;
- int ia, ib, count;
- ca = nsvg__applyOpacity(grad->stops[0].color, opacity);
- ua = nsvg__clampf(grad->stops[0].offset, 0, 1);
- ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1);
- ia = (int)(ua * 255.0f);
- ib = (int)(ub * 255.0f);
- for (i = 0; i < ia; i++) {
- cache->colors[i] = ca;
- }
- for (i = 0; i < grad->nstops-1; i++) {
- ca = nsvg__applyOpacity(grad->stops[i].color, opacity);
- cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity);
- ua = nsvg__clampf(grad->stops[i].offset, 0, 1);
- ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1);
- ia = (int)(ua * 255.0f);
- ib = (int)(ub * 255.0f);
- count = ib - ia;
- if (count <= 0) continue;
- u = 0;
- du = 1.0f / (float)count;
- for (j = 0; j < count; j++) {
- cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u);
- u += du;
- }
- }
- for (i = ib; i < 256; i++)
- cache->colors[i] = cb;
- }
- }
- /*
- static void dumpEdges(NSVGrasterizer* r, const char* name)
- {
- float xmin = 0, xmax = 0, ymin = 0, ymax = 0;
- NSVGedge *e = NULL;
- int i;
- if (r->nedges == 0) return;
- FILE* fp = fopen(name, "w");
- if (fp == NULL) return;
- xmin = xmax = r->edges[0].x0;
- ymin = ymax = r->edges[0].y0;
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- xmin = nsvg__minf(xmin, e->x0);
- xmin = nsvg__minf(xmin, e->x1);
- xmax = nsvg__maxf(xmax, e->x0);
- xmax = nsvg__maxf(xmax, e->x1);
- ymin = nsvg__minf(ymin, e->y0);
- ymin = nsvg__minf(ymin, e->y1);
- ymax = nsvg__maxf(ymax, e->y0);
- ymax = nsvg__maxf(ymax, e->y1);
- }
- fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin));
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1);
- }
- for (i = 0; i < r->npoints; i++) {
- if (i+1 < r->npoints)
- 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);
- 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");
- }
- fprintf(fp, "</svg>");
- fclose(fp);
- }
- */
- void nsvgRasterize(NSVGrasterizer* r,
- NSVGimage* image, float tx, float ty, float scale,
- unsigned char* dst, int w, int h, int stride)
- {
- NSVGshape *shape = NULL;
- NSVGedge *e = NULL;
- NSVGcachedPaint cache;
- int i;
- r->bitmap = dst;
- r->width = w;
- r->height = h;
- r->stride = stride;
- if (w > r->cscanline) {
- r->cscanline = w;
- r->scanline = (unsigned char*)realloc(r->scanline, w);
- if (r->scanline == NULL) return;
- }
- for (i = 0; i < h; i++)
- memset(&dst[i*stride], 0, w*4);
- for (shape = image->shapes; shape != NULL; shape = shape->next) {
- if (!(shape->flags & NSVG_FLAGS_VISIBLE))
- continue;
- if (shape->fill.type != NSVG_PAINT_NONE) {
- nsvg__resetPool(r);
- r->freelist = NULL;
- r->nedges = 0;
- nsvg__flattenShape(r, shape, scale);
- // Scale and translate edges
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- e->x0 = tx + e->x0;
- e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
- e->x1 = tx + e->x1;
- e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
- }
- // Rasterize edges
- if (r->nedges != 0)
- qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
- // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
- nsvg__initPaint(&cache, &shape->fill, shape->opacity);
- nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
- }
- if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
- nsvg__resetPool(r);
- r->freelist = NULL;
- r->nedges = 0;
- nsvg__flattenShapeStroke(r, shape, scale);
- // dumpEdges(r, "edge.svg");
- // Scale and translate edges
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- e->x0 = tx + e->x0;
- e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
- e->x1 = tx + e->x1;
- e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
- }
- // Rasterize edges
- if (r->nedges != 0)
- qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
- // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
- nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
- nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
- }
- }
- nsvg__unpremultiplyAlpha(dst, w, h, stride);
- r->bitmap = NULL;
- r->width = 0;
- r->height = 0;
- r->stride = 0;
- }
- #endif // NANOSVGRAST_IMPLEMENTATION
- #endif // NANOSVGRAST_H
|