diff --git a/.gitattributes b/.gitattributes
index 1d8f8decfe869f7aea760f7634215670cf7ef64b..f9f3ac35892791d19acdef398d82de3f39a52b13 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -50,6 +50,7 @@ testfont binary
 /src/modules/Image/pnm.c foreign_ident
 /src/modules/Image/quant.c foreign_ident
 /src/modules/Image/togif.c foreign_ident
+/src/modules/Image/x.c foreign_ident
 /src/modules/MIME/mime.c foreign_ident
 /src/modules/Mysql/Makefile.in foreign_ident
 /src/modules/Mysql/acconfig.h foreign_ident
diff --git a/src/modules/Image/x.c b/src/modules/Image/x.c
new file mode 100644
index 0000000000000000000000000000000000000000..6f0158272278b951ff7d5356880bd612b0a95544
--- /dev/null
+++ b/src/modules/Image/x.c
@@ -0,0 +1,272 @@
+/* $Id: x.c,v 1.1 1997/03/20 07:56:26 mirar Exp $ */
+
+#include "global.h"
+
+#include <math.h>
+#include <ctype.h>
+
+#include "stralloc.h"
+#include "global.h"
+RCSID("$Id: x.c,v 1.1 1997/03/20 07:56:26 mirar Exp $");
+#include "types.h"
+#include "pike_macros.h"
+#include "object.h"
+#include "constants.h"
+#include "interpret.h"
+#include "svalue.h"
+#include "threads.h"
+#include "array.h"
+#include "error.h"
+
+#include "image.h"
+#include "builtin_functions.h"
+
+struct program *image_program;
+#define THIS ((struct image *)(fp->current_storage))
+#define THISOBJ (fp->current_object)
+
+void image_cast(INT32 args)
+{
+  /* CHECK TYPE TO CAST TO HERE! FIXME FIXME FIXME! */
+  pop_n_elems(args);
+  push_string(make_shared_binary_string((char *)THIS->img,
+					THIS->xsize*THIS->ysize
+					*sizeof(rgb_group)));
+}
+
+void image_to8bit_closest(INT32 args)
+{
+  struct colortable *ct;
+  struct pike_string *res = begin_shared_string((THIS->xsize*THIS->ysize));
+  unsigned long i;
+  rgb_group *s;
+  unsigned char *d;
+
+  if(!res) error("Out of memory\n");
+
+  if (args!=1)
+     error("Illegal number of arguments to image->to8bit(COLORTABLE);\n");
+  if(sp[-args].type != T_ARRAY)
+     error("Wrong type to image->to8bit(COLORTABLE);\n");
+
+  ct=colortable_from_array(sp[-args].u.array,"image->to8bit()\n");
+
+  i=THIS->xsize*THIS->ysize;
+  s=THIS->img;
+  d=res->str;
+
+  THREADS_ALLOW();
+  while (i--)
+  {
+    *d=ct->index[colortable_rgb_nearest(ct,*s)];
+    d++; *s++;
+  }
+  THREADS_DISALLOW();
+
+  colortable_free(ct);
+
+  pop_n_elems(args);
+  push_string(end_shared_string(res));
+}
+
+void image_to8bit(INT32 args)
+{
+  struct colortable *ct;
+  struct pike_string *res = begin_shared_string((THIS->xsize*THIS->ysize));
+  unsigned long i;
+  rgb_group *s;
+  unsigned char *d;
+
+  if(!res) error("Out of memory\n");
+
+  if (args!=1)
+     error("Illegal number of arguments to image->to8bit(COLORTABLE);\n");
+  if(sp[-args].type != T_ARRAY)
+     error("Wrong type to image->to8bit(COLORTABLE);\n");
+
+  ct=colortable_from_array(sp[-args].u.array,"image->to8bit()\n");
+
+  i=THIS->xsize*THIS->ysize;
+  s=THIS->img;
+  d=res->str;
+
+  THREADS_ALLOW();
+  while (i--)
+  {
+    *d=ct->index[colortable_rgb(ct,*s)];
+    d++; *s++;
+  }
+  THREADS_DISALLOW();
+
+  colortable_free(ct);
+
+  pop_n_elems(args);
+  push_string(end_shared_string(res));
+}
+
+void image_to8bit_fs(INT32 args)
+{
+   struct colortable *ct;
+   INT32 i,j,xs;
+   rgb_group *s;
+   struct object *o;
+   int *res,w;
+   unsigned char *d;
+   rgbl_group *errb;
+   struct pike_string *sres = begin_shared_string((THIS->xsize*THIS->ysize));
+   
+   if (!THIS->img) error("no image\n");
+   if (args<1
+       || sp[-args].type!=T_ARRAY)
+      error("illegal argument to image->map_fs()\n");
+
+
+   res=(int*)xalloc(sizeof(int)*THIS->xsize);
+   errb=(rgbl_group*)xalloc(sizeof(rgbl_group)*THIS->xsize);
+      
+   ct=colortable_from_array(sp[-args].u.array,"image->map_closest()\n");
+   pop_n_elems(args);
+
+   for (i=0; i<THIS->xsize; i++)
+      errb[i].r=(rand()%(FS_SCALE*2+1))-FS_SCALE,
+      errb[i].g=(rand()%(FS_SCALE*2+1))-FS_SCALE,
+      errb[i].b=(rand()%(FS_SCALE*2+1))-FS_SCALE;
+
+   i=THIS->ysize;
+   s=THIS->img;
+   d=sres->str;
+   w=0;
+   xs=THIS->xsize;
+   THREADS_ALLOW();
+   while (i--)
+   {
+      image_floyd_steinberg(s,xs,errb,w=!w,res,ct,1);
+      for (j=0; j<THIS->xsize; j++)
+	 *(d++)=ct->index[res[j]];
+      s+=xs;
+   }
+   THREADS_DISALLOW();
+
+   free(errb);
+   free(res);
+   colortable_free(ct);
+   push_string(end_shared_string(sres));
+}
+
+
+void image_tozbgr(INT32 args)
+{
+   unsigned char *d;
+   rgb_group *s;
+   unsigned long i;
+   struct pike_string *sres = begin_shared_string(THIS->xsize*THIS->ysize*4);
+   
+   if (!THIS->img) error("no image\n");
+   pop_n_elems(args);
+
+   i=THIS->ysize*THIS->xsize;
+   s=THIS->img;
+   d=sres->str;
+
+   THREADS_ALLOW();
+   while (i--)
+   {
+      *(d++)=0;
+      *(d++)=s->b;
+      *(d++)=s->g;
+      *(d++)=s->r;
+      s++;
+   }
+   THREADS_DISALLOW();
+
+   push_string(end_shared_string(sres));
+}
+
+void image_to8bit_rgbcube(INT32 args)
+/*
+
+  ->to8bit_rgbcube(int red,int green,int blue,
+                [string map])
+  
+  gives r+red*g+red*green*b       
+ */
+{
+  struct colortable *ct;
+  struct pike_string *res = begin_shared_string((THIS->xsize*THIS->ysize));
+  unsigned long i;
+  rgb_group *s;
+  unsigned char *d;
+  unsigned char *map=NULL;
+
+  int red,green,blue,redgreen,redgreenblue,hred,hgreen,hblue;
+  
+  if(!res) error("Out of memory\n");
+
+  if (!THIS->img) 
+     error("No image\n");
+
+  if (args<3) 
+     error("Too few arguments to image->to8bit_rgbcube()\n");
+  
+  if (sp[-args].type!=T_INT
+      || sp[1-args].type!=T_INT
+      || sp[2-args].type!=T_INT) 
+     error("Illegal argument(s) to image->to8bit_rgbcube()\n");
+
+  red=sp[-args].u.integer; 	hred=red/2;
+  green=sp[1-args].u.integer;	hgreen=green/2;
+  blue=sp[2-args].u.integer; 	hblue=blue/2;
+  redgreen=red*green;
+  redgreenblue=red*green*blue;
+
+  if (args>3)
+     if (sp[3-args].type!=T_STRING)
+	error("Illegal argument 4 to image->to8bit_rgbcube()"
+	      " (expected string or no argument)\n");
+     else if (sp[3-args].u.string->len<red*green*blue)
+	error("map string is not long enough to image->to8bit_rgbcube()\n");
+     else
+	map=sp[3-args].u.string->str;
+
+  i=THIS->xsize*THIS->ysize;
+  s=THIS->img;
+  d=res->str;
+
+  THREADS_ALLOW();
+  if (!map)
+     while (i--)
+     {
+// 	fprintf(stderr,"%02x%02x%02x -> %x,%x,%x -> %d,%d,%d -> %d\n",
+// 		s->r,s->g,s->b,
+// 		s->r*red+hred,
+// 		s->g*green+hgreen,
+// 		s->b*blue+hblue,
+// 		((s->r*red+hred)>>8),
+// 		((s->g*green+hgreen)>>8),
+// 		((s->b*blue+hblue)>>8),
+// 		((s->r*red+hred)>>8)+
+// 		((s->g*green+hgreen)>>8)*red+
+// 		((s->b*blue+hblue)>>8)*redgreen);
+		
+	*(d++)=
+	   (unsigned char)( ((s->r*red+hred)>>8)+
+			    ((s->g*green+hgreen)>>8)*red+
+			    ((s->b*blue+hblue)>>8)*redgreen );
+	s++;
+     }
+  else
+     while (i--)
+     {
+	*(d++)=
+	   map[ ((s->r*red+hred)>>8)+
+	        ((s->g*green+hgreen)>>8)*red+
+	        ((s->b*blue+hblue)>>8)*redgreen ];
+	s++;
+     }
+  THREADS_DISALLOW();
+
+  pop_n_elems(args);
+  push_string(end_shared_string(res));
+}
+
+