diff --git a/.gitattributes b/.gitattributes
index fa20a1fcdd003c1f829a7f250bcdc86f710f8ddd..20ed0021f6e0141653bcc0218dcc63d1fcba6045 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -201,6 +201,7 @@ testfont binary
 /src/modules/Image/illustration.pike foreign_ident
 /src/modules/Image/image.c foreign_ident
 /src/modules/Image/image.h foreign_ident
+/src/modules/Image/layers.c foreign_ident
 /src/modules/Image/matrix.c foreign_ident
 /src/modules/Image/mkwmml.pike foreign_ident
 /src/modules/Image/operator.c foreign_ident
diff --git a/src/modules/Image/layers.c b/src/modules/Image/layers.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ef545d765df87f27782ba62451e368cfb5ee445
--- /dev/null
+++ b/src/modules/Image/layers.c
@@ -0,0 +1,1440 @@
+/*
+**! module Image
+**! note
+**!	$Id: layers.c,v 1.1 1999/04/17 19:41:58 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 $");
+
+#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"
+#include "mapping.h"
+#include "threads.h"
+#include "builtin_functions.h"
+#include "dmalloc.h"
+#include "operators.h"
+#include "module_support.h"
+#include "opcodes.h"
+
+#include "image.h"
+
+extern struct program *image_program;
+struct program *image_layer_program;
+
+static struct mapping *colors=NULL;
+static struct object *colortable=NULL;
+static struct array *colornames=NULL;
+
+struct program *image_layer_program=NULL;
+extern struct program *image_colortable_program;
+
+static const rgb_group black={0,0,0};
+static const rgb_group white={COLORMAX,COLORMAX,COLORMAX};
+
+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);
+                    
+
+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 */ 
+
+   int tiled;            /* true if tiled */
+
+   lm_row_func *row_func;/* layer mode */
+};
+
+#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,
+			int len,float alpha);
+static void lm_behind(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_multiply(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_screen(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_overlay(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_difference(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_addition(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_subtract(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_darken(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_lighten(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_hue(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_saturation(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_color(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_value(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_divide(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_erase(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_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;
+   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            }, */
+} ;
+
+#define LAYER_MODES ((int)NELEM(layer_mode))
+
+/*** 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;
+}
+
+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;
+}
+
+static void exit_layer(struct object *dummy)
+{
+   free_layer(THIS);
+}
+
+/*
+**! method object set_image(object(Image.Image)	image)
+**! method object set_image(object(Image.Image)	image,object(Image.Image) alpha_channel)
+**! method object|int(0) image()
+**! method object|int(0) alpha()
+**!	Set/change/get image and alpha channel for the layer.
+**!	You could also cancel the channels giving 0 
+**!	instead of an image object.
+**! note:
+**!	image and alpha channel must be of the same size,
+**!	or canceled.
+*/
+
+static void image_layer_set_image(INT32 args)
+{
+   struct image *img;
+
+   if (THIS->image)
+      free_object(THIS->image);
+   THIS->image=NULL;
+   THIS->img=NULL;
+
+   if (THIS->alpha)
+      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)");
+      }
+      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)");
+
+   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)");
+      }
+      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)");
+
+   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);
+   else
+      push_int(0);
+}
+
+static void image_layer_alpha(INT32 args)
+{
+   pop_n_elems(args);
+   if (THIS->alpha)
+      ref_push_object(THIS->alpha);
+   else
+      push_int(0);
+}
+
+/*
+**! method object set_alpha_value(float value)
+**! method float alpha_value()
+**!	Set/get the general alpha value of this layer.
+**!	This is a float value between 0 and 1,
+**!	and is multiplied with the alpha channel.
+*/
+
+static void image_layer_set_alpha_value(INT32 args)
+{
+   float f;
+   get_all_args("Image.Layer->set_alpha_value",args,"%F",&f);
+   if (f<0.0 || f>=1.0)
+      SIMPLE_BAD_ARG_ERROR("Image.Layer->set_alpha_value",1,"float(0..1)");
+   THIS->alpha_value=f;
+}
+
+static void image_layer_alpha_value(INT32 args)
+{
+   pop_n_elems(args);
+   push_float(THIS->alpha_value);
+}
+
+/*
+**! method object set_mode(string mode)
+**! method string mode()
+**!	Set/get layer mode. Mode is one of these:
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>normal</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>addition</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>behind</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>color</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>darken</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>difference</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>dissolve</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>divide</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>erase</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>hue</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>lighten</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>multiply</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>overlay</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>replace</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>saturation</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>screen</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>subtract</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!
+**!	<tr><td valign=top align=left>
+**!	<b><tt>value</tt><b></br>
+**!	?
+**!	</td><td align=left valign=top>
+**!	<illustration> return lena(); </illustration>
+**!	<td></tr>
+**!	
+**! note:
+**!	image and alpha channel must be of the same size,
+**!	or canceled.
+*/
+
+static void image_layer_set_mode(INT32 args)
+{
+   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;
+	 pop_n_elems(args);
+	 ref_push_object(THISOBJ);
+	 return;
+      }
+
+   SIMPLE_BAD_ARG_ERROR("Image.Layer->set_mode",1,"string");
+}
+
+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)
+      {
+	 ref_push_string(layer_mode[i].ps);
+	 return;
+      }
+
+   fatal("illegal mode: %p\n",layer_mode[i].func);
+}
+
+/*
+**! method object set_fill(Color color)
+**! method object set_fill(Color color,Color alpha)
+**! method object fill()
+**! method object fill_alpha()
+**!	Set/query fill color and alpha, ie the color used "outside" the 
+**!	image. This is mostly useful if you want to "frame"
+**!	a layer.
+*/
+
+static void image_layer_set_fill(INT32 args)
+{
+   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");
+      
+   if (args>1)
+      if (sp[1-args].type==T_INT && !sp[1-args].u.integer)
+	 THIS->fill_alpha=white;
+      else
+	 if (!image_color_arg(1-args,&(THIS->fill_alpha)))
+	    SIMPLE_BAD_ARG_ERROR("Image.Layer->set_fill",2,"color");
+   else
+      THIS->fill_alpha=white;
+
+   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);
+}
+
+static void image_layer_fill_alpha(INT32 args)
+{
+   pop_n_elems(args);
+   _image_make_rgb_color(THIS->fill_alpha.r,
+			 THIS->fill_alpha.g,
+			 THIS->fill_alpha.b);
+}
+
+/*
+**! method object set_offset(int x,int y)
+**! method int xoffset()
+**! method int yoffset()
+**!	Set/query layer offset.
+*/
+
+static void image_layer_set_offset(INT32 args)
+{
+   get_all_args("Image.Layer->set_offset",args,"%i%i",
+		&(THIS->xoffs),&(THIS->yoffs));
+   pop_n_elems(args);
+   ref_push_object(THISOBJ);
+}
+
+static void image_layer_xoffset(INT32 args)
+{
+   pop_n_elems(args);
+   push_int(THIS->xoffs);
+}
+
+static void image_layer_yoffset(INT32 args)
+{
+   pop_n_elems(args);
+   push_int(THIS->yoffs);
+}
+
+/*
+**! method object set_tiled(int yes)
+**! method int tiled()
+**!	Set/query <i>tiled</i> flag. If set, the
+**!	image and alpha channel will be tiled rather
+**!	then framed by the <ref>fill</ref> values.
+*/
+
+static void image_layer_set_tiled(INT32 args)
+{
+   get_all_args("Image.Layer->set_offset",args,"%i",
+		&(THIS->tiled));
+   THIS->tiled=!!THIS->tiled;
+   pop_n_elems(args);
+   ref_push_object(THISOBJ);
+}
+
+static void image_layer_tiled(INT32 args)
+{
+   pop_n_elems(args);
+   push_int(THIS->tiled);
+}
+
+/*
+**! method void create(object image,object alpha,string mode)
+**! method void create(mapping info)
+**! method void create()
+**!
+**! method void create(int xsize,int ysize,object color)
+**! method void create(object color)
+**!	The Layer construct either three arguments,
+**!	the image object, alpha channel and mode, or
+**!	a mapping with optional elements:
+**!	<pre>
+**!	"image":image,            
+**!		// default: black
+**!
+**!	"alpha":alpha,            
+**!		// alpha channel object
+**!		// default: full opaque
+**!
+**!	"mode":string mode,
+**!		// layer mode, see <ref>mode</ref>.
+**!		// default: "normal"
+**!
+**!	"alpha_value":float(0.0-1.0),
+**!		// layer general alpha value
+**!		// default is 1.0; this is multiplied
+**!		// with the alpha channel.
+**!
+**!	"xoffset":int,
+**!	"yoffset":int,
+**!		// offset of this layer
+**!
+**!	"fill":Color,		  
+**!	"fill_alpha":Color,
+**!		// fill color, ie what color is used
+**!		// "outside" the image. default: black
+**!		// and black (full transparency).
+**!
+**!	"tiled":int(0|1),
+**!		// select tiling; if 1, the image
+**!		// will be tiled. deafult: 0, off
+**!	</pre>	
+**!	The layer can also be created "empty",
+**!	either giving a size and color -
+**!	this will give a filled opaque square,
+**!	or a color, which will set the "fill"
+**!	values and fill the whole layer with an
+**!	opaque color.
+**!
+**!	All values can be modified after object creation.
+**!
+**! note:
+**!	image and alpha channel must be of the same size.
+**!	
+*/
+
+static INLINE void try_parameter(char *a,void (*f)(INT32))
+{
+   stack_dup();
+   push_text(a);
+   f_index(2);
+
+   if (!IS_UNDEFINED(sp-1))
+      f(1);
+   pop_stack();
+}
+
+static INLINE void try_parameter_pair(char *a,char *b,void (*f)(INT32))
+{
+   stack_dup();  /* map map */
+   stack_dup();  /* map map map */
+   push_text(a); /* map map map a */
+   f_index(2);   /* map map map[a] */
+   stack_swap(); /* map map[a] map */
+   push_text(b); /* map map[a] map b */
+   f_index(2);   /* map map[a] map[b] */
+
+   if (!IS_UNDEFINED(sp-2) ||
+       !IS_UNDEFINED(sp-1))
+   {
+      f(2);
+      pop_stack();
+   }
+   else 
+      pop_n_elems(2);
+}
+
+static void image_layer_create(INT32 args)
+{
+   if (!args)
+      return;
+   if (sp[-args].type==T_MAPPING)
+   {
+      pop_n_elems(args-1);
+      try_parameter_pair("image","alpha",image_layer_set_image);
+
+      try_parameter("mode",image_layer_set_mode);
+      try_parameter("alpha_value",image_layer_set_alpha_value);
+
+      try_parameter_pair("xoffset","yoffset",image_layer_set_offset);
+      try_parameter_pair("fill","fill_alpha",image_layer_set_fill);
+      try_parameter("tiled",image_layer_set_tiled);
+      pop_stack();
+      return;
+   }
+   else if (sp[-args].type==T_INT)
+   {
+      rgb_group col=black,alpha=white;
+
+      get_all_args("Image.Layer",args,"%i%i",&(THIS->xsize),&(THIS->ysize));
+      if (args>2)
+	 if (!image_color_arg(2-args,&col))
+	    SIMPLE_BAD_ARG_ERROR("Image.Layer",3,"Image.Color");
+	    
+      if (args>3)
+	 if (!image_color_arg(3-args,&alpha))
+	    SIMPLE_BAD_ARG_ERROR("Image.Layer",4,"Image.Color");
+      
+      push_int(THIS->xsize);
+      push_int(THIS->ysize);
+      push_int(col.r);
+      push_int(col.g);
+      push_int(col.b);
+      push_object(clone_object(image_program,5));
+
+      push_int(THIS->xsize);
+      push_int(THIS->ysize);
+      push_int(alpha.r);
+      push_int(alpha.g);
+      push_int(alpha.b);
+      push_object(clone_object(image_program,5));
+      
+      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);
+	 args=2;
+      }
+      image_layer_set_image(args);
+      pop_stack();
+   }
+   else
+      SIMPLE_BAD_ARG_ERROR("Image.Layer",1,"mapping|int|Image.Image");
+}
+
+/*** layer object *****************************************/
+
+static void image_layer_cast(INT32 args)
+{
+   if (!args)
+      SIMPLE_TOO_FEW_ARGS_ERROR("Image.Layer->cast",1);
+   if (sp[-args].type==T_STRING||sp[-args].u.string->size_shift)
+   {
+      if (strncmp(sp[-args].u.string->str,"mapping",7)==0)
+      {
+	 int n=0;
+	 pop_n_elems(args);
+	 
+	 push_text("xsize");        push_int(THIS->xsize);         n++;
+	 push_text("ysize");        push_int(THIS->ysize);         n++;
+	 push_text("image");        image_layer_image(0);          n++;
+	 push_text("alpha");        image_layer_image(0);          n++;
+	 push_text("xoffset");      push_int(THIS->xoffs);         n++;
+	 push_text("yoffset");      push_int(THIS->yoffs);         n++;
+	 push_text("alpha_value");  push_float(THIS->alpha_value); n++;
+	 push_text("fill");         image_layer_fill(0);           n++;
+	 push_text("fill_alpha");   image_layer_fill_alpha(0);     n++;
+	 push_text("tiled");        push_int(THIS->tiled);         n++;
+	 push_text("mode");         image_layer_mode(0);           n++;
+
+	 f_aggregate_mapping(n*2);
+
+	 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))
+
+#define COMBINE_ALPHA(S,L,aS,aL) \
+    ( (COLORTYPE)((((S)*((int)(COLORMAX-(aL)))*(aS))+ \
+	           ((L)*((int)(aL))*COLORMAX))/ \
+    	          (((COLORMAX*(int)(aL))+(COLORMAX-(int)(aL))*(aS))) ) )
+
+#define COMBINE_ALPHA_V(S,L,aS,aL,V) \
+    COMBINE_ALPHA(S,(int)((L)*(V)),aS,aL)
+
+#else 
+#ifdef ALPHA_METHOD_FLOAT
+
+#define qMAX (1.0/COLORMAX)
+#define C2F(Z) (qMAX*Z)
+#define CCUT(Z) ((COLORTYPE)(qMAX*Z))
+
+#define COMBINE_ALPHA(S,L,aS,aL) \
+    ( (COLORTYPE)( ( (S)*(1.0-C2F(aL)*C2F(aS)) + (L)*C2F(aL) ) /
+	       ( C2F(aL)+(1-C2F(aL))*C2F(aS)) ) )
+
+#define COMBINE_ALPHA_V(S,L,aS,aL,V) \
+    COMBINE_ALPHA(S,(L)*(V),aS,aL)
+
+#define COMBINE_ALPHA_SUM(aS,aL) \
+    ((COLORTYPE)(COLORMAX*(C2F(aL)+(1.0-C2F(aL))*aS))
+#define COMBINE_ALPHA_SUM_V(aS,aL,V) \
+    COMBINE_ALPHA_SUM(aS,(aL)*(V))
+
+#else /* unknown ALPHA_METHOD */
+#error unknown ALPHA_METHOD
+#endif ALPHA_METHOD_FLOAT 
+
+#endif ALPHA_INT_IS_FASTER
+
+
+/*** layer mode definitions ***************************/
+
+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)
+{
+   /* la may be NULL, no other */
+
+   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);
+	 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							\
+	       D->C=COMBINE_ALPHA(S->C,l->C,SA->C,LA->C),		\
+		  DA->C=COMBINE_ALPHA_SUM(SA->C,LA->C);
+
+	    if (la->r==COLORMAX && la->g==COLORMAX && la->b==COLORMAX)
+	    {
+	       *d=*l;
+	       *da=*la;
+	    }
+	    else if (la->r==0 && la->g==0 && la->b==0)
+	    {
+	       *d=*s;
+	       *da=*sa;
+	    }
+	    else
+	    {
+	       ALPHA_ADD(s,l,d,sa,la,da,r);
+	       ALPHA_ADD(s,l,d,sa,la,da,g);
+	       ALPHA_ADD(s,l,d,sa,la,da,b);
+	    }
+#undef ALPHA_ADD
+	    l++; s++; la++; sa++; d++; da++;
+	 }
+   }
+   else
+   {
+      if (!la)  /* no layer alpha => alpha value opaque */
+	 while (len--)
+	 {
+#define ALPHA_ADD_V_NOLA(L,S,D,SA,DA,V,C)				\
+            do {							\
+               if (!SA->C) D->C=l->C,DA->C=0;				\
+               else							\
+               {							\
+                 if (SA->C==COLORMAX)					\
+		 	D->C=COMBINE_ALPHA_V(S->C,l->C,COLORMAX,255,V);	\
+  	         else D->C=COMBINE_ALPHA_V(S->C,l->C,SA->C,255,V);	\
+     	         DA->C=COMBINE_ALPHA_SUM_V(SA->C,255,V);		\
+               }							\
+	    } while(0)
+
+	    ALPHA_ADD_V_NOLA(s,l,d,sa,da,alpha,r);
+	    ALPHA_ADD_V_NOLA(s,l,d,sa,da,alpha,g);
+	    ALPHA_ADD_V_NOLA(s,l,d,sa,da,alpha,b);
+
+#undef ALPHA_ADD_V_NOLA
+	    l++; s++; la++; sa++; da++; d++;
+	 }
+      else
+	 while (len--)
+	 {
+#define ALPHA_ADD_V(L,S,D,LA,SA,DA,V,C) 				\
+            do { 							\
+	       if (!LA->C) 						\
+	       {							\
+		  D->C=COMBINE_ALPHA_V(S->C,l->C,SA->C,0,V); 		\
+		  DA->C=COMBINE_ALPHA_SUM_V(0,SA->C,V);  		\
+	       }							\
+	       else if (!SA->C) 					\
+	       {							\
+		  D->C=COMBINE_ALPHA_V(S->C,l->C,0,LA->C,V); 		\
+		  DA->C=COMBINE_ALPHA_SUM_V(LA->C,0,V);  		\
+	       }							\
+	       else 							\
+	       {							\
+		  D->C=COMBINE_ALPHA_V(S->C,l->C,SA->C,LA->C,V); 	\
+		  DA->C=COMBINE_ALPHA_SUM_V(LA->C,SA->C,V);  		\
+	       }							\
+	    } while (0) 
+
+	    ALPHA_ADD_V(s,l,d,sa,la,da,alpha,r);
+	    ALPHA_ADD_V(s,l,d,sa,la,da,alpha,g);
+	    ALPHA_ADD_V(s,l,d,sa,la,da,alpha,b);
+
+#undef ALPHA_ADD_V
+	    l++; s++; la++; sa++; da++; d++;
+	 }
+      return;
+   }
+}
+
+
+/*** the add-layer function ***************************/
+
+static void INLINE img_lay_first_line(struct layer *l,
+				      int xoffs,int xsize,
+				      int y,
+				      rgb_group *d,rgb_group *da)
+{
+   if (!l->tiled)
+   {
+      rgb_group *s,*sa;
+      int len;
+
+      if (y<l->yoffs ||
+	  y>=l->yoffs+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;
+      len=l->xsize;
+      
+      if (l->xoffs>xoffs) 
+      {
+	 /* fill to the left */
+	 smear_color(d,l->fill,l->xoffs-xoffs);
+	 smear_color(da,l->fill_alpha,l->xoffs-xoffs);
+
+	 xsize-=l->xoffs-xoffs;
+	 d+=l->xoffs-xoffs;
+	 da+=l->xoffs-xoffs;
+      }
+      else
+      {
+	 if (s) s+=xoffs-l->xoffs;
+	 if (sa) sa+=xoffs-l->xoffs;
+	 len-=xoffs-l->xoffs;
+      }
+      if (len<xsize) /* copy bit, fill right */
+      {
+	 if (s) MEMCPY(d,s,len*sizeof(rgb_group)); 
+	 else smear_color(d,l->fill,len);
+	 if (sa) MEMCPY(da,sa,len*sizeof(rgb_group)); 
+	 else smear_color(da,white,len);
+
+	 smear_color(d+len,l->fill,xsize-len);
+	 smear_color(da+len,l->fill_alpha,xsize-len);
+      }
+      else /* copy rest */
+      {
+	 if (s) MEMCPY(d,s,xsize*sizeof(rgb_group)); 
+	 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))
+   {
+      (ly->row_func)(s,l,d,sa,la,da,len,ly->alpha_value);
+      return;
+   }
+   if (!*sinited)
+   {
+      smear_color(stmp,ly->fill,SNUMPIXS);
+      smear_color(satmp,ly->fill_alpha,SNUMPIXS);
+      sinited[0]=1;
+   }
+
+   if (!la && 
+       ly->fill_alpha.r==0 && ly->fill_alpha.g==0 && ly->fill_alpha.b==0)
+   {
+      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:stmp,d,sa,NULL,da,len,ly->alpha_value);
+   }
+   else
+   {
+      while (len>SNUMPIXS)
+      {
+	 (ly->row_func)(s,l?l:stmp,d,sa,la?la:satmp,da,
+			SNUMPIXS,ly->alpha_value);
+	 s+=SNUMPIXS; l+=SNUMPIXS; d+=SNUMPIXS;
+	 sa+=SNUMPIXS; la+=SNUMPIXS; da+=SNUMPIXS;
+      }
+      (ly->row_func)(s,l?l:stmp,d,sa,la?la:satmp,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,
+				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 ||
+	  ly->xoffs+ly->xsize<xoffs ||
+	  ly->xoffs>xoffs+xsize) /* outside */
+      {
+	 img_lay_stroke(ly,stmp,satmp,&sinited,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);
+
+	 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,stmp,satmp,&sinited,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);
+      }
+      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;
+
+      xoffs-=ly->xoffs; /* position in source */
+      if (xoffs%ly->xsize)
+      {
+	 int len=ly->xsize-(xoffs%ly->xsize);
+	 img_lay_stroke(ly,stmp,satmp,&sinited,l?l+(xoffs%ly->xsize):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);
+	 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);
+   }
+}
+
+
+void img_lay(struct layer **layer,
+	     int layers,
+	     struct layer *dest)
+{
+   rgb_group *line1,*line2,*aline1,*aline2,*tmp;
+   rgb_group *d,*da;
+   int width=dest->xsize;
+   int y,z;
+   int xoffs=dest->xoffs,xsize=dest->xsize;
+
+   line1=malloc(sizeof(rgb_group)*width);
+   line2=malloc(sizeof(rgb_group)*width);
+   aline1=malloc(sizeof(rgb_group)*width);
+   aline2=malloc(sizeof(rgb_group)*width);
+   if (!line1 || !line2 || !aline1 || !aline2)
+   {
+      if (line1) free(line1);
+      if (line2) free(line2);
+      if (aline1) free(aline1);
+      if (aline2) free(aline2);
+      resource_error(NULL,0,0,"memory",sizeof(rgb_group)*4*width,
+		     "Out of memory.\n");
+   }
+
+   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,
+			       line1,aline1),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++)
+	 {
+	    img_lay_line(layer[z],line1,aline1,
+			 xoffs,xsize,y,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);
+      }
+      else 
+      {
+	 /* make the layer to destination*/
+	 img_lay_first_line(layer[0],xoffs,xsize,y+dest->yoffs,d,da);
+      }
+      d+=dest->xsize;
+      da+=dest->xsize;
+   }
+
+   free(line1);
+   free(aline1);
+   free(line2);
+   free(aline2);
+}
+
+/*
+**! module Image
+**! method Image.Layer lay(array(Image.Layer|mapping))
+**! method Image.Layer lay(array(Image.Layer|mapping),int xoffset,int yoffset,int xsize,int ysize)
+**!	Combine layers. 
+**! returns a new layer object.
+**!
+**! see also: Image.Layer
+*/
+
+void image_lay(INT32 args)
+{
+   int layers,i;
+   struct layer **l;
+   struct object *o;
+   struct layer *dest;
+   struct array *a;
+   int gotoffs;
+   int xoffset=0,yoffset=0,xsize=0,ysize=0;
+
+   if (!args)
+      SIMPLE_TOO_FEW_ARGS_ERROR("Image.lay",1);
+      
+   if (sp[-args].type!=T_ARRAY)
+      SIMPLE_BAD_ARG_ERROR("Image.lay",1,
+			   "array(Image.Layer|mapping)");
+
+   if (args>1)
+   {
+      get_all_args("Image.lay",args-1,"%i%i%i%i",
+		   &xoffset,&yoffset,&xsize,&ysize);
+      if (xsize<1)
+	 SIMPLE_BAD_ARG_ERROR("Image.lay",4,"int(1..)");
+      if (ysize<1)
+	 SIMPLE_BAD_ARG_ERROR("Image.lay",5,"int(1..)");
+   }
+   
+   layers=(a=sp[-args].u.array)->size;
+
+   if (!layers) /* dummy return empty layer */
+   {
+      pop_n_elems(args);
+      push_object(clone_object(image_layer_program,0));
+      return;
+   }
+      
+   l=(struct layer**)xalloc(sizeof(struct layer)*layers);
+
+   for (i=0; i<layers; i++)
+   {
+      if (a->item[i].type==T_OBJECT)
+      {
+	 if (!(l[i]=(struct layer*)get_storage(a->item[i].u.object,
+					       image_layer_program)))
+	    SIMPLE_BAD_ARG_ERROR("Image.lay",1,
+				 "array(Image.Layer|mapping)");
+      }
+      else if (a->item[i].type==T_MAPPING)
+      {
+	 push_svalue(a->item+i);
+	 push_object(o=clone_object(image_layer_program,1));
+	 args++;
+	 l[i]=(struct layer*)get_storage(o,image_layer_program);
+      }
+      else
+	 SIMPLE_BAD_ARG_ERROR("Image.lay",1,
+			      "array(Image.Layer|mapping)");
+   }
+
+   if (xsize==0) /* figure offset and size */
+   {
+      xoffset=l[0]->xoffs;
+      yoffset=l[0]->yoffs;
+      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;
+	 if (l[i]->ysize+l[i]->yoffs-yoffset>ysize)
+	    ysize=l[i]->ysize+l[i]->yoffs-yoffset>ysize;
+      }
+   }
+
+   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! */
+   img_lay(l,layers,dest);
+   
+   sp--;
+   pop_n_elems(args);
+   push_object(o);
+}
+
+/******************************************************/
+
+void init_image_layers(void)
+{
+   char buf[100];
+   char buf2[sizeof(INT32)];
+   int i;
+   
+   for (i=0; i<LAYER_MODES; i++)
+      layer_mode[i].ps=make_shared_string(layer_mode[i].name);
+
+   start_new_program();
+
+   ADD_STORAGE(struct layer);
+   set_init_callback(init_layer);
+   set_exit_callback(exit_layer);
+
+#define tLayerMap tMap(tString,tOr4(tString,tColor,tFloat,tInt))
+
+   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);
+
+   /* 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);
+   ADD_FUNCTION("fill",image_layer_fill,tFunc(,tObj),0);
+   ADD_FUNCTION("fill_alpha",image_layer_fill_alpha,tFunc(,tObj),0);
+
+   ADD_FUNCTION("tiled",image_layer_tiled,tFunc(,tInt01),0);
+
+   image_layer_program=end_program();
+
+   add_program_constant("Layer",image_layer_program,0);
+
+   ADD_FUNCTION("lay",image_lay,
+		tOr(tFunc(tArr(tOr(tObj,tLayerMap)),tObj),
+		    tFunc(tArr(tOr(tObj,tLayerMap))
+			  tInt tInt tInt tInt,tObj)),0);
+}
+
+void exit_image_layers(void)
+{
+   int i;
+   
+   for (i=0; i<LAYER_MODES; i++)
+      free_string(layer_mode[i].ps);
+
+   if (image_layer_program)
+   {
+      free_program(image_layer_program);
+      image_layer_program=NULL;
+   }
+}