diff --git a/src/modules/Image/encodings/pvr.c b/src/modules/Image/encodings/pvr.c
index d367f681debc5138e0866121d16fa145fc006872..02628af4f61fe6b322a54b499a3bb57f150823f0 100644
--- a/src/modules/Image/encodings/pvr.c
+++ b/src/modules/Image/encodings/pvr.c
@@ -4,7 +4,7 @@
 #include <ctype.h>
 
 #include "stralloc.h"
-RCSID("$Id: pvr.c,v 1.1 2000/02/24 01:11:23 marcus Exp $");
+RCSID("$Id: pvr.c,v 1.2 2000/02/27 12:50:44 marcus Exp $");
 #include "pike_macros.h"
 #include "object.h"
 #include "constants.h"
@@ -12,6 +12,7 @@ RCSID("$Id: pvr.c,v 1.1 2000/02/24 01:11:23 marcus Exp $");
 #include "svalue.h"
 #include "threads.h"
 #include "array.h"
+#include "mapping.h"
 #include "error.h"
 #include "operators.h"
 #include "builtin_functions.h"
@@ -102,13 +103,107 @@ void image_pvr_f_encode(INT32 args)
    error("not implemented\n");
 }
 
+static void pvr_decode_rect(INT32 attr, unsigned char *src, rgb_group *dst,
+			    INT32 stride, unsigned int h, unsigned int w)
+{
+  INT32 cnt = h * w;
+  switch(attr&0xff) {
+   case MODE_ARGB1555:
+   case MODE_RGB555:
+     while(cnt--) {
+       unsigned int p = src[0]|(src[1]<<8);
+       dst->r = ((p&0x7c00)>>7)|((p&0x7000)>>12);
+       dst->g = ((p&0x03e0)>>2)|((p&0x0380)>>7);
+       dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
+       src+=2;
+       dst++;
+     }
+     break;
+   case MODE_RGB565:
+     while(cnt--) {
+       unsigned int p = src[0]|(src[1]<<8);
+       dst->r = ((p&0xf800)>>8)|((p&0xe000)>>13);
+       dst->g = ((p&0x07e0)>>3)|((p&0x0600)>>9);
+       dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
+       src+=2;
+       dst++;
+     }
+     break;
+   case MODE_ARGB4444:
+     while(cnt--) {
+       unsigned int p = src[0]|(src[1]<<8);
+       dst->r = ((p&0x0f00)>>4)|((p&0x0f00)>>8);
+       dst->g = (p&0x00f0)|((p&0x00f0)>>4);
+       dst->b = ((p&0x000f)<<4)|(p&0x000f);
+       src+=2;
+       dst++;
+     }
+     break;
+  }
+}
+
+static void pvr_decode_twiddled(INT32 attr, unsigned char *s, rgb_group *dst,
+				INT32 stride, unsigned int sz)
+{
+  unsigned int x, y;
+  unsigned char *src;
+  switch(attr&0xff) {
+   case MODE_ARGB1555:
+   case MODE_RGB555:
+     for(y=0; y<sz; y++) {
+       for(x=0; x<sz; x++) {
+	 unsigned int p;
+	 src = s+(((twiddletab[x]<<1)|twiddletab[y])<<1);
+	 p = src[0]|(src[1]<<8);
+	 dst->r = ((p&0x7c00)>>7)|((p&0x7000)>>12);
+	 dst->g = ((p&0x03e0)>>2)|((p&0x0380)>>7);
+	 dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
+	 src+=2;
+	 dst++;
+       }
+       dst += stride;
+     }
+     break;
+   case MODE_RGB565:
+     for(y=0; y<sz; y++) {
+       for(x=0; x<sz; x++) {
+	 unsigned int p;
+	 src = s+(((twiddletab[x]<<1)|twiddletab[y])<<1);
+	 p = src[0]|(src[1]<<8);
+	 dst->r = ((p&0xf800)>>8)|((p&0xe000)>>13);
+	 dst->g = ((p&0x07e0)>>3)|((p&0x0600)>>9);
+	 dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
+	 src+=2;
+	 dst++;
+       }
+       dst += stride;
+     }
+     break;
+   case MODE_ARGB4444:
+     for(y=0; y<sz; y++) {
+       for(x=0; x<sz; x++) {
+	 unsigned int p;
+	 src = s+(((twiddletab[x]<<1)|twiddletab[y])<<1);
+	 p = src[0]|(src[1]<<8);
+	 dst->r = ((p&0x0f00)>>4)|((p&0x0f00)>>8);
+	 dst->g = (p&0x00f0)|((p&0x00f0)>>4);
+	 dst->b = ((p&0x000f)<<4)|(p&0x000f);
+	 src+=2;
+	 dst++;
+       }
+       dst += stride;
+     }
+     break;
+  }
+}
+
 void img_pvr_decode(INT32 args,int header_only)
 {
    struct pike_string *str;
    unsigned char *s;
    int n = 0;
    INT32 len, attr;
-   unsigned int h, w, x, y;
+   unsigned int h, w, x;
 
    get_all_args("Image.PVR._decode", args, "%S", &str);
    s=(unsigned char *)str->str;
@@ -160,9 +255,6 @@ void img_pvr_decode(INT32 args,int header_only)
      int twiddle=0, hasalpha=0, bpp=0;
      struct object *o;
      struct image *img;
-     unsigned char *src = s;
-     rgb_group *dst;
-     INT32 cnt;
 
      switch(attr&0xff00) {
       case MODE_TWIDDLE:
@@ -174,7 +266,11 @@ void img_pvr_decode(INT32 args,int header_only)
       case MODE_STRIDE:
 	break;
       case MODE_TWIDDLED_RECTANGLE:
-	error("twiddled rectangle not supported\n");
+	twiddle = 1;
+	if((w<h && (w<8 || w>1024 || (w&(w-1)) || h%w)) ||
+	   (h>=w && (h<8 || h>1024 || (h&(h-1)) || w%h)))
+	  error("invalid size for twiddle rectangle texture\n");
+	break;
       case MODE_COMPRESSED:
       case MODE_COMPRESSED_MIPMAP:
 	error("compressed PVRs not supported\n");
@@ -204,7 +300,7 @@ void img_pvr_decode(INT32 args,int header_only)
 	error("unknown PVR color mode\n");
      }
 
-     if(len < bpp*(cnt = h*w))
+     if(len < (INT32)bpp*h*w)
        error("short PVRT chunk\n");
 
      push_text("image");
@@ -212,7 +308,6 @@ void img_pvr_decode(INT32 args,int header_only)
      push_int(h);
      o=clone_object(image_program,2);
      img=(struct image*)get_storage(o,image_program);
-     dst=img->img;
      push_object(o);
      n++;
 
@@ -220,83 +315,14 @@ void img_pvr_decode(INT32 args,int header_only)
        init_twiddletab();
      
      if(twiddle)
-       switch(attr&0xff) {
-	case MODE_ARGB1555:
-	case MODE_RGB555:
-	  for(y=0; y<h; y++)
-	    for(x=0; x<w; x++) {
-	      unsigned int p;
-	      src = s+(((twiddletab[x]<<1)|twiddletab[y])<<1);
-	      p = src[0]|(src[1]<<8);
-	      dst->r = ((p&0x7c00)>>7)|((p&0x7000)>>12);
-	      dst->g = ((p&0x03e0)>>2)|((p&0x0380)>>7);
-	      dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
-	      src+=2;
-	      dst++;
-	    }
-	  break;
-	case MODE_RGB565:
-	  for(y=0; y<h; y++)
-	    for(x=0; x<w; x++) {
-	      unsigned int p;
-	      src = s+(((twiddletab[x]<<1)|twiddletab[y])<<1);
-	      p = src[0]|(src[1]<<8);
-	      dst->r = ((p&0xf800)>>8)|((p&0xe000)>>13);
-	      dst->g = ((p&0x07e0)>>3)|((p&0x0600)>>9);
-	      dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
-	      src+=2;
-	      dst++;
-	    }
-	  break;
-	case MODE_ARGB4444:
-	  for(y=0; y<h; y++)
-	    for(x=0; x<w; x++) {
-	      unsigned int p;
-	      src = s+(((twiddletab[x]<<1)|twiddletab[y])<<1);
-	      p = src[0]|(src[1]<<8);
-	      dst->r = ((p&0x0f00)>>4)|((p&0x0f00)>>8);
-	      dst->g = (p&0x00f0)|((p&0x00f0)>>4);
-	      dst->b = ((p&0x000f)<<4)|(p&0x000f);
-	      src+=2;
-	      dst++;
-	    }
-	  break;
-       }
+       if(h<w)
+	 for(x=0; x<w; x+=h)
+	   pvr_decode_twiddled(attr, s+bpp*h*x, img->img+x, w-h, h);
+       else
+	 for(x=0; x<h; x+=w)
+	   pvr_decode_twiddled(attr, s+bpp*w*x, img->img+w*x, 0, w);
      else
-       switch(attr&0xff) {
-	case MODE_ARGB1555:
-	case MODE_RGB555:
-	  while(cnt--) {
-	    unsigned int p = src[0]|(src[1]<<8);
-	    dst->r = ((p&0x7c00)>>7)|((p&0x7000)>>12);
-	    dst->g = ((p&0x03e0)>>2)|((p&0x0380)>>7);
-	    dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
-	    src+=2;
-	    dst++;
-	  }
-	  break;
-	case MODE_RGB565:
-	  while(cnt--) {
-	    unsigned int p = src[0]|(src[1]<<8);
-	    dst->r = ((p&0xf800)>>8)|((p&0xe000)>>13);
-	    dst->g = ((p&0x07e0)>>3)|((p&0x0600)>>9);
-	    dst->b = ((p&0x001f)<<3)|((p&0x001c)>>2);
-	    src+=2;
-	    dst++;
-	  }
-	  break;
-	case MODE_ARGB4444:
-	  while(cnt--) {
-	    unsigned int p = src[0]|(src[1]<<8);
-	    dst->r = ((p&0x0f00)>>4)|((p&0x0f00)>>8);
-	    dst->g = (p&0x00f0)|((p&0x00f0)>>4);
-	    dst->b = ((p&0x000f)<<4)|(p&0x000f);
-	    src+=2;
-	    dst++;
-	  }
-	  break;
-       }
-     
+       pvr_decode_rect(attr, s, img->img, 0, h, w);
    }
 
    f_aggregate_mapping(2*n);