pike.git / src / modules / Image / layers.c

version» Context lines:

pike.git/src/modules/Image/layers.c:1:   /*   **! module Image   **! note - **! $Id: layers.c,v 1.1 1999/04/17 19:41:58 mirar Exp $ + **! $Id: layers.c,v 1.2 1999/04/18 22:04:20 mirar Exp $   **! class Layer   */      #include "global.h"   #include <config.h>    - RCSID("$Id: layers.c,v 1.1 1999/04/17 19:41:58 mirar Exp $"); + RCSID("$Id: layers.c,v 1.2 1999/04/18 22:04:20 mirar Exp $");      #include "config.h"      #include "stralloc.h"   #include "pike_macros.h"   #include "object.h"   #include "constants.h"   #include "interpret.h"   #include "svalue.h"   #include "array.h"
pike.git/src/modules/Image/layers.c:45:   typedef void lm_row_func(rgb_group *s,    rgb_group *l,    rgb_group *d,    rgb_group *sa,    rgb_group *la, /* may be NULL */    rgb_group *da,    int len,    float alpha);       + #define SNUMPIXS 64 /* pixels in short-stroke buffer */ +    struct layer   {    int xsize; /* underlaying image size */    int ysize;       int xoffs,yoffs; /* clip offset */       struct object *image; /* image object */    struct object *alpha; /* alpha object or null */       struct image *img; /* image object storage */    struct image *alp; /* alpha object storage */       float alpha_value; /* overall alpha value (1.0=opaque) */       rgb_group fill; /* fill color ("outside" the layer) */    rgb_group fill_alpha; /* fill alpha */    -  +  rgb_group sfill[SNUMPIXS]; /* pre-calculated rows */ +  rgb_group sfill_alpha[SNUMPIXS]; +     int tiled; /* true if tiled */       lm_row_func *row_func;/* layer mode */ -  +  int optimize_alpha;   };      #define THIS ((struct layer *)(fp->current_storage))   #define THISOBJ (fp->current_object)      static void lm_normal(rgb_group *s,rgb_group *l,rgb_group *d,   rgb_group *sa,rgb_group *la,rgb_group *da,    int len,float alpha);   static void lm_dissolve(rgb_group *s,rgb_group *l,rgb_group *d,   rgb_group *sa,rgb_group *la,rgb_group *da,
pike.git/src/modules/Image/layers.c:130:   rgb_group *sa,rgb_group *la,rgb_group *da,    int len,float alpha);   static void lm_replace(rgb_group *s,rgb_group *l,rgb_group *d,   rgb_group *sa,rgb_group *la,rgb_group *da,    int len,float alpha);      struct layer_mode_desc   {    char *name;    lm_row_func *func; +  int optimize_alpha; /* alpha 0 -> skip layer */    struct pike_string *ps;   } layer_mode[]=   { -  {"normal", lm_normal, NULL }, - /* {"dissolve", lm_dissolve, NULL }, */ - /* {"behind", lm_behind, NULL }, */ - /* {"multiply", lm_multiply, NULL }, */ - /* {"screen", lm_screen, NULL }, */ - /* {"overlay", lm_overlay, NULL }, */ - /* {"difference", lm_difference, NULL }, */ - /* {"addition", lm_addition, NULL }, */ - /* {"subtract", lm_subtract, NULL }, */ - /* {"darken", lm_darken, NULL }, */ - /* {"lighten", lm_lighten, NULL }, */ - /* {"hue", lm_hue, NULL }, */ - /* {"saturation", lm_saturation, NULL }, */ - /* {"color", lm_color, NULL }, */ - /* {"value", lm_value, NULL }, */ - /* {"divide", lm_divide, NULL }, */ - /* {"erase", lm_erase, NULL }, */ - /* {"replace", lm_replace, NULL }, */ +  {"normal", lm_normal, 1, NULL }, + /* {"dissolve", lm_dissolve, 1, NULL }, */ + /* {"behind", lm_behind, 1, NULL }, */ + /* {"multiply", lm_multiply, 1, NULL }, */ + /* {"screen", lm_screen, 1, NULL }, */ + /* {"overlay", lm_overlay, 1, NULL }, */ + /* {"difference", lm_difference, 1, NULL }, */ + /* {"addition", lm_addition, 1, NULL }, */ + /* {"subtract", lm_subtract, 1, NULL }, */ + /* {"darken", lm_darken, 1, NULL }, */ + /* {"lighten", lm_lighten, 1, NULL }, */ + /* {"hue", lm_hue, 1, NULL }, */ + /* {"saturation", lm_saturation, 1, NULL }, */ + /* {"color", lm_color, 1, NULL }, */ + /* {"value", lm_value, 1, NULL }, */ + /* {"divide", lm_divide, 1, NULL }, */ + /* {"erase", lm_erase, 1, NULL }, */ + /* {"replace", lm_replace, 1, NULL }, */   } ;      #define LAYER_MODES ((int)NELEM(layer_mode))    -  + /*** layer helpers ****************************************/ +  + static INLINE void smear_color(rgb_group *d,rgb_group s,int len) + { +  while (len--) +  *(d++)=s; + } +    /*** layer object : init and exit *************************/      static void init_layer(struct object *dummy)   {    THIS->xsize=0;    THIS->ysize=0;    THIS->xoffs=0;    THIS->yoffs=0;    THIS->image=NULL;    THIS->alpha=NULL;    THIS->img=NULL;    THIS->alp=NULL;    THIS->fill=black;    THIS->fill_alpha=black;    THIS->tiled=0;    THIS->alpha_value=1.0;    THIS->row_func=lm_normal; -  +  THIS->optimize_alpha=1; +  +  smear_color(THIS->sfill,THIS->fill,SNUMPIXS); +  smear_color(THIS->sfill_alpha,THIS->fill_alpha,SNUMPIXS);   }      static void free_layer(struct layer *l)   {    if (THIS->image) free_object(THIS->image);    if (THIS->alpha) free_object(THIS->alpha);    THIS->image=NULL;    THIS->alpha=NULL;    THIS->img=NULL;    THIS->alp=NULL;
pike.git/src/modules/Image/layers.c:222:    free_object(THIS->alpha);    THIS->alpha=NULL;    THIS->alp=NULL;       if (args>=1)    if ( sp[-args].type!=T_OBJECT )    {    if (sp[-args].type!=T_INT ||    sp[-args].u.integer!=0)    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_image",1, -  "object(image)|int(0)"); +  "object(Image)|int(0)");    }    else if ((img=(struct image*)    get_storage(sp[-args].u.object,image_program)))    {    THIS->image=sp[-args].u.object;    add_ref(THIS->image);    THIS->img=img;    THIS->xsize=img->xsize;    THIS->ysize=img->ysize;    }    else    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_image",1, -  "object(image)|int(0)"); +  "object(Image)|int(0)");       if (args>=2)    if ( sp[1-args].type!=T_OBJECT )    {    if (sp[1-args].type!=T_INT ||    sp[1-args].u.integer!=0)    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_image",2, -  "object(image)|int(0)"); +  "object(Image)|int(0)");    }    else if ((img=(struct image*)    get_storage(sp[1-args].u.object,image_program)))    {    if (THIS->img &&    (img->xsize!=THIS->xsize ||    img->ysize!=THIS->ysize))    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_image",2,    "image of same size");    if (!THIS->img)    {    THIS->xsize=img->xsize;    THIS->ysize=img->ysize;    }    THIS->alpha=sp[1-args].u.object;    add_ref(THIS->alpha);    THIS->alp=img;    }    else    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_image",2, -  "object(image)|int(0)"); +  "object(Image)|int(0)");       pop_n_elems(args);    ref_push_object(THISOBJ);   }      static void image_layer_image(INT32 args)   {    pop_n_elems(args);    if (THIS->image)    ref_push_object(THIS->image);
pike.git/src/modules/Image/layers.c:459:    int i;    if (args!=1)    SIMPLE_TOO_FEW_ARGS_ERROR("Image.Layer->set_mode",1);    if (sp[-args].type!=T_STRING)    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_mode",1,"string");       for (i=0; i<LAYER_MODES; i++)    if (sp[-args].u.string==layer_mode[i].ps)    {    THIS->row_func=layer_mode[i].func; +  THIS->optimize_alpha=layer_mode[i].optimize_alpha;    pop_n_elems(args);    ref_push_object(THISOBJ);    return;    }    -  SIMPLE_BAD_ARG_ERROR("Image.Layer->set_mode",1,"string"); +  SIMPLE_BAD_ARG_ERROR("Image.Layer->set_mode",1,"existing mode");   }      static void image_layer_mode(INT32 args)   {    int i;    pop_n_elems(args);       for (i=0; i<LAYER_MODES; i++)    if (THIS->row_func==layer_mode[i].func)    {
pike.git/src/modules/Image/layers.c:503:   {    if (!args)    SIMPLE_TOO_FEW_ARGS_ERROR("Image.Layer->set_fill",1);       if (sp[-args].type==T_INT && !sp[-args].u.integer)    THIS->fill=black;    else    if (!image_color_arg(-args,&(THIS->fill)))    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_fill",1,"color");    +  smear_color(THIS->sfill,THIS->fill,SNUMPIXS); +  +  THIS->fill_alpha=white;    if (args>1)    if (sp[1-args].type==T_INT && !sp[1-args].u.integer) -  THIS->fill_alpha=white; +  ; /* white is good */    else    if (!image_color_arg(1-args,&(THIS->fill_alpha))) -  +  { +  smear_color(THIS->sfill_alpha,THIS->fill_alpha,SNUMPIXS);    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_fill",2,"color"); -  else -  THIS->fill_alpha=white; +  } +  smear_color(THIS->sfill_alpha,THIS->fill_alpha,SNUMPIXS);    -  + #if 0 +  { +  int i; +  for (i=0; i<SNUMPIXS; i++) +  fprintf(stderr,"#%02x%02x%02x ",THIS->sfill_alpha[i].r,THIS->sfill_alpha[i].g,THIS->sfill_alpha[i].b); +  fprintf(stderr,"\n"); +  for (i=0; i<SNUMPIXS; i++) +  fprintf("stderr,#%02x%02x%02x ",THIS->sfill[i].r,THIS->sfill[i].g,THIS->sfill[i].b); +  fprintf(stderr,"\n"); +  } + #endif +     pop_n_elems(args);    ref_push_object(THISOBJ);   }      static void image_layer_fill(INT32 args)   {    pop_n_elems(args);    _image_make_rgb_color(THIS->fill.r,THIS->fill.g,THIS->fill.b);   }   
pike.git/src/modules/Image/layers.c:720:       image_layer_set_image(2);       pop_n_elems(args);    }    else if (sp[-args].type==T_OBJECT)    {    if (args>2)    {    image_layer_set_mode(args-2); +  pop_stack();    args=2;    }    image_layer_set_image(args);    pop_stack();    }    else    SIMPLE_BAD_ARG_ERROR("Image.Layer",1,"mapping|int|Image.Image");   }      /*** layer object *****************************************/
pike.git/src/modules/Image/layers.c:766:    return;    }    }    SIMPLE_BAD_ARG_ERROR("Image.Colortable->cast",1,    "string(\"mapping\"|\"array\"|\"string\")");      }      /*** layer helpers ************************************/    - static INLINE void smear_color(rgb_group *d,rgb_group s,int len) - { -  while (len--) -  *(d++)=s; - } -  +    #define ALPHA_METHOD_INT      #ifdef ALPHA_METHOD_INT      #define CCUT(Z) ((COLORTYPE)((Z)/COLORMAX))      #define COMBINE_ALPHA_SUM(aS,aL) \    CCUT((COLORMAX*(int)(aL))+(COLORMAX-(int)(aL))*(aS))   #define COMBINE_ALPHA_SUM_V(aS,aL,V) \    COMBINE_ALPHA_SUM(aS,(aL)*(V))
pike.git/src/modules/Image/layers.c:835:    if (alpha==0.0) /* optimized */    {    MEMCPY(s,d,sizeof(rgb_group)*len);    MEMCPY(sa,da,sizeof(rgb_group)*len);    return;    }    else if (alpha==1.0)    {    if (!la) /* no layer alpha => full opaque */    { -  MEMCPY(s,d,sizeof(rgb_group)*len); +  MEMCPY(d,l,sizeof(rgb_group)*len);    smear_color(da,white,len);    }    else    while (len--)    {   #define ALPHA_ADD(S,L,D,SA,LA,DA,C) \    if (!LA->C) d->C=S->C,DA->C=SA->C; \    else if (!SA->C) D->C=l->C,DA->C=LA->C; \    else if (LA->C==COLORMAX) D->C=l->C,DA->C=LA->C; \    else \
pike.git/src/modules/Image/layers.c:931:    }    return;    }   }         /*** the add-layer function ***************************/      static void INLINE img_lay_first_line(struct layer *l,    int xoffs,int xsize, -  int y, +  int y, /* in _this_ layer */    rgb_group *d,rgb_group *da)   {    if (!l->tiled)    {    rgb_group *s,*sa;    int len;    -  if (y<l->yoffs || -  y>=l->yoffs+l->ysize || +  if (y<0 || +  y>=l->ysize ||    l->xoffs+l->xsize<xoffs ||    l->xoffs>xoffs+xsize) /* outside */    {    smear_color(d,l->fill,xsize);    smear_color(da,l->fill_alpha,xsize);    return;    }       if (l->img) s=l->img->img+y*l->xsize; else s=NULL;    if (l->alp) sa=l->alp->img+y*l->xsize; else sa=NULL;
pike.git/src/modules/Image/layers.c:992:    else smear_color(d,l->fill,xsize);    if (sa) MEMCPY(da,sa,xsize*sizeof(rgb_group));    else smear_color(da,white,xsize);    }    return;    }    else    {    rgb_group *s,*sa;    -  y-=l->yoffs; +     y%=l->ysize;    if (y<0) y+=l->ysize;       if (l->img) s=l->img->img+y*l->xsize;    else smear_color(d,l->fill,xsize),s=NULL;       if (l->alp) sa=l->alp->img+y*l->xsize;    else smear_color(da,white,xsize),sa=NULL;       xoffs-=l->xoffs; /* position in source */    xoffs%=l->xsize;    if (xoffs<0) xoffs+=l->xsize;    if (xoffs)    {    int len=l->xsize-xoffs;    if (len>l->xsize) len=l->xsize; -  fprintf(stderr,"len=%d xoffs=%d\n",len,xoffs); +     if (s) MEMCPY(d,s+xoffs,len*sizeof(rgb_group));    if (sa) MEMCPY(da,sa+xoffs,len*sizeof(rgb_group));    da+=len;    d+=len;    xsize-=len;    }    while (xsize>l->xsize)    { -  fprintf(stderr,"s=%p xsize=%d d=%p\n",s,xsize,d); +     if (s) MEMCPY(d,s,l->xsize*sizeof(rgb_group));    if (sa) MEMCPY(d,sa,l->xsize*sizeof(rgb_group));    da+=l->xsize;    d+=l->xsize;    xsize-=l->xsize;    }    if (s) MEMCPY(d,s,xsize*sizeof(rgb_group));    if (sa) MEMCPY(d,sa,xsize*sizeof(rgb_group));    }   }    - #define SNUMPIXS 64 /* pixels in short-stroke buffer */ -  +    static INLINE void img_lay_stroke(struct layer *ly, -  rgb_group *stmp, -  rgb_group *satmp, -  int *sinited, +     rgb_group *l,    rgb_group *la,    rgb_group *s,    rgb_group *sa,    rgb_group *d,    rgb_group *da,    int len)   { -  if ((l && la) || -  (l && -  ly->fill_alpha.r==0 && ly->fill_alpha.g==0 && ly->fill_alpha.b==0)) +  if (l)    {    (ly->row_func)(s,l,d,sa,la,da,len,ly->alpha_value);    return;    } -  if (!*sinited) +  +  if (!la && +  ly->fill_alpha.r==0 && +  ly->fill_alpha.g==0 && +  ly->fill_alpha.b==0 && +  ly->optimize_alpha)    { -  smear_color(stmp,ly->fill,SNUMPIXS); -  smear_color(satmp,ly->fill_alpha,SNUMPIXS); -  sinited[0]=1; +  MEMCPY(d,s,len*sizeof(rgb_group)); +  MEMCPY(da,sa,len*sizeof(rgb_group)); +  return;    }       if (!la && -  ly->fill_alpha.r==0 && ly->fill_alpha.g==0 && ly->fill_alpha.b==0) +  ly->fill_alpha.r==COLORMAX && +  ly->fill_alpha.g==COLORMAX && +  ly->fill_alpha.b==COLORMAX)    {    while (len>SNUMPIXS)    { -  (ly->row_func)(s,l?l:stmp,d,sa,NULL,da,SNUMPIXS,ly->alpha_value); -  s+=SNUMPIXS; l+=SNUMPIXS; d+=SNUMPIXS; -  sa+=SNUMPIXS; la+=SNUMPIXS; da+=SNUMPIXS; +  (ly->row_func)(s,l?l:ly->sfill,d,sa,NULL,da, +  SNUMPIXS,ly->alpha_value); +  s+=SNUMPIXS; d+=SNUMPIXS; +  sa+=SNUMPIXS;da+=SNUMPIXS; +  if (l) l+=SNUMPIXS; +  len-=SNUMPIXS;    } -  (ly->row_func)(s,l?l:stmp,d,sa,NULL,da,len,ly->alpha_value); +  if (len) +  (ly->row_func)(s,l?l:ly->sfill,d,sa,NULL,da,len,ly->alpha_value);    }    else    { -  +  int i; +     while (len>SNUMPIXS)    { -  (ly->row_func)(s,l?l:stmp,d,sa,la?la:satmp,da, +  (ly->row_func)(s,l?l:ly->sfill,d,sa,la?la:ly->sfill_alpha,da,    SNUMPIXS,ly->alpha_value); -  s+=SNUMPIXS; l+=SNUMPIXS; d+=SNUMPIXS; -  sa+=SNUMPIXS; la+=SNUMPIXS; da+=SNUMPIXS; +  s+=SNUMPIXS; d+=SNUMPIXS; +  sa+=SNUMPIXS; da+=SNUMPIXS; +  if (l) l+=SNUMPIXS; +  if (la) la+=SNUMPIXS; +  len-=SNUMPIXS;    } -  (ly->row_func)(s,l?l:stmp,d,sa,la?la:satmp,da,len,ly->alpha_value); +  if (len) +  (ly->row_func)(s,l?l:ly->sfill,d,sa,la?la:ly->sfill_alpha, +  da,len,ly->alpha_value);    }   }      static INLINE void img_lay_line(struct layer *ly,    rgb_group *s,rgb_group *sa,    int xoffs,int xsize, -  int y, +  int y, /* y in ly layer */    rgb_group *d,rgb_group *da)   { -  rgb_group stmp[SNUMPIXS]; -  rgb_group satmp[SNUMPIXS]; -  int sinited=0; -  +     if (!ly->tiled)    {    int len;    rgb_group *l,*la;    -  if (y<ly->yoffs || -  y>=ly->yoffs+ly->ysize || +  if (y<0 || +  y>=ly->ysize ||    ly->xoffs+ly->xsize<xoffs ||    ly->xoffs>xoffs+xsize) /* outside */    { -  img_lay_stroke(ly,stmp,satmp,&sinited,NULL,NULL,s,sa,d,da,xsize); +  img_lay_stroke(ly,NULL,NULL,s,sa,d,da,xsize);    return;    }       if (ly->img) l=ly->img->img+y*ly->xsize; else l=NULL;    if (ly->alp) la=ly->alp->img+y*ly->xsize; else la=NULL;    len=ly->xsize;       if (ly->xoffs>xoffs)    {    /* fill to the left */ -  img_lay_stroke(ly,stmp,satmp,&sinited,NULL,NULL, -  s,sa,d,da,ly->xoffs-xoffs); +  img_lay_stroke(ly,NULL,NULL,s,sa,d,da,ly->xoffs-xoffs);       xsize-=ly->xoffs-xoffs;    d+=ly->xoffs-xoffs;    da+=ly->xoffs-xoffs;    s+=ly->xoffs-xoffs;    sa+=ly->xoffs-xoffs;    }    else    {    if (l) l+=xoffs-ly->xoffs;    if (la) la+=xoffs-ly->xoffs;    len-=xoffs-ly->xoffs;    }    if (len<xsize) /* copy stroke, fill right */    { -  img_lay_stroke(ly,stmp,satmp,&sinited,l,la, -  s,sa,d,da,ly->xoffs-xoffs); +  img_lay_stroke(ly,l,la,s,sa,d,da,len);    -  img_lay_stroke(ly,stmp,satmp,&sinited,NULL,NULL, -  s+len,sa+len,d+len,da+len,xsize-len); +  img_lay_stroke(ly,NULL,NULL,s+len,sa+len,d+len,da+len,xsize-len);    }    else /* copy rest */    { -  img_lay_stroke(ly,stmp,satmp,&sinited,l,la, -  s,sa,d,da,xsize); +  img_lay_stroke(ly,l,la,s,sa,d,da,xsize);    }    return;    }    else    {    rgb_group *l,*la;    -  if (ly->img) l=ly->img->img+y*ly->xsize; else l=NULL; -  if (ly->alp) la=ly->alp->img+y*ly->xsize; else la=NULL; -  -  y-=ly->yoffs; +     y%=ly->ysize;    if (y<0) y+=ly->ysize;    -  +  if (ly->img) l=ly->img->img+y*ly->xsize; else l=NULL; +  if (ly->alp) la=ly->alp->img+y*ly->xsize; else la=NULL; +     xoffs-=ly->xoffs; /* position in source */ -  if (xoffs%ly->xsize) +  if ((xoffs=xoffs%ly->xsize))    { -  int len=ly->xsize-(xoffs%ly->xsize); -  img_lay_stroke(ly,stmp,satmp,&sinited,l?l+(xoffs%ly->xsize):NULL, +  int len; +  if (xoffs<0) xoffs+=ly->xsize; +  len=ly->xsize-xoffs; +  +  img_lay_stroke(ly,l?l+xoffs:NULL,    la?la+(xoffs%ly->xsize):NULL,    s,sa,d,da,len);    da+=len;    d+=len;    sa+=len;    s+=len;    xsize-=len;    }    while (xsize>ly->xsize)    { -  img_lay_stroke(ly,stmp,satmp,&sinited,l,la, -  s,sa,d,da,ly->xsize); +  img_lay_stroke(ly,l,la,s,sa,d,da,ly->xsize);    da+=ly->xsize;    d+=ly->xsize;    sa+=ly->xsize;    s+=ly->xsize;    xsize-=ly->xsize;    }    if (xsize) -  img_lay_stroke(ly,stmp,satmp,&sinited,l,la, -  s,sa,d,da,xsize); +  img_lay_stroke(ly,l,la,s,sa,d,da,xsize);    }   }         void img_lay(struct layer **layer,    int layers,    struct layer *dest)   {    rgb_group *line1,*line2,*aline1,*aline2,*tmp;    rgb_group *d,*da;
pike.git/src/modules/Image/layers.c:1218:    da=dest->alp->img;    d=dest->img->img;       /* loop over lines */    for (y=0; y<dest->ysize; y++)    {    if (layers>1)    {    /* add the bottom layer first */    if (layer[0]->row_func==lm_normal) /* cheat */ -  img_lay_first_line(layer[0],xoffs,xsize,y+dest->yoffs, +  { +  img_lay_first_line(layer[0],xoffs,xsize, +  y+dest->yoffs-layer[0]->yoffs,    line1,aline1),z=1; -  +  z=1; +  }    else    {    smear_color(line1,black,xsize);    smear_color(aline1,black,xsize);    z=0;    }       /* loop over the rest of the layers, except the last */ -  for (; z<layers-2; z++) +  for (; z<layers-1; z++)    {    img_lay_line(layer[z],line1,aline1, -  xoffs,xsize,y,line2,aline2); +  xoffs,xsize,y-layer[z]->yoffs,line2,aline2);    /* swap buffers */    tmp=line1; line1=line2; line2=tmp;    tmp=aline1; aline1=aline2; aline2=tmp;    }       /* make the last layer on the destionation */    img_lay_line(layer[layers-1],line1,aline1, -  xoffs,xsize,y+dest->yoffs,d,da); +  xoffs,xsize,y+dest->yoffs-layer[layers-1]->yoffs,d,da);    }    else    {    /* make the layer to destination*/ -  img_lay_first_line(layer[0],xoffs,xsize,y+dest->yoffs,d,da); +  img_lay_first_line(layer[0],xoffs,xsize, +  y+dest->yoffs-layer[0]->yoffs,d,da);    }    d+=dest->xsize;    da+=dest->xsize;    }       free(line1);    free(aline1);    free(line2);    free(aline2);   }
pike.git/src/modules/Image/layers.c:1339:    xsize=l[0]->xsize;    ysize=l[0]->ysize;    for (i=1; i<layers; i++)    {    int t;    if (l[i]->xoffs<xoffset)    t=xoffset-l[i]->xoffs,xoffset-=t,xsize+=t;    if (l[i]->yoffs<yoffset)    t=yoffset-l[i]->yoffs,yoffset-=t,ysize+=t;    if (l[i]->xsize+l[i]->xoffs-xoffset>xsize) -  xsize=l[i]->xsize+l[i]->xoffs-xoffset>xsize; +  xsize=l[i]->xsize+l[i]->xoffs-xoffset;    if (l[i]->ysize+l[i]->yoffs-yoffset>ysize) -  ysize=l[i]->ysize+l[i]->yoffs-yoffset>ysize; +  ysize=l[i]->ysize+l[i]->yoffs-yoffset;    }    }    -  fprintf(stderr,"%d,%d @ %d,%d\n",xsize,ysize,xoffset,yoffset); -  +     /* get destination layer */    push_int(xsize);    push_int(ysize);    push_object(o=clone_object(image_layer_program,2));       dest=(struct layer*)get_storage(o,image_layer_program);    dest->xoffs=xoffset;    dest->yoffs=yoffset;       /* ok, do it! */
pike.git/src/modules/Image/layers.c:1393:    ADD_FUNCTION("create",image_layer_create,    tOr4(tFunc(,tVoid),    tFunc(tObj tOr(tObj,tVoid) tOr(tString,tVoid),tVoid),    tFunc(tLayerMap,tVoid),    tFunc(tInt tInt    tOr(tColor,tVoid) tOr(tColor,tVoid),tVoid)),0);       ADD_FUNCTION("cast",image_layer_cast,    tFunc(tString,tMapping),0);    +  /* set */ +  +  ADD_FUNCTION("set_offset",image_layer_set_offset,tFunc(tInt tInt,tObj),0); +  ADD_FUNCTION("set_image",image_layer_set_image, +  tFunc(tOr(tObj,tVoid) tOr(tObj,tVoid),tObj),0); +  ADD_FUNCTION("set_fill",image_layer_set_fill, +  tFunc(tOr(tObj,tVoid) tOr(tObj,tVoid),tObj),0); +  ADD_FUNCTION("set_mode",image_layer_set_mode,tFunc(tStr,tObj),0); +  ADD_FUNCTION("set_alpha_value",image_layer_set_mode,tFunc(tFloat,tObj),0); +  ADD_FUNCTION("set_tiled",image_layer_set_tiled,tFunc(tInt,tObj),0); +     /* query */       ADD_FUNCTION("image",image_layer_image,tFunc(,tObj),0);    ADD_FUNCTION("alpha",image_layer_alpha,tFunc(,tObj),0);    ADD_FUNCTION("mode",image_layer_mode,tFunc(,tStr),0);       ADD_FUNCTION("xoffset",image_layer_xoffset,tFunc(,tInt),0);    ADD_FUNCTION("yoffset",image_layer_yoffset,tFunc(,tInt),0);       ADD_FUNCTION("alpha_value",image_layer_alpha_value,tFunc(,tFloat),0);