├── posix ├── string.h ├── stdlib.h ├── math.h ├── mkfile ├── s_fabsf.c ├── math_private.h ├── s_copysignf.c ├── s_tanf.c ├── s_sinf.c ├── s_cosf.c ├── k_sinf.c ├── s_ceilf.c ├── s_floorf.c ├── k_cosf.c ├── s_scalbnf.c ├── e_sqrtf.c ├── e_acosf.c ├── k_tanf.c ├── e_atan2f.c ├── e_fmodf.c ├── s_atanf.c ├── k_rem_pio2f.c └── e_rem_pio2f.c ├── screenshot.png ├── mkfile ├── README.md ├── LICENSE ├── svg.c ├── nanosvgrast.h └── nanosvg.h /posix/string.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posix/stdlib.h: -------------------------------------------------------------------------------- 1 | typedef usize size_t; 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telephil9/svg/HEAD/screenshot.png -------------------------------------------------------------------------------- /posix/math.h: -------------------------------------------------------------------------------- 1 | float cosf(float); 2 | float sinf(float); 3 | float tanf(float); 4 | float atanf(float); 5 | float atan2f(float, float); 6 | float fabsf(float); 7 | float sqrtf(float); 8 | float acosf(float); 9 | float floorf(float); 10 | float ceilf(float); 11 | float fmodf(float, float); 12 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /posix/s_tanf.c: -------------------------------------------------------------------------------- 1 | /* s_tanf.c -- float version of s_tan.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | float 20 | tanf(float x) 21 | { 22 | float y[2],z=0.0; 23 | int32_t n, ix; 24 | 25 | GET_FLOAT_WORD(ix,x); 26 | 27 | /* |x| ~< pi/4 */ 28 | ix &= 0x7fffffff; 29 | if(ix <= 0x3f490fda) return __kernel_tanf(x,z,1); 30 | 31 | /* tan(Inf or NaN) is NaN */ 32 | else if (ix>=0x7f800000) return x-x; /* NaN */ 33 | 34 | /* argument reduction needed */ 35 | else { 36 | n = __ieee754_rem_pio2f(x,y); 37 | return __kernel_tanf(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even 38 | -1 -- n odd */ 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /posix/s_sinf.c: -------------------------------------------------------------------------------- 1 | /* s_sinf.c -- float version of s_sin.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | float 20 | sinf(float x) 21 | { 22 | float y[2],z=0.0; 23 | int32_t n, ix; 24 | 25 | GET_FLOAT_WORD(ix,x); 26 | 27 | /* |x| ~< pi/4 */ 28 | ix &= 0x7fffffff; 29 | if(ix <= 0x3f490fd8) return __kernel_sinf(x,z,0); 30 | 31 | /* sin(Inf or NaN) is NaN */ 32 | else if (ix>=0x7f800000) return x-x; 33 | 34 | /* argument reduction needed */ 35 | else { 36 | n = __ieee754_rem_pio2f(x,y); 37 | switch(n&3) { 38 | case 0: return __kernel_sinf(y[0],y[1],1); 39 | case 1: return __kernel_cosf(y[0],y[1]); 40 | case 2: return -__kernel_sinf(y[0],y[1],1); 41 | default: 42 | return -__kernel_cosf(y[0],y[1]); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /posix/s_cosf.c: -------------------------------------------------------------------------------- 1 | /* s_cosf.c -- float version of s_cos.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | float 20 | cosf(float x) 21 | { 22 | float y[2],z=0.0; 23 | int32_t n,ix; 24 | 25 | GET_FLOAT_WORD(ix,x); 26 | 27 | /* |x| ~< pi/4 */ 28 | ix &= 0x7fffffff; 29 | if(ix <= 0x3f490fd8) return __kernel_cosf(x,z); 30 | 31 | /* cos(Inf or NaN) is NaN */ 32 | else if (ix>=0x7f800000) return x-x; 33 | 34 | /* argument reduction needed */ 35 | else { 36 | n = __ieee754_rem_pio2f(x,y); 37 | switch(n&3) { 38 | case 0: return __kernel_cosf(y[0],y[1]); 39 | case 1: return -__kernel_sinf(y[0],y[1],1); 40 | case 2: return -__kernel_cosf(y[0],y[1]); 41 | default: 42 | return __kernel_sinf(y[0],y[1],1); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /posix/k_sinf.c: -------------------------------------------------------------------------------- 1 | /* k_sinf.c -- float version of k_sin.c 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float 20 | half = 5.0000000000e-01,/* 0x3f000000 */ 21 | S1 = -1.6666667163e-01, /* 0xbe2aaaab */ 22 | S2 = 8.3333337680e-03, /* 0x3c088889 */ 23 | S3 = -1.9841270114e-04, /* 0xb9500d01 */ 24 | S4 = 2.7557314297e-06, /* 0x3638ef1b */ 25 | S5 = -2.5050759689e-08, /* 0xb2d72f34 */ 26 | S6 = 1.5896910177e-10; /* 0x2f2ec9d3 */ 27 | 28 | float 29 | __kernel_sinf(float x, float y, int iy) 30 | { 31 | float z,r,v; 32 | int32_t ix; 33 | GET_FLOAT_WORD(ix,x); 34 | ix &= 0x7fffffff; /* high word of x */ 35 | if(ix<0x32000000) /* |x| < 2**-27 */ 36 | {if((int)x==0) return x;} /* generate inexact */ 37 | z = x*x; 38 | v = z*x; 39 | r = S2+z*(S3+z*(S4+z*(S5+z*S6))); 40 | if(iy==0) return x+v*(S1+z*r); 41 | else return x-((z*(half*y-v*r)-y)-v*S1); 42 | } 43 | -------------------------------------------------------------------------------- /posix/s_ceilf.c: -------------------------------------------------------------------------------- 1 | /* s_ceilf.c -- float version of s_ceil.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float huge = 1.0e30; 20 | 21 | float 22 | ceilf(float x) 23 | { 24 | int32_t i0,jj0; 25 | u_int32_t i; 26 | 27 | GET_FLOAT_WORD(i0,x); 28 | jj0 = ((i0>>23)&0xff)-0x7f; 29 | if(jj0<23) { 30 | if(jj0<0) { /* raise inexact if x != 0 */ 31 | if(huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ 32 | if(i0<0) {i0=0x80000000;} 33 | else if(i0!=0) { i0=0x3f800000;} 34 | } 35 | } else { 36 | i = (0x007fffff)>>jj0; 37 | if((i0&i)==0) return x; /* x is integral */ 38 | if(huge+x>(float)0.0) { /* raise inexact flag */ 39 | if(i0>0) i0 += (0x00800000)>>jj0; 40 | i0 &= (~i); 41 | } 42 | } 43 | } else { 44 | if(jj0==0x80) return x+x; /* inf or NaN */ 45 | else return x; /* x is integral */ 46 | } 47 | SET_FLOAT_WORD(x,i0); 48 | return x; 49 | } 50 | -------------------------------------------------------------------------------- /posix/s_floorf.c: -------------------------------------------------------------------------------- 1 | /* s_floorf.c -- float version of s_floor.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | /* 17 | * floorf(x) 18 | * Return x rounded toward -inf to integral value 19 | * Method: 20 | * Bit twiddling. 21 | * Exception: 22 | * Inexact flag raised if x not equal to floorf(x). 23 | */ 24 | 25 | #include "math.h" 26 | #include "math_private.h" 27 | 28 | static const float huge = 1.0e30; 29 | 30 | float 31 | floorf(float x) 32 | { 33 | int32_t i0,jj0; 34 | u_int32_t i; 35 | GET_FLOAT_WORD(i0,x); 36 | jj0 = ((i0>>23)&0xff)-0x7f; 37 | if(jj0<23) { 38 | if(jj0<0) { /* raise inexact if x != 0 */ 39 | if(huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ 40 | if(i0>=0) {i0=0;} 41 | else if((i0&0x7fffffff)!=0) 42 | { i0=0xbf800000;} 43 | } 44 | } else { 45 | i = (0x007fffff)>>jj0; 46 | if((i0&i)==0) return x; /* x is integral */ 47 | if(huge+x>(float)0.0) { /* raise inexact flag */ 48 | if(i0<0) i0 += (0x00800000)>>jj0; 49 | i0 &= (~i); 50 | } 51 | } 52 | } else { 53 | if(jj0==0x80) return x+x; /* inf or NaN */ 54 | else return x; /* x is integral */ 55 | } 56 | SET_FLOAT_WORD(x,i0); 57 | return x; 58 | } 59 | -------------------------------------------------------------------------------- /posix/k_cosf.c: -------------------------------------------------------------------------------- 1 | /* k_cosf.c -- float version of k_cos.c 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float 20 | one = 1.0000000000e+00, /* 0x3f800000 */ 21 | C1 = 4.1666667908e-02, /* 0x3d2aaaab */ 22 | C2 = -1.3888889225e-03, /* 0xbab60b61 */ 23 | C3 = 2.4801587642e-05, /* 0x37d00d01 */ 24 | C4 = -2.7557314297e-07, /* 0xb493f27c */ 25 | C5 = 2.0875723372e-09, /* 0x310f74f6 */ 26 | C6 = -1.1359647598e-11; /* 0xad47d74e */ 27 | 28 | float 29 | __kernel_cosf(float x, float y) 30 | { 31 | float a,hz,z,r,qx; 32 | int32_t ix; 33 | GET_FLOAT_WORD(ix,x); 34 | ix &= 0x7fffffff; /* ix = |x|'s high word*/ 35 | if(ix<0x32000000) { /* if x < 2**27 */ 36 | if(((int)x)==0) return one; /* generate inexact */ 37 | } 38 | z = x*x; 39 | r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6))))); 40 | if(ix < 0x3e99999a) /* if |x| < 0.3 */ 41 | return one - ((float)0.5*z - (z*r - x*y)); 42 | else { 43 | if(ix > 0x3f480000) { /* x > 0.78125 */ 44 | qx = (float)0.28125; 45 | } else { 46 | SET_FLOAT_WORD(qx,ix-0x01000000); /* x/4 */ 47 | } 48 | hz = (float)0.5*z-qx; 49 | a = one-qx; 50 | return a - (hz - (z*r-x*y)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /posix/s_scalbnf.c: -------------------------------------------------------------------------------- 1 | /* s_scalbnf.c -- float version of s_scalbn.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float 20 | two25 = 3.355443200e+07, /* 0x4c000000 */ 21 | twom25 = 2.9802322388e-08, /* 0x33000000 */ 22 | huge = 1.0e+30, 23 | tiny = 1.0e-30; 24 | 25 | float 26 | scalbnf(float x, int n) 27 | { 28 | int32_t k,ix; 29 | GET_FLOAT_WORD(ix,x); 30 | k = (ix&0x7f800000)>>23; /* extract exponent */ 31 | if (k==0) { /* 0 or subnormal x */ 32 | if ((ix&0x7fffffff)==0) return x; /* +-0 */ 33 | x *= two25; 34 | GET_FLOAT_WORD(ix,x); 35 | k = ((ix&0x7f800000)>>23) - 25; 36 | if (n< -50000) return tiny*x; /*underflow*/ 37 | } 38 | if (k==0xff) return x+x; /* NaN or Inf */ 39 | k = k+n; 40 | if (k > 0xfe) return huge*copysignf(huge,x); /* overflow */ 41 | if (k > 0) /* normal result */ 42 | {SET_FLOAT_WORD(x,(ix&0x807fffff)|(k<<23)); return x;} 43 | if (k <= -25) 44 | if (n > 50000) /* in case integer overflow in n+k */ 45 | return huge*copysignf(huge,x); /*overflow*/ 46 | else return tiny*copysignf(tiny,x); /*underflow*/ 47 | k += 25; /* subnormal result */ 48 | SET_FLOAT_WORD(x,(ix&0x807fffff)|(k<<23)); 49 | return x*twom25; 50 | } 51 | 52 | float 53 | ldexpf(float x, int n) 54 | { 55 | return scalbnf(x, n); 56 | } 57 | -------------------------------------------------------------------------------- /posix/e_sqrtf.c: -------------------------------------------------------------------------------- 1 | /* e_sqrtf.c -- float version of e_sqrt.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float one = 1.0, tiny=1.0e-30; 20 | 21 | float 22 | sqrtf(float x) 23 | { 24 | float z; 25 | int32_t sign = (int)0x80000000; 26 | int32_t ix,s,q,m,t,i; 27 | u_int32_t r; 28 | 29 | GET_FLOAT_WORD(ix,x); 30 | 31 | /* take care of Inf and NaN */ 32 | if((ix&0x7f800000)==0x7f800000) { 33 | return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf 34 | sqrt(-inf)=sNaN */ 35 | } 36 | /* take care of zero */ 37 | if(ix<=0) { 38 | if((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */ 39 | else if(ix<0) 40 | return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ 41 | } 42 | /* normalize x */ 43 | m = (ix>>23); 44 | if(m==0) { /* subnormal x */ 45 | for(i=0;(ix&0x00800000)==0;i++) ix<<=1; 46 | m -= i-1; 47 | } 48 | m -= 127; /* unbias exponent */ 49 | ix = (ix&0x007fffff)|0x00800000; 50 | if(m&1) /* odd m, double x to make it even */ 51 | ix += ix; 52 | m >>= 1; /* m = [m/2] */ 53 | 54 | /* generate sqrt(x) bit by bit */ 55 | ix += ix; 56 | q = s = 0; /* q = sqrt(x) */ 57 | r = 0x01000000; /* r = moving bit from right to left */ 58 | 59 | while(r!=0) { 60 | t = s+r; 61 | if(t<=ix) { 62 | s = t+r; 63 | ix -= t; 64 | q += r; 65 | } 66 | ix += ix; 67 | r>>=1; 68 | } 69 | 70 | /* use floating add to find out rounding direction */ 71 | if(ix!=0) { 72 | z = one-tiny; /* trigger inexact flag */ 73 | if (z>=one) { 74 | z = one+tiny; 75 | if (z>one) 76 | q += 2; 77 | else 78 | q += (q&1); 79 | } 80 | } 81 | ix = (q>>1)+0x3f000000; 82 | ix += (m <<23); 83 | SET_FLOAT_WORD(z,ix); 84 | return z; 85 | } 86 | -------------------------------------------------------------------------------- /posix/e_acosf.c: -------------------------------------------------------------------------------- 1 | /* e_acosf.c -- float version of e_acos.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float 20 | one = 1.0000000000e+00, /* 0x3F800000 */ 21 | pi = 3.1415925026e+00, /* 0x40490fda */ 22 | pio2_hi = 1.5707962513e+00, /* 0x3fc90fda */ 23 | pio2_lo = 7.5497894159e-08, /* 0x33a22168 */ 24 | pS0 = 1.6666667163e-01, /* 0x3e2aaaab */ 25 | pS1 = -3.2556581497e-01, /* 0xbea6b090 */ 26 | pS2 = 2.0121252537e-01, /* 0x3e4e0aa8 */ 27 | pS3 = -4.0055535734e-02, /* 0xbd241146 */ 28 | pS4 = 7.9153501429e-04, /* 0x3a4f7f04 */ 29 | pS5 = 3.4793309169e-05, /* 0x3811ef08 */ 30 | qS1 = -2.4033949375e+00, /* 0xc019d139 */ 31 | qS2 = 2.0209457874e+00, /* 0x4001572d */ 32 | qS3 = -6.8828397989e-01, /* 0xbf303361 */ 33 | qS4 = 7.7038154006e-02; /* 0x3d9dc62e */ 34 | 35 | float 36 | acosf(float x) 37 | { 38 | float z,p,q,r,w,s,c,df; 39 | int32_t hx,ix; 40 | GET_FLOAT_WORD(hx,x); 41 | ix = hx&0x7fffffff; 42 | if(ix==0x3f800000) { /* |x|==1 */ 43 | if(hx>0) return 0.0; /* acos(1) = 0 */ 44 | else return pi+(float)2.0*pio2_lo; /* acos(-1)= pi */ 45 | } else if(ix>0x3f800000) { /* |x| >= 1 */ 46 | return (x-x)/(x-x); /* acos(|x|>1) is NaN */ 47 | } 48 | if(ix<0x3f000000) { /* |x| < 0.5 */ 49 | if(ix<=0x23000000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/ 50 | z = x*x; 51 | p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); 52 | q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); 53 | r = p/q; 54 | return pio2_hi - (x - (pio2_lo-x*r)); 55 | } else if (hx<0) { /* x < -0.5 */ 56 | z = (one+x)*(float)0.5; 57 | p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); 58 | q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); 59 | s = sqrtf(z); 60 | r = p/q; 61 | w = r*s-pio2_lo; 62 | return pi - (float)2.0*(s+w); 63 | } else { /* x > 0.5 */ 64 | int32_t idf; 65 | z = (one-x)*(float)0.5; 66 | s = sqrtf(z); 67 | df = s; 68 | GET_FLOAT_WORD(idf,df); 69 | SET_FLOAT_WORD(df,idf&0xfffff000); 70 | c = (z-df*df)/(s+df); 71 | p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); 72 | q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); 73 | r = p/q; 74 | w = r*s+c; 75 | return (float)2.0*(df+w); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /posix/k_tanf.c: -------------------------------------------------------------------------------- 1 | /* k_tanf.c -- float version of k_tan.c 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float 20 | one = 1.0000000000e+00, /* 0x3f800000 */ 21 | pio4 = 7.8539812565e-01, /* 0x3f490fda */ 22 | pio4lo= 3.7748947079e-08, /* 0x33222168 */ 23 | T[] = { 24 | 3.3333334327e-01, /* 0x3eaaaaab */ 25 | 1.3333334029e-01, /* 0x3e088889 */ 26 | 5.3968254477e-02, /* 0x3d5d0dd1 */ 27 | 2.1869488060e-02, /* 0x3cb327a4 */ 28 | 8.8632395491e-03, /* 0x3c11371f */ 29 | 3.5920790397e-03, /* 0x3b6b6916 */ 30 | 1.4562094584e-03, /* 0x3abede48 */ 31 | 5.8804126456e-04, /* 0x3a1a26c8 */ 32 | 2.4646313977e-04, /* 0x398137b9 */ 33 | 7.8179444245e-05, /* 0x38a3f445 */ 34 | 7.1407252108e-05, /* 0x3895c07a */ 35 | -1.8558637748e-05, /* 0xb79bae5f */ 36 | 2.5907305826e-05, /* 0x37d95384 */ 37 | }; 38 | 39 | float 40 | __kernel_tanf(float x, float y, int iy) 41 | { 42 | float z,r,v,w,s; 43 | int32_t ix,hx; 44 | GET_FLOAT_WORD(hx,x); 45 | ix = hx&0x7fffffff; /* high word of |x| */ 46 | if(ix<0x31800000) /* x < 2**-28 */ 47 | {if((int)x==0) { /* generate inexact */ 48 | if((ix|(iy+1))==0) return one/fabsf(x); 49 | else return (iy==1)? x: -one/x; 50 | } 51 | } 52 | if(ix>=0x3f2ca140) { /* |x|>=0.6744 */ 53 | if(hx<0) {x = -x; y = -y;} 54 | z = pio4-x; 55 | w = pio4lo-y; 56 | x = z+w; y = 0.0; 57 | } 58 | z = x*x; 59 | w = z*z; 60 | /* Break x^5*(T[1]+x^2*T[2]+...) into 61 | * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + 62 | * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) 63 | */ 64 | r = T[1]+w*(T[3]+w*(T[5]+w*(T[7]+w*(T[9]+w*T[11])))); 65 | v = z*(T[2]+w*(T[4]+w*(T[6]+w*(T[8]+w*(T[10]+w*T[12]))))); 66 | s = z*x; 67 | r = y + z*(s*(r+v)+y); 68 | r += T[0]*s; 69 | w = x+r; 70 | if(ix>=0x3f2ca140) { 71 | v = (float)iy; 72 | return (float)(1-((hx>>30)&2))*(v-(float)2.0*(x-(w*w/(w+v)-r))); 73 | } 74 | if(iy==1) return w; 75 | else { /* if allow error up to 2 ulp, 76 | simply return -1.0/(x+r) here */ 77 | /* compute -1.0/(x+r) accurately */ 78 | float a,t; 79 | int32_t i; 80 | z = w; 81 | GET_FLOAT_WORD(i,z); 82 | SET_FLOAT_WORD(z,i&0xfffff000); 83 | v = r-(z - x); /* z+v = r+x */ 84 | t = a = -(float)1.0/w; /* a = -1.0/w */ 85 | GET_FLOAT_WORD(i,t); 86 | SET_FLOAT_WORD(t,i&0xfffff000); 87 | s = (float)1.0+t*z; 88 | return t+a*(s+t*v); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /svg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define NANOSVG_ALL_COLOR_KEYWORDS 8 | #define NANOSVG_IMPLEMENTATION 9 | #include "nanosvg.h" 10 | #define NANOSVGRAST_IMPLEMENTATION 11 | #include "nanosvgrast.h" 12 | 13 | char* filename; 14 | Image *svg; 15 | int nineflag, w, h, sz; 16 | uchar *data; 17 | 18 | void 19 | rasterize(int scale) 20 | { 21 | NSVGimage *image; 22 | NSVGrasterizer *rast; 23 | float s, sx, sy, tx, ty; 24 | Rectangle r; 25 | 26 | image = nsvgParseFromFile(filename, "px", 96); 27 | if(image==nil) 28 | sysfatal("svg parse: %r"); 29 | w = image->width; 30 | h = image->height; 31 | s = 1.0f; 32 | if(scale){ 33 | r = insetrect(screen->r, 10); 34 | w = Dx(r); 35 | h = Dy(r); 36 | sx = (float)w/(image->width+0.5); 37 | sy = (float)h/(image->height+0.5); 38 | s = (sx < sy) ? sx : sy; 39 | } 40 | rast = nsvgCreateRasterizer(); 41 | if(rast==nil) 42 | sysfatal("create rasterizer: %r"); 43 | sz = w*h*4; 44 | data = malloc(sz); 45 | if(data==nil) 46 | sysfatal("malloc: %r"); 47 | nsvgRasterize(rast, image, 0, 0, s, data, w, h, w*4); 48 | nsvgDelete(image); 49 | nsvgDeleteRasterizer(rast); 50 | if(nineflag) 51 | return; 52 | svg = allocimage(display, Rect(0, 0, w, h), ABGR32, 0, DNofill); 53 | if(svg==nil) 54 | sysfatal("allocimage: %r"); 55 | if(loadimage(svg, svg->r, data, sz)r) - Dx(svg->r))/2; 69 | p.y = (Dy(screen->r) - Dy(svg->r))/2; 70 | draw(screen, screen->r, display->white, nil, ZP); 71 | draw(screen, rectaddpt(screen->r, p), svg, nil, ZP); 72 | } 73 | 74 | void 75 | usage(void) 76 | { 77 | fprint(2, "usage: %s [-9] [file.svg]\n", argv0); 78 | } 79 | 80 | void 81 | main(int argc, char *argv[]) 82 | { 83 | Event e; 84 | int ev; 85 | char buf[12+1]; 86 | 87 | ARGBEGIN{ 88 | case 't': /* page compatibility */ 89 | break; 90 | case '9': 91 | nineflag++; 92 | break; 93 | default: 94 | fprint(2, "invalid flag '%c'\n", ARGC()); 95 | usage(); 96 | exits("usage"); 97 | }ARGEND 98 | if(*argv==nil) 99 | filename = "/fd/0"; 100 | else 101 | filename = *argv; 102 | if(nineflag){ 103 | rasterize(0); 104 | chantostr(buf, ABGR32); 105 | print("%11s %11d %11d %11d %11d ", buf, 0, 0, w, h); 106 | if(write(1, data, sz) != sz) 107 | sysfatal("write: %r\n"); 108 | exits(nil); 109 | } 110 | if(initdraw(nil, nil, "svg")<0) 111 | sysfatal("initdraw: %r"); 112 | einit(Emouse|Ekeyboard); 113 | eresized(0); 114 | for(;;){ 115 | ev = event(&e); 116 | switch(ev){ 117 | case Ekeyboard: 118 | if(e.kbdc=='q' || e.kbdc==Kdel) 119 | exits(nil); 120 | break; 121 | case Emouse: 122 | break; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /posix/e_atan2f.c: -------------------------------------------------------------------------------- 1 | /* e_atan2f.c -- float version of e_atan2.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float 20 | tiny = 1.0e-30, 21 | zero = 0.0, 22 | pi_o_4 = 7.8539818525e-01, /* 0x3f490fdb */ 23 | pi_o_2 = 1.5707963705e+00, /* 0x3fc90fdb */ 24 | pi = 3.1415925026e+00, /* 0x40490fda */ 25 | pi_lo = 1.5099578832e-07; /* 0x34222168 */ 26 | 27 | float 28 | atan2f(float y, float x) 29 | { 30 | float z; 31 | int32_t k,m,hx,hy,ix,iy; 32 | 33 | GET_FLOAT_WORD(hx,x); 34 | ix = hx&0x7fffffff; 35 | GET_FLOAT_WORD(hy,y); 36 | iy = hy&0x7fffffff; 37 | if((ix>0x7f800000)|| 38 | (iy>0x7f800000)) /* x or y is NaN */ 39 | return x+y; 40 | if(hx==0x3f800000) return atanf(y); /* x=1.0 */ 41 | m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ 42 | 43 | /* when y = 0 */ 44 | if(iy==0) { 45 | switch(m) { 46 | case 0: 47 | case 1: return y; /* atan(+-0,+anything)=+-0 */ 48 | case 2: return pi+tiny;/* atan(+0,-anything) = pi */ 49 | case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ 50 | } 51 | } 52 | /* when x = 0 */ 53 | if(ix==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; 54 | 55 | /* when x is INF */ 56 | if(ix==0x7f800000) { 57 | if(iy==0x7f800000) { 58 | switch(m) { 59 | case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ 60 | case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ 61 | case 2: return (float)3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ 62 | case 3: return (float)-3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ 63 | } 64 | } else { 65 | switch(m) { 66 | case 0: return zero ; /* atan(+...,+INF) */ 67 | case 1: return -zero ; /* atan(-...,+INF) */ 68 | case 2: return pi+tiny ; /* atan(+...,-INF) */ 69 | case 3: return -pi-tiny ; /* atan(-...,-INF) */ 70 | } 71 | } 72 | } 73 | /* when y is INF */ 74 | if(iy==0x7f800000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; 75 | 76 | /* compute y/x */ 77 | k = (iy-ix)>>23; 78 | if(k > 60) z=pi_o_2+(float)0.5*pi_lo; /* |y/x| > 2**60 */ 79 | else if(hx<0&&k<-60) z=0.0; /* |y|/x < -2**60 */ 80 | else z=atanf(fabsf(y/x)); /* safe to do y/x */ 81 | switch (m) { 82 | case 0: return z ; /* atan(+,+) */ 83 | case 1: { 84 | u_int32_t zh; 85 | GET_FLOAT_WORD(zh,z); 86 | SET_FLOAT_WORD(z,zh ^ 0x80000000); 87 | } 88 | return z ; /* atan(-,+) */ 89 | case 2: return pi-(z-pi_lo);/* atan(+,-) */ 90 | default: /* case 3 */ 91 | return (z-pi_lo)-pi;/* atan(-,-) */ 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /posix/e_fmodf.c: -------------------------------------------------------------------------------- 1 | /* e_fmodf.c -- float version of e_fmod.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | /* 17 | * fmodf(x,y) 18 | * Return x mod y in exact arithmetic 19 | * Method: shift and subtract 20 | */ 21 | 22 | #include "math.h" 23 | #include "math_private.h" 24 | 25 | static const float one = 1.0, Zero[] = {0.0, -0.0,}; 26 | 27 | float 28 | fmodf(float x, float y) 29 | { 30 | int32_t n,hx,hy,hz,ix,iy,sx,i; 31 | 32 | GET_FLOAT_WORD(hx,x); 33 | GET_FLOAT_WORD(hy,y); 34 | sx = hx&0x80000000; /* sign of x */ 35 | hx ^=sx; /* |x| */ 36 | hy &= 0x7fffffff; /* |y| */ 37 | 38 | /* purge off exception values */ 39 | if(hy==0||(hx>=0x7f800000)|| /* y=0,or x not finite */ 40 | (hy>0x7f800000)) /* or y is NaN */ 41 | return (x*y)/(x*y); 42 | if(hx>31]; /* |x|=|y| return x*0*/ 45 | 46 | /* determine ix = ilogb(x) */ 47 | if(hx<0x00800000) { /* subnormal x */ 48 | for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; 49 | } else ix = (hx>>23)-127; 50 | 51 | /* determine iy = ilogb(y) */ 52 | if(hy<0x00800000) { /* subnormal y */ 53 | for (iy = -126,i=(hy<<8); i>=0; i<<=1) iy -=1; 54 | } else iy = (hy>>23)-127; 55 | 56 | /* set up {hx,lx}, {hy,ly} and align y to x */ 57 | if(ix >= -126) 58 | hx = 0x00800000|(0x007fffff&hx); 59 | else { /* subnormal x, shift x to normal */ 60 | n = -126-ix; 61 | hx = hx<= -126) 64 | hy = 0x00800000|(0x007fffff&hy); 65 | else { /* subnormal y, shift y to normal */ 66 | n = -126-iy; 67 | hy = hy<>31]; 78 | hx = hz+hz; 79 | } 80 | } 81 | hz=hx-hy; 82 | if(hz>=0) {hx=hz;} 83 | 84 | /* convert back to floating value and restore the sign */ 85 | if(hx==0) /* return sign(x)*0 */ 86 | return Zero[(u_int32_t)sx>>31]; 87 | while(hx<0x00800000) { /* normalize x */ 88 | hx = hx+hx; 89 | iy -= 1; 90 | } 91 | if(iy>= -126) { /* normalize output */ 92 | hx = ((hx-0x00800000)|((iy+127)<<23)); 93 | SET_FLOAT_WORD(x,hx|sx); 94 | } else { /* subnormal output */ 95 | n = -126 - iy; 96 | hx >>= n; 97 | SET_FLOAT_WORD(x,hx|sx); 98 | x *= one; /* create necessary signal */ 99 | } 100 | return x; /* exact output */ 101 | } 102 | -------------------------------------------------------------------------------- /posix/s_atanf.c: -------------------------------------------------------------------------------- 1 | /* s_atanf.c -- float version of s_atan.c. 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | static const float atanhi[] = { 20 | 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */ 21 | 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */ 22 | 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */ 23 | 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */ 24 | }; 25 | 26 | static const float atanlo[] = { 27 | 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */ 28 | 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */ 29 | 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */ 30 | 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */ 31 | }; 32 | 33 | static const float aT[] = { 34 | 3.3333334327e-01, /* 0x3eaaaaaa */ 35 | -2.0000000298e-01, /* 0xbe4ccccd */ 36 | 1.4285714924e-01, /* 0x3e124925 */ 37 | -1.1111110449e-01, /* 0xbde38e38 */ 38 | 9.0908870101e-02, /* 0x3dba2e6e */ 39 | -7.6918758452e-02, /* 0xbd9d8795 */ 40 | 6.6610731184e-02, /* 0x3d886b35 */ 41 | -5.8335702866e-02, /* 0xbd6ef16b */ 42 | 4.9768779427e-02, /* 0x3d4bda59 */ 43 | -3.6531571299e-02, /* 0xbd15a221 */ 44 | 1.6285819933e-02, /* 0x3c8569d7 */ 45 | }; 46 | 47 | static const float 48 | one = 1.0, 49 | huge = 1.0e30; 50 | 51 | float 52 | atanf(float x) 53 | { 54 | float w,s1,s2,z; 55 | int32_t ix,hx,id; 56 | 57 | GET_FLOAT_WORD(hx,x); 58 | ix = hx&0x7fffffff; 59 | if(ix>=0x50800000) { /* if |x| >= 2^34 */ 60 | if(ix>0x7f800000) 61 | return x+x; /* NaN */ 62 | if(hx>0) return atanhi[3]+atanlo[3]; 63 | else return -atanhi[3]-atanlo[3]; 64 | } 65 | if (ix < 0x3ee00000) { /* |x| < 0.4375 */ 66 | if (ix < 0x31000000) { /* |x| < 2^-29 */ 67 | if(huge+x>one) return x; /* raise inexact */ 68 | } 69 | id = -1; 70 | } else { 71 | x = fabsf(x); 72 | if (ix < 0x3f980000) { /* |x| < 1.1875 */ 73 | if (ix < 0x3f300000) { /* 7/16 <=|x|<11/16 */ 74 | id = 0; x = ((float)2.0*x-one)/((float)2.0+x); 75 | } else { /* 11/16<=|x|< 19/16 */ 76 | id = 1; x = (x-one)/(x+one); 77 | } 78 | } else { 79 | if (ix < 0x401c0000) { /* |x| < 2.4375 */ 80 | id = 2; x = (x-(float)1.5)/(one+(float)1.5*x); 81 | } else { /* 2.4375 <= |x| < 2^66 */ 82 | id = 3; x = -(float)1.0/x; 83 | } 84 | }} 85 | /* end of argument reduction */ 86 | z = x*x; 87 | w = z*z; 88 | /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ 89 | s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10]))))); 90 | s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9])))); 91 | if (id<0) return x - x*(s1+s2); 92 | else { 93 | z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); 94 | return (hx<0)? -z:z; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /posix/k_rem_pio2f.c: -------------------------------------------------------------------------------- 1 | /* k_rem_pio2f.c -- float version of k_rem_pio2.c 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | #include "math.h" 17 | #include "math_private.h" 18 | 19 | /* In the float version, the input parameter x contains 8 bit 20 | integers, not 24 bit integers. 113 bit precision is not supported. */ 21 | 22 | static const int init_jk[] = {4,7,9}; /* initial value for jk */ 23 | 24 | static const float PIo2[] = { 25 | 1.5703125000e+00, /* 0x3fc90000 */ 26 | 4.5776367188e-04, /* 0x39f00000 */ 27 | 2.5987625122e-05, /* 0x37da0000 */ 28 | 7.5437128544e-08, /* 0x33a20000 */ 29 | 6.0026650317e-11, /* 0x2e840000 */ 30 | 7.3896444519e-13, /* 0x2b500000 */ 31 | 5.3845816694e-15, /* 0x27c20000 */ 32 | 5.6378512969e-18, /* 0x22d00000 */ 33 | 8.3009228831e-20, /* 0x1fc40000 */ 34 | 3.2756352257e-22, /* 0x1bc60000 */ 35 | 6.3331015649e-25, /* 0x17440000 */ 36 | }; 37 | 38 | static const float 39 | zero = 0.0, 40 | one = 1.0, 41 | two8 = 2.5600000000e+02, /* 0x43800000 */ 42 | twon8 = 3.9062500000e-03; /* 0x3b800000 */ 43 | 44 | int 45 | __kernel_rem_pio2f(float *x, float *y, int e0, int nx, int prec, 46 | const int32_t *ipio2) 47 | { 48 | int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; 49 | float z,fw,f[20],fq[20],q[20]; 50 | 51 | /* initialize jk*/ 52 | jk = init_jk[prec]; 53 | jp = jk; 54 | 55 | /* determine jx,jv,q0, note that 3>q0 */ 56 | jx = nx-1; 57 | jv = (e0-3)/8; if(jv<0) jv=0; 58 | q0 = e0-8*(jv+1); 59 | 60 | /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ 61 | j = jv-jx; m = jx+jk; 62 | for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (float) ipio2[j]; 63 | 64 | /* compute q[0],q[1],...q[jk] */ 65 | for (i=0;i<=jk;i++) { 66 | for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; q[i] = fw; 67 | } 68 | 69 | jz = jk; 70 | recompute: 71 | /* distill q[] into iq[] reversingly */ 72 | for(i=0,j=jz,z=q[jz];j>0;i++,j--) { 73 | fw = (float)((int32_t)(twon8* z)); 74 | iq[i] = (int32_t)(z-two8*fw); 75 | z = q[j-1]+fw; 76 | } 77 | 78 | /* compute n */ 79 | z = scalbnf(z,q0); /* actual value of z */ 80 | z -= (float)8.0*floorf(z*(float)0.125); /* trim off integer >= 8 */ 81 | n = (int32_t) z; 82 | z -= (float)n; 83 | ih = 0; 84 | if(q0>0) { /* need iq[jz-1] to determine n */ 85 | i = (iq[jz-1]>>(8-q0)); n += i; 86 | iq[jz-1] -= i<<(8-q0); 87 | ih = iq[jz-1]>>(7-q0); 88 | } 89 | else if(q0==0) ih = iq[jz-1]>>8; 90 | else if(z>=(float)0.5) ih=2; 91 | 92 | if(ih>0) { /* q > 0.5 */ 93 | n += 1; carry = 0; 94 | for(i=0;i0) { /* rare case: chance is 1 in 12 */ 103 | switch(q0) { 104 | case 1: 105 | iq[jz-1] &= 0x7f; break; 106 | case 2: 107 | iq[jz-1] &= 0x3f; break; 108 | } 109 | } 110 | if(ih==2) { 111 | z = one - z; 112 | if(carry!=0) z -= scalbnf(one,q0); 113 | } 114 | } 115 | 116 | /* check if recomputation is needed */ 117 | if(z==zero) { 118 | j = 0; 119 | for (i=jz-1;i>=jk;i--) j |= iq[i]; 120 | if(j==0) { /* need recomputation */ 121 | for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ 122 | 123 | for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ 124 | f[jx+i] = (float) ipio2[jv+i]; 125 | for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; 126 | q[i] = fw; 127 | } 128 | jz += k; 129 | goto recompute; 130 | } 131 | } 132 | 133 | /* chop off zero terms */ 134 | if(z==(float)0.0) { 135 | jz -= 1; q0 -= 8; 136 | while(iq[jz]==0) { jz--; q0-=8;} 137 | } else { /* break z into 8-bit if necessary */ 138 | z = scalbnf(z,-q0); 139 | if(z>=two8) { 140 | fw = (float)((int32_t)(twon8*z)); 141 | iq[jz] = (int32_t)(z-two8*fw); 142 | jz += 1; q0 += 8; 143 | iq[jz] = (int32_t) fw; 144 | } else iq[jz] = (int32_t) z ; 145 | } 146 | 147 | /* convert integer "bit" chunk to floating-point value */ 148 | fw = scalbnf(one,q0); 149 | for(i=jz;i>=0;i--) { 150 | q[i] = fw*(float)iq[i]; fw*=twon8; 151 | } 152 | 153 | /* compute PIo2[0,...,jp]*q[jz,...,0] */ 154 | for(i=jz;i>=0;i--) { 155 | for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; 156 | fq[jz-i] = fw; 157 | } 158 | 159 | /* compress fq[] into y[] */ 160 | switch(prec) { 161 | case 0: 162 | fw = 0.0; 163 | for (i=jz;i>=0;i--) fw += fq[i]; 164 | y[0] = (ih==0)? fw: -fw; 165 | break; 166 | case 1: 167 | case 2: 168 | fw = 0.0; 169 | for (i=jz;i>=0;i--) fw += fq[i]; 170 | y[0] = (ih==0)? fw: -fw; 171 | fw = fq[0]-fw; 172 | for (i=1;i<=jz;i++) fw += fq[i]; 173 | y[1] = (ih==0)? fw: -fw; 174 | break; 175 | case 3: /* painful */ 176 | for (i=jz;i>0;i--) { 177 | fw = fq[i-1]+fq[i]; 178 | fq[i] += fq[i-1]-fw; 179 | fq[i-1] = fw; 180 | } 181 | for (i=jz;i>1;i--) { 182 | fw = fq[i-1]+fq[i]; 183 | fq[i] += fq[i-1]-fw; 184 | fq[i-1] = fw; 185 | } 186 | for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; 187 | if(ih==0) { 188 | y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; 189 | } else { 190 | y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; 191 | } 192 | } 193 | return n&7; 194 | } 195 | -------------------------------------------------------------------------------- /posix/e_rem_pio2f.c: -------------------------------------------------------------------------------- 1 | /* e_rem_pio2f.c -- float version of e_rem_pio2.c 2 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 3 | */ 4 | 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | /* __ieee754_rem_pio2f(x,y) 17 | * 18 | * return the remainder of x rem pi/2 in y[0]+y[1] 19 | * use __kernel_rem_pio2f() 20 | */ 21 | 22 | #include "math.h" 23 | #include "math_private.h" 24 | 25 | /* 26 | * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi 27 | */ 28 | static const int32_t two_over_pi[] = { 29 | 0xA2, 0xF9, 0x83, 0x6E, 0x4E, 0x44, 0x15, 0x29, 0xFC, 30 | 0x27, 0x57, 0xD1, 0xF5, 0x34, 0xDD, 0xC0, 0xDB, 0x62, 31 | 0x95, 0x99, 0x3C, 0x43, 0x90, 0x41, 0xFE, 0x51, 0x63, 32 | 0xAB, 0xDE, 0xBB, 0xC5, 0x61, 0xB7, 0x24, 0x6E, 0x3A, 33 | 0x42, 0x4D, 0xD2, 0xE0, 0x06, 0x49, 0x2E, 0xEA, 0x09, 34 | 0xD1, 0x92, 0x1C, 0xFE, 0x1D, 0xEB, 0x1C, 0xB1, 0x29, 35 | 0xA7, 0x3E, 0xE8, 0x82, 0x35, 0xF5, 0x2E, 0xBB, 0x44, 36 | 0x84, 0xE9, 0x9C, 0x70, 0x26, 0xB4, 0x5F, 0x7E, 0x41, 37 | 0x39, 0x91, 0xD6, 0x39, 0x83, 0x53, 0x39, 0xF4, 0x9C, 38 | 0x84, 0x5F, 0x8B, 0xBD, 0xF9, 0x28, 0x3B, 0x1F, 0xF8, 39 | 0x97, 0xFF, 0xDE, 0x05, 0x98, 0x0F, 0xEF, 0x2F, 0x11, 40 | 0x8B, 0x5A, 0x0A, 0x6D, 0x1F, 0x6D, 0x36, 0x7E, 0xCF, 41 | 0x27, 0xCB, 0x09, 0xB7, 0x4F, 0x46, 0x3F, 0x66, 0x9E, 42 | 0x5F, 0xEA, 0x2D, 0x75, 0x27, 0xBA, 0xC7, 0xEB, 0xE5, 43 | 0xF1, 0x7B, 0x3D, 0x07, 0x39, 0xF7, 0x8A, 0x52, 0x92, 44 | 0xEA, 0x6B, 0xFB, 0x5F, 0xB1, 0x1F, 0x8D, 0x5D, 0x08, 45 | 0x56, 0x03, 0x30, 0x46, 0xFC, 0x7B, 0x6B, 0xAB, 0xF0, 46 | 0xCF, 0xBC, 0x20, 0x9A, 0xF4, 0x36, 0x1D, 0xA9, 0xE3, 47 | 0x91, 0x61, 0x5E, 0xE6, 0x1B, 0x08, 0x65, 0x99, 0x85, 48 | 0x5F, 0x14, 0xA0, 0x68, 0x40, 0x8D, 0xFF, 0xD8, 0x80, 49 | 0x4D, 0x73, 0x27, 0x31, 0x06, 0x06, 0x15, 0x56, 0xCA, 50 | 0x73, 0xA8, 0xC9, 0x60, 0xE2, 0x7B, 0xC0, 0x8C, 0x6B, 51 | }; 52 | 53 | /* This array is like the one in e_rem_pio2.c, but the numbers are 54 | single precision and the last 8 bits are forced to 0. */ 55 | static const int32_t npio2_hw[] = { 56 | 0x3fc90f00, 0x40490f00, 0x4096cb00, 0x40c90f00, 0x40fb5300, 0x4116cb00, 57 | 0x412fed00, 0x41490f00, 0x41623100, 0x417b5300, 0x418a3a00, 0x4196cb00, 58 | 0x41a35c00, 0x41afed00, 0x41bc7e00, 0x41c90f00, 0x41d5a000, 0x41e23100, 59 | 0x41eec200, 0x41fb5300, 0x4203f200, 0x420a3a00, 0x42108300, 0x4216cb00, 60 | 0x421d1400, 0x42235c00, 0x4229a500, 0x422fed00, 0x42363600, 0x423c7e00, 61 | 0x4242c700, 0x42490f00 62 | }; 63 | 64 | /* 65 | * invpio2: 24 bits of 2/pi 66 | * pio2_1: first 17 bit of pi/2 67 | * pio2_1t: pi/2 - pio2_1 68 | * pio2_2: second 17 bit of pi/2 69 | * pio2_2t: pi/2 - (pio2_1+pio2_2) 70 | * pio2_3: third 17 bit of pi/2 71 | * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) 72 | */ 73 | 74 | static const float 75 | zero = 0.0000000000e+00, /* 0x00000000 */ 76 | half = 5.0000000000e-01, /* 0x3f000000 */ 77 | two8 = 2.5600000000e+02, /* 0x43800000 */ 78 | invpio2 = 6.3661980629e-01, /* 0x3f22f984 */ 79 | pio2_1 = 1.5707855225e+00, /* 0x3fc90f80 */ 80 | pio2_1t = 1.0804334124e-05, /* 0x37354443 */ 81 | pio2_2 = 1.0804273188e-05, /* 0x37354400 */ 82 | pio2_2t = 6.0770999344e-11, /* 0x2e85a308 */ 83 | pio2_3 = 6.0770943833e-11, /* 0x2e85a300 */ 84 | pio2_3t = 6.1232342629e-17; /* 0x248d3132 */ 85 | 86 | int32_t 87 | __ieee754_rem_pio2f(float x, float *y) 88 | { 89 | float z,w,t,r,fn; 90 | float tx[3]; 91 | int32_t e0,i,j,nx,n,ix,hx; 92 | 93 | GET_FLOAT_WORD(hx,x); 94 | ix = hx&0x7fffffff; 95 | if(ix<=0x3f490fd8) /* |x| ~<= pi/4 , no need for reduction */ 96 | {y[0] = x; y[1] = 0; return 0;} 97 | if(ix<0x4016cbe4) { /* |x| < 3pi/4, special case with n=+-1 */ 98 | if(hx>0) { 99 | z = x - pio2_1; 100 | if((ix&0xfffffff0)!=0x3fc90fd0) { /* 24+24 bit pi OK */ 101 | y[0] = z - pio2_1t; 102 | y[1] = (z-y[0])-pio2_1t; 103 | } else { /* near pi/2, use 24+24+24 bit pi */ 104 | z -= pio2_2; 105 | y[0] = z - pio2_2t; 106 | y[1] = (z-y[0])-pio2_2t; 107 | } 108 | return 1; 109 | } else { /* negative x */ 110 | z = x + pio2_1; 111 | if((ix&0xfffffff0)!=0x3fc90fd0) { /* 24+24 bit pi OK */ 112 | y[0] = z + pio2_1t; 113 | y[1] = (z-y[0])+pio2_1t; 114 | } else { /* near pi/2, use 24+24+24 bit pi */ 115 | z += pio2_2; 116 | y[0] = z + pio2_2t; 117 | y[1] = (z-y[0])+pio2_2t; 118 | } 119 | return -1; 120 | } 121 | } 122 | if(ix<=0x43490f80) { /* |x| ~<= 2^7*(pi/2), medium size */ 123 | t = fabsf(x); 124 | n = (int32_t) (t*invpio2+half); 125 | fn = (float)n; 126 | r = t-fn*pio2_1; 127 | w = fn*pio2_1t; /* 1st round good to 40 bit */ 128 | if(n<32&&(ix&0xffffff00)!=npio2_hw[n-1]) { 129 | y[0] = r-w; /* quick check no cancellation */ 130 | } else { 131 | u_int32_t high; 132 | j = ix>>23; 133 | y[0] = r-w; 134 | GET_FLOAT_WORD(high,y[0]); 135 | i = j-((high>>23)&0xff); 136 | if(i>8) { /* 2nd iteration needed, good to 57 */ 137 | t = r; 138 | w = fn*pio2_2; 139 | r = t-w; 140 | w = fn*pio2_2t-((t-r)-w); 141 | y[0] = r-w; 142 | GET_FLOAT_WORD(high,y[0]); 143 | i = j-((high>>23)&0xff); 144 | if(i>25) { /* 3rd iteration need, 74 bits acc */ 145 | t = r; /* will cover all possible cases */ 146 | w = fn*pio2_3; 147 | r = t-w; 148 | w = fn*pio2_3t-((t-r)-w); 149 | y[0] = r-w; 150 | } 151 | } 152 | } 153 | y[1] = (r-y[0])-w; 154 | if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} 155 | else return n; 156 | } 157 | /* 158 | * all other (large) arguments 159 | */ 160 | if(ix>=0x7f800000) { /* x is inf or NaN */ 161 | y[0]=y[1]=x-x; return 0; 162 | } 163 | /* set z = scalbn(|x|,ilogb(x)-7) */ 164 | e0 = (ix>>23)-134; /* e0 = ilogb(z)-7; */ 165 | SET_FLOAT_WORD(z, ix - ((int32_t)(e0<<23))); 166 | for(i=0;i<2;i++) { 167 | tx[i] = (float)((int32_t)(z)); 168 | z = (z-tx[i])*two8; 169 | } 170 | tx[2] = z; 171 | nx = 3; 172 | while(tx[nx-1]==zero) nx--; /* skip zero term */ 173 | n = __kernel_rem_pio2f(tx,y,e0,nx,2,two_over_pi); 174 | if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} 175 | return n; 176 | } 177 | -------------------------------------------------------------------------------- /nanosvgrast.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-14 Mikko Mononen memon@inside.org 3 | * 4 | * This software is provided 'as-is', without any express or implied 5 | * warranty. In no event will the authors be held liable for any damages 6 | * arising from the use of this software. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | * 20 | * The polygon rasterization is heavily based on stb_truetype rasterizer 21 | * by Sean Barrett - http://nothings.org/ 22 | * 23 | */ 24 | 25 | #ifndef NANOSVGRAST_H 26 | #define NANOSVGRAST_H 27 | 28 | #ifndef NANOSVGRAST_CPLUSPLUS 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | #endif 33 | 34 | typedef struct NSVGrasterizer NSVGrasterizer; 35 | 36 | /* Example Usage: 37 | // Load SVG 38 | NSVGimage* image; 39 | image = nsvgParseFromFile("test.svg", "px", 96); 40 | 41 | // Create rasterizer (can be used to render multiple images). 42 | struct NSVGrasterizer* rast = nsvgCreateRasterizer(); 43 | // Allocate memory for image 44 | unsigned char* img = malloc(w*h*4); 45 | // Rasterize 46 | nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); 47 | */ 48 | 49 | // Allocated rasterizer context. 50 | NSVGrasterizer* nsvgCreateRasterizer(void); 51 | 52 | // Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) 53 | // r - pointer to rasterizer context 54 | // image - pointer to image to rasterize 55 | // tx,ty - image offset (applied after scaling) 56 | // scale - image scale 57 | // dst - pointer to destination image data, 4 bytes per pixel (RGBA) 58 | // w - width of the image to render 59 | // h - height of the image to render 60 | // stride - number of bytes per scaleline in the destination buffer 61 | void nsvgRasterize(NSVGrasterizer* r, 62 | NSVGimage* image, float tx, float ty, float scale, 63 | unsigned char* dst, int w, int h, int stride); 64 | 65 | // Deletes rasterizer context. 66 | void nsvgDeleteRasterizer(NSVGrasterizer*); 67 | 68 | 69 | #ifndef NANOSVGRAST_CPLUSPLUS 70 | #ifdef __cplusplus 71 | } 72 | #endif 73 | #endif 74 | 75 | #endif // NANOSVGRAST_H 76 | 77 | #ifdef NANOSVGRAST_IMPLEMENTATION 78 | 79 | #include 80 | 81 | #define NSVG__SUBSAMPLES 5 82 | #define NSVG__FIXSHIFT 10 83 | #define NSVG__FIX (1 << NSVG__FIXSHIFT) 84 | #define NSVG__FIXMASK (NSVG__FIX-1) 85 | #define NSVG__MEMPAGE_SIZE 1024 86 | 87 | typedef struct NSVGedge { 88 | float x0,y0, x1,y1; 89 | int dir; 90 | struct NSVGedge* next; 91 | } NSVGedge; 92 | 93 | typedef struct NSVGpoint { 94 | float x, y; 95 | float dx, dy; 96 | float len; 97 | float dmx, dmy; 98 | unsigned char flags; 99 | } NSVGpoint; 100 | 101 | typedef struct NSVGactiveEdge { 102 | int x,dx; 103 | float ey; 104 | int dir; 105 | struct NSVGactiveEdge *next; 106 | } NSVGactiveEdge; 107 | 108 | typedef struct NSVGmemPage { 109 | unsigned char mem[NSVG__MEMPAGE_SIZE]; 110 | int size; 111 | struct NSVGmemPage* next; 112 | } NSVGmemPage; 113 | 114 | typedef struct NSVGcachedPaint { 115 | char type; 116 | char spread; 117 | float xform[6]; 118 | unsigned int colors[256]; 119 | } NSVGcachedPaint; 120 | 121 | struct NSVGrasterizer 122 | { 123 | float px, py; 124 | 125 | float tessTol; 126 | float distTol; 127 | 128 | NSVGedge* edges; 129 | int nedges; 130 | int cedges; 131 | 132 | NSVGpoint* points; 133 | int npoints; 134 | int cpoints; 135 | 136 | NSVGpoint* points2; 137 | int npoints2; 138 | int cpoints2; 139 | 140 | NSVGactiveEdge* freelist; 141 | NSVGmemPage* pages; 142 | NSVGmemPage* curpage; 143 | 144 | unsigned char* scanline; 145 | int cscanline; 146 | 147 | unsigned char* bitmap; 148 | int width, height, stride; 149 | }; 150 | 151 | NSVGrasterizer* nsvgCreateRasterizer(void) 152 | { 153 | NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); 154 | if (r == NULL) goto error; 155 | memset(r, 0, sizeof(NSVGrasterizer)); 156 | 157 | r->tessTol = 0.25f; 158 | r->distTol = 0.01f; 159 | 160 | return r; 161 | 162 | error: 163 | nsvgDeleteRasterizer(r); 164 | return NULL; 165 | } 166 | 167 | void nsvgDeleteRasterizer(NSVGrasterizer* r) 168 | { 169 | NSVGmemPage* p; 170 | 171 | if (r == NULL) return; 172 | 173 | p = r->pages; 174 | while (p != NULL) { 175 | NSVGmemPage* next = p->next; 176 | free(p); 177 | p = next; 178 | } 179 | 180 | if (r->edges) free(r->edges); 181 | if (r->points) free(r->points); 182 | if (r->points2) free(r->points2); 183 | if (r->scanline) free(r->scanline); 184 | 185 | free(r); 186 | } 187 | 188 | static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) 189 | { 190 | NSVGmemPage *newp; 191 | 192 | // If using existing chain, return the next page in chain 193 | if (cur != NULL && cur->next != NULL) { 194 | return cur->next; 195 | } 196 | 197 | // Alloc new page 198 | newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); 199 | if (newp == NULL) return NULL; 200 | memset(newp, 0, sizeof(NSVGmemPage)); 201 | 202 | // Add to linked list 203 | if (cur != NULL) 204 | cur->next = newp; 205 | else 206 | r->pages = newp; 207 | 208 | return newp; 209 | } 210 | 211 | static void nsvg__resetPool(NSVGrasterizer* r) 212 | { 213 | NSVGmemPage* p = r->pages; 214 | while (p != NULL) { 215 | p->size = 0; 216 | p = p->next; 217 | } 218 | r->curpage = r->pages; 219 | } 220 | 221 | static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) 222 | { 223 | unsigned char* buf; 224 | if (size > NSVG__MEMPAGE_SIZE) return NULL; 225 | if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { 226 | r->curpage = nsvg__nextPage(r, r->curpage); 227 | } 228 | buf = &r->curpage->mem[r->curpage->size]; 229 | r->curpage->size += size; 230 | return buf; 231 | } 232 | 233 | static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) 234 | { 235 | float dx = x2 - x1; 236 | float dy = y2 - y1; 237 | return dx*dx + dy*dy < tol*tol; 238 | } 239 | 240 | static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) 241 | { 242 | NSVGpoint* pt; 243 | 244 | if (r->npoints > 0) { 245 | pt = &r->points[r->npoints-1]; 246 | if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { 247 | pt->flags = (unsigned char)(pt->flags | flags); 248 | return; 249 | } 250 | } 251 | 252 | if (r->npoints+1 > r->cpoints) { 253 | r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; 254 | r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); 255 | if (r->points == NULL) return; 256 | } 257 | 258 | pt = &r->points[r->npoints]; 259 | pt->x = x; 260 | pt->y = y; 261 | pt->flags = (unsigned char)flags; 262 | r->npoints++; 263 | } 264 | 265 | static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) 266 | { 267 | if (r->npoints+1 > r->cpoints) { 268 | r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; 269 | r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); 270 | if (r->points == NULL) return; 271 | } 272 | r->points[r->npoints] = pt; 273 | r->npoints++; 274 | } 275 | 276 | static void nsvg__duplicatePoints(NSVGrasterizer* r) 277 | { 278 | if (r->npoints > r->cpoints2) { 279 | r->cpoints2 = r->npoints; 280 | r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); 281 | if (r->points2 == NULL) return; 282 | } 283 | 284 | memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); 285 | r->npoints2 = r->npoints; 286 | } 287 | 288 | static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) 289 | { 290 | NSVGedge* e; 291 | 292 | // Skip horizontal edges 293 | if (y0 == y1) 294 | return; 295 | 296 | if (r->nedges+1 > r->cedges) { 297 | r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; 298 | r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); 299 | if (r->edges == NULL) return; 300 | } 301 | 302 | e = &r->edges[r->nedges]; 303 | r->nedges++; 304 | 305 | if (y0 < y1) { 306 | e->x0 = x0; 307 | e->y0 = y0; 308 | e->x1 = x1; 309 | e->y1 = y1; 310 | e->dir = 1; 311 | } else { 312 | e->x0 = x1; 313 | e->y0 = y1; 314 | e->x1 = x0; 315 | e->y1 = y0; 316 | e->dir = -1; 317 | } 318 | } 319 | 320 | static float nsvg__normalize(float *x, float* y) 321 | { 322 | float d = sqrtf((*x)*(*x) + (*y)*(*y)); 323 | if (d > 1e-6f) { 324 | float id = 1.0f / d; 325 | *x *= id; 326 | *y *= id; 327 | } 328 | return d; 329 | } 330 | 331 | static float nsvg__absf(float x) { return x < 0 ? -x : x; } 332 | 333 | static void nsvg__flattenCubicBez(NSVGrasterizer* r, 334 | float x1, float y1, float x2, float y2, 335 | float x3, float y3, float x4, float y4, 336 | int level, int type) 337 | { 338 | float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; 339 | float dx,dy,d2,d3; 340 | 341 | if (level > 10) return; 342 | 343 | x12 = (x1+x2)*0.5f; 344 | y12 = (y1+y2)*0.5f; 345 | x23 = (x2+x3)*0.5f; 346 | y23 = (y2+y3)*0.5f; 347 | x34 = (x3+x4)*0.5f; 348 | y34 = (y3+y4)*0.5f; 349 | x123 = (x12+x23)*0.5f; 350 | y123 = (y12+y23)*0.5f; 351 | 352 | dx = x4 - x1; 353 | dy = y4 - y1; 354 | d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); 355 | d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); 356 | 357 | if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { 358 | nsvg__addPathPoint(r, x4, y4, type); 359 | return; 360 | } 361 | 362 | x234 = (x23+x34)*0.5f; 363 | y234 = (y23+y34)*0.5f; 364 | x1234 = (x123+x234)*0.5f; 365 | y1234 = (y123+y234)*0.5f; 366 | 367 | nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); 368 | nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); 369 | } 370 | 371 | static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) 372 | { 373 | int i, j; 374 | NSVGpath* path; 375 | 376 | for (path = shape->paths; path != NULL; path = path->next) { 377 | r->npoints = 0; 378 | // Flatten path 379 | nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); 380 | for (i = 0; i < path->npts-1; i += 3) { 381 | float* p = &path->pts[i*2]; 382 | 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); 383 | } 384 | // Close path 385 | nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); 386 | // Build edges 387 | for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) 388 | nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); 389 | } 390 | } 391 | 392 | enum NSVGpointFlags 393 | { 394 | NSVG_PT_CORNER = 0x01, 395 | NSVG_PT_BEVEL = 0x02, 396 | NSVG_PT_LEFT = 0x04 397 | }; 398 | 399 | static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) 400 | { 401 | float w = lineWidth * 0.5f; 402 | float dx = p1->x - p0->x; 403 | float dy = p1->y - p0->y; 404 | float len = nsvg__normalize(&dx, &dy); 405 | float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; 406 | float dlx = dy, dly = -dx; 407 | float lx = px - dlx*w, ly = py - dly*w; 408 | float rx = px + dlx*w, ry = py + dly*w; 409 | left->x = lx; left->y = ly; 410 | right->x = rx; right->y = ry; 411 | } 412 | 413 | static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) 414 | { 415 | float w = lineWidth * 0.5f; 416 | float px = p->x, py = p->y; 417 | float dlx = dy, dly = -dx; 418 | float lx = px - dlx*w, ly = py - dly*w; 419 | float rx = px + dlx*w, ry = py + dly*w; 420 | 421 | nsvg__addEdge(r, lx, ly, rx, ry); 422 | 423 | if (connect) { 424 | nsvg__addEdge(r, left->x, left->y, lx, ly); 425 | nsvg__addEdge(r, rx, ry, right->x, right->y); 426 | } 427 | left->x = lx; left->y = ly; 428 | right->x = rx; right->y = ry; 429 | } 430 | 431 | static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) 432 | { 433 | float w = lineWidth * 0.5f; 434 | float px = p->x - dx*w, py = p->y - dy*w; 435 | float dlx = dy, dly = -dx; 436 | float lx = px - dlx*w, ly = py - dly*w; 437 | float rx = px + dlx*w, ry = py + dly*w; 438 | 439 | nsvg__addEdge(r, lx, ly, rx, ry); 440 | 441 | if (connect) { 442 | nsvg__addEdge(r, left->x, left->y, lx, ly); 443 | nsvg__addEdge(r, rx, ry, right->x, right->y); 444 | } 445 | left->x = lx; left->y = ly; 446 | right->x = rx; right->y = ry; 447 | } 448 | 449 | #ifndef NSVG_PI 450 | #define NSVG_PI (3.14159265358979323846264338327f) 451 | #endif 452 | 453 | static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) 454 | { 455 | int i; 456 | float w = lineWidth * 0.5f; 457 | float px = p->x, py = p->y; 458 | float dlx = dy, dly = -dx; 459 | float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; 460 | 461 | for (i = 0; i < ncap; i++) { 462 | float a = (float)i/(float)(ncap-1)*NSVG_PI; 463 | float ax = cosf(a) * w, ay = sinf(a) * w; 464 | float x = px - dlx*ax - dx*ay; 465 | float y = py - dly*ax - dy*ay; 466 | 467 | if (i > 0) 468 | nsvg__addEdge(r, prevx, prevy, x, y); 469 | 470 | prevx = x; 471 | prevy = y; 472 | 473 | if (i == 0) { 474 | lx = x; ly = y; 475 | } else if (i == ncap-1) { 476 | rx = x; ry = y; 477 | } 478 | } 479 | 480 | if (connect) { 481 | nsvg__addEdge(r, left->x, left->y, lx, ly); 482 | nsvg__addEdge(r, rx, ry, right->x, right->y); 483 | } 484 | 485 | left->x = lx; left->y = ly; 486 | right->x = rx; right->y = ry; 487 | } 488 | 489 | static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) 490 | { 491 | float w = lineWidth * 0.5f; 492 | float dlx0 = p0->dy, dly0 = -p0->dx; 493 | float dlx1 = p1->dy, dly1 = -p1->dx; 494 | float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); 495 | float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); 496 | float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); 497 | float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); 498 | 499 | nsvg__addEdge(r, lx0, ly0, left->x, left->y); 500 | nsvg__addEdge(r, lx1, ly1, lx0, ly0); 501 | 502 | nsvg__addEdge(r, right->x, right->y, rx0, ry0); 503 | nsvg__addEdge(r, rx0, ry0, rx1, ry1); 504 | 505 | left->x = lx1; left->y = ly1; 506 | right->x = rx1; right->y = ry1; 507 | } 508 | 509 | static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) 510 | { 511 | float w = lineWidth * 0.5f; 512 | float dlx0 = p0->dy, dly0 = -p0->dx; 513 | float dlx1 = p1->dy, dly1 = -p1->dx; 514 | float lx0, rx0, lx1, rx1; 515 | float ly0, ry0, ly1, ry1; 516 | 517 | if (p1->flags & NSVG_PT_LEFT) { 518 | lx0 = lx1 = p1->x - p1->dmx * w; 519 | ly0 = ly1 = p1->y - p1->dmy * w; 520 | nsvg__addEdge(r, lx1, ly1, left->x, left->y); 521 | 522 | rx0 = p1->x + (dlx0 * w); 523 | ry0 = p1->y + (dly0 * w); 524 | rx1 = p1->x + (dlx1 * w); 525 | ry1 = p1->y + (dly1 * w); 526 | nsvg__addEdge(r, right->x, right->y, rx0, ry0); 527 | nsvg__addEdge(r, rx0, ry0, rx1, ry1); 528 | } else { 529 | lx0 = p1->x - (dlx0 * w); 530 | ly0 = p1->y - (dly0 * w); 531 | lx1 = p1->x - (dlx1 * w); 532 | ly1 = p1->y - (dly1 * w); 533 | nsvg__addEdge(r, lx0, ly0, left->x, left->y); 534 | nsvg__addEdge(r, lx1, ly1, lx0, ly0); 535 | 536 | rx0 = rx1 = p1->x + p1->dmx * w; 537 | ry0 = ry1 = p1->y + p1->dmy * w; 538 | nsvg__addEdge(r, right->x, right->y, rx1, ry1); 539 | } 540 | 541 | left->x = lx1; left->y = ly1; 542 | right->x = rx1; right->y = ry1; 543 | } 544 | 545 | static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) 546 | { 547 | int i, n; 548 | float w = lineWidth * 0.5f; 549 | float dlx0 = p0->dy, dly0 = -p0->dx; 550 | float dlx1 = p1->dy, dly1 = -p1->dx; 551 | float a0 = atan2f(dly0, dlx0); 552 | float a1 = atan2f(dly1, dlx1); 553 | float da = a1 - a0; 554 | float lx, ly, rx, ry; 555 | 556 | if (da < NSVG_PI) da += NSVG_PI*2; 557 | if (da > NSVG_PI) da -= NSVG_PI*2; 558 | 559 | n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); 560 | if (n < 2) n = 2; 561 | if (n > ncap) n = ncap; 562 | 563 | lx = left->x; 564 | ly = left->y; 565 | rx = right->x; 566 | ry = right->y; 567 | 568 | for (i = 0; i < n; i++) { 569 | float u = (float)i/(float)(n-1); 570 | float a = a0 + u*da; 571 | float ax = cosf(a) * w, ay = sinf(a) * w; 572 | float lx1 = p1->x - ax, ly1 = p1->y - ay; 573 | float rx1 = p1->x + ax, ry1 = p1->y + ay; 574 | 575 | nsvg__addEdge(r, lx1, ly1, lx, ly); 576 | nsvg__addEdge(r, rx, ry, rx1, ry1); 577 | 578 | lx = lx1; ly = ly1; 579 | rx = rx1; ry = ry1; 580 | } 581 | 582 | left->x = lx; left->y = ly; 583 | right->x = rx; right->y = ry; 584 | } 585 | 586 | static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) 587 | { 588 | float w = lineWidth * 0.5f; 589 | float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); 590 | float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); 591 | 592 | nsvg__addEdge(r, lx, ly, left->x, left->y); 593 | nsvg__addEdge(r, right->x, right->y, rx, ry); 594 | 595 | left->x = lx; left->y = ly; 596 | right->x = rx; right->y = ry; 597 | } 598 | 599 | static int nsvg__curveDivs(float r, float arc, float tol) 600 | { 601 | float da = acosf(r / (r + tol)) * 2.0f; 602 | int divs = (int)ceilf(arc / da); 603 | if (divs < 2) divs = 2; 604 | return divs; 605 | } 606 | 607 | static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) 608 | { 609 | int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. 610 | NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; 611 | NSVGpoint* p0, *p1; 612 | int j, s, e; 613 | 614 | // Build stroke edges 615 | if (closed) { 616 | // Looping 617 | p0 = &points[npoints-1]; 618 | p1 = &points[0]; 619 | s = 0; 620 | e = npoints; 621 | } else { 622 | // Add cap 623 | p0 = &points[0]; 624 | p1 = &points[1]; 625 | s = 1; 626 | e = npoints-1; 627 | } 628 | 629 | if (closed) { 630 | nsvg__initClosed(&left, &right, p0, p1, lineWidth); 631 | firstLeft = left; 632 | firstRight = right; 633 | } else { 634 | // Add cap 635 | float dx = p1->x - p0->x; 636 | float dy = p1->y - p0->y; 637 | nsvg__normalize(&dx, &dy); 638 | if (lineCap == NSVG_CAP_BUTT) 639 | nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); 640 | else if (lineCap == NSVG_CAP_SQUARE) 641 | nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); 642 | else if (lineCap == NSVG_CAP_ROUND) 643 | nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); 644 | } 645 | 646 | for (j = s; j < e; ++j) { 647 | if (p1->flags & NSVG_PT_CORNER) { 648 | if (lineJoin == NSVG_JOIN_ROUND) 649 | nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); 650 | else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) 651 | nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); 652 | else 653 | nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); 654 | } else { 655 | nsvg__straightJoin(r, &left, &right, p1, lineWidth); 656 | } 657 | p0 = p1++; 658 | } 659 | 660 | if (closed) { 661 | // Loop it 662 | nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); 663 | nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); 664 | } else { 665 | // Add cap 666 | float dx = p1->x - p0->x; 667 | float dy = p1->y - p0->y; 668 | nsvg__normalize(&dx, &dy); 669 | if (lineCap == NSVG_CAP_BUTT) 670 | nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); 671 | else if (lineCap == NSVG_CAP_SQUARE) 672 | nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); 673 | else if (lineCap == NSVG_CAP_ROUND) 674 | nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); 675 | } 676 | } 677 | 678 | static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) 679 | { 680 | int i, j; 681 | NSVGpoint* p0, *p1; 682 | 683 | p0 = &r->points[r->npoints-1]; 684 | p1 = &r->points[0]; 685 | for (i = 0; i < r->npoints; i++) { 686 | // Calculate segment direction and length 687 | p0->dx = p1->x - p0->x; 688 | p0->dy = p1->y - p0->y; 689 | p0->len = nsvg__normalize(&p0->dx, &p0->dy); 690 | // Advance 691 | p0 = p1++; 692 | } 693 | 694 | // calculate joins 695 | p0 = &r->points[r->npoints-1]; 696 | p1 = &r->points[0]; 697 | for (j = 0; j < r->npoints; j++) { 698 | float dlx0, dly0, dlx1, dly1, dmr2, cross; 699 | dlx0 = p0->dy; 700 | dly0 = -p0->dx; 701 | dlx1 = p1->dy; 702 | dly1 = -p1->dx; 703 | // Calculate extrusions 704 | p1->dmx = (dlx0 + dlx1) * 0.5f; 705 | p1->dmy = (dly0 + dly1) * 0.5f; 706 | dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; 707 | if (dmr2 > 0.000001f) { 708 | float s2 = 1.0f / dmr2; 709 | if (s2 > 600.0f) { 710 | s2 = 600.0f; 711 | } 712 | p1->dmx *= s2; 713 | p1->dmy *= s2; 714 | } 715 | 716 | // Clear flags, but keep the corner. 717 | p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; 718 | 719 | // Keep track of left turns. 720 | cross = p1->dx * p0->dy - p0->dx * p1->dy; 721 | if (cross > 0.0f) 722 | p1->flags |= NSVG_PT_LEFT; 723 | 724 | // Check to see if the corner needs to be beveled. 725 | if (p1->flags & NSVG_PT_CORNER) { 726 | if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { 727 | p1->flags |= NSVG_PT_BEVEL; 728 | } 729 | } 730 | 731 | p0 = p1++; 732 | } 733 | } 734 | 735 | static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) 736 | { 737 | int i, j, closed; 738 | NSVGpath* path; 739 | NSVGpoint* p0, *p1; 740 | float miterLimit = shape->miterLimit; 741 | int lineJoin = shape->strokeLineJoin; 742 | int lineCap = shape->strokeLineCap; 743 | float lineWidth = shape->strokeWidth * scale; 744 | 745 | for (path = shape->paths; path != NULL; path = path->next) { 746 | // Flatten path 747 | r->npoints = 0; 748 | nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); 749 | for (i = 0; i < path->npts-1; i += 3) { 750 | float* p = &path->pts[i*2]; 751 | 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); 752 | } 753 | if (r->npoints < 2) 754 | continue; 755 | 756 | closed = path->closed; 757 | 758 | // If the first and last points are the same, remove the last, mark as closed path. 759 | p0 = &r->points[r->npoints-1]; 760 | p1 = &r->points[0]; 761 | if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { 762 | r->npoints--; 763 | p0 = &r->points[r->npoints-1]; 764 | closed = 1; 765 | } 766 | 767 | if (shape->strokeDashCount > 0) { 768 | int idash = 0, dashState = 1; 769 | float totalDist = 0, dashLen, allDashLen, dashOffset; 770 | NSVGpoint cur; 771 | 772 | if (closed) 773 | nsvg__appendPathPoint(r, r->points[0]); 774 | 775 | // Duplicate points -> points2. 776 | nsvg__duplicatePoints(r); 777 | 778 | r->npoints = 0; 779 | cur = r->points2[0]; 780 | nsvg__appendPathPoint(r, cur); 781 | 782 | // Figure out dash offset. 783 | allDashLen = 0; 784 | for (j = 0; j < shape->strokeDashCount; j++) 785 | allDashLen += shape->strokeDashArray[j]; 786 | if (shape->strokeDashCount & 1) 787 | allDashLen *= 2.0f; 788 | // Find location inside pattern 789 | dashOffset = fmodf(shape->strokeDashOffset, allDashLen); 790 | if (dashOffset < 0.0f) 791 | dashOffset += allDashLen; 792 | 793 | while (dashOffset > shape->strokeDashArray[idash]) { 794 | dashOffset -= shape->strokeDashArray[idash]; 795 | idash = (idash + 1) % shape->strokeDashCount; 796 | } 797 | dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; 798 | 799 | for (j = 1; j < r->npoints2; ) { 800 | float dx = r->points2[j].x - cur.x; 801 | float dy = r->points2[j].y - cur.y; 802 | float dist = sqrtf(dx*dx + dy*dy); 803 | 804 | if ((totalDist + dist) > dashLen) { 805 | // Calculate intermediate point 806 | float d = (dashLen - totalDist) / dist; 807 | float x = cur.x + dx * d; 808 | float y = cur.y + dy * d; 809 | nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); 810 | 811 | // Stroke 812 | if (r->npoints > 1 && dashState) { 813 | nsvg__prepareStroke(r, miterLimit, lineJoin); 814 | nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); 815 | } 816 | // Advance dash pattern 817 | dashState = !dashState; 818 | idash = (idash+1) % shape->strokeDashCount; 819 | dashLen = shape->strokeDashArray[idash] * scale; 820 | // Restart 821 | cur.x = x; 822 | cur.y = y; 823 | cur.flags = NSVG_PT_CORNER; 824 | totalDist = 0.0f; 825 | r->npoints = 0; 826 | nsvg__appendPathPoint(r, cur); 827 | } else { 828 | totalDist += dist; 829 | cur = r->points2[j]; 830 | nsvg__appendPathPoint(r, cur); 831 | j++; 832 | } 833 | } 834 | // Stroke any leftover path 835 | if (r->npoints > 1 && dashState) 836 | nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); 837 | } else { 838 | nsvg__prepareStroke(r, miterLimit, lineJoin); 839 | nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); 840 | } 841 | } 842 | } 843 | 844 | static int nsvg__cmpEdge(const void *p, const void *q) 845 | { 846 | const NSVGedge* a = (const NSVGedge*)p; 847 | const NSVGedge* b = (const NSVGedge*)q; 848 | 849 | if (a->y0 < b->y0) return -1; 850 | if (a->y0 > b->y0) return 1; 851 | return 0; 852 | } 853 | 854 | 855 | static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) 856 | { 857 | NSVGactiveEdge* z; 858 | 859 | if (r->freelist != NULL) { 860 | // Restore from freelist. 861 | z = r->freelist; 862 | r->freelist = z->next; 863 | } else { 864 | // Alloc new edge. 865 | z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); 866 | if (z == NULL) return NULL; 867 | } 868 | 869 | float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 870 | // STBTT_assert(e->y0 <= start_point); 871 | // round dx down to avoid going too far 872 | if (dxdy < 0) 873 | z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); 874 | else 875 | z->dx = (int)floorf(NSVG__FIX * dxdy); 876 | z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); 877 | // z->x -= off_x * FIX; 878 | z->ey = e->y1; 879 | z->next = 0; 880 | z->dir = e->dir; 881 | 882 | return z; 883 | } 884 | 885 | static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) 886 | { 887 | z->next = r->freelist; 888 | r->freelist = z; 889 | } 890 | 891 | static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) 892 | { 893 | int i = x0 >> NSVG__FIXSHIFT; 894 | int j = x1 >> NSVG__FIXSHIFT; 895 | if (i < *xmin) *xmin = i; 896 | if (j > *xmax) *xmax = j; 897 | if (i < len && j >= 0) { 898 | if (i == j) { 899 | // x0,x1 are the same pixel, so compute combined coverage 900 | scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); 901 | } else { 902 | if (i >= 0) // add antialiasing for x0 903 | scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); 904 | else 905 | i = -1; // clip 906 | 907 | if (j < len) // add antialiasing for x1 908 | scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); 909 | else 910 | j = len; // clip 911 | 912 | for (++i; i < j; ++i) // fill pixels between x0 and x1 913 | scanline[i] = (unsigned char)(scanline[i] + maxWeight); 914 | } 915 | } 916 | } 917 | 918 | // note: this routine clips fills that extend off the edges... ideally this 919 | // wouldn't happen, but it could happen if the truetype glyph bounding boxes 920 | // are wrong, or if the user supplies a too-small bitmap 921 | static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) 922 | { 923 | // non-zero winding fill 924 | int x0 = 0, w = 0; 925 | 926 | if (fillRule == NSVG_FILLRULE_NONZERO) { 927 | // Non-zero 928 | while (e != NULL) { 929 | if (w == 0) { 930 | // if we're currently at zero, we need to record the edge start point 931 | x0 = e->x; w += e->dir; 932 | } else { 933 | int x1 = e->x; w += e->dir; 934 | // if we went to zero, we need to draw 935 | if (w == 0) 936 | nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); 937 | } 938 | e = e->next; 939 | } 940 | } else if (fillRule == NSVG_FILLRULE_EVENODD) { 941 | // Even-odd 942 | while (e != NULL) { 943 | if (w == 0) { 944 | // if we're currently at zero, we need to record the edge start point 945 | x0 = e->x; w = 1; 946 | } else { 947 | int x1 = e->x; w = 0; 948 | nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); 949 | } 950 | e = e->next; 951 | } 952 | } 953 | } 954 | 955 | static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } 956 | 957 | static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) 958 | { 959 | return (r) | (g << 8) | (b << 16) | (a << 24); 960 | } 961 | 962 | static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) 963 | { 964 | int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); 965 | int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; 966 | int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; 967 | int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; 968 | int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; 969 | return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); 970 | } 971 | 972 | static unsigned int nsvg__applyOpacity(unsigned int c, float u) 973 | { 974 | int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); 975 | int r = (c) & 0xff; 976 | int g = (c>>8) & 0xff; 977 | int b = (c>>16) & 0xff; 978 | int a = (((c>>24) & 0xff)*iu) >> 8; 979 | return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); 980 | } 981 | 982 | static inline int nsvg__div255(int x) 983 | { 984 | return ((x+1) * 257) >> 16; 985 | } 986 | 987 | static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, 988 | float tx, float ty, float scale, NSVGcachedPaint* cache) 989 | { 990 | 991 | if (cache->type == NSVG_PAINT_COLOR) { 992 | int i, cr, cg, cb, ca; 993 | cr = cache->colors[0] & 0xff; 994 | cg = (cache->colors[0] >> 8) & 0xff; 995 | cb = (cache->colors[0] >> 16) & 0xff; 996 | ca = (cache->colors[0] >> 24) & 0xff; 997 | 998 | for (i = 0; i < count; i++) { 999 | int r,g,b; 1000 | int a = nsvg__div255((int)cover[0] * ca); 1001 | int ia = 255 - a; 1002 | // Premultiply 1003 | r = nsvg__div255(cr * a); 1004 | g = nsvg__div255(cg * a); 1005 | b = nsvg__div255(cb * a); 1006 | 1007 | // Blend over 1008 | r += nsvg__div255(ia * (int)dst[0]); 1009 | g += nsvg__div255(ia * (int)dst[1]); 1010 | b += nsvg__div255(ia * (int)dst[2]); 1011 | a += nsvg__div255(ia * (int)dst[3]); 1012 | 1013 | dst[0] = (unsigned char)r; 1014 | dst[1] = (unsigned char)g; 1015 | dst[2] = (unsigned char)b; 1016 | dst[3] = (unsigned char)a; 1017 | 1018 | cover++; 1019 | dst += 4; 1020 | } 1021 | } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { 1022 | // TODO: spread modes. 1023 | // TODO: plenty of opportunities to optimize. 1024 | float fx, fy, dx, gy; 1025 | float* t = cache->xform; 1026 | int i, cr, cg, cb, ca; 1027 | unsigned int c; 1028 | 1029 | fx = ((float)x - tx) / scale; 1030 | fy = ((float)y - ty) / scale; 1031 | dx = 1.0f / scale; 1032 | 1033 | for (i = 0; i < count; i++) { 1034 | int r,g,b,a,ia; 1035 | gy = fx*t[1] + fy*t[3] + t[5]; 1036 | c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; 1037 | cr = (c) & 0xff; 1038 | cg = (c >> 8) & 0xff; 1039 | cb = (c >> 16) & 0xff; 1040 | ca = (c >> 24) & 0xff; 1041 | 1042 | a = nsvg__div255((int)cover[0] * ca); 1043 | ia = 255 - a; 1044 | 1045 | // Premultiply 1046 | r = nsvg__div255(cr * a); 1047 | g = nsvg__div255(cg * a); 1048 | b = nsvg__div255(cb * a); 1049 | 1050 | // Blend over 1051 | r += nsvg__div255(ia * (int)dst[0]); 1052 | g += nsvg__div255(ia * (int)dst[1]); 1053 | b += nsvg__div255(ia * (int)dst[2]); 1054 | a += nsvg__div255(ia * (int)dst[3]); 1055 | 1056 | dst[0] = (unsigned char)r; 1057 | dst[1] = (unsigned char)g; 1058 | dst[2] = (unsigned char)b; 1059 | dst[3] = (unsigned char)a; 1060 | 1061 | cover++; 1062 | dst += 4; 1063 | fx += dx; 1064 | } 1065 | } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { 1066 | // TODO: spread modes. 1067 | // TODO: plenty of opportunities to optimize. 1068 | // TODO: focus (fx,fy) 1069 | float fx, fy, dx, gx, gy, gd; 1070 | float* t = cache->xform; 1071 | int i, cr, cg, cb, ca; 1072 | unsigned int c; 1073 | 1074 | fx = ((float)x - tx) / scale; 1075 | fy = ((float)y - ty) / scale; 1076 | dx = 1.0f / scale; 1077 | 1078 | for (i = 0; i < count; i++) { 1079 | int r,g,b,a,ia; 1080 | gx = fx*t[0] + fy*t[2] + t[4]; 1081 | gy = fx*t[1] + fy*t[3] + t[5]; 1082 | gd = sqrtf(gx*gx + gy*gy); 1083 | c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; 1084 | cr = (c) & 0xff; 1085 | cg = (c >> 8) & 0xff; 1086 | cb = (c >> 16) & 0xff; 1087 | ca = (c >> 24) & 0xff; 1088 | 1089 | a = nsvg__div255((int)cover[0] * ca); 1090 | ia = 255 - a; 1091 | 1092 | // Premultiply 1093 | r = nsvg__div255(cr * a); 1094 | g = nsvg__div255(cg * a); 1095 | b = nsvg__div255(cb * a); 1096 | 1097 | // Blend over 1098 | r += nsvg__div255(ia * (int)dst[0]); 1099 | g += nsvg__div255(ia * (int)dst[1]); 1100 | b += nsvg__div255(ia * (int)dst[2]); 1101 | a += nsvg__div255(ia * (int)dst[3]); 1102 | 1103 | dst[0] = (unsigned char)r; 1104 | dst[1] = (unsigned char)g; 1105 | dst[2] = (unsigned char)b; 1106 | dst[3] = (unsigned char)a; 1107 | 1108 | cover++; 1109 | dst += 4; 1110 | fx += dx; 1111 | } 1112 | } 1113 | } 1114 | 1115 | static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) 1116 | { 1117 | NSVGactiveEdge *active = NULL; 1118 | int y, s; 1119 | int e = 0; 1120 | int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline 1121 | int xmin, xmax; 1122 | 1123 | for (y = 0; y < r->height; y++) { 1124 | memset(r->scanline, 0, r->width); 1125 | xmin = r->width; 1126 | xmax = 0; 1127 | for (s = 0; s < NSVG__SUBSAMPLES; ++s) { 1128 | // find center of pixel for this scanline 1129 | float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; 1130 | NSVGactiveEdge **step = &active; 1131 | 1132 | // update all active edges; 1133 | // remove all active edges that terminate before the center of this scanline 1134 | while (*step) { 1135 | NSVGactiveEdge *z = *step; 1136 | if (z->ey <= scany) { 1137 | *step = z->next; // delete from list 1138 | // NSVG__assert(z->valid); 1139 | nsvg__freeActive(r, z); 1140 | } else { 1141 | z->x += z->dx; // advance to position for current scanline 1142 | step = &((*step)->next); // advance through list 1143 | } 1144 | } 1145 | 1146 | // resort the list if needed 1147 | for (;;) { 1148 | int changed = 0; 1149 | step = &active; 1150 | while (*step && (*step)->next) { 1151 | if ((*step)->x > (*step)->next->x) { 1152 | NSVGactiveEdge* t = *step; 1153 | NSVGactiveEdge* q = t->next; 1154 | t->next = q->next; 1155 | q->next = t; 1156 | *step = q; 1157 | changed = 1; 1158 | } 1159 | step = &(*step)->next; 1160 | } 1161 | if (!changed) break; 1162 | } 1163 | 1164 | // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 1165 | while (e < r->nedges && r->edges[e].y0 <= scany) { 1166 | if (r->edges[e].y1 > scany) { 1167 | NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); 1168 | if (z == NULL) break; 1169 | // find insertion point 1170 | if (active == NULL) { 1171 | active = z; 1172 | } else if (z->x < active->x) { 1173 | // insert at front 1174 | z->next = active; 1175 | active = z; 1176 | } else { 1177 | // find thing to insert AFTER 1178 | NSVGactiveEdge* p = active; 1179 | while (p->next && p->next->x < z->x) 1180 | p = p->next; 1181 | // at this point, p->next->x is NOT < z->x 1182 | z->next = p->next; 1183 | p->next = z; 1184 | } 1185 | } 1186 | e++; 1187 | } 1188 | 1189 | // now process all active edges in non-zero fashion 1190 | if (active != NULL) 1191 | nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); 1192 | } 1193 | // Blit 1194 | if (xmin < 0) xmin = 0; 1195 | if (xmax > r->width-1) xmax = r->width-1; 1196 | if (xmin <= xmax) { 1197 | nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); 1198 | } 1199 | } 1200 | 1201 | } 1202 | 1203 | static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) 1204 | { 1205 | int x,y; 1206 | 1207 | // Unpremultiply 1208 | for (y = 0; y < h; y++) { 1209 | unsigned char *row = &image[y*stride]; 1210 | for (x = 0; x < w; x++) { 1211 | int r = row[0], g = row[1], b = row[2], a = row[3]; 1212 | if (a != 0) { 1213 | row[0] = (unsigned char)(r*255/a); 1214 | row[1] = (unsigned char)(g*255/a); 1215 | row[2] = (unsigned char)(b*255/a); 1216 | } 1217 | row += 4; 1218 | } 1219 | } 1220 | 1221 | // Defringe 1222 | for (y = 0; y < h; y++) { 1223 | unsigned char *row = &image[y*stride]; 1224 | for (x = 0; x < w; x++) { 1225 | int r = 0, g = 0, b = 0, a = row[3], n = 0; 1226 | if (a == 0) { 1227 | if (x-1 > 0 && row[-1] != 0) { 1228 | r += row[-4]; 1229 | g += row[-3]; 1230 | b += row[-2]; 1231 | n++; 1232 | } 1233 | if (x+1 < w && row[7] != 0) { 1234 | r += row[4]; 1235 | g += row[5]; 1236 | b += row[6]; 1237 | n++; 1238 | } 1239 | if (y-1 > 0 && row[-stride+3] != 0) { 1240 | r += row[-stride]; 1241 | g += row[-stride+1]; 1242 | b += row[-stride+2]; 1243 | n++; 1244 | } 1245 | if (y+1 < h && row[stride+3] != 0) { 1246 | r += row[stride]; 1247 | g += row[stride+1]; 1248 | b += row[stride+2]; 1249 | n++; 1250 | } 1251 | if (n > 0) { 1252 | row[0] = (unsigned char)(r/n); 1253 | row[1] = (unsigned char)(g/n); 1254 | row[2] = (unsigned char)(b/n); 1255 | } 1256 | } 1257 | row += 4; 1258 | } 1259 | } 1260 | } 1261 | 1262 | 1263 | static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) 1264 | { 1265 | int i, j; 1266 | NSVGgradient* grad; 1267 | 1268 | cache->type = paint->type; 1269 | 1270 | if (paint->type == NSVG_PAINT_COLOR) { 1271 | cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); 1272 | return; 1273 | } 1274 | 1275 | grad = paint->gradient; 1276 | 1277 | cache->spread = grad->spread; 1278 | memcpy(cache->xform, grad->xform, sizeof(float)*6); 1279 | 1280 | if (grad->nstops == 0) { 1281 | for (i = 0; i < 256; i++) 1282 | cache->colors[i] = 0; 1283 | } if (grad->nstops == 1) { 1284 | for (i = 0; i < 256; i++) 1285 | cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); 1286 | } else { 1287 | unsigned int ca, cb = 0; 1288 | float ua, ub, du, u; 1289 | int ia, ib, count; 1290 | 1291 | ca = nsvg__applyOpacity(grad->stops[0].color, opacity); 1292 | ua = nsvg__clampf(grad->stops[0].offset, 0, 1); 1293 | ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); 1294 | ia = (int)(ua * 255.0f); 1295 | ib = (int)(ub * 255.0f); 1296 | for (i = 0; i < ia; i++) { 1297 | cache->colors[i] = ca; 1298 | } 1299 | 1300 | for (i = 0; i < grad->nstops-1; i++) { 1301 | ca = nsvg__applyOpacity(grad->stops[i].color, opacity); 1302 | cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); 1303 | ua = nsvg__clampf(grad->stops[i].offset, 0, 1); 1304 | ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); 1305 | ia = (int)(ua * 255.0f); 1306 | ib = (int)(ub * 255.0f); 1307 | count = ib - ia; 1308 | if (count <= 0) continue; 1309 | u = 0; 1310 | du = 1.0f / (float)count; 1311 | for (j = 0; j < count; j++) { 1312 | cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); 1313 | u += du; 1314 | } 1315 | } 1316 | 1317 | for (i = ib; i < 256; i++) 1318 | cache->colors[i] = cb; 1319 | } 1320 | 1321 | } 1322 | 1323 | /* 1324 | static void dumpEdges(NSVGrasterizer* r, const char* name) 1325 | { 1326 | float xmin = 0, xmax = 0, ymin = 0, ymax = 0; 1327 | NSVGedge *e = NULL; 1328 | int i; 1329 | if (r->nedges == 0) return; 1330 | FILE* fp = fopen(name, "w"); 1331 | if (fp == NULL) return; 1332 | 1333 | xmin = xmax = r->edges[0].x0; 1334 | ymin = ymax = r->edges[0].y0; 1335 | for (i = 0; i < r->nedges; i++) { 1336 | e = &r->edges[i]; 1337 | xmin = nsvg__minf(xmin, e->x0); 1338 | xmin = nsvg__minf(xmin, e->x1); 1339 | xmax = nsvg__maxf(xmax, e->x0); 1340 | xmax = nsvg__maxf(xmax, e->x1); 1341 | ymin = nsvg__minf(ymin, e->y0); 1342 | ymin = nsvg__minf(ymin, e->y1); 1343 | ymax = nsvg__maxf(ymax, e->y0); 1344 | ymax = nsvg__maxf(ymax, e->y1); 1345 | } 1346 | 1347 | fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); 1348 | 1349 | for (i = 0; i < r->nedges; i++) { 1350 | e = &r->edges[i]; 1351 | fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); 1352 | } 1353 | 1354 | for (i = 0; i < r->npoints; i++) { 1355 | if (i+1 < r->npoints) 1356 | fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); 1357 | fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); 1358 | } 1359 | 1360 | fprintf(fp, ""); 1361 | fclose(fp); 1362 | } 1363 | */ 1364 | 1365 | void nsvgRasterize(NSVGrasterizer* r, 1366 | NSVGimage* image, float tx, float ty, float scale, 1367 | unsigned char* dst, int w, int h, int stride) 1368 | { 1369 | NSVGshape *shape = NULL; 1370 | NSVGedge *e = NULL; 1371 | NSVGcachedPaint cache; 1372 | int i; 1373 | 1374 | r->bitmap = dst; 1375 | r->width = w; 1376 | r->height = h; 1377 | r->stride = stride; 1378 | 1379 | if (w > r->cscanline) { 1380 | r->cscanline = w; 1381 | r->scanline = (unsigned char*)realloc(r->scanline, w); 1382 | if (r->scanline == NULL) return; 1383 | } 1384 | 1385 | for (i = 0; i < h; i++) 1386 | memset(&dst[i*stride], 0, w*4); 1387 | 1388 | for (shape = image->shapes; shape != NULL; shape = shape->next) { 1389 | if (!(shape->flags & NSVG_FLAGS_VISIBLE)) 1390 | continue; 1391 | 1392 | if (shape->fill.type != NSVG_PAINT_NONE) { 1393 | nsvg__resetPool(r); 1394 | r->freelist = NULL; 1395 | r->nedges = 0; 1396 | 1397 | nsvg__flattenShape(r, shape, scale); 1398 | 1399 | // Scale and translate edges 1400 | for (i = 0; i < r->nedges; i++) { 1401 | e = &r->edges[i]; 1402 | e->x0 = tx + e->x0; 1403 | e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; 1404 | e->x1 = tx + e->x1; 1405 | e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; 1406 | } 1407 | 1408 | // Rasterize edges 1409 | qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); 1410 | 1411 | // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule 1412 | nsvg__initPaint(&cache, &shape->fill, shape->opacity); 1413 | 1414 | nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); 1415 | } 1416 | if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { 1417 | nsvg__resetPool(r); 1418 | r->freelist = NULL; 1419 | r->nedges = 0; 1420 | 1421 | nsvg__flattenShapeStroke(r, shape, scale); 1422 | 1423 | // dumpEdges(r, "edge.svg"); 1424 | 1425 | // Scale and translate edges 1426 | for (i = 0; i < r->nedges; i++) { 1427 | e = &r->edges[i]; 1428 | e->x0 = tx + e->x0; 1429 | e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; 1430 | e->x1 = tx + e->x1; 1431 | e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; 1432 | } 1433 | 1434 | // Rasterize edges 1435 | qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); 1436 | 1437 | // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule 1438 | nsvg__initPaint(&cache, &shape->stroke, shape->opacity); 1439 | 1440 | nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); 1441 | } 1442 | } 1443 | 1444 | nsvg__unpremultiplyAlpha(dst, w, h, stride); 1445 | 1446 | r->bitmap = NULL; 1447 | r->width = 0; 1448 | r->height = 0; 1449 | r->stride = 0; 1450 | } 1451 | 1452 | #endif 1453 | -------------------------------------------------------------------------------- /nanosvg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-14 Mikko Mononen memon@inside.org 3 | * 4 | * This software is provided 'as-is', without any express or implied 5 | * warranty. In no event will the authors be held liable for any damages 6 | * arising from the use of this software. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | * 20 | * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example 21 | * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) 22 | * 23 | * Arc calculation code based on canvg (https://code.google.com/p/canvg/) 24 | * 25 | * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html 26 | * 27 | */ 28 | 29 | #ifndef NANOSVG_H 30 | #define NANOSVG_H 31 | 32 | #ifndef NANOSVG_CPLUSPLUS 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | #endif 37 | 38 | // NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. 39 | // 40 | // The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. 41 | // 42 | // NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! 43 | // 44 | // The shapes in the SVG images are transformed by the viewBox and converted to specified units. 45 | // That is, you should get the same looking data as your designed in your favorite app. 46 | // 47 | // NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose 48 | // to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. 49 | // 50 | // The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. 51 | // DPI (dots-per-inch) controls how the unit conversion is done. 52 | // 53 | // If you don't know or care about the units stuff, "px" and 96 should get you going. 54 | 55 | 56 | /* Example Usage: 57 | // Load SVG 58 | NSVGimage* image; 59 | image = nsvgParseFromFile("test.svg", "px", 96); 60 | printf("size: %f x %f\n", image->width, image->height); 61 | // Use... 62 | for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { 63 | for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { 64 | for (int i = 0; i < path->npts-1; i += 3) { 65 | float* p = &path->pts[i*2]; 66 | drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); 67 | } 68 | } 69 | } 70 | // Delete 71 | nsvgDelete(image); 72 | */ 73 | 74 | enum NSVGpaintType { 75 | NSVG_PAINT_NONE = 0, 76 | NSVG_PAINT_COLOR = 1, 77 | NSVG_PAINT_LINEAR_GRADIENT = 2, 78 | NSVG_PAINT_RADIAL_GRADIENT = 3 79 | }; 80 | 81 | enum NSVGspreadType { 82 | NSVG_SPREAD_PAD = 0, 83 | NSVG_SPREAD_REFLECT = 1, 84 | NSVG_SPREAD_REPEAT = 2 85 | }; 86 | 87 | enum NSVGlineJoin { 88 | NSVG_JOIN_MITER = 0, 89 | NSVG_JOIN_ROUND = 1, 90 | NSVG_JOIN_BEVEL = 2 91 | }; 92 | 93 | enum NSVGlineCap { 94 | NSVG_CAP_BUTT = 0, 95 | NSVG_CAP_ROUND = 1, 96 | NSVG_CAP_SQUARE = 2 97 | }; 98 | 99 | enum NSVGfillRule { 100 | NSVG_FILLRULE_NONZERO = 0, 101 | NSVG_FILLRULE_EVENODD = 1 102 | }; 103 | 104 | enum NSVGflags { 105 | NSVG_FLAGS_VISIBLE = 0x01 106 | }; 107 | 108 | typedef struct NSVGgradientStop { 109 | unsigned int color; 110 | float offset; 111 | } NSVGgradientStop; 112 | 113 | typedef struct NSVGgradient { 114 | float xform[6]; 115 | char spread; 116 | float fx, fy; 117 | int nstops; 118 | NSVGgradientStop stops[1]; 119 | } NSVGgradient; 120 | 121 | typedef struct NSVGpaint { 122 | char type; 123 | union { 124 | unsigned int color; 125 | NSVGgradient* gradient; 126 | }; 127 | } NSVGpaint; 128 | 129 | typedef struct NSVGpath 130 | { 131 | float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... 132 | int npts; // Total number of bezier points. 133 | char closed; // Flag indicating if shapes should be treated as closed. 134 | float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. 135 | struct NSVGpath* next; // Pointer to next path, or NULL if last element. 136 | } NSVGpath; 137 | 138 | typedef struct NSVGshape 139 | { 140 | char id[64]; // Optional 'id' attr of the shape or its group 141 | NSVGpaint fill; // Fill paint 142 | NSVGpaint stroke; // Stroke paint 143 | float opacity; // Opacity of the shape. 144 | float strokeWidth; // Stroke width (scaled). 145 | float strokeDashOffset; // Stroke dash offset (scaled). 146 | float strokeDashArray[8]; // Stroke dash array (scaled). 147 | char strokeDashCount; // Number of dash values in dash array. 148 | char strokeLineJoin; // Stroke join type. 149 | char strokeLineCap; // Stroke cap type. 150 | float miterLimit; // Miter limit 151 | char fillRule; // Fill rule, see NSVGfillRule. 152 | unsigned char flags; // Logical or of NSVG_FLAGS_* flags 153 | float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. 154 | NSVGpath* paths; // Linked list of paths in the image. 155 | struct NSVGshape* next; // Pointer to next shape, or NULL if last element. 156 | } NSVGshape; 157 | 158 | typedef struct NSVGimage 159 | { 160 | float width; // Width of the image. 161 | float height; // Height of the image. 162 | NSVGshape* shapes; // Linked list of shapes in the image. 163 | } NSVGimage; 164 | 165 | // Parses SVG file from a file, returns SVG image as paths. 166 | NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); 167 | 168 | // Parses SVG file from a null terminated string, returns SVG image as paths. 169 | // Important note: changes the string. 170 | NSVGimage* nsvgParse(char* input, const char* units, float dpi); 171 | 172 | // Duplicates a path. 173 | NSVGpath* nsvgDuplicatePath(NSVGpath* p); 174 | 175 | // Deletes an image. 176 | void nsvgDelete(NSVGimage* image); 177 | 178 | #ifndef NANOSVG_CPLUSPLUS 179 | #ifdef __cplusplus 180 | } 181 | #endif 182 | #endif 183 | 184 | #endif // NANOSVG_H 185 | 186 | #ifdef NANOSVG_IMPLEMENTATION 187 | 188 | #include 189 | #include 190 | #include 191 | 192 | #define NSVG_PI (3.14159265358979323846264338327f) 193 | #define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. 194 | 195 | #define NSVG_ALIGN_MIN 0 196 | #define NSVG_ALIGN_MID 1 197 | #define NSVG_ALIGN_MAX 2 198 | #define NSVG_ALIGN_NONE 0 199 | #define NSVG_ALIGN_MEET 1 200 | #define NSVG_ALIGN_SLICE 2 201 | 202 | #define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) 203 | #define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) 204 | 205 | #ifdef _MSC_VER 206 | #pragma warning (disable: 4996) // Switch off security warnings 207 | #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings 208 | #ifdef __cplusplus 209 | #define NSVG_INLINE inline 210 | #else 211 | #define NSVG_INLINE 212 | #endif 213 | #else 214 | #define NSVG_INLINE inline 215 | #endif 216 | 217 | 218 | static int nsvg__isspace(char c) 219 | { 220 | return strchr(" \t\n\v\f\r", c) != 0; 221 | } 222 | 223 | static int nsvg__isdigit(char c) 224 | { 225 | return c >= '0' && c <= '9'; 226 | } 227 | 228 | static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } 229 | static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } 230 | 231 | 232 | // Simple XML parser 233 | 234 | #define NSVG_XML_TAG 1 235 | #define NSVG_XML_CONTENT 2 236 | #define NSVG_XML_MAX_ATTRIBS 256 237 | 238 | static void nsvg__parseContent(char* s, 239 | void (*contentCb)(void* ud, const char* s), 240 | void* ud) 241 | { 242 | // Trim start white spaces 243 | while (*s && nsvg__isspace(*s)) s++; 244 | if (!*s) return; 245 | 246 | if (contentCb) 247 | (*contentCb)(ud, s); 248 | } 249 | 250 | static void nsvg__parseElement(char* s, 251 | void (*startelCb)(void* ud, const char* el, const char** attr), 252 | void (*endelCb)(void* ud, const char* el), 253 | void* ud) 254 | { 255 | const char* attr[NSVG_XML_MAX_ATTRIBS]; 256 | int nattr = 0; 257 | char* name; 258 | int start = 0; 259 | int end = 0; 260 | char quote; 261 | 262 | // Skip white space after the '<' 263 | while (*s && nsvg__isspace(*s)) s++; 264 | 265 | // Check if the tag is end tag 266 | if (*s == '/') { 267 | s++; 268 | end = 1; 269 | } else { 270 | start = 1; 271 | } 272 | 273 | // Skip comments, data and preprocessor stuff. 274 | if (!*s || *s == '?' || *s == '!') 275 | return; 276 | 277 | // Get tag name 278 | name = s; 279 | while (*s && !nsvg__isspace(*s)) s++; 280 | if (*s) { *s++ = '\0'; } 281 | 282 | // Get attribs 283 | while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { 284 | char* name = NULL; 285 | char* value = NULL; 286 | 287 | // Skip white space before the attrib name 288 | while (*s && nsvg__isspace(*s)) s++; 289 | if (!*s) break; 290 | if (*s == '/') { 291 | end = 1; 292 | break; 293 | } 294 | name = s; 295 | // Find end of the attrib name. 296 | while (*s && !nsvg__isspace(*s) && *s != '=') s++; 297 | if (*s) { *s++ = '\0'; } 298 | // Skip until the beginning of the value. 299 | while (*s && *s != '\"' && *s != '\'') s++; 300 | if (!*s) break; 301 | quote = *s; 302 | s++; 303 | // Store value and find the end of it. 304 | value = s; 305 | while (*s && *s != quote) s++; 306 | if (*s) { *s++ = '\0'; } 307 | 308 | // Store only well formed attributes 309 | if (name && value) { 310 | attr[nattr++] = name; 311 | attr[nattr++] = value; 312 | } 313 | } 314 | 315 | // List terminator 316 | attr[nattr++] = 0; 317 | attr[nattr++] = 0; 318 | 319 | // Call callbacks. 320 | if (start && startelCb) 321 | (*startelCb)(ud, name, attr); 322 | if (end && endelCb) 323 | (*endelCb)(ud, name); 324 | } 325 | 326 | int nsvg__parseXML(char* input, 327 | void (*startelCb)(void* ud, const char* el, const char** attr), 328 | void (*endelCb)(void* ud, const char* el), 329 | void (*contentCb)(void* ud, const char* s), 330 | void* ud) 331 | { 332 | char* s = input; 333 | char* mark = s; 334 | int state = NSVG_XML_CONTENT; 335 | while (*s) { 336 | if (*s == '<' && state == NSVG_XML_CONTENT) { 337 | // Start of a tag 338 | *s++ = '\0'; 339 | nsvg__parseContent(mark, contentCb, ud); 340 | mark = s; 341 | state = NSVG_XML_TAG; 342 | } else if (*s == '>' && state == NSVG_XML_TAG) { 343 | // Start of a content or new tag. 344 | *s++ = '\0'; 345 | nsvg__parseElement(mark, startelCb, endelCb, ud); 346 | mark = s; 347 | state = NSVG_XML_CONTENT; 348 | } else { 349 | s++; 350 | } 351 | } 352 | 353 | return 1; 354 | } 355 | 356 | 357 | /* Simple SVG parser. */ 358 | 359 | #define NSVG_MAX_ATTR 128 360 | 361 | enum NSVGgradientUnits { 362 | NSVG_USER_SPACE = 0, 363 | NSVG_OBJECT_SPACE = 1 364 | }; 365 | 366 | #define NSVG_MAX_DASHES 8 367 | 368 | enum NSVGunits { 369 | NSVG_UNITS_USER, 370 | NSVG_UNITS_PX, 371 | NSVG_UNITS_PT, 372 | NSVG_UNITS_PC, 373 | NSVG_UNITS_MM, 374 | NSVG_UNITS_CM, 375 | NSVG_UNITS_IN, 376 | NSVG_UNITS_PERCENT, 377 | NSVG_UNITS_EM, 378 | NSVG_UNITS_EX 379 | }; 380 | 381 | typedef struct NSVGcoordinate { 382 | float value; 383 | int units; 384 | } NSVGcoordinate; 385 | 386 | typedef struct NSVGlinearData { 387 | NSVGcoordinate x1, y1, x2, y2; 388 | } NSVGlinearData; 389 | 390 | typedef struct NSVGradialData { 391 | NSVGcoordinate cx, cy, r, fx, fy; 392 | } NSVGradialData; 393 | 394 | typedef struct NSVGgradientData 395 | { 396 | char id[64]; 397 | char ref[64]; 398 | char type; 399 | union { 400 | NSVGlinearData linear; 401 | NSVGradialData radial; 402 | }; 403 | char spread; 404 | char units; 405 | float xform[6]; 406 | int nstops; 407 | NSVGgradientStop* stops; 408 | struct NSVGgradientData* next; 409 | } NSVGgradientData; 410 | 411 | typedef struct NSVGattrib 412 | { 413 | char id[64]; 414 | float xform[6]; 415 | unsigned int fillColor; 416 | unsigned int strokeColor; 417 | float opacity; 418 | float fillOpacity; 419 | float strokeOpacity; 420 | char fillGradient[64]; 421 | char strokeGradient[64]; 422 | float strokeWidth; 423 | float strokeDashOffset; 424 | float strokeDashArray[NSVG_MAX_DASHES]; 425 | int strokeDashCount; 426 | char strokeLineJoin; 427 | char strokeLineCap; 428 | float miterLimit; 429 | char fillRule; 430 | float fontSize; 431 | unsigned int stopColor; 432 | float stopOpacity; 433 | float stopOffset; 434 | char hasFill; 435 | char hasStroke; 436 | char visible; 437 | } NSVGattrib; 438 | 439 | typedef struct NSVGparser 440 | { 441 | NSVGattrib attr[NSVG_MAX_ATTR]; 442 | int attrHead; 443 | float* pts; 444 | int npts; 445 | int cpts; 446 | NSVGpath* plist; 447 | NSVGimage* image; 448 | NSVGgradientData* gradients; 449 | NSVGshape* shapesTail; 450 | float viewMinx, viewMiny, viewWidth, viewHeight; 451 | int alignX, alignY, alignType; 452 | float dpi; 453 | char pathFlag; 454 | char defsFlag; 455 | } NSVGparser; 456 | 457 | static void nsvg__xformIdentity(float* t) 458 | { 459 | t[0] = 1.0f; t[1] = 0.0f; 460 | t[2] = 0.0f; t[3] = 1.0f; 461 | t[4] = 0.0f; t[5] = 0.0f; 462 | } 463 | 464 | static void nsvg__xformSetTranslation(float* t, float tx, float ty) 465 | { 466 | t[0] = 1.0f; t[1] = 0.0f; 467 | t[2] = 0.0f; t[3] = 1.0f; 468 | t[4] = tx; t[5] = ty; 469 | } 470 | 471 | static void nsvg__xformSetScale(float* t, float sx, float sy) 472 | { 473 | t[0] = sx; t[1] = 0.0f; 474 | t[2] = 0.0f; t[3] = sy; 475 | t[4] = 0.0f; t[5] = 0.0f; 476 | } 477 | 478 | static void nsvg__xformSetSkewX(float* t, float a) 479 | { 480 | t[0] = 1.0f; t[1] = 0.0f; 481 | t[2] = tanf(a); t[3] = 1.0f; 482 | t[4] = 0.0f; t[5] = 0.0f; 483 | } 484 | 485 | static void nsvg__xformSetSkewY(float* t, float a) 486 | { 487 | t[0] = 1.0f; t[1] = tanf(a); 488 | t[2] = 0.0f; t[3] = 1.0f; 489 | t[4] = 0.0f; t[5] = 0.0f; 490 | } 491 | 492 | static void nsvg__xformSetRotation(float* t, float a) 493 | { 494 | float cs = cosf(a), sn = sinf(a); 495 | t[0] = cs; t[1] = sn; 496 | t[2] = -sn; t[3] = cs; 497 | t[4] = 0.0f; t[5] = 0.0f; 498 | } 499 | 500 | static void nsvg__xformMultiply(float* t, float* s) 501 | { 502 | float t0 = t[0] * s[0] + t[1] * s[2]; 503 | float t2 = t[2] * s[0] + t[3] * s[2]; 504 | float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; 505 | t[1] = t[0] * s[1] + t[1] * s[3]; 506 | t[3] = t[2] * s[1] + t[3] * s[3]; 507 | t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; 508 | t[0] = t0; 509 | t[2] = t2; 510 | t[4] = t4; 511 | } 512 | 513 | static void nsvg__xformInverse(float* inv, float* t) 514 | { 515 | double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; 516 | if (det > -1e-6 && det < 1e-6) { 517 | nsvg__xformIdentity(t); 518 | return; 519 | } 520 | invdet = 1.0 / det; 521 | inv[0] = (float)(t[3] * invdet); 522 | inv[2] = (float)(-t[2] * invdet); 523 | inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); 524 | inv[1] = (float)(-t[1] * invdet); 525 | inv[3] = (float)(t[0] * invdet); 526 | inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); 527 | } 528 | 529 | static void nsvg__xformPremultiply(float* t, float* s) 530 | { 531 | float s2[6]; 532 | memcpy(s2, s, sizeof(float)*6); 533 | nsvg__xformMultiply(s2, t); 534 | memcpy(t, s2, sizeof(float)*6); 535 | } 536 | 537 | static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) 538 | { 539 | *dx = x*t[0] + y*t[2] + t[4]; 540 | *dy = x*t[1] + y*t[3] + t[5]; 541 | } 542 | 543 | static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) 544 | { 545 | *dx = x*t[0] + y*t[2]; 546 | *dy = x*t[1] + y*t[3]; 547 | } 548 | 549 | #define NSVG_EPSILON (1e-12) 550 | 551 | static int nsvg__ptInBounds(float* pt, float* bounds) 552 | { 553 | return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; 554 | } 555 | 556 | 557 | static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) 558 | { 559 | double it = 1.0-t; 560 | return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; 561 | } 562 | 563 | static void nsvg__curveBounds(float* bounds, float* curve) 564 | { 565 | int i, j, count; 566 | double roots[2], a, b, c, b2ac, t, v; 567 | float* v0 = &curve[0]; 568 | float* v1 = &curve[2]; 569 | float* v2 = &curve[4]; 570 | float* v3 = &curve[6]; 571 | 572 | // Start the bounding box by end points 573 | bounds[0] = nsvg__minf(v0[0], v3[0]); 574 | bounds[1] = nsvg__minf(v0[1], v3[1]); 575 | bounds[2] = nsvg__maxf(v0[0], v3[0]); 576 | bounds[3] = nsvg__maxf(v0[1], v3[1]); 577 | 578 | // Bezier curve fits inside the convex hull of it's control points. 579 | // If control points are inside the bounds, we're done. 580 | if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) 581 | return; 582 | 583 | // Add bezier curve inflection points in X and Y. 584 | for (i = 0; i < 2; i++) { 585 | a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; 586 | b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; 587 | c = 3.0 * v1[i] - 3.0 * v0[i]; 588 | count = 0; 589 | if (fabs(a) < NSVG_EPSILON) { 590 | if (fabs(b) > NSVG_EPSILON) { 591 | t = -c / b; 592 | if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) 593 | roots[count++] = t; 594 | } 595 | } else { 596 | b2ac = b*b - 4.0*c*a; 597 | if (b2ac > NSVG_EPSILON) { 598 | t = (-b + sqrt(b2ac)) / (2.0 * a); 599 | if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) 600 | roots[count++] = t; 601 | t = (-b - sqrt(b2ac)) / (2.0 * a); 602 | if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) 603 | roots[count++] = t; 604 | } 605 | } 606 | for (j = 0; j < count; j++) { 607 | v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); 608 | bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); 609 | bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); 610 | } 611 | } 612 | } 613 | 614 | static NSVGparser* nsvg__createParser(void) 615 | { 616 | NSVGparser* p; 617 | p = (NSVGparser*)malloc(sizeof(NSVGparser)); 618 | if (p == NULL) goto error; 619 | memset(p, 0, sizeof(NSVGparser)); 620 | 621 | p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); 622 | if (p->image == NULL) goto error; 623 | memset(p->image, 0, sizeof(NSVGimage)); 624 | 625 | // Init style 626 | nsvg__xformIdentity(p->attr[0].xform); 627 | memset(p->attr[0].id, 0, sizeof p->attr[0].id); 628 | p->attr[0].fillColor = NSVG_RGB(0,0,0); 629 | p->attr[0].strokeColor = NSVG_RGB(0,0,0); 630 | p->attr[0].opacity = 1; 631 | p->attr[0].fillOpacity = 1; 632 | p->attr[0].strokeOpacity = 1; 633 | p->attr[0].stopOpacity = 1; 634 | p->attr[0].strokeWidth = 1; 635 | p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; 636 | p->attr[0].strokeLineCap = NSVG_CAP_BUTT; 637 | p->attr[0].miterLimit = 4; 638 | p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; 639 | p->attr[0].hasFill = 1; 640 | p->attr[0].visible = 1; 641 | 642 | return p; 643 | 644 | error: 645 | if (p) { 646 | if (p->image) free(p->image); 647 | free(p); 648 | } 649 | return NULL; 650 | } 651 | 652 | static void nsvg__deletePaths(NSVGpath* path) 653 | { 654 | while (path) { 655 | NSVGpath *next = path->next; 656 | if (path->pts != NULL) 657 | free(path->pts); 658 | free(path); 659 | path = next; 660 | } 661 | } 662 | 663 | static void nsvg__deletePaint(NSVGpaint* paint) 664 | { 665 | if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) 666 | free(paint->gradient); 667 | } 668 | 669 | static void nsvg__deleteGradientData(NSVGgradientData* grad) 670 | { 671 | NSVGgradientData* next; 672 | while (grad != NULL) { 673 | next = grad->next; 674 | free(grad->stops); 675 | free(grad); 676 | grad = next; 677 | } 678 | } 679 | 680 | static void nsvg__deleteParser(NSVGparser* p) 681 | { 682 | if (p != NULL) { 683 | nsvg__deletePaths(p->plist); 684 | nsvg__deleteGradientData(p->gradients); 685 | nsvgDelete(p->image); 686 | free(p->pts); 687 | free(p); 688 | } 689 | } 690 | 691 | static void nsvg__resetPath(NSVGparser* p) 692 | { 693 | p->npts = 0; 694 | } 695 | 696 | static void nsvg__addPoint(NSVGparser* p, float x, float y) 697 | { 698 | if (p->npts+1 > p->cpts) { 699 | p->cpts = p->cpts ? p->cpts*2 : 8; 700 | p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); 701 | if (!p->pts) return; 702 | } 703 | p->pts[p->npts*2+0] = x; 704 | p->pts[p->npts*2+1] = y; 705 | p->npts++; 706 | } 707 | 708 | static void nsvg__moveTo(NSVGparser* p, float x, float y) 709 | { 710 | if (p->npts > 0) { 711 | p->pts[(p->npts-1)*2+0] = x; 712 | p->pts[(p->npts-1)*2+1] = y; 713 | } else { 714 | nsvg__addPoint(p, x, y); 715 | } 716 | } 717 | 718 | static void nsvg__lineTo(NSVGparser* p, float x, float y) 719 | { 720 | float px,py, dx,dy; 721 | if (p->npts > 0) { 722 | px = p->pts[(p->npts-1)*2+0]; 723 | py = p->pts[(p->npts-1)*2+1]; 724 | dx = x - px; 725 | dy = y - py; 726 | nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); 727 | nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); 728 | nsvg__addPoint(p, x, y); 729 | } 730 | } 731 | 732 | static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) 733 | { 734 | if (p->npts > 0) { 735 | nsvg__addPoint(p, cpx1, cpy1); 736 | nsvg__addPoint(p, cpx2, cpy2); 737 | nsvg__addPoint(p, x, y); 738 | } 739 | } 740 | 741 | static NSVGattrib* nsvg__getAttr(NSVGparser* p) 742 | { 743 | return &p->attr[p->attrHead]; 744 | } 745 | 746 | static void nsvg__pushAttr(NSVGparser* p) 747 | { 748 | if (p->attrHead < NSVG_MAX_ATTR-1) { 749 | p->attrHead++; 750 | memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); 751 | } 752 | } 753 | 754 | static void nsvg__popAttr(NSVGparser* p) 755 | { 756 | if (p->attrHead > 0) 757 | p->attrHead--; 758 | } 759 | 760 | static float nsvg__actualOrigX(NSVGparser* p) 761 | { 762 | return p->viewMinx; 763 | } 764 | 765 | static float nsvg__actualOrigY(NSVGparser* p) 766 | { 767 | return p->viewMiny; 768 | } 769 | 770 | static float nsvg__actualWidth(NSVGparser* p) 771 | { 772 | return p->viewWidth; 773 | } 774 | 775 | static float nsvg__actualHeight(NSVGparser* p) 776 | { 777 | return p->viewHeight; 778 | } 779 | 780 | static float nsvg__actualLength(NSVGparser* p) 781 | { 782 | float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); 783 | return sqrtf(w*w + h*h) / sqrtf(2.0f); 784 | } 785 | 786 | static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) 787 | { 788 | NSVGattrib* attr = nsvg__getAttr(p); 789 | switch (c.units) { 790 | case NSVG_UNITS_USER: return c.value; 791 | case NSVG_UNITS_PX: return c.value; 792 | case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; 793 | case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; 794 | case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; 795 | case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; 796 | case NSVG_UNITS_IN: return c.value * p->dpi; 797 | case NSVG_UNITS_EM: return c.value * attr->fontSize; 798 | case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. 799 | case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; 800 | default: return c.value; 801 | } 802 | return c.value; 803 | } 804 | 805 | static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) 806 | { 807 | NSVGgradientData* grad = p->gradients; 808 | if (id == NULL || *id == '\0') 809 | return NULL; 810 | while (grad != NULL) { 811 | if (strcmp(grad->id, id) == 0) 812 | return grad; 813 | grad = grad->next; 814 | } 815 | return NULL; 816 | } 817 | 818 | static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) 819 | { 820 | NSVGattrib* attr = nsvg__getAttr(p); 821 | NSVGgradientData* data = NULL; 822 | NSVGgradientData* ref = NULL; 823 | NSVGgradientStop* stops = NULL; 824 | NSVGgradient* grad; 825 | float ox, oy, sw, sh, sl; 826 | int nstops = 0; 827 | int refIter; 828 | 829 | data = nsvg__findGradientData(p, id); 830 | if (data == NULL) return NULL; 831 | 832 | // TODO: use ref to fill in all unset values too. 833 | ref = data; 834 | refIter = 0; 835 | while (ref != NULL) { 836 | NSVGgradientData* nextRef = NULL; 837 | if (stops == NULL && ref->stops != NULL) { 838 | stops = ref->stops; 839 | nstops = ref->nstops; 840 | break; 841 | } 842 | nextRef = nsvg__findGradientData(p, ref->ref); 843 | if (nextRef == ref) break; // prevent infite loops on malformed data 844 | ref = nextRef; 845 | refIter++; 846 | if (refIter > 32) break; // prevent infite loops on malformed data 847 | } 848 | if (stops == NULL) return NULL; 849 | 850 | grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); 851 | if (grad == NULL) return NULL; 852 | 853 | // The shape width and height. 854 | if (data->units == NSVG_OBJECT_SPACE) { 855 | ox = localBounds[0]; 856 | oy = localBounds[1]; 857 | sw = localBounds[2] - localBounds[0]; 858 | sh = localBounds[3] - localBounds[1]; 859 | } else { 860 | ox = nsvg__actualOrigX(p); 861 | oy = nsvg__actualOrigY(p); 862 | sw = nsvg__actualWidth(p); 863 | sh = nsvg__actualHeight(p); 864 | } 865 | sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); 866 | 867 | if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { 868 | float x1, y1, x2, y2, dx, dy; 869 | x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); 870 | y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); 871 | x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); 872 | y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); 873 | // Calculate transform aligned to the line 874 | dx = x2 - x1; 875 | dy = y2 - y1; 876 | grad->xform[0] = dy; grad->xform[1] = -dx; 877 | grad->xform[2] = dx; grad->xform[3] = dy; 878 | grad->xform[4] = x1; grad->xform[5] = y1; 879 | } else { 880 | float cx, cy, fx, fy, r; 881 | cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); 882 | cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); 883 | fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); 884 | fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); 885 | r = nsvg__convertToPixels(p, data->radial.r, 0, sl); 886 | // Calculate transform aligned to the circle 887 | grad->xform[0] = r; grad->xform[1] = 0; 888 | grad->xform[2] = 0; grad->xform[3] = r; 889 | grad->xform[4] = cx; grad->xform[5] = cy; 890 | grad->fx = fx / r; 891 | grad->fy = fy / r; 892 | } 893 | 894 | nsvg__xformMultiply(grad->xform, data->xform); 895 | nsvg__xformMultiply(grad->xform, attr->xform); 896 | 897 | grad->spread = data->spread; 898 | memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); 899 | grad->nstops = nstops; 900 | 901 | *paintType = data->type; 902 | 903 | return grad; 904 | } 905 | 906 | static float nsvg__getAverageScale(float* t) 907 | { 908 | float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); 909 | float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); 910 | return (sx + sy) * 0.5f; 911 | } 912 | 913 | static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) 914 | { 915 | NSVGpath* path; 916 | float curve[4*2], curveBounds[4]; 917 | int i, first = 1; 918 | for (path = shape->paths; path != NULL; path = path->next) { 919 | nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); 920 | for (i = 0; i < path->npts-1; i += 3) { 921 | nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); 922 | nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); 923 | nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); 924 | nsvg__curveBounds(curveBounds, curve); 925 | if (first) { 926 | bounds[0] = curveBounds[0]; 927 | bounds[1] = curveBounds[1]; 928 | bounds[2] = curveBounds[2]; 929 | bounds[3] = curveBounds[3]; 930 | first = 0; 931 | } else { 932 | bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); 933 | bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); 934 | bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); 935 | bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); 936 | } 937 | curve[0] = curve[6]; 938 | curve[1] = curve[7]; 939 | } 940 | } 941 | } 942 | 943 | static void nsvg__addShape(NSVGparser* p) 944 | { 945 | NSVGattrib* attr = nsvg__getAttr(p); 946 | float scale = 1.0f; 947 | NSVGshape* shape; 948 | NSVGpath* path; 949 | int i; 950 | 951 | if (p->plist == NULL) 952 | return; 953 | 954 | shape = (NSVGshape*)malloc(sizeof(NSVGshape)); 955 | if (shape == NULL) goto error; 956 | memset(shape, 0, sizeof(NSVGshape)); 957 | 958 | memcpy(shape->id, attr->id, sizeof shape->id); 959 | scale = nsvg__getAverageScale(attr->xform); 960 | shape->strokeWidth = attr->strokeWidth * scale; 961 | shape->strokeDashOffset = attr->strokeDashOffset * scale; 962 | shape->strokeDashCount = (char)attr->strokeDashCount; 963 | for (i = 0; i < attr->strokeDashCount; i++) 964 | shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; 965 | shape->strokeLineJoin = attr->strokeLineJoin; 966 | shape->strokeLineCap = attr->strokeLineCap; 967 | shape->miterLimit = attr->miterLimit; 968 | shape->fillRule = attr->fillRule; 969 | shape->opacity = attr->opacity; 970 | 971 | shape->paths = p->plist; 972 | p->plist = NULL; 973 | 974 | // Calculate shape bounds 975 | shape->bounds[0] = shape->paths->bounds[0]; 976 | shape->bounds[1] = shape->paths->bounds[1]; 977 | shape->bounds[2] = shape->paths->bounds[2]; 978 | shape->bounds[3] = shape->paths->bounds[3]; 979 | for (path = shape->paths->next; path != NULL; path = path->next) { 980 | shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); 981 | shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); 982 | shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); 983 | shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); 984 | } 985 | 986 | // Set fill 987 | if (attr->hasFill == 0) { 988 | shape->fill.type = NSVG_PAINT_NONE; 989 | } else if (attr->hasFill == 1) { 990 | shape->fill.type = NSVG_PAINT_COLOR; 991 | shape->fill.color = attr->fillColor; 992 | shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; 993 | } else if (attr->hasFill == 2) { 994 | float inv[6], localBounds[4]; 995 | nsvg__xformInverse(inv, attr->xform); 996 | nsvg__getLocalBounds(localBounds, shape, inv); 997 | shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); 998 | if (shape->fill.gradient == NULL) { 999 | shape->fill.type = NSVG_PAINT_NONE; 1000 | } 1001 | } 1002 | 1003 | // Set stroke 1004 | if (attr->hasStroke == 0) { 1005 | shape->stroke.type = NSVG_PAINT_NONE; 1006 | } else if (attr->hasStroke == 1) { 1007 | shape->stroke.type = NSVG_PAINT_COLOR; 1008 | shape->stroke.color = attr->strokeColor; 1009 | shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; 1010 | } else if (attr->hasStroke == 2) { 1011 | float inv[6], localBounds[4]; 1012 | nsvg__xformInverse(inv, attr->xform); 1013 | nsvg__getLocalBounds(localBounds, shape, inv); 1014 | shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); 1015 | if (shape->stroke.gradient == NULL) 1016 | shape->stroke.type = NSVG_PAINT_NONE; 1017 | } 1018 | 1019 | // Set flags 1020 | shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); 1021 | 1022 | // Add to tail 1023 | if (p->image->shapes == NULL) 1024 | p->image->shapes = shape; 1025 | else 1026 | p->shapesTail->next = shape; 1027 | p->shapesTail = shape; 1028 | 1029 | return; 1030 | 1031 | error: 1032 | if (shape) free(shape); 1033 | } 1034 | 1035 | static void nsvg__addPath(NSVGparser* p, char closed) 1036 | { 1037 | NSVGattrib* attr = nsvg__getAttr(p); 1038 | NSVGpath* path = NULL; 1039 | float bounds[4]; 1040 | float* curve; 1041 | int i; 1042 | 1043 | if (p->npts < 4) 1044 | return; 1045 | 1046 | if (closed) 1047 | nsvg__lineTo(p, p->pts[0], p->pts[1]); 1048 | 1049 | // Expect 1 + N*3 points (N = number of cubic bezier segments). 1050 | if ((p->npts % 3) != 1) 1051 | return; 1052 | 1053 | path = (NSVGpath*)malloc(sizeof(NSVGpath)); 1054 | if (path == NULL) goto error; 1055 | memset(path, 0, sizeof(NSVGpath)); 1056 | 1057 | path->pts = (float*)malloc(p->npts*2*sizeof(float)); 1058 | if (path->pts == NULL) goto error; 1059 | path->closed = closed; 1060 | path->npts = p->npts; 1061 | 1062 | // Transform path. 1063 | for (i = 0; i < p->npts; ++i) 1064 | nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); 1065 | 1066 | // Find bounds 1067 | for (i = 0; i < path->npts-1; i += 3) { 1068 | curve = &path->pts[i*2]; 1069 | nsvg__curveBounds(bounds, curve); 1070 | if (i == 0) { 1071 | path->bounds[0] = bounds[0]; 1072 | path->bounds[1] = bounds[1]; 1073 | path->bounds[2] = bounds[2]; 1074 | path->bounds[3] = bounds[3]; 1075 | } else { 1076 | path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); 1077 | path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); 1078 | path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); 1079 | path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); 1080 | } 1081 | } 1082 | 1083 | path->next = p->plist; 1084 | p->plist = path; 1085 | 1086 | return; 1087 | 1088 | error: 1089 | if (path != NULL) { 1090 | if (path->pts != NULL) free(path->pts); 1091 | free(path); 1092 | } 1093 | } 1094 | 1095 | // We roll our own string to float because the std library one uses locale and messes things up. 1096 | static double nsvg__atof(const char* s) 1097 | { 1098 | char* cur = (char*)s; 1099 | char* end = NULL; 1100 | double res = 0.0, sign = 1.0; 1101 | long long intPart = 0, fracPart = 0; 1102 | char hasIntPart = 0, hasFracPart = 0; 1103 | 1104 | // Parse optional sign 1105 | if (*cur == '+') { 1106 | cur++; 1107 | } else if (*cur == '-') { 1108 | sign = -1; 1109 | cur++; 1110 | } 1111 | 1112 | // Parse integer part 1113 | if (nsvg__isdigit(*cur)) { 1114 | // Parse digit sequence 1115 | intPart = strtoll(cur, &end, 10); 1116 | if (cur != end) { 1117 | res = (double)intPart; 1118 | hasIntPart = 1; 1119 | cur = end; 1120 | } 1121 | } 1122 | 1123 | // Parse fractional part. 1124 | if (*cur == '.') { 1125 | cur++; // Skip '.' 1126 | if (nsvg__isdigit(*cur)) { 1127 | // Parse digit sequence 1128 | fracPart = strtoll(cur, &end, 10); 1129 | if (cur != end) { 1130 | res += (double)fracPart / pow(10.0, (double)(end - cur)); 1131 | hasFracPart = 1; 1132 | cur = end; 1133 | } 1134 | } 1135 | } 1136 | 1137 | // A valid number should have integer or fractional part. 1138 | if (!hasIntPart && !hasFracPart) 1139 | return 0.0; 1140 | 1141 | // Parse optional exponent 1142 | if (*cur == 'e' || *cur == 'E') { 1143 | long expPart = 0; 1144 | cur++; // skip 'E' 1145 | expPart = strtol(cur, &end, 10); // Parse digit sequence with sign 1146 | if (cur != end) { 1147 | res *= pow(10.0, (double)expPart); 1148 | } 1149 | } 1150 | 1151 | return res * sign; 1152 | } 1153 | 1154 | 1155 | static const char* nsvg__parseNumber(const char* s, char* it, const int size) 1156 | { 1157 | const int last = size-1; 1158 | int i = 0; 1159 | 1160 | // sign 1161 | if (*s == '-' || *s == '+') { 1162 | if (i < last) it[i++] = *s; 1163 | s++; 1164 | } 1165 | // integer part 1166 | while (*s && nsvg__isdigit(*s)) { 1167 | if (i < last) it[i++] = *s; 1168 | s++; 1169 | } 1170 | if (*s == '.') { 1171 | // decimal point 1172 | if (i < last) it[i++] = *s; 1173 | s++; 1174 | // fraction part 1175 | while (*s && nsvg__isdigit(*s)) { 1176 | if (i < last) it[i++] = *s; 1177 | s++; 1178 | } 1179 | } 1180 | // exponent 1181 | if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { 1182 | if (i < last) it[i++] = *s; 1183 | s++; 1184 | if (*s == '-' || *s == '+') { 1185 | if (i < last) it[i++] = *s; 1186 | s++; 1187 | } 1188 | while (*s && nsvg__isdigit(*s)) { 1189 | if (i < last) it[i++] = *s; 1190 | s++; 1191 | } 1192 | } 1193 | it[i] = '\0'; 1194 | 1195 | return s; 1196 | } 1197 | 1198 | static const char* nsvg__getNextPathItem(const char* s, char* it) 1199 | { 1200 | it[0] = '\0'; 1201 | // Skip white spaces and commas 1202 | while (*s && (nsvg__isspace(*s) || *s == ',')) s++; 1203 | if (!*s) return s; 1204 | if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { 1205 | s = nsvg__parseNumber(s, it, 64); 1206 | } else { 1207 | // Parse command 1208 | it[0] = *s++; 1209 | it[1] = '\0'; 1210 | return s; 1211 | } 1212 | 1213 | return s; 1214 | } 1215 | 1216 | static unsigned int nsvg__parseColorHex(const char* str) 1217 | { 1218 | unsigned int c = 0, r = 0, g = 0, b = 0; 1219 | int n = 0; 1220 | str++; // skip # 1221 | // Calculate number of characters. 1222 | while(str[n] && !nsvg__isspace(str[n])) 1223 | n++; 1224 | if (n == 6) { 1225 | sscanf(str, "%x", &c); 1226 | } else if (n == 3) { 1227 | sscanf(str, "%x", &c); 1228 | c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8); 1229 | c |= c<<4; 1230 | } 1231 | r = (c >> 16) & 0xff; 1232 | g = (c >> 8) & 0xff; 1233 | b = c & 0xff; 1234 | return NSVG_RGB(r,g,b); 1235 | } 1236 | 1237 | static unsigned int nsvg__parseColorRGB(const char* str) 1238 | { 1239 | int r = -1, g = -1, b = -1; 1240 | char s1[32]="", s2[32]=""; 1241 | sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); 1242 | if (strchr(s1, '%')) { 1243 | return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100); 1244 | } else { 1245 | return NSVG_RGB(r,g,b); 1246 | } 1247 | } 1248 | 1249 | typedef struct NSVGNamedColor { 1250 | const char* name; 1251 | unsigned int color; 1252 | } NSVGNamedColor; 1253 | 1254 | NSVGNamedColor nsvg__colors[] = { 1255 | 1256 | { "red", NSVG_RGB(255, 0, 0) }, 1257 | { "green", NSVG_RGB( 0, 128, 0) }, 1258 | { "blue", NSVG_RGB( 0, 0, 255) }, 1259 | { "yellow", NSVG_RGB(255, 255, 0) }, 1260 | { "cyan", NSVG_RGB( 0, 255, 255) }, 1261 | { "magenta", NSVG_RGB(255, 0, 255) }, 1262 | { "black", NSVG_RGB( 0, 0, 0) }, 1263 | { "grey", NSVG_RGB(128, 128, 128) }, 1264 | { "gray", NSVG_RGB(128, 128, 128) }, 1265 | { "white", NSVG_RGB(255, 255, 255) }, 1266 | 1267 | #ifdef NANOSVG_ALL_COLOR_KEYWORDS 1268 | { "aliceblue", NSVG_RGB(240, 248, 255) }, 1269 | { "antiquewhite", NSVG_RGB(250, 235, 215) }, 1270 | { "aqua", NSVG_RGB( 0, 255, 255) }, 1271 | { "aquamarine", NSVG_RGB(127, 255, 212) }, 1272 | { "azure", NSVG_RGB(240, 255, 255) }, 1273 | { "beige", NSVG_RGB(245, 245, 220) }, 1274 | { "bisque", NSVG_RGB(255, 228, 196) }, 1275 | { "blanchedalmond", NSVG_RGB(255, 235, 205) }, 1276 | { "blueviolet", NSVG_RGB(138, 43, 226) }, 1277 | { "brown", NSVG_RGB(165, 42, 42) }, 1278 | { "burlywood", NSVG_RGB(222, 184, 135) }, 1279 | { "cadetblue", NSVG_RGB( 95, 158, 160) }, 1280 | { "chartreuse", NSVG_RGB(127, 255, 0) }, 1281 | { "chocolate", NSVG_RGB(210, 105, 30) }, 1282 | { "coral", NSVG_RGB(255, 127, 80) }, 1283 | { "cornflowerblue", NSVG_RGB(100, 149, 237) }, 1284 | { "cornsilk", NSVG_RGB(255, 248, 220) }, 1285 | { "crimson", NSVG_RGB(220, 20, 60) }, 1286 | { "darkblue", NSVG_RGB( 0, 0, 139) }, 1287 | { "darkcyan", NSVG_RGB( 0, 139, 139) }, 1288 | { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, 1289 | { "darkgray", NSVG_RGB(169, 169, 169) }, 1290 | { "darkgreen", NSVG_RGB( 0, 100, 0) }, 1291 | { "darkgrey", NSVG_RGB(169, 169, 169) }, 1292 | { "darkkhaki", NSVG_RGB(189, 183, 107) }, 1293 | { "darkmagenta", NSVG_RGB(139, 0, 139) }, 1294 | { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, 1295 | { "darkorange", NSVG_RGB(255, 140, 0) }, 1296 | { "darkorchid", NSVG_RGB(153, 50, 204) }, 1297 | { "darkred", NSVG_RGB(139, 0, 0) }, 1298 | { "darksalmon", NSVG_RGB(233, 150, 122) }, 1299 | { "darkseagreen", NSVG_RGB(143, 188, 143) }, 1300 | { "darkslateblue", NSVG_RGB( 72, 61, 139) }, 1301 | { "darkslategray", NSVG_RGB( 47, 79, 79) }, 1302 | { "darkslategrey", NSVG_RGB( 47, 79, 79) }, 1303 | { "darkturquoise", NSVG_RGB( 0, 206, 209) }, 1304 | { "darkviolet", NSVG_RGB(148, 0, 211) }, 1305 | { "deeppink", NSVG_RGB(255, 20, 147) }, 1306 | { "deepskyblue", NSVG_RGB( 0, 191, 255) }, 1307 | { "dimgray", NSVG_RGB(105, 105, 105) }, 1308 | { "dimgrey", NSVG_RGB(105, 105, 105) }, 1309 | { "dodgerblue", NSVG_RGB( 30, 144, 255) }, 1310 | { "firebrick", NSVG_RGB(178, 34, 34) }, 1311 | { "floralwhite", NSVG_RGB(255, 250, 240) }, 1312 | { "forestgreen", NSVG_RGB( 34, 139, 34) }, 1313 | { "fuchsia", NSVG_RGB(255, 0, 255) }, 1314 | { "gainsboro", NSVG_RGB(220, 220, 220) }, 1315 | { "ghostwhite", NSVG_RGB(248, 248, 255) }, 1316 | { "gold", NSVG_RGB(255, 215, 0) }, 1317 | { "goldenrod", NSVG_RGB(218, 165, 32) }, 1318 | { "greenyellow", NSVG_RGB(173, 255, 47) }, 1319 | { "honeydew", NSVG_RGB(240, 255, 240) }, 1320 | { "hotpink", NSVG_RGB(255, 105, 180) }, 1321 | { "indianred", NSVG_RGB(205, 92, 92) }, 1322 | { "indigo", NSVG_RGB( 75, 0, 130) }, 1323 | { "ivory", NSVG_RGB(255, 255, 240) }, 1324 | { "khaki", NSVG_RGB(240, 230, 140) }, 1325 | { "lavender", NSVG_RGB(230, 230, 250) }, 1326 | { "lavenderblush", NSVG_RGB(255, 240, 245) }, 1327 | { "lawngreen", NSVG_RGB(124, 252, 0) }, 1328 | { "lemonchiffon", NSVG_RGB(255, 250, 205) }, 1329 | { "lightblue", NSVG_RGB(173, 216, 230) }, 1330 | { "lightcoral", NSVG_RGB(240, 128, 128) }, 1331 | { "lightcyan", NSVG_RGB(224, 255, 255) }, 1332 | { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, 1333 | { "lightgray", NSVG_RGB(211, 211, 211) }, 1334 | { "lightgreen", NSVG_RGB(144, 238, 144) }, 1335 | { "lightgrey", NSVG_RGB(211, 211, 211) }, 1336 | { "lightpink", NSVG_RGB(255, 182, 193) }, 1337 | { "lightsalmon", NSVG_RGB(255, 160, 122) }, 1338 | { "lightseagreen", NSVG_RGB( 32, 178, 170) }, 1339 | { "lightskyblue", NSVG_RGB(135, 206, 250) }, 1340 | { "lightslategray", NSVG_RGB(119, 136, 153) }, 1341 | { "lightslategrey", NSVG_RGB(119, 136, 153) }, 1342 | { "lightsteelblue", NSVG_RGB(176, 196, 222) }, 1343 | { "lightyellow", NSVG_RGB(255, 255, 224) }, 1344 | { "lime", NSVG_RGB( 0, 255, 0) }, 1345 | { "limegreen", NSVG_RGB( 50, 205, 50) }, 1346 | { "linen", NSVG_RGB(250, 240, 230) }, 1347 | { "maroon", NSVG_RGB(128, 0, 0) }, 1348 | { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, 1349 | { "mediumblue", NSVG_RGB( 0, 0, 205) }, 1350 | { "mediumorchid", NSVG_RGB(186, 85, 211) }, 1351 | { "mediumpurple", NSVG_RGB(147, 112, 219) }, 1352 | { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, 1353 | { "mediumslateblue", NSVG_RGB(123, 104, 238) }, 1354 | { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, 1355 | { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, 1356 | { "mediumvioletred", NSVG_RGB(199, 21, 133) }, 1357 | { "midnightblue", NSVG_RGB( 25, 25, 112) }, 1358 | { "mintcream", NSVG_RGB(245, 255, 250) }, 1359 | { "mistyrose", NSVG_RGB(255, 228, 225) }, 1360 | { "moccasin", NSVG_RGB(255, 228, 181) }, 1361 | { "navajowhite", NSVG_RGB(255, 222, 173) }, 1362 | { "navy", NSVG_RGB( 0, 0, 128) }, 1363 | { "oldlace", NSVG_RGB(253, 245, 230) }, 1364 | { "olive", NSVG_RGB(128, 128, 0) }, 1365 | { "olivedrab", NSVG_RGB(107, 142, 35) }, 1366 | { "orange", NSVG_RGB(255, 165, 0) }, 1367 | { "orangered", NSVG_RGB(255, 69, 0) }, 1368 | { "orchid", NSVG_RGB(218, 112, 214) }, 1369 | { "palegoldenrod", NSVG_RGB(238, 232, 170) }, 1370 | { "palegreen", NSVG_RGB(152, 251, 152) }, 1371 | { "paleturquoise", NSVG_RGB(175, 238, 238) }, 1372 | { "palevioletred", NSVG_RGB(219, 112, 147) }, 1373 | { "papayawhip", NSVG_RGB(255, 239, 213) }, 1374 | { "peachpuff", NSVG_RGB(255, 218, 185) }, 1375 | { "peru", NSVG_RGB(205, 133, 63) }, 1376 | { "pink", NSVG_RGB(255, 192, 203) }, 1377 | { "plum", NSVG_RGB(221, 160, 221) }, 1378 | { "powderblue", NSVG_RGB(176, 224, 230) }, 1379 | { "purple", NSVG_RGB(128, 0, 128) }, 1380 | { "rosybrown", NSVG_RGB(188, 143, 143) }, 1381 | { "royalblue", NSVG_RGB( 65, 105, 225) }, 1382 | { "saddlebrown", NSVG_RGB(139, 69, 19) }, 1383 | { "salmon", NSVG_RGB(250, 128, 114) }, 1384 | { "sandybrown", NSVG_RGB(244, 164, 96) }, 1385 | { "seagreen", NSVG_RGB( 46, 139, 87) }, 1386 | { "seashell", NSVG_RGB(255, 245, 238) }, 1387 | { "sienna", NSVG_RGB(160, 82, 45) }, 1388 | { "silver", NSVG_RGB(192, 192, 192) }, 1389 | { "skyblue", NSVG_RGB(135, 206, 235) }, 1390 | { "slateblue", NSVG_RGB(106, 90, 205) }, 1391 | { "slategray", NSVG_RGB(112, 128, 144) }, 1392 | { "slategrey", NSVG_RGB(112, 128, 144) }, 1393 | { "snow", NSVG_RGB(255, 250, 250) }, 1394 | { "springgreen", NSVG_RGB( 0, 255, 127) }, 1395 | { "steelblue", NSVG_RGB( 70, 130, 180) }, 1396 | { "tan", NSVG_RGB(210, 180, 140) }, 1397 | { "teal", NSVG_RGB( 0, 128, 128) }, 1398 | { "thistle", NSVG_RGB(216, 191, 216) }, 1399 | { "tomato", NSVG_RGB(255, 99, 71) }, 1400 | { "turquoise", NSVG_RGB( 64, 224, 208) }, 1401 | { "violet", NSVG_RGB(238, 130, 238) }, 1402 | { "wheat", NSVG_RGB(245, 222, 179) }, 1403 | { "whitesmoke", NSVG_RGB(245, 245, 245) }, 1404 | { "yellowgreen", NSVG_RGB(154, 205, 50) }, 1405 | #endif 1406 | }; 1407 | 1408 | static unsigned int nsvg__parseColorName(const char* str) 1409 | { 1410 | int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); 1411 | 1412 | for (i = 0; i < ncolors; i++) { 1413 | if (strcmp(nsvg__colors[i].name, str) == 0) { 1414 | return nsvg__colors[i].color; 1415 | } 1416 | } 1417 | 1418 | return NSVG_RGB(128, 128, 128); 1419 | } 1420 | 1421 | static unsigned int nsvg__parseColor(const char* str) 1422 | { 1423 | size_t len = 0; 1424 | while(*str == ' ') ++str; 1425 | len = strlen(str); 1426 | if (len >= 1 && *str == '#') 1427 | return nsvg__parseColorHex(str); 1428 | else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') 1429 | return nsvg__parseColorRGB(str); 1430 | return nsvg__parseColorName(str); 1431 | } 1432 | 1433 | static float nsvg__parseOpacity(const char* str) 1434 | { 1435 | float val = nsvg__atof(str); 1436 | if (val < 0.0f) val = 0.0f; 1437 | if (val > 1.0f) val = 1.0f; 1438 | return val; 1439 | } 1440 | 1441 | static float nsvg__parseMiterLimit(const char* str) 1442 | { 1443 | float val = nsvg__atof(str); 1444 | if (val < 0.0f) val = 0.0f; 1445 | return val; 1446 | } 1447 | 1448 | static int nsvg__parseUnits(const char* units) 1449 | { 1450 | if (units[0] == 'p' && units[1] == 'x') 1451 | return NSVG_UNITS_PX; 1452 | else if (units[0] == 'p' && units[1] == 't') 1453 | return NSVG_UNITS_PT; 1454 | else if (units[0] == 'p' && units[1] == 'c') 1455 | return NSVG_UNITS_PC; 1456 | else if (units[0] == 'm' && units[1] == 'm') 1457 | return NSVG_UNITS_MM; 1458 | else if (units[0] == 'c' && units[1] == 'm') 1459 | return NSVG_UNITS_CM; 1460 | else if (units[0] == 'i' && units[1] == 'n') 1461 | return NSVG_UNITS_IN; 1462 | else if (units[0] == '%') 1463 | return NSVG_UNITS_PERCENT; 1464 | else if (units[0] == 'e' && units[1] == 'm') 1465 | return NSVG_UNITS_EM; 1466 | else if (units[0] == 'e' && units[1] == 'x') 1467 | return NSVG_UNITS_EX; 1468 | return NSVG_UNITS_USER; 1469 | } 1470 | 1471 | static int nsvg__isCoordinate(const char* s) 1472 | { 1473 | // optional sign 1474 | if (*s == '-' || *s == '+') 1475 | s++; 1476 | // must have at least one digit, or start by a dot 1477 | return (nsvg__isdigit(*s) || *s == '.'); 1478 | } 1479 | 1480 | static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) 1481 | { 1482 | NSVGcoordinate coord = {0, NSVG_UNITS_USER}; 1483 | char buf[64]; 1484 | coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); 1485 | coord.value = nsvg__atof(buf); 1486 | return coord; 1487 | } 1488 | 1489 | static NSVGcoordinate nsvg__coord(float v, int units) 1490 | { 1491 | NSVGcoordinate coord = {v, units}; 1492 | return coord; 1493 | } 1494 | 1495 | static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) 1496 | { 1497 | NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); 1498 | return nsvg__convertToPixels(p, coord, orig, length); 1499 | } 1500 | 1501 | static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) 1502 | { 1503 | const char* end; 1504 | const char* ptr; 1505 | char it[64]; 1506 | 1507 | *na = 0; 1508 | ptr = str; 1509 | while (*ptr && *ptr != '(') ++ptr; 1510 | if (*ptr == 0) 1511 | return 1; 1512 | end = ptr; 1513 | while (*end && *end != ')') ++end; 1514 | if (*end == 0) 1515 | return 1; 1516 | 1517 | while (ptr < end) { 1518 | if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { 1519 | if (*na >= maxNa) return 0; 1520 | ptr = nsvg__parseNumber(ptr, it, 64); 1521 | args[(*na)++] = (float)nsvg__atof(it); 1522 | } else { 1523 | ++ptr; 1524 | } 1525 | } 1526 | return (int)(end - str); 1527 | } 1528 | 1529 | 1530 | static int nsvg__parseMatrix(float* xform, const char* str) 1531 | { 1532 | float t[6]; 1533 | int na = 0; 1534 | int len = nsvg__parseTransformArgs(str, t, 6, &na); 1535 | if (na != 6) return len; 1536 | memcpy(xform, t, sizeof(float)*6); 1537 | return len; 1538 | } 1539 | 1540 | static int nsvg__parseTranslate(float* xform, const char* str) 1541 | { 1542 | float args[2]; 1543 | float t[6]; 1544 | int na = 0; 1545 | int len = nsvg__parseTransformArgs(str, args, 2, &na); 1546 | if (na == 1) args[1] = 0.0; 1547 | 1548 | nsvg__xformSetTranslation(t, args[0], args[1]); 1549 | memcpy(xform, t, sizeof(float)*6); 1550 | return len; 1551 | } 1552 | 1553 | static int nsvg__parseScale(float* xform, const char* str) 1554 | { 1555 | float args[2]; 1556 | int na = 0; 1557 | float t[6]; 1558 | int len = nsvg__parseTransformArgs(str, args, 2, &na); 1559 | if (na == 1) args[1] = args[0]; 1560 | nsvg__xformSetScale(t, args[0], args[1]); 1561 | memcpy(xform, t, sizeof(float)*6); 1562 | return len; 1563 | } 1564 | 1565 | static int nsvg__parseSkewX(float* xform, const char* str) 1566 | { 1567 | float args[1]; 1568 | int na = 0; 1569 | float t[6]; 1570 | int len = nsvg__parseTransformArgs(str, args, 1, &na); 1571 | nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); 1572 | memcpy(xform, t, sizeof(float)*6); 1573 | return len; 1574 | } 1575 | 1576 | static int nsvg__parseSkewY(float* xform, const char* str) 1577 | { 1578 | float args[1]; 1579 | int na = 0; 1580 | float t[6]; 1581 | int len = nsvg__parseTransformArgs(str, args, 1, &na); 1582 | nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); 1583 | memcpy(xform, t, sizeof(float)*6); 1584 | return len; 1585 | } 1586 | 1587 | static int nsvg__parseRotate(float* xform, const char* str) 1588 | { 1589 | float args[3]; 1590 | int na = 0; 1591 | float m[6]; 1592 | float t[6]; 1593 | int len = nsvg__parseTransformArgs(str, args, 3, &na); 1594 | if (na == 1) 1595 | args[1] = args[2] = 0.0f; 1596 | nsvg__xformIdentity(m); 1597 | 1598 | if (na > 1) { 1599 | nsvg__xformSetTranslation(t, -args[1], -args[2]); 1600 | nsvg__xformMultiply(m, t); 1601 | } 1602 | 1603 | nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); 1604 | nsvg__xformMultiply(m, t); 1605 | 1606 | if (na > 1) { 1607 | nsvg__xformSetTranslation(t, args[1], args[2]); 1608 | nsvg__xformMultiply(m, t); 1609 | } 1610 | 1611 | memcpy(xform, m, sizeof(float)*6); 1612 | 1613 | return len; 1614 | } 1615 | 1616 | static void nsvg__parseTransform(float* xform, const char* str) 1617 | { 1618 | float t[6]; 1619 | int len; 1620 | nsvg__xformIdentity(xform); 1621 | while (*str) 1622 | { 1623 | if (strncmp(str, "matrix", 6) == 0) 1624 | len = nsvg__parseMatrix(t, str); 1625 | else if (strncmp(str, "translate", 9) == 0) 1626 | len = nsvg__parseTranslate(t, str); 1627 | else if (strncmp(str, "scale", 5) == 0) 1628 | len = nsvg__parseScale(t, str); 1629 | else if (strncmp(str, "rotate", 6) == 0) 1630 | len = nsvg__parseRotate(t, str); 1631 | else if (strncmp(str, "skewX", 5) == 0) 1632 | len = nsvg__parseSkewX(t, str); 1633 | else if (strncmp(str, "skewY", 5) == 0) 1634 | len = nsvg__parseSkewY(t, str); 1635 | else{ 1636 | ++str; 1637 | continue; 1638 | } 1639 | if (len != 0) { 1640 | str += len; 1641 | } else { 1642 | ++str; 1643 | continue; 1644 | } 1645 | 1646 | nsvg__xformPremultiply(xform, t); 1647 | } 1648 | } 1649 | 1650 | static void nsvg__parseUrl(char* id, const char* str) 1651 | { 1652 | int i = 0; 1653 | str += 4; // "url("; 1654 | if (*str == '#') 1655 | str++; 1656 | while (i < 63 && *str != ')') { 1657 | id[i] = *str++; 1658 | i++; 1659 | } 1660 | id[i] = '\0'; 1661 | } 1662 | 1663 | static char nsvg__parseLineCap(const char* str) 1664 | { 1665 | if (strcmp(str, "butt") == 0) 1666 | return NSVG_CAP_BUTT; 1667 | else if (strcmp(str, "round") == 0) 1668 | return NSVG_CAP_ROUND; 1669 | else if (strcmp(str, "square") == 0) 1670 | return NSVG_CAP_SQUARE; 1671 | // TODO: handle inherit. 1672 | return NSVG_CAP_BUTT; 1673 | } 1674 | 1675 | static char nsvg__parseLineJoin(const char* str) 1676 | { 1677 | if (strcmp(str, "miter") == 0) 1678 | return NSVG_JOIN_MITER; 1679 | else if (strcmp(str, "round") == 0) 1680 | return NSVG_JOIN_ROUND; 1681 | else if (strcmp(str, "bevel") == 0) 1682 | return NSVG_JOIN_BEVEL; 1683 | // TODO: handle inherit. 1684 | return NSVG_JOIN_MITER; 1685 | } 1686 | 1687 | static char nsvg__parseFillRule(const char* str) 1688 | { 1689 | if (strcmp(str, "nonzero") == 0) 1690 | return NSVG_FILLRULE_NONZERO; 1691 | else if (strcmp(str, "evenodd") == 0) 1692 | return NSVG_FILLRULE_EVENODD; 1693 | // TODO: handle inherit. 1694 | return NSVG_FILLRULE_NONZERO; 1695 | } 1696 | 1697 | static const char* nsvg__getNextDashItem(const char* s, char* it) 1698 | { 1699 | int n = 0; 1700 | it[0] = '\0'; 1701 | // Skip white spaces and commas 1702 | while (*s && (nsvg__isspace(*s) || *s == ',')) s++; 1703 | // Advance until whitespace, comma or end. 1704 | while (*s && (!nsvg__isspace(*s) && *s != ',')) { 1705 | if (n < 63) 1706 | it[n++] = *s; 1707 | s++; 1708 | } 1709 | it[n++] = '\0'; 1710 | return s; 1711 | } 1712 | 1713 | static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) 1714 | { 1715 | char item[64]; 1716 | int count = 0, i; 1717 | float sum = 0.0f; 1718 | 1719 | // Handle "none" 1720 | if (str[0] == 'n') 1721 | return 0; 1722 | 1723 | // Parse dashes 1724 | while (*str) { 1725 | str = nsvg__getNextDashItem(str, item); 1726 | if (!*item) break; 1727 | if (count < NSVG_MAX_DASHES) 1728 | strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); 1729 | } 1730 | 1731 | for (i = 0; i < count; i++) 1732 | sum += strokeDashArray[i]; 1733 | if (sum <= 1e-6f) 1734 | count = 0; 1735 | 1736 | return count; 1737 | } 1738 | 1739 | static void nsvg__parseStyle(NSVGparser* p, const char* str); 1740 | 1741 | static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) 1742 | { 1743 | float xform[6]; 1744 | NSVGattrib* attr = nsvg__getAttr(p); 1745 | if (!attr) return 0; 1746 | 1747 | if (strcmp(name, "style") == 0) { 1748 | nsvg__parseStyle(p, value); 1749 | } else if (strcmp(name, "display") == 0) { 1750 | if (strcmp(value, "none") == 0) 1751 | attr->visible = 0; 1752 | // Don't reset ->visible on display:inline, one display:none hides the whole subtree 1753 | 1754 | } else if (strcmp(name, "fill") == 0) { 1755 | if (strcmp(value, "none") == 0) { 1756 | attr->hasFill = 0; 1757 | } else if (strncmp(value, "url(", 4) == 0) { 1758 | attr->hasFill = 2; 1759 | nsvg__parseUrl(attr->fillGradient, value); 1760 | } else { 1761 | attr->hasFill = 1; 1762 | attr->fillColor = nsvg__parseColor(value); 1763 | } 1764 | } else if (strcmp(name, "opacity") == 0) { 1765 | attr->opacity = nsvg__parseOpacity(value); 1766 | } else if (strcmp(name, "fill-opacity") == 0) { 1767 | attr->fillOpacity = nsvg__parseOpacity(value); 1768 | } else if (strcmp(name, "stroke") == 0) { 1769 | if (strcmp(value, "none") == 0) { 1770 | attr->hasStroke = 0; 1771 | } else if (strncmp(value, "url(", 4) == 0) { 1772 | attr->hasStroke = 2; 1773 | nsvg__parseUrl(attr->strokeGradient, value); 1774 | } else { 1775 | attr->hasStroke = 1; 1776 | attr->strokeColor = nsvg__parseColor(value); 1777 | } 1778 | } else if (strcmp(name, "stroke-width") == 0) { 1779 | attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); 1780 | } else if (strcmp(name, "stroke-dasharray") == 0) { 1781 | attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); 1782 | } else if (strcmp(name, "stroke-dashoffset") == 0) { 1783 | attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); 1784 | } else if (strcmp(name, "stroke-opacity") == 0) { 1785 | attr->strokeOpacity = nsvg__parseOpacity(value); 1786 | } else if (strcmp(name, "stroke-linecap") == 0) { 1787 | attr->strokeLineCap = nsvg__parseLineCap(value); 1788 | } else if (strcmp(name, "stroke-linejoin") == 0) { 1789 | attr->strokeLineJoin = nsvg__parseLineJoin(value); 1790 | } else if (strcmp(name, "stroke-miterlimit") == 0) { 1791 | attr->miterLimit = nsvg__parseMiterLimit(value); 1792 | } else if (strcmp(name, "fill-rule") == 0) { 1793 | attr->fillRule = nsvg__parseFillRule(value); 1794 | } else if (strcmp(name, "font-size") == 0) { 1795 | attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); 1796 | } else if (strcmp(name, "transform") == 0) { 1797 | nsvg__parseTransform(xform, value); 1798 | nsvg__xformPremultiply(attr->xform, xform); 1799 | } else if (strcmp(name, "stop-color") == 0) { 1800 | attr->stopColor = nsvg__parseColor(value); 1801 | } else if (strcmp(name, "stop-opacity") == 0) { 1802 | attr->stopOpacity = nsvg__parseOpacity(value); 1803 | } else if (strcmp(name, "offset") == 0) { 1804 | attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); 1805 | } else if (strcmp(name, "id") == 0) { 1806 | strncpy(attr->id, value, 63); 1807 | attr->id[63] = '\0'; 1808 | } else { 1809 | return 0; 1810 | } 1811 | return 1; 1812 | } 1813 | 1814 | static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) 1815 | { 1816 | const char* str; 1817 | const char* val; 1818 | char name[512]; 1819 | char value[512]; 1820 | int n; 1821 | 1822 | str = start; 1823 | while (str < end && *str != ':') ++str; 1824 | 1825 | val = str; 1826 | 1827 | // Right Trim 1828 | while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; 1829 | ++str; 1830 | 1831 | n = (int)(str - start); 1832 | if (n > 511) n = 511; 1833 | if (n) memcpy(name, start, n); 1834 | name[n] = 0; 1835 | 1836 | while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; 1837 | 1838 | n = (int)(end - val); 1839 | if (n > 511) n = 511; 1840 | if (n) memcpy(value, val, n); 1841 | value[n] = 0; 1842 | 1843 | return nsvg__parseAttr(p, name, value); 1844 | } 1845 | 1846 | static void nsvg__parseStyle(NSVGparser* p, const char* str) 1847 | { 1848 | const char* start; 1849 | const char* end; 1850 | 1851 | while (*str) { 1852 | // Left Trim 1853 | while(*str && nsvg__isspace(*str)) ++str; 1854 | start = str; 1855 | while(*str && *str != ';') ++str; 1856 | end = str; 1857 | 1858 | // Right Trim 1859 | while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; 1860 | ++end; 1861 | 1862 | nsvg__parseNameValue(p, start, end); 1863 | if (*str) ++str; 1864 | } 1865 | } 1866 | 1867 | static void nsvg__parseAttribs(NSVGparser* p, const char** attr) 1868 | { 1869 | int i; 1870 | for (i = 0; attr[i]; i += 2) 1871 | { 1872 | if (strcmp(attr[i], "style") == 0) 1873 | nsvg__parseStyle(p, attr[i + 1]); 1874 | else 1875 | nsvg__parseAttr(p, attr[i], attr[i + 1]); 1876 | } 1877 | } 1878 | 1879 | static int nsvg__getArgsPerElement(char cmd) 1880 | { 1881 | switch (cmd) { 1882 | case 'v': 1883 | case 'V': 1884 | case 'h': 1885 | case 'H': 1886 | return 1; 1887 | case 'm': 1888 | case 'M': 1889 | case 'l': 1890 | case 'L': 1891 | case 't': 1892 | case 'T': 1893 | return 2; 1894 | case 'q': 1895 | case 'Q': 1896 | case 's': 1897 | case 'S': 1898 | return 4; 1899 | case 'c': 1900 | case 'C': 1901 | return 6; 1902 | case 'a': 1903 | case 'A': 1904 | return 7; 1905 | case 'z': 1906 | case 'Z': 1907 | return 0; 1908 | } 1909 | return -1; 1910 | } 1911 | 1912 | static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) 1913 | { 1914 | if (rel) { 1915 | *cpx += args[0]; 1916 | *cpy += args[1]; 1917 | } else { 1918 | *cpx = args[0]; 1919 | *cpy = args[1]; 1920 | } 1921 | nsvg__moveTo(p, *cpx, *cpy); 1922 | } 1923 | 1924 | static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) 1925 | { 1926 | if (rel) { 1927 | *cpx += args[0]; 1928 | *cpy += args[1]; 1929 | } else { 1930 | *cpx = args[0]; 1931 | *cpy = args[1]; 1932 | } 1933 | nsvg__lineTo(p, *cpx, *cpy); 1934 | } 1935 | 1936 | static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) 1937 | { 1938 | if (rel) 1939 | *cpx += args[0]; 1940 | else 1941 | *cpx = args[0]; 1942 | nsvg__lineTo(p, *cpx, *cpy); 1943 | } 1944 | 1945 | static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) 1946 | { 1947 | if (rel) 1948 | *cpy += args[0]; 1949 | else 1950 | *cpy = args[0]; 1951 | nsvg__lineTo(p, *cpx, *cpy); 1952 | } 1953 | 1954 | static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, 1955 | float* cpx2, float* cpy2, float* args, int rel) 1956 | { 1957 | float x2, y2, cx1, cy1, cx2, cy2; 1958 | 1959 | if (rel) { 1960 | cx1 = *cpx + args[0]; 1961 | cy1 = *cpy + args[1]; 1962 | cx2 = *cpx + args[2]; 1963 | cy2 = *cpy + args[3]; 1964 | x2 = *cpx + args[4]; 1965 | y2 = *cpy + args[5]; 1966 | } else { 1967 | cx1 = args[0]; 1968 | cy1 = args[1]; 1969 | cx2 = args[2]; 1970 | cy2 = args[3]; 1971 | x2 = args[4]; 1972 | y2 = args[5]; 1973 | } 1974 | 1975 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); 1976 | 1977 | *cpx2 = cx2; 1978 | *cpy2 = cy2; 1979 | *cpx = x2; 1980 | *cpy = y2; 1981 | } 1982 | 1983 | static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, 1984 | float* cpx2, float* cpy2, float* args, int rel) 1985 | { 1986 | float x1, y1, x2, y2, cx1, cy1, cx2, cy2; 1987 | 1988 | x1 = *cpx; 1989 | y1 = *cpy; 1990 | if (rel) { 1991 | cx2 = *cpx + args[0]; 1992 | cy2 = *cpy + args[1]; 1993 | x2 = *cpx + args[2]; 1994 | y2 = *cpy + args[3]; 1995 | } else { 1996 | cx2 = args[0]; 1997 | cy2 = args[1]; 1998 | x2 = args[2]; 1999 | y2 = args[3]; 2000 | } 2001 | 2002 | cx1 = 2*x1 - *cpx2; 2003 | cy1 = 2*y1 - *cpy2; 2004 | 2005 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); 2006 | 2007 | *cpx2 = cx2; 2008 | *cpy2 = cy2; 2009 | *cpx = x2; 2010 | *cpy = y2; 2011 | } 2012 | 2013 | static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, 2014 | float* cpx2, float* cpy2, float* args, int rel) 2015 | { 2016 | float x1, y1, x2, y2, cx, cy; 2017 | float cx1, cy1, cx2, cy2; 2018 | 2019 | x1 = *cpx; 2020 | y1 = *cpy; 2021 | if (rel) { 2022 | cx = *cpx + args[0]; 2023 | cy = *cpy + args[1]; 2024 | x2 = *cpx + args[2]; 2025 | y2 = *cpy + args[3]; 2026 | } else { 2027 | cx = args[0]; 2028 | cy = args[1]; 2029 | x2 = args[2]; 2030 | y2 = args[3]; 2031 | } 2032 | 2033 | // Convert to cubic bezier 2034 | cx1 = x1 + 2.0f/3.0f*(cx - x1); 2035 | cy1 = y1 + 2.0f/3.0f*(cy - y1); 2036 | cx2 = x2 + 2.0f/3.0f*(cx - x2); 2037 | cy2 = y2 + 2.0f/3.0f*(cy - y2); 2038 | 2039 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); 2040 | 2041 | *cpx2 = cx; 2042 | *cpy2 = cy; 2043 | *cpx = x2; 2044 | *cpy = y2; 2045 | } 2046 | 2047 | static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, 2048 | float* cpx2, float* cpy2, float* args, int rel) 2049 | { 2050 | float x1, y1, x2, y2, cx, cy; 2051 | float cx1, cy1, cx2, cy2; 2052 | 2053 | x1 = *cpx; 2054 | y1 = *cpy; 2055 | if (rel) { 2056 | x2 = *cpx + args[0]; 2057 | y2 = *cpy + args[1]; 2058 | } else { 2059 | x2 = args[0]; 2060 | y2 = args[1]; 2061 | } 2062 | 2063 | cx = 2*x1 - *cpx2; 2064 | cy = 2*y1 - *cpy2; 2065 | 2066 | // Convert to cubix bezier 2067 | cx1 = x1 + 2.0f/3.0f*(cx - x1); 2068 | cy1 = y1 + 2.0f/3.0f*(cy - y1); 2069 | cx2 = x2 + 2.0f/3.0f*(cx - x2); 2070 | cy2 = y2 + 2.0f/3.0f*(cy - y2); 2071 | 2072 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); 2073 | 2074 | *cpx2 = cx; 2075 | *cpy2 = cy; 2076 | *cpx = x2; 2077 | *cpy = y2; 2078 | } 2079 | 2080 | static float nsvg__sqr(float x) { return x*x; } 2081 | static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } 2082 | 2083 | static float nsvg__vecrat(float ux, float uy, float vx, float vy) 2084 | { 2085 | return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); 2086 | } 2087 | 2088 | static float nsvg__vecang(float ux, float uy, float vx, float vy) 2089 | { 2090 | float r = nsvg__vecrat(ux,uy, vx,vy); 2091 | if (r < -1.0f) r = -1.0f; 2092 | if (r > 1.0f) r = 1.0f; 2093 | return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); 2094 | } 2095 | 2096 | static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) 2097 | { 2098 | // Ported from canvg (https://code.google.com/p/canvg/) 2099 | float rx, ry, rotx; 2100 | float x1, y1, x2, y2, cx, cy, dx, dy, d; 2101 | float x1p, y1p, cxp, cyp, s, sa, sb; 2102 | float ux, uy, vx, vy, a1, da; 2103 | float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; 2104 | float sinrx, cosrx; 2105 | int fa, fs; 2106 | int i, ndivs; 2107 | float hda, kappa; 2108 | 2109 | rx = fabsf(args[0]); // y radius 2110 | ry = fabsf(args[1]); // x radius 2111 | rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle 2112 | fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc 2113 | fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction 2114 | x1 = *cpx; // start point 2115 | y1 = *cpy; 2116 | if (rel) { // end point 2117 | x2 = *cpx + args[5]; 2118 | y2 = *cpy + args[6]; 2119 | } else { 2120 | x2 = args[5]; 2121 | y2 = args[6]; 2122 | } 2123 | 2124 | dx = x1 - x2; 2125 | dy = y1 - y2; 2126 | d = sqrtf(dx*dx + dy*dy); 2127 | if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { 2128 | // The arc degenerates to a line 2129 | nsvg__lineTo(p, x2, y2); 2130 | *cpx = x2; 2131 | *cpy = y2; 2132 | return; 2133 | } 2134 | 2135 | sinrx = sinf(rotx); 2136 | cosrx = cosf(rotx); 2137 | 2138 | // Convert to center point parameterization. 2139 | // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes 2140 | // 1) Compute x1', y1' 2141 | x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; 2142 | y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; 2143 | d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); 2144 | if (d > 1) { 2145 | d = sqrtf(d); 2146 | rx *= d; 2147 | ry *= d; 2148 | } 2149 | // 2) Compute cx', cy' 2150 | s = 0.0f; 2151 | sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); 2152 | sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); 2153 | if (sa < 0.0f) sa = 0.0f; 2154 | if (sb > 0.0f) 2155 | s = sqrtf(sa / sb); 2156 | if (fa == fs) 2157 | s = -s; 2158 | cxp = s * rx * y1p / ry; 2159 | cyp = s * -ry * x1p / rx; 2160 | 2161 | // 3) Compute cx,cy from cx',cy' 2162 | cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; 2163 | cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; 2164 | 2165 | // 4) Calculate theta1, and delta theta. 2166 | ux = (x1p - cxp) / rx; 2167 | uy = (y1p - cyp) / ry; 2168 | vx = (-x1p - cxp) / rx; 2169 | vy = (-y1p - cyp) / ry; 2170 | a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle 2171 | da = nsvg__vecang(ux,uy, vx,vy); // Delta angle 2172 | 2173 | // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; 2174 | // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; 2175 | 2176 | if (fs == 0 && da > 0) 2177 | da -= 2 * NSVG_PI; 2178 | else if (fs == 1 && da < 0) 2179 | da += 2 * NSVG_PI; 2180 | 2181 | // Approximate the arc using cubic spline segments. 2182 | t[0] = cosrx; t[1] = sinrx; 2183 | t[2] = -sinrx; t[3] = cosrx; 2184 | t[4] = cx; t[5] = cy; 2185 | 2186 | // Split arc into max 90 degree segments. 2187 | // The loop assumes an iteration per end point (including start and end), this +1. 2188 | ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); 2189 | hda = (da / (float)ndivs) / 2.0f; 2190 | // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) 2191 | if ((hda < 1e-3f) && (hda > -1e-3f)) 2192 | hda *= 0.5f; 2193 | else 2194 | hda = (1.0f - cosf(hda)) / sinf(hda); 2195 | kappa = fabsf(4.0f / 3.0f * hda); 2196 | if (da < 0.0f) 2197 | kappa = -kappa; 2198 | 2199 | for (i = 0; i <= ndivs; i++) { 2200 | a = a1 + da * ((float)i/(float)ndivs); 2201 | dx = cosf(a); 2202 | dy = sinf(a); 2203 | nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position 2204 | nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent 2205 | if (i > 0) 2206 | nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); 2207 | px = x; 2208 | py = y; 2209 | ptanx = tanx; 2210 | ptany = tany; 2211 | } 2212 | 2213 | *cpx = x2; 2214 | *cpy = y2; 2215 | } 2216 | 2217 | static void nsvg__parsePath(NSVGparser* p, const char** attr) 2218 | { 2219 | const char* s = NULL; 2220 | char cmd = '\0'; 2221 | float args[10]; 2222 | int nargs; 2223 | int rargs = 0; 2224 | char initPoint; 2225 | float cpx, cpy, cpx2, cpy2; 2226 | const char* tmp[4]; 2227 | char closedFlag; 2228 | int i; 2229 | char item[64]; 2230 | 2231 | for (i = 0; attr[i]; i += 2) { 2232 | if (strcmp(attr[i], "d") == 0) { 2233 | s = attr[i + 1]; 2234 | } else { 2235 | tmp[0] = attr[i]; 2236 | tmp[1] = attr[i + 1]; 2237 | tmp[2] = 0; 2238 | tmp[3] = 0; 2239 | nsvg__parseAttribs(p, tmp); 2240 | } 2241 | } 2242 | 2243 | if (s) { 2244 | nsvg__resetPath(p); 2245 | cpx = 0; cpy = 0; 2246 | cpx2 = 0; cpy2 = 0; 2247 | initPoint = 0; 2248 | closedFlag = 0; 2249 | nargs = 0; 2250 | 2251 | while (*s) { 2252 | s = nsvg__getNextPathItem(s, item); 2253 | if (!*item) break; 2254 | if (cmd != '\0' && nsvg__isCoordinate(item)) { 2255 | if (nargs < 10) 2256 | args[nargs++] = (float)nsvg__atof(item); 2257 | if (nargs >= rargs) { 2258 | switch (cmd) { 2259 | case 'm': 2260 | case 'M': 2261 | nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); 2262 | // Moveto can be followed by multiple coordinate pairs, 2263 | // which should be treated as linetos. 2264 | cmd = (cmd == 'm') ? 'l' : 'L'; 2265 | rargs = nsvg__getArgsPerElement(cmd); 2266 | cpx2 = cpx; cpy2 = cpy; 2267 | initPoint = 1; 2268 | break; 2269 | case 'l': 2270 | case 'L': 2271 | nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); 2272 | cpx2 = cpx; cpy2 = cpy; 2273 | break; 2274 | case 'H': 2275 | case 'h': 2276 | nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); 2277 | cpx2 = cpx; cpy2 = cpy; 2278 | break; 2279 | case 'V': 2280 | case 'v': 2281 | nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); 2282 | cpx2 = cpx; cpy2 = cpy; 2283 | break; 2284 | case 'C': 2285 | case 'c': 2286 | nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); 2287 | break; 2288 | case 'S': 2289 | case 's': 2290 | nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); 2291 | break; 2292 | case 'Q': 2293 | case 'q': 2294 | nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); 2295 | break; 2296 | case 'T': 2297 | case 't': 2298 | nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); 2299 | break; 2300 | case 'A': 2301 | case 'a': 2302 | nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); 2303 | cpx2 = cpx; cpy2 = cpy; 2304 | break; 2305 | default: 2306 | if (nargs >= 2) { 2307 | cpx = args[nargs-2]; 2308 | cpy = args[nargs-1]; 2309 | cpx2 = cpx; cpy2 = cpy; 2310 | } 2311 | break; 2312 | } 2313 | nargs = 0; 2314 | } 2315 | } else { 2316 | cmd = item[0]; 2317 | if (cmd == 'M' || cmd == 'm') { 2318 | // Commit path. 2319 | if (p->npts > 0) 2320 | nsvg__addPath(p, closedFlag); 2321 | // Start new subpath. 2322 | nsvg__resetPath(p); 2323 | closedFlag = 0; 2324 | nargs = 0; 2325 | } else if (initPoint == 0) { 2326 | // Do not allow other commands until initial point has been set (moveTo called once). 2327 | cmd = '\0'; 2328 | } 2329 | if (cmd == 'Z' || cmd == 'z') { 2330 | closedFlag = 1; 2331 | // Commit path. 2332 | if (p->npts > 0) { 2333 | // Move current point to first point 2334 | cpx = p->pts[0]; 2335 | cpy = p->pts[1]; 2336 | cpx2 = cpx; cpy2 = cpy; 2337 | nsvg__addPath(p, closedFlag); 2338 | } 2339 | // Start new subpath. 2340 | nsvg__resetPath(p); 2341 | nsvg__moveTo(p, cpx, cpy); 2342 | closedFlag = 0; 2343 | nargs = 0; 2344 | } 2345 | rargs = nsvg__getArgsPerElement(cmd); 2346 | if (rargs == -1) { 2347 | // Command not recognized 2348 | cmd = '\0'; 2349 | rargs = 0; 2350 | } 2351 | } 2352 | } 2353 | // Commit path. 2354 | if (p->npts) 2355 | nsvg__addPath(p, closedFlag); 2356 | } 2357 | 2358 | nsvg__addShape(p); 2359 | } 2360 | 2361 | static void nsvg__parseRect(NSVGparser* p, const char** attr) 2362 | { 2363 | float x = 0.0f; 2364 | float y = 0.0f; 2365 | float w = 0.0f; 2366 | float h = 0.0f; 2367 | float rx = -1.0f; // marks not set 2368 | float ry = -1.0f; 2369 | int i; 2370 | 2371 | for (i = 0; attr[i]; i += 2) { 2372 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2373 | if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 2374 | if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 2375 | if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); 2376 | if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); 2377 | if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); 2378 | if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); 2379 | } 2380 | } 2381 | 2382 | if (rx < 0.0f && ry > 0.0f) rx = ry; 2383 | if (ry < 0.0f && rx > 0.0f) ry = rx; 2384 | if (rx < 0.0f) rx = 0.0f; 2385 | if (ry < 0.0f) ry = 0.0f; 2386 | if (rx > w/2.0f) rx = w/2.0f; 2387 | if (ry > h/2.0f) ry = h/2.0f; 2388 | 2389 | if (w != 0.0f && h != 0.0f) { 2390 | nsvg__resetPath(p); 2391 | 2392 | if (rx < 0.00001f || ry < 0.0001f) { 2393 | nsvg__moveTo(p, x, y); 2394 | nsvg__lineTo(p, x+w, y); 2395 | nsvg__lineTo(p, x+w, y+h); 2396 | nsvg__lineTo(p, x, y+h); 2397 | } else { 2398 | // Rounded rectangle 2399 | nsvg__moveTo(p, x+rx, y); 2400 | nsvg__lineTo(p, x+w-rx, y); 2401 | nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); 2402 | nsvg__lineTo(p, x+w, y+h-ry); 2403 | nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); 2404 | nsvg__lineTo(p, x+rx, y+h); 2405 | nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); 2406 | nsvg__lineTo(p, x, y+ry); 2407 | nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); 2408 | } 2409 | 2410 | nsvg__addPath(p, 1); 2411 | 2412 | nsvg__addShape(p); 2413 | } 2414 | } 2415 | 2416 | static void nsvg__parseCircle(NSVGparser* p, const char** attr) 2417 | { 2418 | float cx = 0.0f; 2419 | float cy = 0.0f; 2420 | float r = 0.0f; 2421 | int i; 2422 | 2423 | for (i = 0; attr[i]; i += 2) { 2424 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2425 | if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 2426 | if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 2427 | if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); 2428 | } 2429 | } 2430 | 2431 | if (r > 0.0f) { 2432 | nsvg__resetPath(p); 2433 | 2434 | nsvg__moveTo(p, cx+r, cy); 2435 | nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); 2436 | nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); 2437 | nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); 2438 | nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); 2439 | 2440 | nsvg__addPath(p, 1); 2441 | 2442 | nsvg__addShape(p); 2443 | } 2444 | } 2445 | 2446 | static void nsvg__parseEllipse(NSVGparser* p, const char** attr) 2447 | { 2448 | float cx = 0.0f; 2449 | float cy = 0.0f; 2450 | float rx = 0.0f; 2451 | float ry = 0.0f; 2452 | int i; 2453 | 2454 | for (i = 0; attr[i]; i += 2) { 2455 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2456 | if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 2457 | if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 2458 | if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); 2459 | if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); 2460 | } 2461 | } 2462 | 2463 | if (rx > 0.0f && ry > 0.0f) { 2464 | 2465 | nsvg__resetPath(p); 2466 | 2467 | nsvg__moveTo(p, cx+rx, cy); 2468 | nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); 2469 | nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); 2470 | nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); 2471 | nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); 2472 | 2473 | nsvg__addPath(p, 1); 2474 | 2475 | nsvg__addShape(p); 2476 | } 2477 | } 2478 | 2479 | static void nsvg__parseLine(NSVGparser* p, const char** attr) 2480 | { 2481 | float x1 = 0.0; 2482 | float y1 = 0.0; 2483 | float x2 = 0.0; 2484 | float y2 = 0.0; 2485 | int i; 2486 | 2487 | for (i = 0; attr[i]; i += 2) { 2488 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2489 | if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 2490 | if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 2491 | if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 2492 | if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 2493 | } 2494 | } 2495 | 2496 | nsvg__resetPath(p); 2497 | 2498 | nsvg__moveTo(p, x1, y1); 2499 | nsvg__lineTo(p, x2, y2); 2500 | 2501 | nsvg__addPath(p, 0); 2502 | 2503 | nsvg__addShape(p); 2504 | } 2505 | 2506 | static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) 2507 | { 2508 | int i; 2509 | const char* s; 2510 | float args[2]; 2511 | int nargs, npts = 0; 2512 | char item[64]; 2513 | 2514 | nsvg__resetPath(p); 2515 | 2516 | for (i = 0; attr[i]; i += 2) { 2517 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2518 | if (strcmp(attr[i], "points") == 0) { 2519 | s = attr[i + 1]; 2520 | nargs = 0; 2521 | while (*s) { 2522 | s = nsvg__getNextPathItem(s, item); 2523 | args[nargs++] = (float)nsvg__atof(item); 2524 | if (nargs >= 2) { 2525 | if (npts == 0) 2526 | nsvg__moveTo(p, args[0], args[1]); 2527 | else 2528 | nsvg__lineTo(p, args[0], args[1]); 2529 | nargs = 0; 2530 | npts++; 2531 | } 2532 | } 2533 | } 2534 | } 2535 | } 2536 | 2537 | nsvg__addPath(p, (char)closeFlag); 2538 | 2539 | nsvg__addShape(p); 2540 | } 2541 | 2542 | static void nsvg__parseSVG(NSVGparser* p, const char** attr) 2543 | { 2544 | int i; 2545 | for (i = 0; attr[i]; i += 2) { 2546 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2547 | if (strcmp(attr[i], "width") == 0) { 2548 | p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); 2549 | } else if (strcmp(attr[i], "height") == 0) { 2550 | p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); 2551 | } else if (strcmp(attr[i], "viewBox") == 0) { 2552 | const char *s = attr[i + 1]; 2553 | char buf[64]; 2554 | s = nsvg__parseNumber(s, buf, 64); 2555 | p->viewMinx = nsvg__atof(buf); 2556 | while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; 2557 | if (!*s) return; 2558 | s = nsvg__parseNumber(s, buf, 64); 2559 | p->viewMiny = nsvg__atof(buf); 2560 | while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; 2561 | if (!*s) return; 2562 | s = nsvg__parseNumber(s, buf, 64); 2563 | p->viewWidth = nsvg__atof(buf); 2564 | while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; 2565 | if (!*s) return; 2566 | s = nsvg__parseNumber(s, buf, 64); 2567 | p->viewHeight = nsvg__atof(buf); 2568 | } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { 2569 | if (strstr(attr[i + 1], "none") != 0) { 2570 | // No uniform scaling 2571 | p->alignType = NSVG_ALIGN_NONE; 2572 | } else { 2573 | // Parse X align 2574 | if (strstr(attr[i + 1], "xMin") != 0) 2575 | p->alignX = NSVG_ALIGN_MIN; 2576 | else if (strstr(attr[i + 1], "xMid") != 0) 2577 | p->alignX = NSVG_ALIGN_MID; 2578 | else if (strstr(attr[i + 1], "xMax") != 0) 2579 | p->alignX = NSVG_ALIGN_MAX; 2580 | // Parse X align 2581 | if (strstr(attr[i + 1], "yMin") != 0) 2582 | p->alignY = NSVG_ALIGN_MIN; 2583 | else if (strstr(attr[i + 1], "yMid") != 0) 2584 | p->alignY = NSVG_ALIGN_MID; 2585 | else if (strstr(attr[i + 1], "yMax") != 0) 2586 | p->alignY = NSVG_ALIGN_MAX; 2587 | // Parse meet/slice 2588 | p->alignType = NSVG_ALIGN_MEET; 2589 | if (strstr(attr[i + 1], "slice") != 0) 2590 | p->alignType = NSVG_ALIGN_SLICE; 2591 | } 2592 | } 2593 | } 2594 | } 2595 | } 2596 | 2597 | static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) 2598 | { 2599 | int i; 2600 | NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); 2601 | if (grad == NULL) return; 2602 | memset(grad, 0, sizeof(NSVGgradientData)); 2603 | grad->units = NSVG_OBJECT_SPACE; 2604 | grad->type = type; 2605 | if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { 2606 | grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); 2607 | grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); 2608 | grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); 2609 | grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); 2610 | } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { 2611 | grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); 2612 | grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); 2613 | grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); 2614 | } 2615 | 2616 | nsvg__xformIdentity(grad->xform); 2617 | 2618 | for (i = 0; attr[i]; i += 2) { 2619 | if (strcmp(attr[i], "id") == 0) { 2620 | strncpy(grad->id, attr[i+1], 63); 2621 | grad->id[63] = '\0'; 2622 | } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { 2623 | if (strcmp(attr[i], "gradientUnits") == 0) { 2624 | if (strcmp(attr[i+1], "objectBoundingBox") == 0) 2625 | grad->units = NSVG_OBJECT_SPACE; 2626 | else 2627 | grad->units = NSVG_USER_SPACE; 2628 | } else if (strcmp(attr[i], "gradientTransform") == 0) { 2629 | nsvg__parseTransform(grad->xform, attr[i + 1]); 2630 | } else if (strcmp(attr[i], "cx") == 0) { 2631 | grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); 2632 | } else if (strcmp(attr[i], "cy") == 0) { 2633 | grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); 2634 | } else if (strcmp(attr[i], "r") == 0) { 2635 | grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); 2636 | } else if (strcmp(attr[i], "fx") == 0) { 2637 | grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); 2638 | } else if (strcmp(attr[i], "fy") == 0) { 2639 | grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); 2640 | } else if (strcmp(attr[i], "x1") == 0) { 2641 | grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); 2642 | } else if (strcmp(attr[i], "y1") == 0) { 2643 | grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); 2644 | } else if (strcmp(attr[i], "x2") == 0) { 2645 | grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); 2646 | } else if (strcmp(attr[i], "y2") == 0) { 2647 | grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); 2648 | } else if (strcmp(attr[i], "spreadMethod") == 0) { 2649 | if (strcmp(attr[i+1], "pad") == 0) 2650 | grad->spread = NSVG_SPREAD_PAD; 2651 | else if (strcmp(attr[i+1], "reflect") == 0) 2652 | grad->spread = NSVG_SPREAD_REFLECT; 2653 | else if (strcmp(attr[i+1], "repeat") == 0) 2654 | grad->spread = NSVG_SPREAD_REPEAT; 2655 | } else if (strcmp(attr[i], "xlink:href") == 0) { 2656 | const char *href = attr[i+1]; 2657 | strncpy(grad->ref, href+1, 62); 2658 | grad->ref[62] = '\0'; 2659 | } 2660 | } 2661 | } 2662 | 2663 | grad->next = p->gradients; 2664 | p->gradients = grad; 2665 | } 2666 | 2667 | static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) 2668 | { 2669 | NSVGattrib* curAttr = nsvg__getAttr(p); 2670 | NSVGgradientData* grad; 2671 | NSVGgradientStop* stop; 2672 | int i, idx; 2673 | 2674 | curAttr->stopOffset = 0; 2675 | curAttr->stopColor = 0; 2676 | curAttr->stopOpacity = 1.0f; 2677 | 2678 | for (i = 0; attr[i]; i += 2) { 2679 | nsvg__parseAttr(p, attr[i], attr[i + 1]); 2680 | } 2681 | 2682 | // Add stop to the last gradient. 2683 | grad = p->gradients; 2684 | if (grad == NULL) return; 2685 | 2686 | grad->nstops++; 2687 | grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); 2688 | if (grad->stops == NULL) return; 2689 | 2690 | // Insert 2691 | idx = grad->nstops-1; 2692 | for (i = 0; i < grad->nstops-1; i++) { 2693 | if (curAttr->stopOffset < grad->stops[i].offset) { 2694 | idx = i; 2695 | break; 2696 | } 2697 | } 2698 | if (idx != grad->nstops-1) { 2699 | for (i = grad->nstops-1; i > idx; i--) 2700 | grad->stops[i] = grad->stops[i-1]; 2701 | } 2702 | 2703 | stop = &grad->stops[idx]; 2704 | stop->color = curAttr->stopColor; 2705 | stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; 2706 | stop->offset = curAttr->stopOffset; 2707 | } 2708 | 2709 | static void nsvg__startElement(void* ud, const char* el, const char** attr) 2710 | { 2711 | NSVGparser* p = (NSVGparser*)ud; 2712 | 2713 | if (p->defsFlag) { 2714 | // Skip everything but gradients in defs 2715 | if (strcmp(el, "linearGradient") == 0) { 2716 | nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); 2717 | } else if (strcmp(el, "radialGradient") == 0) { 2718 | nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); 2719 | } else if (strcmp(el, "stop") == 0) { 2720 | nsvg__parseGradientStop(p, attr); 2721 | } 2722 | return; 2723 | } 2724 | 2725 | if (strcmp(el, "g") == 0) { 2726 | nsvg__pushAttr(p); 2727 | nsvg__parseAttribs(p, attr); 2728 | } else if (strcmp(el, "path") == 0) { 2729 | if (p->pathFlag) // Do not allow nested paths. 2730 | return; 2731 | nsvg__pushAttr(p); 2732 | nsvg__parsePath(p, attr); 2733 | nsvg__popAttr(p); 2734 | } else if (strcmp(el, "rect") == 0) { 2735 | nsvg__pushAttr(p); 2736 | nsvg__parseRect(p, attr); 2737 | nsvg__popAttr(p); 2738 | } else if (strcmp(el, "circle") == 0) { 2739 | nsvg__pushAttr(p); 2740 | nsvg__parseCircle(p, attr); 2741 | nsvg__popAttr(p); 2742 | } else if (strcmp(el, "ellipse") == 0) { 2743 | nsvg__pushAttr(p); 2744 | nsvg__parseEllipse(p, attr); 2745 | nsvg__popAttr(p); 2746 | } else if (strcmp(el, "line") == 0) { 2747 | nsvg__pushAttr(p); 2748 | nsvg__parseLine(p, attr); 2749 | nsvg__popAttr(p); 2750 | } else if (strcmp(el, "polyline") == 0) { 2751 | nsvg__pushAttr(p); 2752 | nsvg__parsePoly(p, attr, 0); 2753 | nsvg__popAttr(p); 2754 | } else if (strcmp(el, "polygon") == 0) { 2755 | nsvg__pushAttr(p); 2756 | nsvg__parsePoly(p, attr, 1); 2757 | nsvg__popAttr(p); 2758 | } else if (strcmp(el, "linearGradient") == 0) { 2759 | nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); 2760 | } else if (strcmp(el, "radialGradient") == 0) { 2761 | nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); 2762 | } else if (strcmp(el, "stop") == 0) { 2763 | nsvg__parseGradientStop(p, attr); 2764 | } else if (strcmp(el, "defs") == 0) { 2765 | p->defsFlag = 1; 2766 | } else if (strcmp(el, "svg") == 0) { 2767 | nsvg__parseSVG(p, attr); 2768 | } 2769 | } 2770 | 2771 | static void nsvg__endElement(void* ud, const char* el) 2772 | { 2773 | NSVGparser* p = (NSVGparser*)ud; 2774 | 2775 | if (strcmp(el, "g") == 0) { 2776 | nsvg__popAttr(p); 2777 | } else if (strcmp(el, "path") == 0) { 2778 | p->pathFlag = 0; 2779 | } else if (strcmp(el, "defs") == 0) { 2780 | p->defsFlag = 0; 2781 | } 2782 | } 2783 | 2784 | static void nsvg__content(void* ud, const char* s) 2785 | { 2786 | NSVG_NOTUSED(ud); 2787 | NSVG_NOTUSED(s); 2788 | // empty 2789 | } 2790 | 2791 | static void nsvg__imageBounds(NSVGparser* p, float* bounds) 2792 | { 2793 | NSVGshape* shape; 2794 | shape = p->image->shapes; 2795 | if (shape == NULL) { 2796 | bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; 2797 | return; 2798 | } 2799 | bounds[0] = shape->bounds[0]; 2800 | bounds[1] = shape->bounds[1]; 2801 | bounds[2] = shape->bounds[2]; 2802 | bounds[3] = shape->bounds[3]; 2803 | for (shape = shape->next; shape != NULL; shape = shape->next) { 2804 | bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); 2805 | bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); 2806 | bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); 2807 | bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); 2808 | } 2809 | } 2810 | 2811 | static float nsvg__viewAlign(float content, float container, int type) 2812 | { 2813 | if (type == NSVG_ALIGN_MIN) 2814 | return 0; 2815 | else if (type == NSVG_ALIGN_MAX) 2816 | return container - content; 2817 | // mid 2818 | return (container - content) * 0.5f; 2819 | } 2820 | 2821 | static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) 2822 | { 2823 | float t[6]; 2824 | nsvg__xformSetTranslation(t, tx, ty); 2825 | nsvg__xformMultiply (grad->xform, t); 2826 | 2827 | nsvg__xformSetScale(t, sx, sy); 2828 | nsvg__xformMultiply (grad->xform, t); 2829 | } 2830 | 2831 | static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) 2832 | { 2833 | NSVGshape* shape; 2834 | NSVGpath* path; 2835 | float tx, ty, sx, sy, us, bounds[4], t[6], avgs; 2836 | int i; 2837 | float* pt; 2838 | 2839 | // Guess image size if not set completely. 2840 | nsvg__imageBounds(p, bounds); 2841 | 2842 | if (p->viewWidth == 0) { 2843 | if (p->image->width > 0) { 2844 | p->viewWidth = p->image->width; 2845 | } else { 2846 | p->viewMinx = bounds[0]; 2847 | p->viewWidth = bounds[2] - bounds[0]; 2848 | } 2849 | } 2850 | if (p->viewHeight == 0) { 2851 | if (p->image->height > 0) { 2852 | p->viewHeight = p->image->height; 2853 | } else { 2854 | p->viewMiny = bounds[1]; 2855 | p->viewHeight = bounds[3] - bounds[1]; 2856 | } 2857 | } 2858 | if (p->image->width == 0) 2859 | p->image->width = p->viewWidth; 2860 | if (p->image->height == 0) 2861 | p->image->height = p->viewHeight; 2862 | 2863 | tx = -p->viewMinx; 2864 | ty = -p->viewMiny; 2865 | sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; 2866 | sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; 2867 | // Unit scaling 2868 | us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); 2869 | 2870 | // Fix aspect ratio 2871 | if (p->alignType == NSVG_ALIGN_MEET) { 2872 | // fit whole image into viewbox 2873 | sx = sy = nsvg__minf(sx, sy); 2874 | tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; 2875 | ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; 2876 | } else if (p->alignType == NSVG_ALIGN_SLICE) { 2877 | // fill whole viewbox with image 2878 | sx = sy = nsvg__maxf(sx, sy); 2879 | tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; 2880 | ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; 2881 | } 2882 | 2883 | // Transform 2884 | sx *= us; 2885 | sy *= us; 2886 | avgs = (sx+sy) / 2.0f; 2887 | for (shape = p->image->shapes; shape != NULL; shape = shape->next) { 2888 | shape->bounds[0] = (shape->bounds[0] + tx) * sx; 2889 | shape->bounds[1] = (shape->bounds[1] + ty) * sy; 2890 | shape->bounds[2] = (shape->bounds[2] + tx) * sx; 2891 | shape->bounds[3] = (shape->bounds[3] + ty) * sy; 2892 | for (path = shape->paths; path != NULL; path = path->next) { 2893 | path->bounds[0] = (path->bounds[0] + tx) * sx; 2894 | path->bounds[1] = (path->bounds[1] + ty) * sy; 2895 | path->bounds[2] = (path->bounds[2] + tx) * sx; 2896 | path->bounds[3] = (path->bounds[3] + ty) * sy; 2897 | for (i =0; i < path->npts; i++) { 2898 | pt = &path->pts[i*2]; 2899 | pt[0] = (pt[0] + tx) * sx; 2900 | pt[1] = (pt[1] + ty) * sy; 2901 | } 2902 | } 2903 | 2904 | if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { 2905 | nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); 2906 | memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); 2907 | nsvg__xformInverse(shape->fill.gradient->xform, t); 2908 | } 2909 | if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { 2910 | nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); 2911 | memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); 2912 | nsvg__xformInverse(shape->stroke.gradient->xform, t); 2913 | } 2914 | 2915 | shape->strokeWidth *= avgs; 2916 | shape->strokeDashOffset *= avgs; 2917 | for (i = 0; i < shape->strokeDashCount; i++) 2918 | shape->strokeDashArray[i] *= avgs; 2919 | } 2920 | } 2921 | 2922 | NSVGimage* nsvgParse(char* input, const char* units, float dpi) 2923 | { 2924 | NSVGparser* p; 2925 | NSVGimage* ret = 0; 2926 | 2927 | p = nsvg__createParser(); 2928 | if (p == NULL) { 2929 | return NULL; 2930 | } 2931 | p->dpi = dpi; 2932 | 2933 | nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); 2934 | 2935 | // Scale to viewBox 2936 | nsvg__scaleToViewbox(p, units); 2937 | 2938 | ret = p->image; 2939 | p->image = NULL; 2940 | 2941 | nsvg__deleteParser(p); 2942 | 2943 | return ret; 2944 | } 2945 | 2946 | NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) 2947 | { 2948 | FILE* fp = NULL; 2949 | size_t size; 2950 | char* data = NULL; 2951 | NSVGimage* image = NULL; 2952 | 2953 | fp = fopen(filename, "rb"); 2954 | if (!fp) goto error; 2955 | fseek(fp, 0, SEEK_END); 2956 | size = ftell(fp); 2957 | fseek(fp, 0, SEEK_SET); 2958 | data = (char*)malloc(size+1); 2959 | if (data == NULL) goto error; 2960 | if (fread(data, 1, size, fp) != size) goto error; 2961 | data[size] = '\0'; // Must be null terminated. 2962 | fclose(fp); 2963 | image = nsvgParse(data, units, dpi); 2964 | free(data); 2965 | 2966 | return image; 2967 | 2968 | error: 2969 | if (fp) fclose(fp); 2970 | if (data) free(data); 2971 | if (image) nsvgDelete(image); 2972 | return NULL; 2973 | } 2974 | 2975 | NSVGpath* nsvgDuplicatePath(NSVGpath* p) 2976 | { 2977 | NSVGpath* res = NULL; 2978 | 2979 | if (p == NULL) 2980 | return NULL; 2981 | 2982 | res = (NSVGpath*)malloc(sizeof(NSVGpath)); 2983 | if (res == NULL) goto error; 2984 | memset(res, 0, sizeof(NSVGpath)); 2985 | 2986 | res->pts = (float*)malloc(p->npts*2*sizeof(float)); 2987 | if (res->pts == NULL) goto error; 2988 | memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); 2989 | res->npts = p->npts; 2990 | 2991 | memcpy(res->bounds, p->bounds, sizeof(p->bounds)); 2992 | 2993 | res->closed = p->closed; 2994 | 2995 | return res; 2996 | 2997 | error: 2998 | if (res != NULL) { 2999 | free(res->pts); 3000 | free(res); 3001 | } 3002 | return NULL; 3003 | } 3004 | 3005 | void nsvgDelete(NSVGimage* image) 3006 | { 3007 | NSVGshape *snext, *shape; 3008 | if (image == NULL) return; 3009 | shape = image->shapes; 3010 | while (shape != NULL) { 3011 | snext = shape->next; 3012 | nsvg__deletePaths(shape->paths); 3013 | nsvg__deletePaint(&shape->fill); 3014 | nsvg__deletePaint(&shape->stroke); 3015 | free(shape); 3016 | shape = snext; 3017 | } 3018 | free(image); 3019 | } 3020 | 3021 | #endif 3022 | --------------------------------------------------------------------------------