X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fdrawer.c;h=671ac84aa07b3ef2a606da6ae0c0e817c7235839;hp=f0d3d348b01cee9125bd42debcd7c3af105e1ddf;hb=879d0eec420fe0fd5ddcd56c8fe62b82a6744edd;hpb=898ceddb63e8fb4bd04cc8a87e0b93b6def552e1 diff --git a/lib/drawer.c b/lib/drawer.c index f0d3d34..671ac84 100644 --- a/lib/drawer.c +++ b/lib/drawer.c @@ -22,28 +22,41 @@ #include #include +#include #include #include +#include #include "drawer.h" static char* getToken(const char**p) { const char*start; char*result; - while(**p && strchr(" ,\t\n\r", **p)) { + while(**p && strchr(" ,()\t\n\r", **p)) { (*p)++; } start = *p; - while(**p && !strchr(" ,\t\n\r", **p)) { + + /* + SVF pathdata can exclude whitespace after L and M commands. + Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation + This allows us to use svg files output from gnuplot. + Also checks for relative MoveTo and LineTo (m and l). + 051106 Magnus Lundin, lundin@mlu.mine.nu + */ + if (strchr("LMlm", **p) && (isdigit(*(*p+1))||strchr("+-", *(*p+1)))) { + (*p)++; + } + else while(**p && !strchr(" ,()\t\n\r", **p)) { (*p)++; } - result = malloc((*p)-start+1); + result = (char*)malloc((*p)-start+1); memcpy(result,start,(*p)-start+1); result[(*p)-start] = 0; return result; } -void draw_conicto(drawer_t*draw, FPOINT* c, FPOINT* to) +void draw_conicTo(drawer_t*draw, FPOINT* c, FPOINT* to) { FPOINT* pos = &draw->pos; FPOINT c1,c2; @@ -51,36 +64,132 @@ void draw_conicto(drawer_t*draw, FPOINT* c, FPOINT* to) c1.y = (pos->y + 2 * c->y) / 3; c2.x = (2 * c->x + to->x) / 3; c2.y = (2 * c->y + to->y) / 3; - draw_cubicto(draw, &c1,&c2,to); + draw_cubicTo(draw, &c1,&c2,to); draw->pos = *to; } +/* convenience routine */ +static void draw_conicTo2(drawer_t*draw, double x1, double y1, double x2, double y2) +{ + FPOINT c1,c2; + c1.x = x1; + c1.y = y1; + c2.x = x2; + c2.y = y2; + draw_conicTo(draw, &c1, &c2); +} +/* convenience routine */ +static void draw_moveTo2(drawer_t*draw, double x, double y) +{ + FPOINT c; + c.x = x; c.y = y; + draw->moveTo(draw, &c); +} +/* convenience routine */ +static void draw_lineTo2(drawer_t*draw, double x, double y) +{ + FPOINT c; + c.x = x; c.y = y; + draw->lineTo(draw, &c); +} + +static float getFloat(const char** p) +{ + char* token = getToken(p); + float result = atof(token); + free(token); + return result; +} + void draw_string(drawer_t*draw, const char*string) { const char*p = string; while(*p) { char*token = getToken(&p); - if(!strncmp(token, "moveTo", 6)) { + if(!token) + break; + if (!*token) + { + free(token); + break; + } + if(!strncmp(token, "moveTo", 6) || + !strncmp(token, "M", 1) //svg + ) { FPOINT to; - to.x = atoi(getToken(&p)); - to.y = atoi(getToken(&p)); + to.x = getFloat(&p); + to.y = getFloat(&p); draw->moveTo(draw, &to); } - else if(!strncmp(token, "lineTo", 6)) { + else if(!strncmp(token, "lineTo", 6) || + !strncmp(token, "L", 1) //svg + ) { FPOINT to; - to.x = atoi(getToken(&p)); - to.y = atoi(getToken(&p)); + to.x = getFloat(&p); + to.y = getFloat(&p); draw->lineTo(draw, &to); } else if(!strncmp(token, "curveTo", 7) || !strncmp(token, "splineTo", 8)) { FPOINT mid,to; - mid.x = atoi(getToken(&p)); - mid.y = atoi(getToken(&p)); - to.x = atoi(getToken(&p)); - to.y = atoi(getToken(&p)); + mid.x = getFloat(&p); + mid.y = getFloat(&p); + to.x = getFloat(&p); + to.y = getFloat(&p); draw->splineTo(draw, &mid, &to); } + else if(!strncmp(token, "conicTo", 5)) { + FPOINT mid,to; + mid.x = getFloat(&p); + mid.y = getFloat(&p); + to.x = getFloat(&p); + to.y = getFloat(&p); + draw_conicTo(draw, &mid, &to); + } + else if(!strncmp(token, "circle", 6)) { + int mx,my,r; + double r2; + mx = getFloat(&p); + my = getFloat(&p); + r = getFloat(&p); + r2 = 0.70710678118654757*r; + draw_moveTo2(draw, mx, my-r); + draw_conicTo2(draw, mx+r2, my-r2, mx+r, my); + draw_conicTo2(draw, mx+r2, my+r2, mx, my+r); + draw_conicTo2(draw, mx-r2, my+r2, mx-r, my); + draw_conicTo2(draw, mx-r2, my-r2, mx, my-r); + } + else if(!strncmp(token, "box", 3)) { + int x1,y1,x2,y2; + x1 = getFloat(&p); + y1 = getFloat(&p); + x2 = getFloat(&p); + y2 = getFloat(&p); + draw_moveTo2(draw, x1, y1); + draw_lineTo2(draw, x1, y2); + draw_lineTo2(draw, x2, y2); + draw_lineTo2(draw, x2, y1); + draw_lineTo2(draw, x1, y1); + } + else if(!strncmp(token, "cubicTo", 5) || + !strncmp(token, "C", 1) //svg + ) { + FPOINT mid1,mid2,to; + mid1.x = getFloat(&p); + mid1.y = getFloat(&p); + mid2.x = getFloat(&p); + mid2.y = getFloat(&p); + to.x = getFloat(&p); + to.y = getFloat(&p); + draw_cubicTo(draw, &mid1, &mid2, &to); + } + else if(!strncmp(token, "z", 1) //svg + ) { + // ignore + } + else + fprintf(stderr, "drawer: Warning: unknown primitive '%s'\n", token); + free(token); } } @@ -105,29 +214,21 @@ struct cspline struct SPLINEPOINT end; }; -/* move the control point so that the spline runs through the original - control point */ -static void fixcp(struct qspline*s) -{ - struct SPLINEPOINT mid,dir; - mid.x = (s->end.x + s->start.x)/2; - mid.y = (s->end.y + s->start.y)/2; - dir.x = s->control.x - mid.x; - dir.y = s->control.y - mid.y; - s->control.x = mid.x + 2*dir.x; - s->control.y = mid.y + 2*dir.y; -} - -static inline struct SPLINEPOINT cspline_getpoint(struct cspline*s, double t) +static inline struct SPLINEPOINT cspline_getpoint(const struct cspline*s, double t) { struct SPLINEPOINT p; - p.x= s->end.x*t*t*t + 3*s->control2.x*t*t*(1-t) - + 3*s->control1.x*t*(1-t)*(1-t) + s->start.x*(1-t)*(1-t)*(1-t); - p.y= s->end.y*t*t*t + 3*s->control2.y*t*t*(1-t) - + 3*s->control1.y*t*(1-t)*(1-t) + s->start.y*(1-t)*(1-t)*(1-t); + double tt = t*t; + double ttt = tt*t; + double mt = (1-t); + double mtmt = mt*(1-t); + double mtmtmt = mtmt*(1-t); + p.x= s->end.x*ttt + 3*s->control2.x*tt*mt + + 3*s->control1.x*t*mtmt + s->start.x*mtmtmt; + p.y= s->end.y*ttt + 3*s->control2.y*tt*mt + + 3*s->control1.y*t*mtmt + s->start.y*mtmtmt; return p; } -static struct SPLINEPOINT qspline_getpoint(struct qspline*s, double t) +static struct SPLINEPOINT qspline_getpoint(const struct qspline*s, double t) { struct SPLINEPOINT p; p.x= s->end.x*t*t + 2*s->control.x*t*(1-t) + s->start.x*(1-t)*(1-t); @@ -135,88 +236,135 @@ static struct SPLINEPOINT qspline_getpoint(struct qspline*s, double t) return p; } -static struct SPLINEPOINT cspline_getderivative(struct cspline*s, double t) +static int approximate3(const struct cspline*s, struct qspline*q, int size, double quality2) { - struct SPLINEPOINT d; - d.x = s->end.x*(3*t*t) + 3*s->control2.x*(2*t-3*t*t) + - 3*s->control1.x*(1-4*t+3*t*t) + s->start.x*(-3+6*t-3*t*t); - d.y = s->end.y*(3*t*t) + 3*s->control2.y*(2*t-3*t*t) + - 3*s->control1.y*(1-4*t+3*t*t) + s->start.y*(-3+6*t-3*t*t); - return d; -} + unsigned int gran = 0; + unsigned int istep = 0x80000000; + unsigned int istart = 0; + int num = 0; + int level = 0; + + while(istart<0x80000000) + { + unsigned int iend = istart + istep; + double start = istart/(double)0x80000000; + double end = iend/(double)0x80000000; + struct qspline test; + double pos,qpos; + char left = 0,recurse=0; + int t; + int probes = 15; -static int approximate2(struct cspline*s, struct qspline*q, double quality, double start, double end, int max, int depth) -{ - int num=0; - struct SPLINEPOINT qr1,qr2,cr1,cr2; - double dist1,dist2; - double qquality = quality*quality; - int t; - int recurse = 0; - int probes = 15; - struct qspline test; - char left = 0; - test.start = cspline_getpoint(s, start); - test.control = cspline_getpoint(s, (start+end)/2); - test.end = cspline_getpoint(s, end); - fixcp(&test); - - if(start< 0.5) { - test.control = cspline_getderivative(s, start); - test.control.x *= (end-start)/2; - test.control.y *= (end-start)/2; - test.control.x += test.start.x; - test.control.y += test.start.y; - } else { - test.control = cspline_getderivative(s, end); - test.control.x *= -(end-start)/2; - test.control.y *= -(end-start)/2; - test.control.x += test.end.x; - test.control.y += test.end.y; - } + /* create simple approximation: a qspline which run's through the + qspline point at 0.5 */ + test.start = cspline_getpoint(s, start); + test.control = cspline_getpoint(s, (start+end)/2); + test.end = cspline_getpoint(s, end); + /* fix the control point: + move it so that the new spline does runs through it */ + test.control.x = -(test.end.x + test.start.x)/2 + 2*(test.control.x); + test.control.y = -(test.end.y + test.start.y)/2 + 2*(test.control.y); - for(t=0;tend.x*(3*qpos) + 3*s->control2.x*(2*pos-3*qpos) + + 3*s->control1.x*(1-4*pos+3*qpos) + s->start.x*(-3+6*pos-3*qpos); + test.control.y = s->end.y*(3*qpos) + 3*s->control2.y*(2*pos-3*qpos) + + 3*s->control1.y*(1-4*pos+3*qpos) + s->start.y*(-3+6*pos-3*qpos); + if(left) { + test.control.x *= (end-start)/2; + test.control.y *= (end-start)/2; + test.control.x += test.start.x; + test.control.y += test.start.y; + } else { + test.control.x *= -(end-start)/2; + test.control.y *= -(end-start)/2; + test.control.x += test.end.x; + test.control.y += test.end.y; + } - dx = qr1.x - cr1.x; - dy = qr1.y - cr1.y; - dist1 = dx*dx+dy*dy; +#define PROBES +#ifdef PROBES + /* measure the spline's accurancy, by taking a number of probes */ + for(t=0;tqquality) { - recurse=1;break; - } - qr2 = qspline_getpoint(&test, (1-pos)); - cr2 = cspline_getpoint(s, start+(1-pos)*(end-start)); + dx = qr1.x - cr1.x; + dy = qr1.y - cr1.y; + dist1 = dx*dx+dy*dy; + + if(dist1>quality2) { + recurse=1;break; + } + qr2 = qspline_getpoint(&test, (1-pos)); + cr2 = cspline_getpoint(s, start+(1-pos)*(end-start)); - dx = qr2.x - cr2.x; - dy = qr2.y - cr2.y; - dist2 = dx*dx+dy*dy; + dx = qr2.x - cr2.x; + dy = qr2.y - cr2.y; + dist2 = dx*dx+dy*dy; - if(dist2>qquality) { - recurse=1;break; + if(dist2>quality2) { + recurse=1;break; + } } - } +#else // quadratic error: *much* faster! - if(recurse && (end-start)>1.0/120 && max-depth > 0) { - /* quality is too bad, split it up recursively */ - num += approximate2(s, q, quality, start, (start+end)/2, max, depth+1); - q+=num; - max-=num; - num += approximate2(s, q, quality, (start+end)/2, end, max, depth+1); - return num; - } else { - *q = test; - return 1; + /* convert control point representation to + d*x^3 + c*x^2 + b*x + a */ + double dx,dy; + dx= s->end.x - s->control2.x*3 + s->control1.x*3 - s->start.x; + dy= s->end.y - s->control2.y*3 + s->control1.y*3 - s->start.y; + + /* we need to do this for the subspline between [start,end], not [0,1] + as a transformation of t->a*t+b does nothing to highest coefficient + of the spline except multiply it with a^3, we just need to modify + d here. */ + {double m = end-start; + dx*=m*m*m; + dy*=m*m*m; + } + + /* use the integral over (f(x)-g(x))^2 between 0 and 1 + to measure the approximation quality. + (it boils down to const*d^2) + */ + recurse = (dx*dx + dy*dy > quality2); +#endif + + if(recurse && istep>1 && size-level > num) { + istep >>= 1; + level++; + } else { + *q++ = test; + num++; + istart += istep; + while(!(istart & istep)) { + level--; + istep <<= 1; + } + } } + return num; } -void draw_cubicto(drawer_t*draw, FPOINT* control1, FPOINT* control2, FPOINT* to) +void draw_cubicTo(drawer_t*draw, FPOINT* control1, FPOINT* control2, FPOINT* to) { struct qspline q[128]; struct cspline c; + //double quality = 80; + double maxerror = 1;//(500-(quality*5)>1?500-(quality*5):1)/20.0; + int t,num; + c.start.x = draw->pos.x; c.start.y = draw->pos.y; c.control1.x = control1->x; @@ -225,11 +373,9 @@ void draw_cubicto(drawer_t*draw, FPOINT* control1, FPOINT* control2, FPOINT* t c.control2.y = control2->y; c.end.x = to->x; c.end.y = to->y; - double quality = 0.8; - double maxerror = (500-(quality*5)>1?500-(quality*5):1)/20.0; + + num = approximate3(&c, q, 128, maxerror*maxerror); - int num = approximate2(&c, q, maxerror, 0.0, 1.0, 128, 0); - int t; for(t=0;t