X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fgfxtools.c;h=f6853daff5caa667b665277df2342cbc6d29b7de;hp=486e285401782d095ab0901e031fcaff662b891f;hb=395359a0831639fcc30aab4ea897e23ff8ab420c;hpb=6ac96fe8d278883885cf1b885eade140ab76afe3 diff --git a/lib/gfxtools.c b/lib/gfxtools.c index 486e285..f6853da 100644 --- a/lib/gfxtools.c +++ b/lib/gfxtools.c @@ -1,10 +1,10 @@ -/* gfxtools.c +/* gfxtools.c Various utility functions for dealing with gfxdevices. Part of the swftools package. - Copyright (c) 2005 Matthias Kramm + Copyright (c) 2005 Matthias Kramm This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,11 +28,15 @@ #include #include "gfxtools.h" #include "gfxfont.h" +#include "jpeg.h" +#include "q.h" typedef struct _linedraw_internal { gfxline_t*start; gfxline_t*next; + gfxcoord_t x0,y0; + char has_moveto; } linedraw_internal_t; static void linedraw_moveTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) @@ -40,12 +44,9 @@ static void linedraw_moveTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) linedraw_internal_t*i = (linedraw_internal_t*)d->internal; gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); l->type = gfx_moveTo; - if((int)((d->x * 5120) == (int)(x * 5120)) && - (int)((d->y * 5120) == (int)(y * 5120))) { - /* never mind- we're already there */ - return; - - } + i->has_moveto = 1; + i->x0 = x; + i->y0 = y; l->sx = l->sy = 0; d->x = l->x = x; d->y = l->y = y; @@ -59,14 +60,14 @@ static void linedraw_moveTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) static void linedraw_lineTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) { linedraw_internal_t*i = (linedraw_internal_t*)d->internal; - gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); - - if(!i->start) { - /* starts with a line, not with a moveto. That needs we first - need an explicit moveto to (0,0) */ - linedraw_moveTo(d, 0, 0); + if(!i->has_moveto) { + /* starts with a line, not with a moveto. As this is the first + entry in the list, this is probably *meant* to be a moveto */ + linedraw_moveTo(d, x, y); + return; } - + + gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); l->type = gfx_lineTo; d->x = l->x = x; d->y = l->y = y; @@ -81,18 +82,16 @@ static void linedraw_lineTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) static void linedraw_splineTo(gfxdrawer_t*d, gfxcoord_t sx, gfxcoord_t sy, gfxcoord_t x, gfxcoord_t y) { linedraw_internal_t*i = (linedraw_internal_t*)d->internal; - gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); - - if(!i->start) { - /* starts with a line, not with a moveto. That needs we first - need an explicit moveto to (0,0) */ - linedraw_moveTo(d, 0, 0); + if(!i->has_moveto) { + linedraw_moveTo(d, x, y); + return; } + gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); l->type = gfx_splineTo; - d->x = l->x = x; + d->x = l->x = x; d->y = l->y = y; - l->sx = sx; + l->sx = sx; l->sy = sy; l->next = 0; if(i->next) @@ -101,6 +100,16 @@ static void linedraw_splineTo(gfxdrawer_t*d, gfxcoord_t sx, gfxcoord_t sy, gfxco if(!i->start) i->start = l; } +static void linedraw_close(gfxdrawer_t*d) +{ + linedraw_internal_t*i = (linedraw_internal_t*)d->internal; + if(!i->has_moveto) + return; + linedraw_lineTo(d, i->x0, i->y0); + i->has_moveto = 0; + i->x0 = 0; + i->y0 = 0; +} static void* linedraw_result(gfxdrawer_t*d) { linedraw_internal_t*i = (linedraw_internal_t*)d->internal; @@ -119,6 +128,7 @@ void gfxdrawer_target_gfxline(gfxdrawer_t*d) d->moveTo = linedraw_moveTo; d->lineTo = linedraw_lineTo; d->splineTo = linedraw_splineTo; + d->close = linedraw_close; d->result = linedraw_result; } @@ -145,7 +155,7 @@ typedef struct cspline_t static void mkspline(qspline_abc_t*s, double x, double y, gfxline_t*l) { - /* + /* Form 1: x = t*t*l->x + 2*t*(1-t)*l->sx + (1-t)*(1-t)*x; Form 2: x = a*t*t + b*t + c */ @@ -182,7 +192,7 @@ static double get_spline_len(qspline_abc_t*s) double dy = s->ay*(2*i+1)*r2 + s->by*r; len += sqrt(dx*dx+dy*dy); } - /*printf("Spline from %f,%f to %f,%f has len %f (%f)\n", s->cx, s->cy, + /*printf("Spline from %f,%f to %f,%f has len %f (%f)\n", s->cx, s->cy, s->cx + s->bx + s->ax, s->cy + s->by + s->ay, len, sqrt((s->bx + s->ax)*(s->bx + s->ax) + (s->by + s->ay)*(s->by + s->ay)) @@ -197,14 +207,34 @@ void gfxtool_draw_dashed_line(gfxdrawer_t*d, gfxline_t*line, float*r, float phas double x=0,y=0; double linepos,nextpos; char on; - int apos; + int apos=0; if(line && line->type != gfx_moveTo) { fprintf(stderr, "gfxtool: outline doesn't start with a moveTo"); return; } - if(!r || r[0]<0 || phase<0) { - fprintf(stderr, "gfxtool: invalid dashes"); + + int i; + double dashlen=0; + for(i=0;r[i]>=0;i++) { + dashlen+=r[i]; + } + if(!r || (r[0]<=0 && r[0]>-0.01) || dashlen<0.001) { + // no dashing. just draw the thing + while(line) { + if(line->type == gfx_moveTo) { + d->moveTo(d, line->x, line->y); + } else if(line->type == gfx_lineTo) { + d->lineTo(d, line->x, line->y); + } else if(line->type == gfx_splineTo) { + d->splineTo(d, line->sx, line->sy, line->x, line->y); + } + line = line->next; + } + return; + } + if(r[0]<0 || phase<0) { + fprintf(stderr, "gfxtool: invalid (negative) dashes: %f, phase=%f\n", r[0], phase); return; } @@ -319,20 +349,31 @@ gfxline_t * gfxline_clone(gfxline_t*line) } return dest; } + +static char splineIsStraight(double x, double y, gfxline_t*l) +{ + if(l->type == gfx_moveTo) + return 0; + if(l->type == gfx_lineTo) + return 1; + double dx = l->x-x; + double dy = l->y-y; + double sx = l->sx-x; + double sy = l->sy-y; + if(fabs(dx*sy - dy*sx) < 0.000001 && (dx*sx + dy*sy) >= 0) { + return 1; + } + return 0; +} + void gfxline_optimize(gfxline_t*line) { gfxline_t*l = line; /* step 1: convert splines to lines, where possible */ double x=0,y=0; while(l) { - if(l->type == gfx_splineTo) { - double dx = l->x-x; - double dy = l->y-y; - double sx = l->sx-x; - double sy = l->sy-y; - if(fabs(dx*sy - dy*sx) < 0.000001 && (dx*sx + dy*sy) >= 0) { - l->type = gfx_lineTo; - } + if(l->type == gfx_splineTo && splineIsStraight(x,y,l)) { + l->type = gfx_lineTo; } x = l->x; y = l->y; @@ -442,7 +483,7 @@ static int approximate3(const cspline_t*s, qspline_t*q, int size, double quality unsigned int istart = 0; int num = 0; int level = 0; - + while(istart<0x80000000) { unsigned int iend = istart + istep; @@ -467,14 +508,14 @@ static int approximate3(const cspline_t*s, qspline_t*q, int size, double quality /* depending on where we are in the spline, we either try to match the left or right tangent */ - if(start<0.5) + if(start<0.5) left=1; /* get derivative */ pos = left?start:end; qpos = pos*pos; - test.control.x = s->end.x*(3*qpos) + 3*s->control2.x*(2*pos-3*qpos) + + test.control.x = s->end.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) + + 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; @@ -519,12 +560,12 @@ static int approximate3(const cspline_t*s, qspline_t*q, int size, double quality } #else // quadratic error: *much* faster! - /* convert control point representation to + /* convert control point representation to d*x^3 + c*x^2 + b*x + a */ 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] + + /* 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. */ @@ -532,9 +573,9 @@ static int approximate3(const cspline_t*s, qspline_t*q, int size, double quality 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. + to measure the approximation quality. (it boils down to const*d^2) */ recurse = (dx*dx + dy*dy > quality2); #endif @@ -580,7 +621,7 @@ void gfxdraw_cubicTo(gfxdrawer_t*draw, double c1x, double c1y, double c2x, doubl c.control2.y = c2y; c.end.x = x; c.end.y = y; - + num = approximate3(&c, q, 128, maxerror); for(t=0;txmin > box1->xmin) @@ -656,20 +704,6 @@ gfxbbox_t gfxline_getbbox(gfxline_t*line) return bbox; } -void gfxline_dump(gfxline_t*line, FILE*fi, char*prefix) -{ - while(line) { - if(line->type == gfx_moveTo) { - fprintf(fi, "%smoveTo %.2f %.2f\n", prefix, line->x, line->y); - } else if(line->type == gfx_lineTo) { - fprintf(fi, "%slineTo %.2f %.2f\n", prefix, line->x, line->y); - } else if(line->type == gfx_splineTo) { - fprintf(fi, "%ssplineTo (%.2f %.2f) %.2f %.2f\n", prefix, line->sx, line->sy, line->x, line->y); - } - line = line->next; - } -} - gfxline_t* gfxline_append(gfxline_t*line1, gfxline_t*line2) { gfxline_t*l = line1;; @@ -701,8 +735,8 @@ void gfxline_transform(gfxline_t*line, gfxmatrix_t*matrix) void gfxmatrix_dump(gfxmatrix_t*m, FILE*fi, char*prefix) { - fprintf(fi, "%f %f | %f\n", m->m00, m->m10, m->tx); - fprintf(fi, "%f %f | %f\n", m->m01, m->m11, m->ty); + fprintf(fi, "%s%f %f | %f\n", prefix, m->m00, m->m10, m->tx); + fprintf(fi, "%s%f %f | %f\n", prefix, m->m01, m->m11, m->ty); } void gfxmatrix_transform(gfxmatrix_t*m, double* v, double*dest) @@ -769,12 +803,23 @@ char gfxfontlist_hasfont(gfxfontlist_t*list, gfxfont_t*font) } return 0; } -gfxfontlist_t*gfxfontlist_addfont(gfxfontlist_t*list, gfxfont_t*font) +void*gfxfontlist_getuserdata(gfxfontlist_t*list, const char*id) +{ + gfxfontlist_t*l = list; + while(l) { + if(!strcmp((char*)l->font->id, id)) { + return l->user; + } + l = l->next; + } + return 0; +} +gfxfontlist_t*gfxfontlist_addfont2(gfxfontlist_t*list, gfxfont_t*font, void*user) { gfxfontlist_t*last=0,*l = list; while(l) { last = l; - if(!strcmp((char*)l->font->id, font->id)) { + if(l->font == font) { return list; // we already know this font } l = l->next; @@ -784,6 +829,7 @@ gfxfontlist_t*gfxfontlist_addfont(gfxfontlist_t*list, gfxfont_t*font) } l = (gfxfontlist_t*)rfx_calloc(sizeof(gfxfontlist_t)); l->font = font; + l->user = user; l->next = 0; if(last) { last->next = l; @@ -792,6 +838,10 @@ gfxfontlist_t*gfxfontlist_addfont(gfxfontlist_t*list, gfxfont_t*font) return l; } } +gfxfontlist_t*gfxfontlist_addfont(gfxfontlist_t*list, gfxfont_t*font) +{ + return gfxfontlist_addfont2(list, font, 0); +} void gfxfontlist_free(gfxfontlist_t*list, char deletefonts) { gfxfontlist_t*l = list; @@ -806,7 +856,7 @@ void gfxfontlist_free(gfxfontlist_t*list, char deletefonts) } } -gfxline_t*gfxline_makerectangle(int x1,int y1,int x2, int y2) +gfxline_t*gfxline_makerectangle(double x1,double y1,double x2, double y2) { gfxline_t* line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t)*5); line[0].x = x1;line[0].y = y1;line[0].type = gfx_moveTo;line[0].next = &line[1]; @@ -817,6 +867,107 @@ gfxline_t*gfxline_makerectangle(int x1,int y1,int x2, int y2) return line; } +gfxline_t*gfxline_makecircle(double x,double y,double rx, double ry) +{ + double C1 = 0.2930; + double C2 = 0.4140; + double begin = 0.7070; + gfxline_t** line = (gfxline_t**)rfx_calloc(sizeof(gfxline_t*)*9); + int t; + for(t=0;t<9;t++) { + line[t] = rfx_calloc(sizeof(gfxline_t)); + } + line[0]->type = gfx_moveTo; + line[0]->x = x+begin*rx; + line[0]->y = y+begin*ry; + for(t=1;t<9;t++) { + line[t-1]->next = line[t]; + line[t]->type = gfx_splineTo; + } + line[8]->next = 0; +#define R(nr,cx,cy,mx,my) \ + line[nr]->sx = line[nr-1]->x + (cx); \ + line[nr]->sy = line[nr-1]->y + (cy); \ + line[nr]->x = line[nr]->sx + (mx); \ + line[nr]->y = line[nr]->sy + (my); + R(1, -C1*rx, C1*ry, -C2*rx, 0); + R(2, -C2*rx, 0, -C1*rx, -C1*ry); + R(3, -C1*rx, -C1*ry, 0, -C2*ry); + R(4, 0, -C2*ry, C1*rx, -C1*ry); + R(5, C1*rx, -C1*ry, C2*rx, 0); + R(6, C2*rx, 0, C1*rx, C1*ry); + R(7, C1*rx, C1*ry, 0, C2*ry); + R(8, 0, C2*ry, -C1*rx, C1*ry); + gfxline_t*l = line[0]; + free(line); + return l; +} + +gfxbbox_t* gfxline_isrectangle(gfxline_t*_l) +{ + if(!_l) + return 0; + + gfxline_t*l = gfxline_clone(_l); + gfxline_optimize(l); + + double x1,x2,y1,y2; + int xc=0,yc=0; + char corners=0; + + char prev=0; + char fail=0; + for(;l; l=l->next) { + double x = l->x; + double y = l->y; + + char top=0,left=0; + + if(xc==2 && x!=x1 && x!=x2) {fail=1;break;} + else if(xc>=1 && x==x1) {left=0;} + else if(xc==2 && x==x2) {left=1;} + else if(xc==1 && x!=x1) {x2 = x; xc=2; left=1;} + else if(xc==0) {x1 = x; xc=1;left=0;} + else {fprintf(stderr, "Internal error in rectangle detection\n");} + + if(yc==2 && y!=y1 && y!=y2) {fail=1;break;} + else if(yc>=1 && y==y1) {top=0;} + else if(yc==2 && y==y2) {top=1;} + else if(yc==1 && y!=y1) {y2 = y; yc=2; top=1;} + else if(yc==0) {y1 = y; yc=1;top=0;} + else {fprintf(stderr, "Internal error in rectangle detection\n");} + + char pos=top<<1|left; + + if((pos^prev)==3) { + /* diagonal lines not allowed */ + fail=1;break; + } + prev = pos; + + /* no corner except the first one may be touched twice */ + if(pos && (corners & 1<xmin = x1; r->ymin = y1; + r->xmax = x2; r->ymax = y2; + return r; +} + void gfximage_transform(gfximage_t*img, gfxcxform_t*cxform) { int t; @@ -847,3 +998,202 @@ void gfximage_transform(gfximage_t*img, gfxcxform_t*cxform) pixel->a = a; } } +void gfxline_dump(gfxline_t*line, FILE*fi, char*prefix) +{ + while(line) { + if(line->type == gfx_moveTo) { + fprintf(fi, "%smoveTo %.2f %.2f\n", prefix, line->x, line->y); + } else if(line->type == gfx_lineTo) { + fprintf(fi, "%slineTo %.2f %.2f\n", prefix, line->x, line->y); + } else if(line->type == gfx_splineTo) { + fprintf(fi, "%ssplineTo (%.2f %.2f) %.2f %.2f\n", prefix, line->sx, line->sy, line->x, line->y); + } + line = line->next; + } +} + +static char gfxpoint_equals(void*c1, void*c2) +{ + return !memcmp(c1, c2, sizeof(gfxpoint_t)); +} +static unsigned int gfxpoint_hash(void*c) +{ + return string_hash3(c, sizeof(gfxpoint_t)); +} +static void* gfxpoint_clone(void*c) +{ + void*n = malloc(sizeof(gfxpoint_t)); + memcpy(n, c, sizeof(gfxpoint_t)); + return n; +} +static void gfxpoint_destroy(void*c) +{ + free(c); +} +static type_t gfxpoint_type = { + hash: (hash_func)gfxpoint_hash, + equals: (equals_func)gfxpoint_equals, + dup: (dup_func)gfxpoint_clone, + free: (free_func)gfxpoint_destroy, +}; + +gfxline_t* gfxline_restitch(gfxline_t*line) +{ + dict_t*ff = dict_new2(&gfxpoint_type); + dict_t*rev = dict_new2(&gfxpoint_type); + + gfxline_t*prev=0; + while(line) { + gfxline_t*next = line->next; + if(line->type == gfx_moveTo && (line->next && line->next->type != gfx_moveTo)) { + gfxpoint_t xy = {line->x, line->y}; + dict_put(ff, &xy, line); + prev = line; + } else if(!line->next || line->next->type == gfx_moveTo) { + if(prev) { + gfxpoint_t xy = {line->x, line->y}; + dict_put(rev, &xy, prev); + line->next = 0; + } + } + line = next; + } + + gfxpoint_t pos = {0,0}; + + gfxline_t*result = 0; + gfxline_t*last = 0; + + char first = 1; + while(dict_count(ff)) { + char reverse = 0, stitch = 1; + gfxline_t*l = dict_lookup(ff, &pos); + if(l) { + char d = dict_del2(ff,&pos,l);assert(d); + } else { + l = dict_lookup(rev, &pos); + if(l) { + reverse = 1; + char d = dict_del2(rev,&pos,l);assert(d); + } + } + if(!l) { + /* try to find *any* entry. this is costly, but + doesn't happen too often */ + stitch = 0; + DICT_ITERATE_DATA(ff, gfxline_t*, l2) { + l = l2; + break; + } + assert(l); + gfxpoint_t xy = {l->x,l->y}; + char d = dict_del2(ff,&xy,l);assert(d); + } + + gfxline_t*end = l; + if(!reverse) { + while(end->next) end = end->next; + pos.x = end->x; + pos.y = end->y; + char d = dict_del2(rev,&pos,l);assert(d); + } else { + l = gfxline_reverse(l); + pos.x = end->x; + pos.y = end->y; + char d = dict_del2(ff,&pos,end);assert(d); + } + + assert(l->type == gfx_moveTo); + if(stitch && !first) { + /* cut away the moveTo */ + gfxline_t*next = l->next; + free(l); + l = next; + } + + if(!last) { + result = l; + last = end; + } else { + last->next = l; + last = end; + } + first = 0; + } + dict_destroy(ff); + dict_destroy(rev); + return result; +} + +gfxline_t* gfxline_reverse(gfxline_t*line) +{ + gfxline_t*b = 0; + while(line) { + gfxline_t*next = line->next; + if(next && next->type != gfx_moveTo) { + line->type = next->type; + line->sx = next->sx; + line->sy = next->sy; + } else { + line->type = gfx_moveTo; + } + line->next = b; + b = line; + line = next; + } + return b; +} + +void gfxgradient_destroy(gfxgradient_t*gradient) +{ + while(gradient) { + gfxgradient_t*next = gradient->next; + free(gradient); + gradient = next; + } +} + +gfxparams_t* gfxparams_new() +{ + return (gfxparams_t*)rfx_calloc(sizeof(gfxparams_t)); +} + +void gfxparams_store(gfxparams_t*params, const char*key, const char*value) +{ + gfxparam_t*o = params->params; + while(o) { + if(!strcmp(key, o->key)) { + /* overwrite old value */ + free((void*)o->value); + o->value = strdup(value); + return; + } + o = o->next; + } + gfxparam_t*p = (gfxparam_t*)malloc(sizeof(gfxparam_t)); + p->key = strdup(key); + p->value = strdup(value); + p->next = 0; + + if(params->last) { + params->last->next = p; + params->last = p; + } else { + params->params = p; + params->last = p; + } +} + +void gfxparams_free(gfxparams_t*params) +{ + gfxparam_t*p = params->params; + while(p) { + gfxparam_t*next = p->next; + free((void*)p->key); + if(p->value) free((void*)p->value); + free(p); + p = next; + } + free(params); +} +