Added gradients

- renamed NSVGParser to NSVGparser
- first stab at parsing gradients
- first stab at rendering gradients
- fixed overflow in rasterizer
This commit is contained in:
Mikko Mononen 2014-01-30 23:33:41 +02:00
parent 135a658741
commit 47864e21bc
3 changed files with 691 additions and 148 deletions

View File

@ -55,10 +55,11 @@ nsvgDelete(image);
In order to use NanoSVG in your own project, just copy nanosvg.h to your project. In order to use NanoSVG in your own project, just copy nanosvg.h to your project.
In one C/C++ define `NANOSVG_IMPLEMENTATION` before including the library to expand the NanoSVG implementation in that file. In one C/C++ define `NANOSVG_IMPLEMENTATION` before including the library to expand the NanoSVG implementation in that file.
NanoSVG depends on `stdio.h` and `math.h`, they should be included where the implementation is expanded before including NanoSVG. NanoSVG depends on `stdio.h` ,`string.h` and `math.h`, they should be included where the implementation is expanded before including NanoSVG.
``` C ``` C
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <math.h> #include <math.h>
#define NANOSVG_IMPLEMENTATION // Expands implementation #define NANOSVG_IMPLEMENTATION // Expands implementation
#include "nanosvg.h" #include "nanosvg.h"
@ -68,6 +69,7 @@ By default, NanoSVG parses only the most common colors. In order to get support
``` C ``` C
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <math.h> #include <math.h>
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords. #define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
#define NANOSVG_IMPLEMENTATION // Expands implementation #define NANOSVG_IMPLEMENTATION // Expands implementation

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,13 @@ struct NSVGmemPage {
struct NSVGmemPage* next; struct NSVGmemPage* next;
}; };
struct NSVGcachedPaint {
char type;
char spread;
float xform[6];
unsigned int colors[256];
};
struct NSVGrasterizer struct NSVGrasterizer
{ {
float px, py; float px, py;
@ -259,11 +266,10 @@ static void nsvg__flattenCubicBez(struct NSVGrasterizer* r,
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1); nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1);
} }
static void nsvg__flattenShape(struct NSVGrasterizer* r, static void nsvg__flattenShape(struct NSVGrasterizer* r, struct NSVGshape* shape, float scale)
struct NSVGshape* shape, float tx, float ty, float scale)
{ {
struct NSVGpath* path; struct NSVGpath* path;
float tol = 0.5f * 4.0f / scale; float tol = 0.25f * 4.0f / scale;
int i; int i;
for (path = shape->paths; path != NULL; path = path->next) { for (path = shape->paths; path != NULL; path = path->next) {
@ -371,41 +377,152 @@ static void nsvg__fillActiveEdges(unsigned char* scanline, int len, struct NSVGa
} }
} }
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, unsigned int color) 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)
{ {
int x, cr, cg, cb, ca; return (r) | (g << 8) | (b << 16) | (a << 24);
}
cr = color & 0xff; static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
cg = (color >> 8) & 0xff; {
cb = (color >> 16) & 0xff; int iu = (float)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
ca = (color >> 24) & 0xff; 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(r,g,b,a);
}
for (x = 0; x < count; x++) { static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
int r,g,b; float tx, float ty, float scale, struct NSVGcachedPaint* cache)
int a = ((int)cover[0] * ca) >> 8; {
int ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
// Blend over if (cache->type == NSVG_PAINT_COLOR) {
r += ((ia * (int)dst[0]) >> 8); int i, cr, cg, cb, ca;
g += ((ia * (int)dst[1]) >> 8); cr = cache->colors[0] & 0xff;
b += ((ia * (int)dst[2]) >> 8); cg = (cache->colors[0] >> 8) & 0xff;
a += ((ia * (int)dst[3]) >> 8); cb = (cache->colors[0] >> 16) & 0xff;
ca = (cache->colors[0] >> 24) & 0xff;
dst[0] = (unsigned char)r; for (i = 0; i < count; i++) {
dst[1] = (unsigned char)g; int r,g,b;
dst[2] = (unsigned char)b; int a = ((int)cover[0] * ca) >> 8;
dst[3] = (unsigned char)a; int ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
cover++; // Blend over
dst += 4; r += ((ia * (int)dst[0]) >> 8);
g += ((ia * (int)dst[1]) >> 8);
b += ((ia * (int)dst[2]) >> 8);
a += ((ia * (int)dst[3]) >> 8);
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 = (x - tx) / scale;
fy = (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 = ((int)cover[0] * ca) >> 8;
ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
// Blend over
r += ((ia * (int)dst[0]) >> 8);
g += ((ia * (int)dst[1]) >> 8);
b += ((ia * (int)dst[2]) >> 8);
a += ((ia * (int)dst[3]) >> 8);
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 = (x - tx) / scale;
fy = (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 = ((int)cover[0] * ca) >> 8;
ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
// Blend over
r += ((ia * (int)dst[0]) >> 8);
g += ((ia * (int)dst[1]) >> 8);
b += ((ia * (int)dst[2]) >> 8);
a += ((ia * (int)dst[3]) >> 8);
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(struct NSVGrasterizer *r, unsigned int color) static void nsvg__rasterizeSortedEdges(struct NSVGrasterizer *r, float tx, float ty, float scale, struct NSVGcachedPaint* cache)
{ {
struct NSVGactiveEdge *active = NULL; struct NSVGactiveEdge *active = NULL;
int y, s; int y, s;
@ -484,8 +601,10 @@ static void nsvg__rasterizeSortedEdges(struct NSVGrasterizer *r, unsigned int co
nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax); nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax);
} }
// Blit // Blit
if (xmin < 0) xmin = 0;
if (xmax > r->width-1) xmax = r->width-1;
if (xmin <= xmax) { if (xmin <= xmax) {
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], color); nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty,scale,cache);
} }
} }
@ -550,12 +669,75 @@ static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int str
} }
} }
static void nsvg__initPaint(struct NSVGcachedPaint* cache, struct NSVGpaint* paint)
{
int i, j;
struct NSVGgradient* grad;
cache->type = paint->type;
if (paint->type == NSVG_PAINT_COLOR) {
cache->colors[0] = paint->color;
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;
} if (grad->nstops == 1) {
for (i = 0; i < 256; i++)
cache->colors[i] = grad->stops[i].color;
} else {
unsigned int ca, cb;
float ua, ub, du, u;
int ia, ib, count;
ca = grad->stops[0].color;
cb = grad->stops[grad->nstops-1].color;
ua = nsvg__clampf(grad->stops[0].offset, 0, 1);
ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1);
ia = ua * 255.0f;
ib = ub * 255.0f;
for (i = 0; i < ia; i++) {
cache->colors[i] = ca;
}
for (i = 0; i < grad->nstops-1; i++) {
ca = grad->stops[i].color;
cb = grad->stops[i+1].color;
ua = nsvg__clampf(grad->stops[i].offset, 0, 1);
ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1);
ia = ua * 255.0f;
ib = 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;
}
}
void nsvgRasterize(struct NSVGrasterizer* r, void nsvgRasterize(struct NSVGrasterizer* r,
struct NSVGimage* image, float tx, float ty, float scale, struct NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride) unsigned char* dst, int w, int h, int stride)
{ {
struct NSVGshape *shape = NULL; struct NSVGshape *shape = NULL;
struct NSVGedge *e = NULL; struct NSVGedge *e = NULL;
struct NSVGcachedPaint cache;
int i; int i;
r->bitmap = dst; r->bitmap = dst;
@ -574,14 +756,14 @@ void nsvgRasterize(struct NSVGrasterizer* r,
for (shape = image->shapes; shape != NULL; shape = shape->next) { for (shape = image->shapes; shape != NULL; shape = shape->next) {
if (!shape->hasFill) if (shape->fill.type == NSVG_PAINT_NONE)
continue; continue;
nsvg__resetPool(r); nsvg__resetPool(r);
r->freelist = NULL; r->freelist = NULL;
r->nedges = 0; r->nedges = 0;
nsvg__flattenShape(r, shape, tx,ty,scale); nsvg__flattenShape(r, shape, scale);
// Scale and translate edges // Scale and translate edges
for (i = 0; i < r->nedges; i++) { for (i = 0; i < r->nedges; i++) {
@ -596,7 +778,9 @@ void nsvgRasterize(struct NSVGrasterizer* r,
qsort(r->edges, r->nedges, sizeof(struct NSVGedge), nsvg__cmpEdge); qsort(r->edges, r->nedges, sizeof(struct NSVGedge), nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
nsvg__rasterizeSortedEdges(r, shape->fillColor); nsvg__initPaint(&cache, &shape->fill);
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
} }
nsvg__unpremultiplyAlpha(dst, w, h, stride); nsvg__unpremultiplyAlpha(dst, w, h, stride);