diff --git a/src/modules/_Image_JPEG/image_jpeg.c b/src/modules/_Image_JPEG/image_jpeg.c
index 913687ef97794470bd419853205be5b4037323e9..118b854abe2a6fed8d3820f6be4ddb0a7c20aba1 100644
--- a/src/modules/_Image_JPEG/image_jpeg.c
+++ b/src/modules/_Image_JPEG/image_jpeg.c
@@ -18,66 +18,391 @@ RCSID("$id: $");
 #include "svalue.h"
 #include "threads.h"
 #include "array.h"
+#include "mapping.h"
 #include "error.h"
-#include "threads.h"
+#include "stralloc.h"
+#include "dynamic_buffer.h"
 
 #include "../Image/image.h"
 
+static struct program *image_program=NULL;
+
+
+static struct pike_string *param_baseline;
+static struct pike_string *param_quality;
+static struct pike_string *param_optimize;
+static struct pike_string *param_smoothing;
+static struct pike_string *param_x_density;
+static struct pike_string *param_y_density;
+static struct pike_string *param_density;
+static struct pike_string *param_density_unit;
+static struct pike_string *param_method;
+static struct pike_string *param_progressive;
+
+
 /*
 **! module Image
 **! submodule JPEG
+**!
+**! note
+**!	This module uses <tt>libjpeg</tt>, a software from
+**!	Independent JPEG Group.
 */
 
-void my_exit_error(struct jpeg_common_struct *cinfo)
+static void my_output_message(struct jpeg_common_struct *cinfo)
+{
+   /* no message */
+   /* (this should not be called) */
+}
+
+static void my_error_exit(struct jpeg_common_struct *cinfo)
+{
+   char buffer[JMSG_LENGTH_MAX];
+   (*cinfo->err->format_message) (cinfo, buffer);
+
+   jpeg_abort(cinfo);
+   error("Image.JPEG: fatal error in libjpeg; %s\n",buffer);
+}
+
+static void my_emit_message(struct jpeg_common_struct *cinfo,int msg_level)
+{
+   /* no trace */
+}
+
+struct my_destination_mgr
+{
+   struct jpeg_destination_mgr pub;
+
+   char *buf;
+   size_t len;
+};
+
+#define DEFAULT_BUF_SIZE 8192
+#define BUF_INCREMENT 8192
+
+static void my_init_destination(struct jpeg_compress_struct *cinfo)
 {
+   struct my_destination_mgr *dm=(struct my_destination_mgr *)cinfo->dest;
+
+   dm->buf=malloc(DEFAULT_BUF_SIZE);
+   if (dm->buf==0) dm->len=0; else dm->len=DEFAULT_BUF_SIZE;
+
+   dm->pub.free_in_buffer=DEFAULT_BUF_SIZE;
+   dm->pub.next_output_byte=(JOCTET*)dm->buf;
 }
 
-void my_emit_message(struct jpeg_common_struct *cinfo,int msg_level)
+boolean my_empty_output_buffer(struct jpeg_compress_struct *cinfo)
 {
+   struct my_destination_mgr *dm=(struct my_destination_mgr *)cinfo->dest;
+   int pos;
+   char *new;
+
+   pos=dm->len-dm->pub.free_in_buffer;
+   new=realloc(dm->buf,dm->len+BUF_INCREMENT);
+   if (!new) return FALSE;
+
+   dm->buf=new;
+   dm->pub.free_in_buffer+=BUF_INCREMENT;
+   dm->len+=BUF_INCREMENT;
+   dm->pub.next_output_byte=new+pos;
+
+   return TRUE;
 }
 
-void my_output_message(struct jpeg_common_struct *cinfo)
+static void my_term_destination(struct jpeg_compress_struct *cinfo)
 {
+   /* don't do anything */
 }
 
-void my_format_message(struct jpeg_common_struct *cinfo,char *buffer)
+struct pike_string* my_result_and_clean(struct jpeg_compress_struct *cinfo)
 {
+   struct my_destination_mgr *dm=(struct my_destination_mgr *)cinfo->dest;
+
+   if (dm->buf)
+   {
+      struct pike_string *ps;
+      ps=make_shared_binary_string(dm->buf,
+				   (char*)dm->pub.next_output_byte-(char*)dm->buf);
+      
+      free(dm->buf);
+      dm->buf=NULL;
+      return ps;
+   }
+   return make_shared_string("");
 }
 
-void my_reset_error_mgr(struct jpeg_common_struct *cinfo)
+int parameter_int(struct svalue *map,struct pike_string *what,INT32 *p)
 {
+   struct svalue *v;
+   v=low_mapping_string_lookup(map->u.mapping,what);
+
+   if (!v || v->type!=T_INT) return 0;
+
+   *p=v->u.integer;
+   return 1;
 }
 
 /*
-**! method object decode
+**! method object decode(string data)
 */
 
 static void image_jpeg_decode(INT32 args)
 {
-   struct jpeg_decompress_struct cinfo;
-   struct jpeg_error_mgr jerr;
-   
-   struct jpeg_error_mgr my_error_mgr=
-   { my_exit_error,
-     my_emit_message,
-     my_output_message,
-     my_format_message,
-     my_reset_error_mgr
-   };
+}
+
+/*
+**! method string encode(object image)
+**! method string encode(object image, mapping options)
+**! 	Encodes a JPEG image. 
+**!
+**!     The <tt>options<tt> argument may be a mapping
+**!	containing zero or more encoding options:
+**!
+**!	<pre>
+**!	normal options:
+**!	    "quality":0..100
+**!		Set quality of result. Default is 75.
+**!	    "optimize":0|1
+**!		Optimize Huffman table. Default is on (1) for
+**!		images smaller than 50kpixels.
+**!	    "progressive":0|1
+**!		Make a progressive JPEG. Default is off.
+**!
+**!	advanced options:
+**!	    "smooth":1..100
+**!		Smooth input. Value is strength.
+**!	    "method":JPEG.IFAST|JPEG.ISLOW|JPEG.FLOAT|JPEG.DEFAULT|JPEG.FASTEST
+**!		DCT method to use.
+**!		DEFAULT and FASTEST is from the jpeg library,
+**!		probably ISLOW and IFAST respective.
+**!
+**!	wizard options:
+**!	    "baseline":0|1
+**!		Force baseline output. Useful for quality<20.
+**!	</pre>
+**!
+**! note
+**!	Please read some about JPEG files. A quality 
+**!	setting of 100 does not mean the result is 
+**!	lossless.
+*/
+
+static void image_jpeg_encode(INT32 args)
+{
+   struct jpeg_error_mgr errmgr;
+   struct my_destination_mgr destmgr;
+   struct jpeg_compress_struct cinfo;
+
+   struct image *img;
+
+   unsigned char *tmp;
+   INT32 y;
+   rgb_group *s;
+   JSAMPROW row_pointer[8];
+
+   if (args<1 
+       || sp[-args].type!=T_OBJECT
+       || !(img=(struct image*)
+	    get_storage(sp[-args].u.object,image_program))
+       || (args>1 && sp[1-args].type!=T_MAPPING))
+      error("Image.JPEG.encode: Illegal arguments\n");
+
+
+   if (!img->img)
+      error("Image.JPEG.encode: Given image is empty.\n");
+
+   tmp=malloc(img->xsize*3*8);
+   if (!tmp) 
+      error("Image.JPEG.encode: out of memory\n");
+
+   /* init jpeg library objects */
+
+   jpeg_std_error(&errmgr);
+
+   errmgr.error_exit=my_error_exit;
+   errmgr.emit_message=my_emit_message;
+   errmgr.output_message=my_output_message;
+
+   destmgr.pub.init_destination=my_init_destination;
+   destmgr.pub.empty_output_buffer=my_empty_output_buffer;
+   destmgr.pub.term_destination=my_term_destination;
+
+   cinfo.err=&errmgr;
+
+   jpeg_create_compress(&cinfo);
+
+   cinfo.dest=(struct jpeg_destination_mgr*)&destmgr;
+
+   cinfo.image_width=img->xsize;
+   cinfo.image_height=img->ysize;
+   cinfo.input_components=3;     /* 1 */
+   cinfo.in_color_space=JCS_RGB; /* JCS_GRAYSVALE */
+
+   jpeg_set_defaults(&cinfo);
+
+   cinfo.optimize_coding=(img->xsize*img->ysize)<50000;
+
+   /* check configuration */
+
+   if (args>1)
+   {
+      INT32 p,q;
+
+      p=0;
+      q=75;
+      if (parameter_int(sp+1-args,param_baseline,&p)
+	  || parameter_int(sp+1-args,param_quality,&q))
+      {
+	 if (q<0) q=0; else if (q>100) q=100;
+	 jpeg_set_quality(&cinfo,q,!!p);
+      }
+
+      if (parameter_int(sp+1-args,param_optimize,&p))
+      {
+	 cinfo.optimize_coding=!!p;
+      }
+
+      if (parameter_int(sp+1-args,param_smoothing,&p))
+      {
+	 if (p<1) p=1; else if (p>100) p=100;
+	 cinfo.smoothing_factor=p;
+      }
+
+      
+      if (parameter_int(sp+1-args,param_x_density,&p) &&
+	  parameter_int(sp+1-args,param_y_density,&q))
+      {
+	 cinfo.X_density=p;
+	 cinfo.Y_density=q;
+	 cinfo.density_unit=1;
+      }
+
+      if (parameter_int(sp+1-args,param_density,&p))
+      {
+	 cinfo.X_density=p;
+	 cinfo.Y_density=p;
+	 cinfo.density_unit=1;
+      }
+
+      if (parameter_int(sp+1-args,param_density_unit,&p))
+	 cinfo.density_unit=p;
+
+      if (parameter_int(sp+1-args,param_method,&p)
+	  && (p==JDCT_IFAST ||
+	      p==JDCT_FLOAT ||
+	      p==JDCT_DEFAULT ||
+	      p==JDCT_ISLOW ||
+	      p==JDCT_FASTEST))
+	 cinfo.dct_method=p;
+      
+      if (parameter_int(sp+1-args,param_progressive,&p))
+	 jpeg_simple_progression(&cinfo);
+   }
+
+   jpeg_start_compress(&cinfo, TRUE);
    
+   y=img->ysize;
+   s=img->img;
+
+   while (y)
+   {
+      int n,i,y2=y;
+      if (y2>8) y2=8;
+      n=img->xsize*y2; 
+      i=0;
+      while (n--)
+	 tmp[i++]=s->r, tmp[i++]=s->g, tmp[i++]=s->b, s++;
+
+      row_pointer[0]=tmp;
+      row_pointer[1]=tmp+img->xsize*3;
+      row_pointer[2]=tmp+img->xsize*3*2;
+      row_pointer[3]=tmp+img->xsize*3*3;
+      row_pointer[4]=tmp+img->xsize*3*4;
+      row_pointer[5]=tmp+img->xsize*3*5;
+      row_pointer[6]=tmp+img->xsize*3*6;
+      row_pointer[7]=tmp+img->xsize*3*7;
+      jpeg_write_scanlines(&cinfo, row_pointer, y2);
+      
+      y-=y2;
+   }
+
+   jpeg_finish_compress(&cinfo);
+
+   pop_n_elems(args);
+   push_string(my_result_and_clean(&cinfo));
+
+   jpeg_destroy_compress(&cinfo);
 }
 
 #endif /* HAVE_JPEGLIB_H */
 
+/*** module init & exit & stuff *****************************************/
+
+void f_index(INT32 args);
+
 
 void pike_module_exit(void)
 {
-   add_function("decode",image_jpeg_decode,
-		"function(string:object)",0);
+   free_string(param_baseline);
+   free_string(param_quality);
+   free_string(param_optimize);
+   free_string(param_smoothing);
+   free_string(param_x_density);
+   free_string(param_y_density);
+   free_string(param_density);
+   free_string(param_density_unit);
+   free_string(param_method);
+   free_string(param_progressive);
 }
 
 void pike_module_init(void)
 {
 #ifdef HAVE_JPEGLIB_H
+   push_string(make_shared_string("Image"));
+   push_int(0);
+   SAFE_APPLY_MASTER("resolv",2);
+   if (sp[-1].type==T_OBJECT) 
+   {
+      push_string(make_shared_string("image"));
+      f_index(2);
+      image_program=program_from_svalue(sp-1);
+   }
+   pop_n_elems(1);
+
+   if (image_program)
+   {
+      add_function("decode",image_jpeg_decode,
+		   "function(string,void|mapping(string:int):object)",0);
+      add_function("encode",image_jpeg_encode,
+		   "function(object,void|mapping(string:int):string)",0);
+
+      push_int(JDCT_IFAST);
+      add_constant(make_shared_string("IFAST"),sp-1,0);
+      pop_stack();
+      push_int(JDCT_FLOAT);
+      add_constant(make_shared_string("FLOAT"),sp-1,0);
+      pop_stack();
+      push_int(JDCT_DEFAULT);
+      add_constant(make_shared_string("DEFAULT"),sp-1,0);
+      pop_stack();
+      push_int(JDCT_ISLOW);
+      add_constant(make_shared_string("ISLOW"),sp-1,0);
+      pop_stack();
+      push_int(JDCT_FASTEST);
+      add_constant(make_shared_string("FASTEST"),sp-1,0);
+      pop_stack();
+   }
+
 #endif /* HAVE_JPEGLIB_H */
+
+   param_baseline=make_shared_string("baseline");
+   param_quality=make_shared_string("quality");
+   param_optimize=make_shared_string("optimize");
+   param_smoothing=make_shared_string("smoothing");
+   param_x_density=make_shared_string("x_density");
+   param_y_density=make_shared_string("y_density");
+   param_density=make_shared_string("density");
+   param_density_unit=make_shared_string("density_unit");
+   param_method=make_shared_string("method");
+   param_progressive=make_shared_string("progressive");
 }