Changed struct names, fixed color parser bug and added rasterizer

- changed struct names from NSVGShape to NSVGshape
- fixed silly bug in color parser (plus reversed the r/b)
- changed the shape order so that they are in order they are parsed
- added super simple svg rasterizer based on stb_truetype rasterizer
- added example for the rasterizer
This commit is contained in:
Mikko Mononen 2014-01-09 22:15:26 +02:00
parent 297c2d5252
commit ad2841fccb
7 changed files with 1223 additions and 66 deletions

View File

@ -3,7 +3,7 @@
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
height="800">
width="800" height="800">
<g transform="translate(200,200)" style="fill-opacity:1; fill:none;">
<g style="fill: #ffffff; stroke:#000000; stroke-width:0.172">
<path d="M-122.304 84.285C-122.304 84.285 -122.203 86.179 -123.027 86.16C-123.851 86.141 -140.305 38.066 -160.833 40.309C-160.833 40.309 -143.05 32.956 -122.304 84.285z"/>

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -24,7 +24,7 @@
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
struct NSVGImage* g_image = NULL;
struct NSVGimage* g_image = NULL;
static unsigned char bgColor[4] = {205,202,200,255};
static unsigned char lineColor[4] = {0,160,192,255};
@ -80,10 +80,10 @@ static void cubicBez(float x1, float y1, float x2, float y2,
}
}
static void calcBounds(struct NSVGImage* image, float* bounds)
static void calcBounds(struct NSVGimage* image, float* bounds)
{
struct NSVGShape* shape;
struct NSVGPath* path;
struct NSVGshape* shape;
struct NSVGpath* path;
int i;
bounds[0] = FLT_MAX;
bounds[1] = FLT_MAX;
@ -167,8 +167,8 @@ void drawframe(GLFWwindow* window)
{
int width = 0, height = 0;
float bounds[4], view[4], cx, cy, w, h, aspect, px;
struct NSVGShape* shape;
struct NSVGPath* path;
struct NSVGshape* shape;
struct NSVGpath* path;
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwGetFramebufferSize(window, &width, &height);
@ -253,7 +253,7 @@ int main()
glEnable(GL_LINE_SMOOTH);
g_image = nsvgParseFromFile("../example/nano.svg");
g_image = nsvgParseFromFile("../example/23.svg");
if (g_image == NULL) {
printf("Could not open SVG image.\n");
glfwTerminate();

72
example/example2.c Normal file
View File

@ -0,0 +1,72 @@
//
// Copyright (c) 2013 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.
//
#include <stdio.h>
#include <string.h>
#include <float.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
int main()
{
struct NSVGimage *image = NULL;
struct NSVGrasterizer *rast = NULL;
unsigned char* img = NULL;
int w, h;
image = nsvgParseFromFile("../example/23.svg");
if (image == NULL) {
printf("Could not open SVG image.\n");
goto error;
}
w = image->width;
h = image->height;
if (w < 1 || h < 1) {
printf("Size of SVG not specified.\n");
goto error;
}
rast = nsvgCreateRasterizer();
if (rast == NULL) {
printf("Could not init rasterizer.\n");
goto error;
}
img = malloc(w*h*4);
if (img == NULL) {
printf("Could not alloc image buffer.\n");
goto error;
}
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
stbi_write_png("svg.png", w, h, 4, img, w*4);
error:
printf("delete rast\n");
nsvgDeleteRasterizer(rast);
printf("delete image\n");
nsvgDelete(image);
return 0;
}

511
example/stb_image_write.h Normal file
View File

@ -0,0 +1,511 @@
/* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
no warranty implied; use at your own risk
Before including,
#define STB_IMAGE_WRITE_IMPLEMENTATION
in the file that you want to have the implementation.
ABOUT:
This header file is a library for writing images to C stdio. It could be
adapted to write to memory or a general streaming interface; let me know.
The PNG output is not optimal; it is 20-50% larger than the file
written by a decent optimizing implementation. This library is designed
for source code compactness and simplicitly, not optimal image file size
or run-time performance.
USAGE:
There are three functions, one for each image file format:
int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
Each function returns 0 on failure and non-0 on success.
The functions create an image file defined by the parameters. The image
is a rectangle of pixels stored from left-to-right, top-to-bottom.
Each pixel contains 'comp' channels of data stored interleaved with 8-bits
per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
The *data pointer points to the first byte of the top-left-most pixel.
For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
a row of pixels to the first byte of the next row of pixels.
PNG creates output files with the same number of components as the input.
The BMP and TGA formats expand Y to RGB in the file format. BMP does not
output alpha.
PNG supports writing rectangles of data even when the bytes storing rows of
data are not consecutive in memory (e.g. sub-rectangles of a larger image),
by supplying the stride between the beginning of adjacent rows. The other
formats do not. (Thus you cannot write a native-format BMP through the BMP
writer, both because it is in BGR order and because it may have padding
at the end of the line.)
*/
#ifndef INCLUDE_STB_IMAGE_WRITE_H
#define INCLUDE_STB_IMAGE_WRITE_H
#ifdef __cplusplus
extern "C" {
#endif
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
#ifdef __cplusplus
}
#endif
#endif//INCLUDE_STB_IMAGE_WRITE_H
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
typedef unsigned int stbiw_uint32;
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
static void writefv(FILE *f, const char *fmt, va_list v)
{
while (*fmt) {
switch (*fmt++) {
case ' ': break;
case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
case '2': { int x = va_arg(v,int); unsigned char b[2];
b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
fwrite(b,2,1,f); break; }
case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
fwrite(b,4,1,f); break; }
default:
assert(0);
return;
}
}
}
static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
{
unsigned char arr[3];
arr[0] = a, arr[1] = b, arr[2] = c;
fwrite(arr, 3, 1, f);
}
static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
{
unsigned char bg[3] = { 255, 0, 255}, px[3];
stbiw_uint32 zero = 0;
int i,j,k, j_end;
if (y <= 0)
return;
if (vdir < 0)
j_end = -1, j = y-1;
else
j_end = y, j = 0;
for (; j != j_end; j += vdir) {
for (i=0; i < x; ++i) {
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
if (write_alpha < 0)
fwrite(&d[comp-1], 1, 1, f);
switch (comp) {
case 1:
case 2: write3(f, d[0],d[0],d[0]);
break;
case 4:
if (!write_alpha) {
// composite against pink background
for (k=0; k < 3; ++k)
px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);
break;
}
/* FALLTHROUGH */
case 3:
write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);
break;
}
if (write_alpha > 0)
fwrite(&d[comp-1], 1, 1, f);
}
fwrite(&zero,scanline_pad,1,f);
}
}
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
{
FILE *f;
if (y < 0 || x < 0) return 0;
f = fopen(filename, "wb");
if (f) {
va_list v;
va_start(v, fmt);
writefv(f, fmt, v);
va_end(v);
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
fclose(f);
}
return f != NULL;
}
int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
{
int pad = (-x*3) & 3;
return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad,
"11 4 22 4" "4 44 22 444444",
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
}
int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
{
int has_alpha = !(comp & 1);
return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0,
"111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
}
// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size()
#define stbi__sbraw(a) ((int *) (a) - 2)
#define stbi__sbm(a) stbi__sbraw(a)[0]
#define stbi__sbn(a) stbi__sbraw(a)[1]
#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a))
#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0)
#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a)))
#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v))
#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0)
#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0)
static void *stbi__sbgrowf(void **arr, int increment, int itemsize)
{
int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1;
void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
assert(p);
if (p) {
if (!*arr) ((int *) p)[1] = 0;
*arr = (void *) ((int *) p + 2);
stbi__sbm(*arr) = m;
}
return *arr;
}
static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
{
while (*bitcount >= 8) {
stbi__sbpush(data, (unsigned char) *bitbuffer);
*bitbuffer >>= 8;
*bitcount -= 8;
}
return data;
}
static int stbi__zlib_bitrev(int code, int codebits)
{
int res=0;
while (codebits--) {
res = (res << 1) | (code & 1);
code >>= 1;
}
return res;
}
static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit)
{
int i;
for (i=0; i < limit && i < 258; ++i)
if (a[i] != b[i]) break;
return i;
}
static unsigned int stbi__zhash(unsigned char *data)
{
stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount))
#define stbi__zlib_add(code,codebits) \
(bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush())
#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c)
// default huffman tables
#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8)
#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9)
#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7)
#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8)
#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n))
#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n))
#define stbi__ZHASH 16384
unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
{
static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
unsigned int bitbuf=0;
int i,j, bitcount=0;
unsigned char *out = NULL;
unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack!
if (quality < 5) quality = 5;
stbi__sbpush(out, 0x78); // DEFLATE 32K window
stbi__sbpush(out, 0x5e); // FLEVEL = 1
stbi__zlib_add(1,1); // BFINAL = 1
stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
for (i=0; i < stbi__ZHASH; ++i)
hash_table[i] = NULL;
i=0;
while (i < data_len-3) {
// hash next 3 bytes of data to be compressed
int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3;
unsigned char *bestloc = 0;
unsigned char **hlist = hash_table[h];
int n = stbi__sbcount(hlist);
for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32768) { // if entry lies within window
int d = stbi__zlib_countm(hlist[j], data+i, data_len-i);
if (d >= best) best=d,bestloc=hlist[j];
}
}
// when hash table entry is too long, delete half the entries
if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) {
memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
stbi__sbn(hash_table[h]) = quality;
}
stbi__sbpush(hash_table[h],data+i);
if (bestloc) {
// "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
h = stbi__zhash(data+i+1)&(stbi__ZHASH-1);
hlist = hash_table[h];
n = stbi__sbcount(hlist);
for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32767) {
int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1);
if (e > best) { // if next match is better, bail on current match
bestloc = NULL;
break;
}
}
}
}
if (bestloc) {
int d = data+i - bestloc; // distance back
assert(d <= 32767 && best <= 258);
for (j=0; best > lengthc[j+1]-1; ++j);
stbi__zlib_huff(j+257);
if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]);
for (j=0; d > distc[j+1]-1; ++j);
stbi__zlib_add(stbi__zlib_bitrev(j,5),5);
if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]);
i += best;
} else {
stbi__zlib_huffb(data[i]);
++i;
}
}
// write out final bytes
for (;i < data_len; ++i)
stbi__zlib_huffb(data[i]);
stbi__zlib_huff(256); // end of block
// pad with 0 bits to byte boundary
while (bitcount)
stbi__zlib_add(0,1);
for (i=0; i < stbi__ZHASH; ++i)
(void) stbi__sbfree(hash_table[i]);
{
// compute adler32 on input
unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
int j=0;
while (j < data_len) {
for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
s1 %= 65521, s2 %= 65521;
j += blocklen;
blocklen = 5552;
}
stbi__sbpush(out, (unsigned char) (s2 >> 8));
stbi__sbpush(out, (unsigned char) s2);
stbi__sbpush(out, (unsigned char) (s1 >> 8));
stbi__sbpush(out, (unsigned char) s1);
}
*out_len = stbi__sbn(out);
// make returned pointer freeable
memmove(stbi__sbraw(out), out, *out_len);
return (unsigned char *) stbi__sbraw(out);
}
unsigned int stbi__crc32(unsigned char *buffer, int len)
{
static unsigned int crc_table[256];
unsigned int crc = ~0u;
int i,j;
if (crc_table[1] == 0)
for(i=0; i < 256; i++)
for (crc_table[i]=i, j=0; j < 8; ++j)
crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
for (i=0; i < len; ++i)
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
return ~crc;
}
#define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)
#define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3])
static void stbi__wpcrc(unsigned char **data, int len)
{
unsigned int crc = stbi__crc32(*data - len - 4, len+4);
stbi__wp32(*data, crc);
}
static unsigned char stbi__paeth(int a, int b, int c)
{
int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
if (pa <= pb && pa <= pc) return (unsigned char) a;
if (pb <= pc) return (unsigned char) b;
return (unsigned char) c;
}
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
{
int ctype[5] = { -1, 0, 4, 2, 6 };
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
unsigned char *out,*o, *filt, *zlib;
signed char *line_buffer;
int i,j,k,p,zlen;
if (stride_bytes == 0)
stride_bytes = x * n;
filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0;
line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; }
for (j=0; j < y; ++j) {
static int mapping[] = { 0,1,2,3,4 };
static int firstmap[] = { 0,1,0,5,6 };
int *mymap = j ? mapping : firstmap;
int best = 0, bestval = 0x7fffffff;
for (p=0; p < 2; ++p) {
for (k= p?best:0; k < 5; ++k) {
int type = mymap[k],est=0;
unsigned char *z = pixels + stride_bytes*j;
for (i=0; i < n; ++i)
switch (type) {
case 0: line_buffer[i] = z[i]; break;
case 1: line_buffer[i] = z[i]; break;
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;
case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break;
case 5: line_buffer[i] = z[i]; break;
case 6: line_buffer[i] = z[i]; break;
}
for (i=n; i < x*n; ++i) {
switch (type) {
case 0: line_buffer[i] = z[i]; break;
case 1: line_buffer[i] = z[i] - z[i-n]; break;
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;
case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;
case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;
case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break;
}
}
if (p) break;
for (i=0; i < x*n; ++i)
est += abs((signed char) line_buffer[i]);
if (est < bestval) { bestval = est; best = k; }
}
}
// when we get here, best contains the filter type, and line_buffer contains the data
filt[j*(x*n+1)] = (unsigned char) best;
memcpy(filt+j*(x*n+1)+1, line_buffer, x*n);
}
free(line_buffer);
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
free(filt);
if (!zlib) return 0;
// each tag requires 12 bytes of overhead
out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12);
if (!out) return 0;
*out_len = 8 + 12+13 + 12+zlen + 12;
o=out;
memcpy(o,sig,8); o+= 8;
stbi__wp32(o, 13); // header length
stbi__wptag(o, "IHDR");
stbi__wp32(o, x);
stbi__wp32(o, y);
*o++ = 8;
*o++ = (unsigned char) ctype[n];
*o++ = 0;
*o++ = 0;
*o++ = 0;
stbi__wpcrc(&o,13);
stbi__wp32(o, zlen);
stbi__wptag(o, "IDAT");
memcpy(o, zlib, zlen); o += zlen; free(zlib);
stbi__wpcrc(&o, zlen);
stbi__wp32(o,0);
stbi__wptag(o, "IEND");
stbi__wpcrc(&o,0);
assert(o == out + *out_len);
return out;
}
int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
{
FILE *f;
int len;
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
if (!png) return 0;
f = fopen(filename, "wb");
if (!f) { free(png); return 0; }
fwrite(png, 1, len, f);
fclose(f);
free(png);
return 1;
}
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history
0.92 (2010-08-01)
casts to unsigned char to fix warnings
0.91 (2010-07-17)
first public release
0.90 first internal release
*/

View File

@ -6,10 +6,35 @@ solution "nanosvg"
configurations { "Debug", "Release" }
platforms {"native", "x64", "x32"}
project "example"
project "example1"
kind "ConsoleApp"
language "C++"
files { "example/*.c", "example/*.h", "src/*.h" }
files { "example/example1.c", "example/*.h", "src/*.h" }
includedirs { "example", "src" }
targetdir("build")
configuration { "linux" }
links { "X11","Xrandr", "rt", "GL", "GLU", "pthread" }
configuration { "windows" }
links { "glu32","opengl32", "gdi32", "winmm", "user32" }
configuration { "macosx" }
links { "glfw3" }
linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit" }
configuration "Debug"
defines { "DEBUG" }
flags { "Symbols", "ExtraWarnings"}
configuration "Release"
defines { "NDEBUG" }
flags { "Optimize", "ExtraWarnings"}
project "example2"
kind "ConsoleApp"
language "C++"
files { "example/example2.c", "example/*.h", "src/*.h" }
includedirs { "example", "src" }
targetdir("build")

View File

@ -49,42 +49,42 @@ extern "C" {
nsvgDelete(image);
*/
struct NSVGPath
struct NSVGpath
{
float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
int npts; // Total number of bezier points.
char closed; // Flag indicating if shapes should be treated as closed.
struct NSVGPath* next; // Pointer to next path, or NULL if last element.
struct NSVGpath* next; // Pointer to next path, or NULL if last element.
};
struct NSVGShape
struct NSVGshape
{
unsigned int fillColor; // Fill color
unsigned int strokeColor; // Stroke color
float strokeWidth; // Stroke width (scaled)
char hasFill; // Flag indicating if fill exists.
char hasStroke; // Flag indicating id store exists
struct NSVGPath* paths; // Linked list of paths in the image.
struct NSVGShape* next; // Pointer to next shape, or NULL if last element.
struct NSVGpath* paths; // Linked list of paths in the image.
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
};
struct NSVGImage
struct NSVGimage
{
float width; // Width of the image, or -1.0f of not set.
float height; // Height of the image, or -1.0f of not set.
char wunits[8]; // Units of the width attribute
char hunits[8]; // Units of the height attribute
struct NSVGShape* shapes; // Linked list of shapes in the image.
struct NSVGshape* shapes; // Linked list of shapes in the image.
};
// Parses SVG file from a file, returns linked list of paths.
struct NSVGImage* nsvgParseFromFile(const char* filename);
struct NSVGimage* nsvgParseFromFile(const char* filename);
// Parses SVG file from a null terminated string, returns linked list of paths.
struct NSVGImage* nsvgParse(char* input);
struct NSVGimage* nsvgParse(char* input);
// Deletes list of paths.
void nsvgDelete(struct NSVGImage* image);
void nsvgDelete(struct NSVGimage* image);
#ifdef __cplusplus
};
@ -265,8 +265,8 @@ struct NSVGParser
float* pts;
int npts;
int cpts;
struct NSVGPath* plist;
struct NSVGImage* image;
struct NSVGpath* plist;
struct NSVGimage* image;
char pathFlag;
char defsFlag;
};
@ -354,9 +354,9 @@ static struct NSVGParser* nsvg__createParser()
if (p == NULL) goto error;
memset(p, 0, sizeof(struct NSVGParser));
p->image = (struct NSVGImage*)malloc(sizeof(struct NSVGImage));
p->image = (struct NSVGimage*)malloc(sizeof(struct NSVGimage));
if (p->image == NULL) goto error;
memset(p->image, 0, sizeof(struct NSVGImage));
memset(p->image, 0, sizeof(struct NSVGimage));
p->image->width = -1.0f;
p->image->height = -1.0f;
@ -381,12 +381,11 @@ error:
return NULL;
}
static void nsvg__deletePaths(struct NSVGPath* path)
static void nsvg__deletePaths(struct NSVGpath* path)
{
struct NSVGPath* next;
while (path) {
next = path->next;
if (path->pts)
struct NSVGpath *next = path->next;
if (path->pts != NULL)
free(path->pts);
free(path);
path = next;
@ -410,18 +409,10 @@ static void nsvg__resetPath(struct NSVGParser* p)
static void nsvg__addPoint(struct NSVGParser* p, float x, float y)
{
int cap;
float* buf;
if (p->npts+1 > p->cpts) {
cap = p->cpts ? p->cpts*2 : 8;
buf = (float*)malloc(cap*2*sizeof(float));
if (!buf) return;
if (p->npts)
memcpy(buf, p->pts, p->npts*2*sizeof(float));
if (p->pts)
free(p->pts);
p->pts = buf;
p->cpts = cap;
p->cpts = p->cpts ? p->cpts*2 : 8;
p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
if (!p->pts) return;
}
p->pts[p->npts*2+0] = x;
p->pts[p->npts*2+1] = y;
@ -477,14 +468,14 @@ static void nsvg__addShape(struct NSVGParser* p)
{
struct NSVGAttrib* attr = nsvg__getAttr(p);
float scale = 1.0f;
struct NSVGShape* shape;
struct NSVGshape *shape, *cur, *prev;
if (p->plist == NULL)
return;
shape = (struct NSVGShape*)malloc(sizeof(struct NSVGShape));
shape = (struct NSVGshape*)malloc(sizeof(struct NSVGshape));
if (shape == NULL) goto error;
memset(shape, 0, sizeof(struct NSVGShape));
memset(shape, 0, sizeof(struct NSVGshape));
scale = nsvg__maxf(fabsf(attr->xform[0]), fabsf(attr->xform[3]));
shape->hasFill = attr->hasFill;
@ -502,8 +493,17 @@ static void nsvg__addShape(struct NSVGParser* p)
shape->paths = p->plist;
p->plist = NULL;
shape->next = p->image->shapes;
// Add to tail
prev = NULL;
cur = p->image->shapes;
while (cur != NULL) {
prev = cur;
cur = cur->next;
}
if (prev == NULL)
p->image->shapes = shape;
else
prev->next = shape;
return;
@ -515,7 +515,7 @@ static void nsvg__addPath(struct NSVGParser* p, char closed)
{
float* t = NULL;
struct NSVGAttrib* attr = nsvg__getAttr(p);
struct NSVGPath* path = NULL;
struct NSVGpath* path = NULL;
int i;
if (p->npts == 0)
@ -524,9 +524,9 @@ static void nsvg__addPath(struct NSVGParser* p, char closed)
if (closed)
nsvg__lineTo(p, p->pts[0], p->pts[1]);
path = (struct NSVGPath*)malloc(sizeof(struct NSVGPath));
path = (struct NSVGpath*)malloc(sizeof(struct NSVGpath));
if (path == NULL) goto error;
memset(path, 0, sizeof(struct NSVGPath));
memset(path, 0, sizeof(struct NSVGpath));
path->pts = (float*)malloc(p->npts*2*sizeof(float));
if (path->pts == NULL) goto error;
@ -579,21 +579,23 @@ static const char* nsvg__getNextPathItem(const char* s, char* it)
static unsigned int nsvg__parseColorHex(const char* str)
{
unsigned int c = 0;
unsigned int c = 0, r = 0, g = 0, b = 0;
int n = 0;
str++; // skip #
// Calculate number of characters.
while(str[n] && !nsvg__isspace(str[n]))
n++;
if (n == 6) {
sscanf(str + 1, "%x", &c);
sscanf(str, "%x", &c);
} else if (n == 3) {
sscanf(str + 1, "%x", &c);
sscanf(str, "%x", &c);
c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
c |= c<<4;
}
return c;
r = (c >> 16) & 0xff;
g = (c >> 8) & 0xff;
b = c & 0xff;
return NSVG_RGB(r,g,b);
}
static unsigned int nsvg__parseColorRGB(const char* str)
@ -1746,10 +1748,25 @@ static void nsvg__content(void* ud, const char* s)
// empty
}
struct NSVGImage* nsvgParse(char* input)
static void dump(struct NSVGimage* image)
{
struct NSVGshape *shape;
if (image == NULL) return;
shape = image->shapes;
while (shape != NULL) {
struct NSVGpath* path;
path = shape->paths;
while (path)
path = path->next;
shape = shape->next;
}
}
struct NSVGimage* nsvgParse(char* input)
{
struct NSVGParser* p;
struct NSVGImage* ret = 0;
struct NSVGimage* ret = 0;
p = nsvg__createParser();
if (p == NULL) {
@ -1761,17 +1778,19 @@ struct NSVGImage* nsvgParse(char* input)
ret = p->image;
p->image = NULL;
dump(ret);
nsvg__deleteParser(p);
return ret;
}
struct NSVGImage* nsvgParseFromFile(const char* filename)
struct NSVGimage* nsvgParseFromFile(const char* filename)
{
FILE* fp = NULL;
int size;
char* data = NULL;
struct NSVGImage* image = NULL;
struct NSVGimage* image = NULL;
fp = fopen(filename, "rb");
if (!fp) goto error;
@ -1795,17 +1814,18 @@ error:
return NULL;
}
void nsvgDelete(struct NSVGImage* image)
void nsvgDelete(struct NSVGimage* image)
{
if (image) {
struct NSVGShape *next, *shape = image->shapes;
struct NSVGshape *next, *shape;
if (image == NULL) return;
shape = image->shapes;
while (shape != NULL) {
next = shape->next;
nsvg__deletePaths(shape->paths);
free(shape);
shape = next;
}
}
free(image);
}
#endif

529
src/nanosvgrast.h Normal file
View File

@ -0,0 +1,529 @@
#ifndef NANOSVGRAST_H
#define NANOSVGRAST_H
#ifdef __cplusplus
extern "C" {
#endif
struct NSVGrasterizer* nsvgCreateRasterizer();
void nsvgRasterize(struct NSVGrasterizer* r,
struct NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride);
void nsvgDeleteRasterizer(struct NSVGrasterizer*);
#ifdef __cplusplus
};
#endif
#endif // NANOSVGRAST_H
#ifdef NANOSVGRAST_IMPLEMENTATION
#include <math.h>
// The polygon rasterization is heavily based on stb_truetype rasterizer by Sean Barrett - http://nothings.org/
#define NSVG__SUBSAMPLES 5
#define NSVG__FIXSHIFT 10
#define NSVG__FIX (1 << NSVG__FIXSHIFT)
#define NSVG__FIXMASK (NSVG__FIX-1)
struct NSVGedge {
float x0,y0, x1,y1;
int dir;
// struct NSVGedge* next;
};
struct NSVGactedge {
int x,dx;
float ey;
int dir;
struct NSVGactedge *next;
};
struct NSVGrasterizer
{
float px, py;
struct NSVGedge* edges;
int nedges;
int cedges;
struct NSVGactedge* actedges;
int nactedges;
int cactedges;
struct NSVGactedge* freelist;
unsigned char* scanline;
int cscanline;
unsigned char* bitmap;
int width, height, stride;
};
struct NSVGrasterizer* nsvgCreateRasterizer()
{
struct NSVGrasterizer* r = (struct NSVGrasterizer*)malloc(sizeof(struct NSVGrasterizer));
if (r == NULL) goto error;
memset(r, 0, sizeof(struct NSVGrasterizer));
return r;
error:
nsvgDeleteRasterizer(r);
return NULL;
}
void nsvgDeleteRasterizer(struct NSVGrasterizer* r)
{
if (r == NULL) return;
if (r->edges) free(r->edges);
if (r->actedges) free(r->actedges);
if (r->scanline) free(r->scanline);
free(r);
}
static void nsvg__addEdge(struct NSVGrasterizer* r, float x0, float y0, float x1, float y1)
{
struct 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 = (struct NSVGedge*)realloc(r->edges, sizeof(struct 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__distPtSeg(float x, float y, float px, float py, float qx, float qy)
{
float pqx, pqy, dx, dy, d, t;
pqx = qx-px;
pqy = qy-py;
dx = x-px;
dy = y-py;
d = pqx*pqx + pqy*pqy;
t = pqx*dx + pqy*dy;
if (d > 0) t /= d;
if (t < 0) t = 0;
else if (t > 1) t = 1;
dx = px + t*pqx - x;
dy = py + t*pqy - y;
return dx*dx + dy*dy;
}
static void nsvg__flattenCubicBez(struct NSVGrasterizer* r,
float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4,
float tol, int level)
{
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
float d;
if (level > 12) 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;
x234 = (x23+x34)*0.5f;
y234 = (y23+y34)*0.5f;
x1234 = (x123+x234)*0.5f;
y1234 = (y123+y234)*0.5f;
d = nsvg__distPtSeg(x1234, y1234, x1,y1, x4,y4);
if (d > tol*tol) {
nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, tol, level+1);
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1);
} else {
nsvg__addEdge(r, r->px, r->py, x4, y4);
r->px = x4;
r->py = y4;
}
}
static int nsvg__cmpEdge(const void *p, const void *q)
{
struct NSVGedge* a = (struct NSVGedge*)p;
struct NSVGedge* b = (struct NSVGedge*)q;
if (a->y0 < b->y0) return -1;
if (a->y0 > b->y0) return 1;
return 0;
}
static struct NSVGactedge* nsvg__addActive(struct NSVGrasterizer* r, struct NSVGedge* e, float startPoint)
{
struct NSVGactedge* z;
if (r->freelist != NULL) {
// Restore from freelist.
z = r->freelist;
r->freelist = z->next;
} else {
// Alloc new edge.
if (r->nactedges+1 > r->cactedges) {
r->cactedges = r->cactedges > 0 ? r->cactedges * 2 : 64;
r->actedges = (struct NSVGactedge*)realloc(r->actedges, sizeof(struct NSVGactedge) * r->cactedges);
if (r->actedges == NULL) return NULL;
}
z = &r->actedges[r->nactedges];
r->nactedges++;
}
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 = -floorf(NSVG__FIX * -dxdy);
else
z->dx = floorf(NSVG__FIX * dxdy);
z->x = floorf(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(struct NSVGrasterizer* r, struct NSVGactedge* z)
{
z->next = r->freelist;
r->freelist = z;
}
// 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, struct NSVGactedge* e, int maxWeight, int* xmin, int* xmax)
{
// non-zero winding fill
int x0 = 0, w = 0;
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) {
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)((x1 - x0) * maxWeight >> NSVG__FIXSHIFT);
} else {
if (i >= 0) // add antialiasing for x0
scanline[i] += (unsigned char)(((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT);
else
i = -1; // clip
if (j < len) // add antialiasing for x1
scanline[j] += (unsigned char)(((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)maxWeight;
}
}
}
}
e = e->next;
}
}
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, unsigned int color)
{
int x, cr, cg, cb, ca;
cr = color & 0xff;
cg = (color >> 8) & 0xff;
cb = (color >> 16) & 0xff;
ca = (color >> 24) & 0xff;
for (x = 0; x < count; x++) {
int r,g,b;
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
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;
}
}
static void nsvg__rasterizeSortedEdges(struct NSVGrasterizer *r, unsigned int color)
{
struct NSVGactedge *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 = y*NSVG__SUBSAMPLES + s + 0.5f;
struct NSVGactedge **step = &active;
// update all active edges;
// remove all active edges that terminate before the center of this scanline
while (*step) {
struct NSVGactedge *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) {
struct NSVGactedge* t = *step;
struct NSVGactedge* 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) {
struct NSVGactedge* 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
struct NSVGactedge* 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);
}
// Blit
if (xmin <= xmax) {
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], color);
}
}
}
static void nsvg__flattenShape(struct NSVGrasterizer* r,
struct NSVGshape* shape, float tx, float ty, float scale)
{
struct NSVGpath* path;
float tol = 0.5f * scale;
int i;
for (path = shape->paths; path != NULL; path = path->next) {
// Flatten path
r->px = path->pts[0];
r->py = path->pts[1];
for (i = 0; i < path->npts-1; i += 3) {
float* p = &path->pts[i*2];
nsvg__flattenCubicBez(r, p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7], tol, 0);
}
// Close path
nsvg__addEdge(r, r->px,r->py, path->pts[0],path->pts[1]);
}
}
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) {
r = (r*255/a);
g = (g*255/a);
b = (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] = r/n;
row[1] = g/n;
row[2] = b/n;
}
}
row += 4;
}
}
}
void nsvgRasterize(struct NSVGrasterizer* r,
struct NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride)
{
struct NSVGshape *shape = NULL;
struct NSVGedge *e = NULL;
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->hasFill)
continue;
r->nedges = 0;
r->nactedges = 0;
r->freelist = NULL;
nsvg__flattenShape(r, shape, tx,ty,scale);
// Scale and translate edges
for (i = 0; i < r->nedges; i++) {
e = &r->edges[i];
e->x0 = tx + e->x0 * scale;
e->y0 = (ty + e->y0 * scale) * NSVG__SUBSAMPLES;
e->x1 = tx + e->x1 * scale;
e->y1 = (ty + e->y1 * scale) * NSVG__SUBSAMPLES;
}
// Rasterize edges
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
nsvg__rasterizeSortedEdges(r, shape->fillColor);
}
nsvg__unpremultiplyAlpha(dst, w, h, stride);
r->bitmap = NULL;
r->width = 0;
r->height = 0;
r->stride = 0;
}
#endif