Browse Source
- 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 rasterizerpull/8/head

7 changed files with 1223 additions and 66 deletions
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
@ -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; |
|||
} |
@ -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 |
|||
*/ |
@ -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; |
|||
} |
|||
} |
|||