diff --git a/src/modules/Image/polyfill.c b/src/modules/Image/polyfill.c new file mode 100644 index 0000000000000000000000000000000000000000..3d3ba969162f112efd369dc37e6a3b8f004fea9e --- /dev/null +++ b/src/modules/Image/polyfill.c @@ -0,0 +1,667 @@ +#include "global.h" + +#include <unistd.h> +#include <math.h> + +#include "config.h" + +#include "stralloc.h" +#include "types.h" +#include "pike_macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "threads.h" +#include "builtin_functions.h" + +#include "image.h" + +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#undef POLYDEBUG + +/* +**! module Image +**! class image +*/ + +/* +**! method object polygone(array(int|float) ... curve) +**! fills an area with the current color +**! +**! returns the current object +**! arg array(int|float) curve +**! curve(s), <tt>({x1,y1,x2,y2,...,xn,yn})</tt>, +**! automatically closed. +**! +**! If any given curve is inside another, it +**! will make a hole. +**! +**! NOTE +**! This function is new (april-97) and rather untested. +**! see also: box, setcolor +*/ + +struct vertex_list +{ + struct vertex *above,*below; + float dx,dy; + struct vertex_list *next; + float xmin,xmax,yxmin,yxmax; /* temporary storage */ +}; + +struct vertex +{ + float x,y; + struct vertex *next; /* total list, sorted downwards */ + struct vertex_list *below,*above; /* childs */ + int done; +}; + +#define VY(V,X) ((V)->above->y+(V)->dy*((X)-(V)->above->x)) + +struct vertex *vertex_new(float x,float y,struct vertex **top) +{ + struct vertex *c; + while (*top && (*top)->y<y) top=&((*top)->next); + + if (*top && + (*top)->x==x && (*top)->y==y) return *top; /* found one */ + + c=malloc(sizeof(struct vertex)); + c->x=x; + c->y=y; + c->next=*top; + c->above=c->below=NULL; + c->done=0; + *top=c; + + return c; +} + +static void vertex_connect(struct vertex *above, + struct vertex *below) +{ + struct vertex_list *c,*d; + + if (below==above) return; + + c=malloc(sizeof(struct vertex_list)); + c->above=above; c->below=below; + c->next=above->below; + if (below->y!=above->y) + c->dx=(below->x-above->x)/(below->y-above->y); + else + c->dx=1e10; + if (below->x!=above->x) + c->dy=(below->y-above->y)/(below->x-above->x); + else + c->dy=1e10; + above->below=c; + + d=malloc(sizeof(struct vertex_list)); + d->above=above; d->below=below; + d->next=below->above; + d->dx=c->dx; + d->dy=c->dy; + below->above=d; +} + +static INLINE float vertex_xmax(struct vertex_list *v,float yp,float *ydest) +{ + if (v->dx>0.0) + if (v->below->y>yp+1.0) + return v->above->x+v->dx*((*ydest=(yp+1.0))-v->above->y); + else + return (*ydest=v->below->y),v->below->x; + else if (v->dx<0.0) + if (v->above->y<yp) + return v->above->x+v->dx*((*ydest=yp)-v->above->y); + else + return (*ydest=v->above->y),v->above->x; + else if (v->below->y>yp+1.0) + return (*ydest=yp+1.0),v->above->x; + else + return (*ydest=v->below->y),v->below->x; +} + +static INLINE float vertex_xmin(struct vertex_list *v,float yp,float *ydest) +{ + if (v->dx<0.0) + if (v->below->y>yp+1.0) + return v->above->x+v->dx*((*ydest=(yp+1.0))-v->above->y); + else + return (*ydest=v->below->y),v->below->x; + else if (v->dx>0.0) + if (v->above->y<yp) + return v->above->x+v->dx*((*ydest=yp)-v->above->y); + else + return (*ydest=v->above->y),v->above->x; + else if (v->above->y<yp) + return (*ydest=yp),v->above->x; + else + return (*ydest=v->above->y),v->above->x; +} + +static void add_vertices(struct vertex_list **first, + struct vertex_list *what, + float yp) +{ + struct vertex_list **ins,*c; + + while (what) + { + float xi,yi; + float xw = what->above->x; + float yw = what->above->y; + + ins=first; + +#ifdef POLYDEBUG + fprintf(stderr,"insert %g,%g - %g,%g %g xw==%g\n", + what->above->x,what->above->y, + what->below->x,what->below->y,what->dx,xw); +#endif + + + while (*ins) + { + if ((*ins)->dy==0 || what->dy==0) xi=xw; + else xi = (*ins)->above->x + (*ins)->dx*(yw-(*ins)->above->y); + yi=yw; + + if (xw<xi && what->dx<0) break; + if (xw>xi && what->dx>0) break; + + if (xw==xi && +/* ((*ins)->below->x>xw || (*ins)->above->x>xw) &&*/ + (*ins)->dx>what->dx) break; + +#ifdef POLYDEBUG + fprintf(stderr," after %g,%g - %g,%g %g i=%g,%g w=%g,%g\n", + (*ins)->above->x,(*ins)->above->y, + (*ins)->below->x,(*ins)->below->y,(*ins)->dx, + xi,yi,xw,yw); + +#endif + + ins=&((*ins)->next); + } + +#ifdef POLYDEBUG + if (*ins) + fprintf(stderr," before %g,%g - %g,%g %g i=%g,%g w=%g,%g\n", + (*ins)->above->x,(*ins)->above->y, + (*ins)->below->x,(*ins)->below->y,(*ins)->dy, + xi,yi,xw,yw); +#endif + + + c=malloc(sizeof(struct vertex_list)); + *c=*what; + c->next=*ins; + *ins=c; + + what=what->next; + } +} + +static void sub_vertices(struct vertex_list **first, + struct vertex *below, + float yp) +{ + struct vertex_list **ins,*c; + + ins=first; + + while (*ins) + if ((*ins)->below==below) + { + c=*ins; + *ins=(*ins)->next; + free(c); + } + else ins=&((*ins)->next); +} + +static void polygone_row_fill(float *buf, + float xmin,float xmax) +{ + int i; + int max; + if (floor(xmin)==floor(xmax)) + buf[(int)floor(xmin)]+=xmax-xmin; + else + { + buf[(int)floor(xmin)]+=1-(xmin-floor(xmin)); + max=floor(xmax); + for (i=(int)floor(xmin)+1; i<max; i++) buf[i]=1.0; + buf[(int)floor(xmax)]+=xmax-floor(xmax); + } +} + +static int polygone_row_vertices(float *buf, + struct vertex_list *v1, + struct vertex_list *v2, + float xmin, + float xmax, + float yp, + int fill) +{ + struct vertex_list *v; + int xofill=1; + float x; + +#ifdef POLYDEBUG + int i; + fprintf(stderr,"aa %g..%g fill %d\n",xmin,xmax,fill); +#endif + + fill=fill?-1:1; + + v=v1; + while (v) + { + if (v->xmin==xmin && v->yxmin==yp) + { + fill=-fill; +#ifdef POLYDEBUG + fprintf(stderr,"aa toggle fill: %d\n",fill); +#endif + } + v=v->next; + } + + if (fill<0) polygone_row_fill(buf,xmin,xmax); + + v=v1; + while (v!=v2) + { +#ifdef POLYDEBUG + fprintf(stderr,"aa << %g,%g-%g,%g %g,%g-%g,%g fill=%d xofill=%d\n", + v->above->x,v->above->y, + v->below->x,v->below->y, + v->xmin,v->yxmin, + v->xmax,v->yxmax, + fill,xofill); +#endif + + if (xmin!=xmax && v->xmin<xmax && v->xmax>xmin) + { +#define CALC_AREA(FILL,X,Y1,Y2,YP) \ + ((FILL)*(X)*( 1-0.5*((Y1)+(Y2))+(YP) )) +/* (fprintf(stderr,"area: %d*%g*%g y1=%g y2=%g yp=%g == %g\n", FILL,X,( 1-0.5*(Y1+Y2)+YP ),Y1,Y2,YP,((FILL)*(X)*( 1-0.5*((Y1)+(Y2))+(YP) ))), \*/ + + + if (floor(xmin)==floor(xmax)) + buf[(int)floor(xmin)]+= + CALC_AREA(fill*xofill,(xmax-xmin), VY(v,xmin),VY(v,xmax),yp); + else + { + buf[(int)floor(xmin)]+= + CALC_AREA(xofill*fill, (1+floor(xmin)-xmin), + VY(v,xmin),VY(v,1+floor(xmin)), yp); + + for (x=1+floor(xmin); x<floor(xmax); x+=1.0) + buf[(int)x]+= + CALC_AREA(fill*xofill,1.0, VY(v,x),VY(v,x+1.0),yp); + + buf[(int)floor(xmax)]+= + CALC_AREA(fill*xofill, xmax-floor(xmax), + VY(v,xmax),VY(v,floor(xmax)),yp); + } +#ifdef POLYDEBUG + fprintf(stderr,"aa "); + for (i=0; i<10; i++) fprintf(stderr,"%5.3f,",buf[i]); + fprintf(stderr,"\n"); +#endif + } + + if (v->xmin<=xmin && v->xmax>=xmax) + xofill=-xofill; + + v=v->next; + } +#ifdef POLYDEBUG + if (fill<0) fprintf(stderr,"aa fill is on\n"); +#endif + return fill<0; +} + +static void polygone_row(struct image *img, + float *buf, + struct vertex_list *vertices, + float yp, + int *pixmin,int *pixmax) +{ + struct vertex_list *v,*v1; + float xmax,xmin,nxmax; + int fill=0,i; + int ixmin,ixmax; + + xmin=1e10; + xmax=-1e10; + + v=vertices; + while (v) + { + v->xmin=vertex_xmin(v,yp,&v->yxmin); + v->xmax=vertex_xmax(v,yp,&v->yxmax); + if (v->xmin<xmin) xmin=v->xmin; + if (v->xmax>xmax) xmax=v->xmax; + v=v->next; + } + + *pixmin=ixmin=floor(xmin); + *pixmax=ixmax=ceil(xmax); + + if (ixmin<0) + { + *pixmin=ixmin=xmin=0; + v=vertices; + while (v) + { + if (v->xmin<0 && v->yxmin==yp) fill=!fill; + if (v->xmax<0 && v->yxmax==yp) fill=!fill; + v=v->next; + } + } + else if (ixmin>=img->xsize) { *pixmax=ixmin; return; } + if (ixmax>=img->xsize) *pixmax=ixmax=img->xsize; + else if (ixmax<0) { *pixmax=ixmin; return; } + + for (i=ixmin; i<ixmax; i++) buf[i]=0.0; + + v=vertices; + nxmax=xmax=v->xmax; + v1=v; + + v1=v=vertices; + +#ifdef POLYDEBUG + while (v) + fprintf(stderr," >> %g,%g-%g,%g %g,%g-%g,%g\n", + v->above->x,v->above->y, + v->below->x,v->below->y, + v->xmin,v->yxmin, + v->xmax,v->yxmax),v=v->next; + + v=vertices; +#endif + + for (;;) + { +#ifdef POLYDEBUG + fprintf(stderr,">>>again...xmin=%g xmax=%g nxmax=%g\n", + xmin,xmax,nxmax); +#endif + + /* skip uninteresting, ends < v->xmin */ + while (v1 && v1->xmax<xmin) v1=v1->next; + if (!v1) break; + + /* get next start or end */ + + v=v1; + nxmax=0; + xmax=1e10; + /* known: v->xmin>=xmin */ + while (v) + { +#ifdef POLYDEBUG + fprintf(stderr," ++ %g,%g-%g,%g %g,%g-%g,%g xmin=%g xmax=%g nxmax=%g -> ", + v->above->x,v->above->y, + v->below->x,v->below->y, + v->xmin,v->yxmin, + v->xmax,v->yxmax,xmin,xmax,nxmax); +#endif + if (v->xmin>xmin && v->xmin<xmax) xmax=v->xmin; + if (v->xmax>xmin && v->xmax<xmax) xmax=v->xmax; + if (v->xmax>nxmax) nxmax=v->xmax; + +#ifdef POLYDEBUG + fprintf(stderr,"xmin=%g xmax=%g nxmax=%g\n", + xmin,xmax,nxmax); +#endif + + v=v->next; + } + + if (xmax>nxmax) xmax=nxmax; + if (xmin>xmax) xmax=xmin; + if (xmax>ixmax) xmax=ixmax; + + if (xmin==nxmax) break; + + fill=polygone_row_vertices(buf,v1,v,xmin,xmax,yp,fill); + + if ((xmin=xmax)>=ixmax) break; + + while (v1 && v1->xmax<xmin) v1=v1->next; + if (!v1) break; + v=vertices; + while (v) + { + if (v->xmax==xmax && v->yxmax==yp) fill=!fill; + v=v->next; + } + + if (v && xmax==nxmax && v->xmin>nxmax) /* jump */ + { + xmin=v->xmin; + if (nxmax>=ixmax) break; + if (fill) + if (v->xmin>ixmax) + polygone_row_fill(buf,nxmax,ixmax); + else + polygone_row_fill(buf,nxmax,v->xmin); + } + } +} + +static void polygone_some(struct image *img, + struct vertex *top) +{ + struct vertex *tn; + struct vertex_list *vertices,*v,*vn,*v1; + struct vertex *next,*nextb; + float yp; + int i; + + + rgb_group *dest=img->img,rgb=img->rgb; + float *buf=alloca(sizeof(float)*img->xsize*2); + + if (!buf) error("out of stack, typ\n"); + for (i=0; i<img->xsize; i++) buf[i]=0.0; + + nextb=next=top; + + yp=floor(top->y); + vertices=NULL; + + if (yp>0) dest+=((int)yp)*img->xsize; + + while (next) + { +#ifdef POLYDEBUG +fprintf(stderr,"\n\nrow y=%g..%g\n",yp,yp+1); +#endif + +/* add new vertices if any */ + + while (next && next->y<=yp+1.0) + { + add_vertices(&vertices,next->below,yp); + next=next->next; + } + +/* POLYDEBUG */ + +#ifdef POLYDEBUG + fprintf(stderr,"vertices:\n"); + v1=vertices; + while (v1) + { + fprintf(stderr," (%g,%g)-(%g,%g) at ", + v1->above->x,v1->above->y, + v1->below->x,v1->below->y); + if (v1->above->y>yp) + fprintf(stderr,"(%g,%g), ",v1->above->x,v1->above->y); + else + fprintf(stderr,"(%g,%g), ",v1->above->x+v1->dx*(yp-v1->above->y),yp); + + if (v1->below->y<yp+1.0) + fprintf(stderr,"(%g,%g)\n",v1->below->x,v1->below->y); + else + fprintf(stderr,"(%g,%g)\n",v1->above->x+v1->dx*(1+yp-v1->above->y), + (yp+1.0)); + + v1=v1->next; + } +#endif + +/* find out what to antialias, and what to fill, and if to resort stuff */ + + if (yp>=0 && yp<img->ysize) + { + int xmin,xmax; + + polygone_row(img, buf, vertices,yp, &xmin,&xmax); + +#if POLYDEBUG + fprintf(stderr,"yp=%g dest=&%lx (&%lx..&%lx) xmin=%d xmax=%d; ", + yp,(int)dest,(int)img->img, + (int)img->img+img->xsize*img->ysize, + xmin,xmax); + for (i=xmin; i<xmax; i++) fprintf(stderr,"%5.3f,",buf[i]); + fprintf(stderr,"\n"); +#endif + + for (;xmin<xmax;xmin++) + dest[xmin].r=(unsigned char)(dest[xmin].r*(1.0-buf[xmin])+rgb.r*buf[xmin]), + dest[xmin].g=(unsigned char)(dest[xmin].g*(1.0-buf[xmin])+rgb.g*buf[xmin]), + dest[xmin].b=(unsigned char)(dest[xmin].b*(1.0-buf[xmin])+rgb.b*buf[xmin]); + dest+=img->xsize; + } + +/* remove done vertices if any */ + + yp+=1.0; + + while (nextb && nextb->y<=yp) + { + sub_vertices(&vertices,nextb,yp); + nextb=nextb->next; + } + } +} + +static INLINE void polygone_free(struct vertex *top) +{ + struct vertex_list *v,*vn; + struct vertex *tn; + + while (top) + { + v=top->above; + while (v) { vn=v->next; free(v); v=vn; } + v=top->below; + while (v) { vn=v->next; free(v); v=vn; } + tn=top->next; + free(top); + top=tn; + } +} + +static INLINE struct vertex *polygone_begin() +{ + return NULL; +} + +static INLINE struct vertex *polygone_add(struct vertex *top, + struct array *a, + int arg, + char* what) +{ + struct vertex *first,*last,*cur; + int n; + + for (n=0; n<a->size; n++) + if (a->item[n].type!=T_FLOAT && + a->item[n].type!=T_INT) + { + polygone_free(top); + error("Illegal argument %d to %s, array index %d is not int nor float\n",arg,what,n); + return NULL; + } + + if (a->size<6) return; /* no polygon with less then tree corners */ + +#define POINT(A,N) (((A)->item[N].type==T_FLOAT)?((A)->item[N].u.float_number):((float)((A)->item[N].u.integer))) + + last=first=vertex_new(POINT(a,0),POINT(a,1),&top); + + for (n=2; n+1<a->size; n+=2) + { + cur=vertex_new(POINT(a,n),POINT(a,n+1),&top); + if (cur->y<last->y) + vertex_connect(cur,last); + else if (cur->y>last->y) + vertex_connect(last,cur); + else if (cur->x<last->x) + vertex_connect(cur,last); + else + vertex_connect(last,cur); + + last=cur; + } + + if (cur->y<first->y) + vertex_connect(cur,first); + else if (cur->y>first->y) + vertex_connect(first,cur); + else if (cur->x<first->x) + vertex_connect(cur,first); + else + vertex_connect(first,cur); + + return top; +} + +void image_polygone(INT32 args) +{ + struct vertex *v; + int n; + + if (!THIS->img) + error("No image when calling image::polygone()\n"); + + v=polygone_begin(); + + n=args; + while (args) + { + if (sp[-1].type!=T_ARRAY) + { + polygone_free(v); + error("Illegal argument %d to image::polygone(), expected array\n",n); + } + v=polygone_add(v,sp[-1].u.array,n,"image::polygone()"); + n++; + args--; + pop_stack(); + } + + if (!v) return; /* no vertices */ + + polygone_some(THIS,v); + + polygone_free(v); + + THISOBJ->refs++; + push_object(THISOBJ); +}