Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 56 additions & 27 deletions src/nanosvgrast.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*
*/


#ifndef NANOSVGRAST_H
#define NANOSVGRAST_H

Expand All @@ -46,6 +47,9 @@ typedef struct NSVGrasterizer NSVGrasterizer;
unsigned char* img = malloc(w*h*4);
// Rasterize
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);

// For non-square X,Y scaling, use
nsvgRasterizeXY(rast, image, 0,0,1,1, img, w, h, w*4);
*/

// Allocated rasterizer context.
Expand All @@ -55,7 +59,7 @@ NSVGrasterizer* nsvgCreateRasterizer(void);
// r - pointer to rasterizer context
// image - pointer to image to rasterize
// tx,ty - image offset (applied after scaling)
// scale - image scale
// scale - image scale (assumes square aspect ratio)
// 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
Expand All @@ -64,6 +68,21 @@ void nsvgRasterize(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride);

// Similar to nsvgRasterize, but allow X and Y axes to scale independently for
// non-square aspects
// r - pointer to rasterizer context
// image - pointer to image to rasterize
// tx,ty - image offset (applied after scaling)
// sx,sy - image scale (assumes square aspect ratio)
// 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 nsvgRasterizeXY(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty,
float sx, float sy,
unsigned char* dst, int w, int h, int stride);

// Deletes rasterizer context.
void nsvgDeleteRasterizer(NSVGrasterizer*);

Expand Down Expand Up @@ -371,21 +390,21 @@ static void nsvg__flattenCubicBez(NSVGrasterizer* r,
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
}

static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy)
{
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);
nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 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);
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);
}
// Close path
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 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);
Expand Down Expand Up @@ -735,23 +754,24 @@ static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoi
}
}

static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does not seem correct. When scaling non-proportionally, the stroke should scale non-proportionally too.

I think the correct way could be to:

  1. flatten the paths, but adjust the tessellation so that it takes scaling into account
  2. expand strokes, including dashing
  3. scale paths

It should be possible to implement pt 1 by multiplying dx and dy in nsvg__flattenCubicBez() by the scale factors. This needs to be verified, though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this is a bit hacky at the moment.

I'll try taking a crack at it later, but if I'm being honest, this might be a bit beyond me right now, given that I don't understand bezier math yet 😅

{
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;
const float sw = (sx + sy) / 2; // average scaling factor
const float lineWidth = shape->strokeWidth * sw; // FIXME (?)

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);
nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 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);
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);
}
if (r->npoints < 2)
continue;
Expand Down Expand Up @@ -797,7 +817,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
dashOffset -= shape->strokeDashArray[idash];
idash = (idash + 1) % shape->strokeDashCount;
}
dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
dashLen = (shape->strokeDashArray[idash] - dashOffset) * sw;

for (j = 1; j < r->npoints2; ) {
float dx = r->points2[j].x - cur.x;
Expand All @@ -819,7 +839,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
// Advance dash pattern
dashState = !dashState;
idash = (idash+1) % shape->strokeDashCount;
dashLen = shape->strokeDashArray[idash] * scale;
dashLen = shape->strokeDashArray[idash] * sw;
// Restart
cur.x = x;
cur.y = y;
Expand Down Expand Up @@ -988,7 +1008,7 @@ static inline int nsvg__div255(int x)
}

static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
float tx, float ty, float scale, NSVGcachedPaint* cache)
float tx, float ty, float sx, float sy, NSVGcachedPaint* cache)
{

if (cache->type == NSVG_PAINT_COLOR) {
Expand Down Expand Up @@ -1029,9 +1049,9 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
int i, cr, cg, cb, ca;
unsigned int c;

fx = ((float)x - tx) / scale;
fy = ((float)y - ty) / scale;
dx = 1.0f / scale;
fx = ((float)x - tx) / sx;
fy = ((float)y - ty) / sy;
dx = 1.0f / sx;

for (i = 0; i < count; i++) {
int r,g,b,a,ia;
Expand Down Expand Up @@ -1074,9 +1094,9 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
int i, cr, cg, cb, ca;
unsigned int c;

fx = ((float)x - tx) / scale;
fy = ((float)y - ty) / scale;
dx = 1.0f / scale;
fx = ((float)x - tx) / sx;
fy = ((float)y - ty) / sy;
dx = 1.0f / sx;

for (i = 0; i < count; i++) {
int r,g,b,a,ia;
Expand Down Expand Up @@ -1115,7 +1135,7 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
}
}

static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float sx, float sy, NSVGcachedPaint* cache, char fillRule)
{
NSVGactiveEdge *active = NULL;
int y, s;
Expand Down Expand Up @@ -1197,7 +1217,7 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
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);
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, sx, sy, cache);
}
}

Expand Down Expand Up @@ -1366,8 +1386,9 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
}
*/

void nsvgRasterize(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty, float scale,
void nsvgRasterizeXY(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty,
float sx, float sy,
unsigned char* dst, int w, int h, int stride)
{
NSVGshape *shape = NULL;
Expand All @@ -1376,6 +1397,7 @@ void nsvgRasterize(NSVGrasterizer* r,
int i;
int j;
unsigned char paintOrder;
const float sw = (sx + sy) / 2; // average scaling factor

r->bitmap = dst;
r->width = w;
Expand Down Expand Up @@ -1403,7 +1425,7 @@ void nsvgRasterize(NSVGrasterizer* r,
r->freelist = NULL;
r->nedges = 0;

nsvg__flattenShape(r, shape, scale);
nsvg__flattenShape(r, shape, sx, sy);

// Scale and translate edges
for (i = 0; i < r->nedges; i++) {
Expand All @@ -1421,14 +1443,14 @@ void nsvgRasterize(NSVGrasterizer* r,
// 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);
nsvg__rasterizeSortedEdges(r, tx, ty, sx, sy, &cache, shape->fillRule);
}
if (paintOrder == NSVG_PAINT_STROKE && shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
if (paintOrder == NSVG_PAINT_STROKE && shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * sw) > 0.01f) {
nsvg__resetPool(r);
r->freelist = NULL;
r->nedges = 0;

nsvg__flattenShapeStroke(r, shape, scale);
nsvg__flattenShapeStroke(r, shape, sx, sy);

// dumpEdges(r, "edge.svg");

Expand All @@ -1448,7 +1470,7 @@ void nsvgRasterize(NSVGrasterizer* r,
// 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__rasterizeSortedEdges(r, tx, ty, sx, sy, &cache, NSVG_FILLRULE_NONZERO);
}
}
}
Expand All @@ -1461,6 +1483,13 @@ void nsvgRasterize(NSVGrasterizer* r,
r->stride = 0;
}

void nsvgRasterize(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride)
{
nsvgRasterizeXY(r,image, tx, ty, scale, scale, dst, w, h, stride);
}

#endif // NANOSVGRAST_IMPLEMENTATION

#endif // NANOSVGRAST_H