diff --git a/src/modules/Image/encodings/png.c b/src/modules/Image/encodings/png.c
index 2577a64e9d687d950c6461f9ac6b50dbd56298ad..fe2eee9238dfc1f41f5ef35c11a191ebffc415c9 100644
--- a/src/modules/Image/encodings/png.c
+++ b/src/modules/Image/encodings/png.c
@@ -1,5 +1,5 @@
 #include "global.h"
-RCSID("$Id: png.c,v 1.7 1998/04/05 21:10:43 mirar Exp $");
+RCSID("$Id: png.c,v 1.8 1998/04/06 00:51:24 mirar Exp $");
 
 #include "config.h"
 
@@ -31,6 +31,7 @@ static struct pike_string *param_image;
 static struct pike_string *param_alpha;
 static struct pike_string *param_type;
 static struct pike_string *param_bpp;
+static struct pike_string *param_background;
 
 
 void f_add(INT32 args);
@@ -59,6 +60,25 @@ static INLINE unsigned long int_from_32bit(unsigned char *data)
    return (data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]);
 }
 
+#define int_from_16bit(X) _int_from_16bit((unsigned char*)(X))
+static INLINE unsigned long _int_from_16bit(unsigned char *data)
+{
+   return (data[0]<<8)|(data[1]);
+}
+
+static INLINE COLORTYPE _png_c16(unsigned long z,int bpp)
+{
+   switch (bpp)
+   {
+      case 16: return z>>8; break;
+      case 4:  return z*17; break;
+      case 2:  return z*0x55; break;
+      case 1:  return z*255; break;
+      default: return z;
+   }
+
+}
+
 static INLINE INT32 call_gz_crc32(INT32 args)
 {
    INT32 z;
@@ -381,6 +401,7 @@ static struct pike_string *_png_unfilter(unsigned char *data,
       }
       x=xsize;
 
+#if 0
       fprintf(stderr,
 	      "%05d 0x%08lx %02x %02x %02x > %02x < %02x %02x %02x "
 	      "len=%d xsize=%d sbb=%d next=0x%08lx d=0x%lx nd=0x%lx\n",
@@ -390,6 +411,7 @@ static struct pike_string *_png_unfilter(unsigned char *data,
 	      (unsigned long)s+xsize+1,
 	      (unsigned long)d,
 	      (unsigned long)d+xsize);
+#endif
 
       switch (*(s++))
       {
@@ -479,8 +501,9 @@ static int _png_write_rgb(rgb_group *w1,
 			  unsigned char *s,
 			  unsigned long len,
 			  unsigned long width,
-			  int n,
-			  struct neo_colortable *ct)
+			  unsigned long n,
+			  struct neo_colortable *ct,
+			  struct pike_string *trns)
 {
    /* returns 1 if interlace, 0 if not */
    /* w1, wa1 will be freed upon error */
@@ -492,6 +515,8 @@ static int _png_write_rgb(rgb_group *w1,
    rgb_group *d1=w1;
    rgb_group *da1=wa1;
 
+   unsigned long n0=n;
+
    unsigned long x,mz;
 
    /* write stuff to d1 */
@@ -544,7 +569,7 @@ static int _png_write_rgb(rgb_group *w1,
 		  if (x) 
 		  {
 		     x--,q=(((*s)>>4)&15)|((*s)&240);
-		  d1->r=d1->g=d1->b=q; d1++;
+		     d1->r=d1->g=d1->b=q; d1++;
 		  }
 		  if (x) 
 		  {
@@ -578,6 +603,14 @@ static int _png_write_rgb(rgb_group *w1,
 	       error("Image.PNG->_decode: Unsupported color type/bit depth %d (grey)/%d bit.\n",
 		     type,bpp);
 	 }
+	 if (trns && trns->len==2)
+	 {
+	    rgb_group tr;
+	    tr.r=tr.g=tr.b=_png_c16(int_from_16bit(trns->str),bpp);
+	    n=n0;
+	    while (n--) *(da1++)=tr;
+	    return 1; /* alpha channel */
+	 }
 	 return 0; /* no alpha channel */
 
       case 2: /* 8 or 16 bit r,g,b */
@@ -611,6 +644,17 @@ static int _png_write_rgb(rgb_group *w1,
 	       error("Image.PNG->_decode: Unsupported color type/bit depth %d (rgb)/%d bit.\n",
 		     type,bpp);
 	 }
+	 if (trns && trns->len==6)
+	 {
+	    rgb_group tr;
+	    tr.r=_png_c16(int_from_16bit(trns->str),bpp);
+	    tr.g=_png_c16(int_from_16bit(trns->str+2),bpp);
+	    tr.b=_png_c16(int_from_16bit(trns->str+4),bpp);
+
+	    n=n0;
+	    while (n--) *(da1++)=tr;
+	    return 1; /* alpha channel */
+	 }
 	 return 0; /* no alpha channel */
 
       case 3: /* 1,2,4,8 bit palette index */
@@ -634,65 +678,174 @@ static int _png_write_rgb(rgb_group *w1,
 	    case 1:
 	       if (n>len*8) n=len*8;
 	       x=width;
-	       while (n)
-	       {
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>7)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>5)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>3)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>2)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>1)&1,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&1,mz)].color;
-		  s++;
-		  if (n<8) break;
-		  n-=8;
-		  if (!x) x=width;
-	       }
+	       if (!trns)
+		  while (n)
+		  {
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>7)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>5)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>3)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>2)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>1)&1,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&1,mz)].color;
+		     s++;
+		     if (n<8) break;
+		     n-=8;
+		     if (!x) x=width;
+		  }
+	       else
+		  while (n)
+		  {
+		     int i;
+		     for (i=8; i;)
+		     {
+			i--;
+			if (x) 
+			{
+			   int m=((*s)>>i)&1;
+			   x--;
+			   *(d1++)=ct->u.flat.entries[CUTPLTE(m,mz)].color;
+			   if (m>=trns->len)
+			      *(da1++)=white;
+			   else
+			   {
+			      da1->r=trns->str[m];
+			      da1->g=trns->str[m];
+			      da1->b=trns->str[m];
+			      da1++;
+			   }
+			}
+		     }
+		     s++;
+		     if (n<8) break;
+		     n-=8;
+		     if (!x) x=width;
+		  }
 	       break;
 	    case 2:
 	       if (n>len*4) n=len*4;
 	       x=width;
-	       while (n)
-	       {
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&3,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&3,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>2)&3,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&3,mz)].color;
-		  s++;
-		  if (n<4) break;
-		  n-=4;
-		  if (!x) x=width;
-	       }
+	       if (trns)
+		  while (n)
+		  {
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&3,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&3,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>2)&3,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&3,mz)].color;
+		     s++;
+		     if (n<4) break;
+		     n-=4;
+		     if (!x) x=width;
+		  }
+	       else
+		  while (n)
+		  {
+		     int i;
+		     for (i=8; i;)
+		     {
+			i-=2;
+			if (x) 
+			{
+			   int m=((*s)>>i)&3;
+			   x--;
+			   *(d1++)=ct->u.flat.entries[CUTPLTE(m,mz)].color;
+			   if (m>=trns->len)
+			      *(da1++)=white;
+			   else
+			   {
+			      da1->r=trns->str[m];
+			      da1->g=trns->str[m];
+			      da1->b=trns->str[m];
+			      da1++;
+			   }
+			}
+		     }
+		     s++;
+		     if (n<8) break;
+		     n-=8;
+		     if (!x) x=width;
+		  }
 	       break;
 	    case 4:
 	       if (n>len*2) n=len*2;
 	       x=width;
-	       while (n)
-	       {
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&15,mz)].color;
-		  if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&15,mz)].color;
-		  s++;
-		  if (n<2) break;
-		  n--;
-		  if (!x) x=width;
-	       }
+	       if (trns)
+		  while (n)
+		  {
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&15,mz)].color;
+		     if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&15,mz)].color;
+		     s++;
+		     if (n<2) break;
+		     n--;
+		     if (!x) x=width;
+		  }
+	       else
+		  while (n)
+		  {
+		     int i;
+		     for (i=8; i>4;)
+		     {
+			i-=4;
+			if (x) 
+			{
+			   int m=((*s)>>i)&3;
+			   x--;
+			   *(d1++)=ct->u.flat.entries[CUTPLTE(m,mz)].color;
+			   if (m>=trns->len)
+			      *(da1++)=white;
+			   else
+			   {
+			      da1->r=trns->str[m];
+			      da1->g=trns->str[m];
+			      da1->b=trns->str[m];
+			      da1++;
+			   }
+			}
+		     }
+		     s++;
+		     if (n<8) break;
+		     n-=8;
+		     if (!x) x=width;
+		  }
 	       break;
 	    case 8:
 	       if (n>len) n=len;
-	       while (n)
-	       {
-		  *(d1++)=ct->u.flat.entries[CUTPLTE(*s,mz)].color;
-		  s++;
-		  n--;
-	       }
+
+	       if (trns)
+		  while (n)
+		  {
+		     *(d1++)=ct->u.flat.entries[CUTPLTE(*s,mz)].color;
+
+		     s++;
+		     n--;
+		  }
+	       else
+		  while (n)
+		  {
+		     int m=CUTPLTE(*s,mz);
+		     *(d1++)=ct->u.flat.entries[m].color;
+		     if (m>=trns->len)
+			*(da1++)=white;
+		     else
+		     {
+			da1->r=trns->str[m];
+			da1->g=trns->str[m];
+			da1->b=trns->str[m];
+			da1++;
+		     }
+
+		     s++;
+		     n--;
+		  }
 	       break;
 	       
 	    default:
+	       free(w1); free(wa1);
 	       error("Image.PNG->_decode: Unsupported color type/bit depth %d (palette)/%d bit.\n",
 		     type,bpp);
 	 }
-	 return 0; /* no alpha channel */
+	 return !trns; /* alpha channel if trns chunk */
 
       case 4: /* 8 or 16 bit grey,a */
 	 switch (bpp)
@@ -791,12 +944,12 @@ static void image_png__decode(INT32 args)
    struct array *a;
    struct mapping *m;
    struct neo_colortable *ct=NULL;
-   rgb_group *d1,*da1,*w1,*wa1,*t1;
-   struct pike_string *fs;
+   rgb_group *d1,*da1,*w1,*wa1,*t1,*ta1=NULL;
+   struct pike_string *fs,*trns=NULL;
    unsigned char *s,*s0;
    struct image *img;
 
-   int n=0,i,x,y;
+   int n=0,i,x=-1,y;
    struct ihdr
    {
       INT32 width,height;
@@ -845,6 +998,11 @@ static void image_png__decode(INT32 args)
       data=(unsigned char*)b->item[1].u.string->str;
       len=(unsigned long)b->item[1].u.string->len;
 
+      if (!i &&
+	  int_from_32bit((unsigned char*)b->item[0].u.string->str)
+	  != 0x49484452 )
+	 error("Imge.PNG.decode: first chunk isn't IHDR\n");
+
       switch (int_from_32bit((unsigned char*)b->item[0].u.string->str))
       {
 	 /* ------ major chunks ------------ */
@@ -889,7 +1047,8 @@ static void image_png__decode(INT32 args)
          case 0x49444154: /* IDAT */
 	    /* compressed image data. push, n++ */
 	    if (ihdr.compression!=0)
-	       free_mapping(m);
+	       error("Image.PNG._decode: unknown compression (%d)\n",
+		     ihdr.compression);
 
 	    push_string(b->item[1].u.string);
 	    b->item[1].u.string->refs++;
@@ -913,12 +1072,60 @@ static void image_png__decode(INT32 args)
 	    break;
 
          case 0x624b4744: /* bKGD */
+	    switch (ihdr.type)
+	    {
+	       case 0:
+	       case 4:
+		  if (b->item[1].u.string->len==2)
+		  {
+		     int z=_png_c16(b->item[1].u.string->str[0],ihdr.bpp);
+		     push_int(z);
+		     push_int(z);
+		     push_int(z);
+		  }
+		  else
+		     continue;
+		  break;
+	       case 3:
+		  if (b->item[1].u.string->len!=1 ||
+		      !ct || ct->type!=NCT_FLAT ||
+		      b->item[1].u.string->str[0] >=
+		      ct->u.flat.numentries)
+		     continue;
+		  else
+		  {
+		     push_int(ct->u.flat.entries[(int)b->item[1].u.string->str[0]].color.r);
+		     push_int(ct->u.flat.entries[(int)b->item[1].u.string->str[0]].color.g);
+		     push_int(ct->u.flat.entries[(int)b->item[1].u.string->str[0]].color.b);
+		  }
+		  break;
+	       default:
+		  if (b->item[1].u.string->len!=6)
+		  {
+		     int j;
+		     for (j=0; j<3; j++)
+		     {
+			int z=_png_c16(b->item[1].u.string->str[j],ihdr.bpp);
+			push_int(z);
+		     }
+		  }
+		  break;
+	    }
+	    f_aggregate(3);
+	    push_string(param_background);
+	    param_background->refs++;
+	    mapping_insert(m,sp-1,sp-2);
+	    pop_n_elems(2);
 	    break;
 
          case 0x68495354: /* hIST */
 	    break;
 
          case 0x74524e53: /* tRNS */
+	    push_string(trns=b->item[1].u.string);
+	    push_int(-2);
+	    mapping_insert(m,sp-1,sp-2);
+	    sp-=2; /* we have no own ref to trns */
 	    break;
 
          case 0x70485973: /* pHYs */
@@ -961,7 +1168,7 @@ static void image_png__decode(INT32 args)
 
    fs=sp[-1].u.string; 
    push_int(-1);
-   mapping_insert(m,sp-2,sp-1);
+   mapping_insert(m,sp-1,sp-2);
 
    pop_n_elems(2);
 
@@ -989,11 +1196,11 @@ static void image_png__decode(INT32 args)
 			  NULL);
 	 push_string(fs);
 	 if (!_png_write_rgb(w1,wa1,
-			     ihdr.type,ihdr.bpp,fs->str,
+			     ihdr.type,ihdr.bpp,(unsigned char*)fs->str,
 			     fs->len,
 			     ihdr.width,
 			     ihdr.width*ihdr.height,
-			     ct))
+			     ct,trns))
 	 {
 	    free(wa1);
 	    wa1=NULL;
@@ -1005,12 +1212,15 @@ static void image_png__decode(INT32 args)
 
 	 /* need arena */
 	 t1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height);
-	 if (!t1)
+	 if (wa1) ta1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height);
+	 if (!t1 || (wa1 && !ta1))
 	 {
-	    if (wa1) free(wa1); free(w1); 
+	    free(w1); 
+	    if (wa1) free(wa1); 
+	    if (ta1) free(ta1); 
+	    if (ta1) free(t1); 
 	    error("Image.PNG->_decode: out of memory (close one)\n");
 	 }
-
 	 /* loop over adam7 interlace's 
 	    and write them to the arena */
 
@@ -1028,14 +1238,15 @@ static void image_png__decode(INT32 args)
 			     &s0);
 
 	    push_string(ds);
-	    if (!_png_write_rgb(w1,wa1,ihdr.type,ihdr.bpp,ds->str,ds->len,
+	    if (!_png_write_rgb(w1,wa1,ihdr.type,ihdr.bpp,
+				(unsigned char*)ds->str,ds->len,
 				(ihdr.width+adam7[i].xd-1-adam7[i].x0)/
 				adam7[i].xd,
 				(ihdr.width+adam7[i].xd-1-adam7[i].x0)/
 				adam7[i].xd*
 				(ihdr.height+adam7[i].yd-1-adam7[i].y0)/
 				adam7[i].yd,
-				ct))
+				ct,trns))
 	    {
 	       if (wa1) free(wa1);
 	       wa1=NULL;
@@ -1045,12 +1256,20 @@ static void image_png__decode(INT32 args)
 	       for (x=adam7[i].x0;x<ihdr.width;x+=adam7[i].xd)
 		  t1[x+y*ihdr.width]=*(d1++);
 
+	    if (wa1)
+	    {
+	       da1=wa1;
+	       for (y=adam7[i].y0;y<ihdr.height;y+=adam7[i].yd)
+		  for (x=adam7[i].x0;x<ihdr.width;x+=adam7[i].xd)
+		     ta1[x+y*ihdr.width]=*(da1++);
+	    }
+
 	    pop_stack();
 	 }
 	 
 	 free(w1);
-	 if (wa1) free(wa1);
 	 w1=t1;
+	 if (wa1) { free(wa1); wa1=ta1; }
 
 	 break;
       default:
@@ -1073,7 +1292,7 @@ static void image_png__decode(INT32 args)
    img->img=w1;
    mapping_insert(m,sp-2,sp-1);
    pop_n_elems(2);
-
+   
    if (wa1)
    {
       push_string(param_alpha);
@@ -1097,9 +1316,21 @@ static void image_png__decode(INT32 args)
    mapping_insert(m,sp-2,sp-1);
    pop_n_elems(2);
 
+   push_string(make_shared_string("xsize"));
+   push_int(ihdr.width);
+   mapping_insert(m,sp-2,sp-1);
+   pop_n_elems(2);
+   push_string(make_shared_string("ysize"));
+   push_int(ihdr.height);
+   mapping_insert(m,sp-2,sp-1);
+   pop_n_elems(2);
+
    push_int(-1);
    map_delete(m,sp-1);
    pop_stack();
+   push_int(-2);
+   map_delete(m,sp-1);
+   pop_stack();
 }
 
 
@@ -1171,6 +1402,33 @@ static void image_png_decode(INT32 args)
    f_index(2);
 }
 
+static void image_png_decode_alpha(INT32 args)
+{
+   struct svalue s;
+   if (!args)
+      error("Image.PNG.decode: missing argument(s)\n");
+   
+   image_png__decode(args);
+   assign_svalue_no_free(&s,sp-1);
+   push_string(make_shared_string("alpha"));
+   f_index(2);
+
+   if (sp[-1].type==T_INT)
+   {
+      push_svalue(&s);
+      push_string(make_shared_string("xsize"));
+      f_index(2);
+      push_svalue(&s);
+      push_string(make_shared_string("ysize"));
+      f_index(2);
+      push_int(255);
+      push_int(255);
+      push_int(255);
+      push_object(clone_object(image_program,3));
+   }
+   free_svalue(&s);
+}
+
 /*** module init & exit & stuff *****************************************/
 
 void exit_image_png(void)
@@ -1180,6 +1438,7 @@ void exit_image_png(void)
    free_string(param_image);
    free_string(param_alpha);
    free_string(param_bpp);
+   free_string(param_background);
    free_string(param_type);
 }
 
@@ -1238,6 +1497,8 @@ struct object *init_image_png(void)
 		      "function(array|string,void|mapping(string:int):object)",0);
 	 add_function("decode",image_png_decode,
 		      "function(string,void|mapping(string:int):object)",0);
+	 add_function("decode_alpha",image_png_decode_alpha,
+		      "function(string,void|mapping(string:int):object)",0);
       }
       add_function("encode",image_png_encode,
 		   "function(object,void|mapping(string:int):string)",
@@ -1250,6 +1511,7 @@ struct object *init_image_png(void)
    param_alpha=make_shared_string("alpha");
    param_bpp=make_shared_string("bpp");
    param_type=make_shared_string("type");
+   param_background=make_shared_string("background");
    
    return clone_object(end_program(),0);
 }