Index: app/base/tile-manager-private.h
===================================================================
RCS file: /cvs/gnome/gimp/app/base/tile-manager-private.h,v
retrieving revision 1.9
diff -u -r1.9 tile-manager-private.h
--- app/base/tile-manager-private.h	3 Dec 2001 13:44:50 -0000	1.9
+++ app/base/tile-manager-private.h	3 Apr 2003 13:42:10 -0000
@@ -35,6 +35,8 @@
   TileValidateProc   validate_proc; /*  this proc is called when an attempt
 				     *  to get an invalid tile is made.
 				     */
+  gint               cached_num;    /*  number of cached tile */
+  Tile              *cached_tile;   /*  the actual cached tile */
 
   gpointer           user_data;     /*  hook for hanging data off of         */
 };
Index: app/base/tile-manager.c
===================================================================
RCS file: /cvs/gnome/gimp/app/base/tile-manager.c,v
retrieving revision 1.35
diff -u -r1.35 tile-manager.c
--- app/base/tile-manager.c	30 Jan 2002 16:14:16 -0000	1.35
+++ app/base/tile-manager.c	3 Apr 2003 13:42:10 -0000
@@ -32,11 +32,6 @@
 #include "tile-swap.h"
 
 
-static gint tile_manager_get_tile_num (TileManager *tm,
-				       gint         xpixel,
-				       gint         ypixel);
-
-
 TileManager *
 tile_manager_new (gint toplevel_width,
 		  gint toplevel_height,
@@ -54,14 +49,17 @@
   width  = toplevel_width;
   height = toplevel_height;
 
-  tm->x          = 0;
-  tm->y          = 0;
-  tm->width      = width;
-  tm->height     = height;
-  tm->bpp        = bpp;
-  tm->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
-  tm->ntile_cols = (width  + TILE_WIDTH  - 1) / TILE_WIDTH;
-  tm->tiles      = NULL;
+  tm->x           = 0;
+  tm->y           = 0;
+  tm->width       = width;
+  tm->height      = height;
+  tm->bpp         = bpp;
+  tm->ntile_rows  = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
+  tm->ntile_cols  = (width  + TILE_WIDTH  - 1) / TILE_WIDTH;
+  tm->tiles       = NULL;
+  
+  tm->cached_num  = -1;
+  tm->cached_tile = NULL;
   
   return tm;
 }
@@ -74,6 +72,9 @@
 
   g_return_if_fail (tm != NULL);
   
+  if (tm->cached_tile)
+    tile_release (tm->cached_tile, FALSE);
+
   if (tm->tiles)
     {
       ntiles = tm->ntile_rows * tm->ntile_cols;
@@ -501,7 +502,7 @@
   /*  printf("}");fflush(stdout);*/
 }
 
-static gint
+gint
 tile_manager_get_tile_num (TileManager *tm,
 		           gint         xpixel,
 		           gint         ypixel)
@@ -809,14 +810,39 @@
 		   gint	         x,
 		   gint          y,
 		   guchar       *buffer)
-{
-  Tile *t;
-  guchar *s;
+{  
+  if( x >= 0 && y >= 0 && x < tm->width && y < tm->height )
+    {
+      gint num = tile_manager_get_tile_num (tm, x, y);
 
-  t = tile_manager_get_tile (tm, x, y, TRUE, FALSE);
-  s = tile_data_pointer (t, x % TILE_WIDTH, y % TILE_HEIGHT);
-  memcpy (buffer, s, tm->bpp);
-  tile_release (t, FALSE);
+      if (num != tm->cached_num)    /* must fetch a new tile */
+        {    
+           if (tm->cached_tile)
+             tile_release (tm->cached_tile, FALSE);
+             
+           tm->cached_num = num;
+           tm->cached_tile = tile_manager_get (tm, num, TRUE, FALSE);
+        }
+        
+      if (tm->cached_tile)
+        {
+           guchar *data;
+           data = tile_data_pointer (tm->cached_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
+           switch (tm->bpp)
+             {
+               case 4:
+                 *(guint*)buffer = *(guint*)data;
+                 break;
+               default:
+                 {
+                   gint i;
+                   for(i=0;i<tm->bpp;i++)
+                     buffer[i]=data[i];
+                 }
+                break;
+             }        
+        }
+    }
 }
 
 void
Index: app/base/tile-manager.h
===================================================================
RCS file: /cvs/gnome/gimp/app/base/tile-manager.h,v
retrieving revision 1.15
diff -u -r1.15 tile-manager.h
--- app/base/tile-manager.h	30 Jan 2002 16:14:16 -0000	1.15
+++ app/base/tile-manager.h	3 Apr 2003 13:42:10 -0000
@@ -63,6 +63,13 @@
 					      gint         wantread,
 					      gint         wantwrite);
 
+/* Get the number referring to a tile, given it's coordinates
+ */
+gint tile_manager_get_tile_num (TileManager *tm,
+				       gint         xpixel,
+				       gint         ypixel);
+
+
 /* Get a specified tile from a tile manager.
  */
 Tile        * tile_manager_get               (TileManager *tm,
@@ -85,7 +92,7 @@
 					      Tile        *srctile);
 
 void          tile_manager_map               (TileManager *tm,
-					      gint         time_num,
+					      gint         tile_num,
 					      Tile        *srctile);
 
 /* Validate a tiles memory.
@@ -159,6 +166,9 @@
 				    guchar      *buffer,
 				    guint        stride);
 
+/*   Fill buffer with the pixeldata for the pixel at coordinates x,y
+ *   if x,y is outside the area of the tilemanger, nothing is done.
+ */
 void		  read_pixel_data_1 (TileManager *tm,
 				     gint	  x,
 				     gint	  y,
Index: app/core/gimpdrawable-transform-utils.c
===================================================================
RCS file: /cvs/gnome/gimp/app/core/gimpdrawable-transform-utils.c,v
retrieving revision 1.4
diff -u -r1.4 gimpdrawable-transform-utils.c
--- app/core/gimpdrawable-transform-utils.c	31 Mar 2003 08:32:30 -0000	1.4
+++ app/core/gimpdrawable-transform-utils.c	3 Apr 2003 13:42:10 -0000
@@ -155,7 +155,6 @@
    */
   {
     gdouble dx1, dx2, dx3, dy1, dy2, dy3;
-    gdouble det1, det2;
 
     dx1 = tx2 - tx4;
     dx2 = tx3 - tx4;
@@ -179,11 +178,12 @@
       }
     else
       {
+        gdouble det1, det2;
+
         det1 = dx3 * dy2 - dy3 * dx2;
         det2 = dx1 * dy2 - dy1 * dx2;
         matrix[2][0] = det1 / det2;
         det1 = dx1 * dy3 - dy1 * dx3;
-        det2 = dx1 * dy2 - dy1 * dx2;
         matrix[2][1] = det1 / det2;
 
         matrix[0][0] = tx2 - tx1 + matrix[2][0] * tx2;
Index: app/core/gimpdrawable-transform.c
===================================================================
RCS file: /cvs/gnome/gimp/app/core/gimpdrawable-transform.c,v
retrieving revision 1.60
diff -u -r1.60 gimpdrawable-transform.c
--- app/core/gimpdrawable-transform.c	25 Mar 2003 16:36:34 -0000	1.60
+++ app/core/gimpdrawable-transform.c	3 Apr 2003 13:42:10 -0000
@@ -1,5 +1,5 @@
 /* The GIMP -- an image manipulation program
- * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
+ * Copyright (C) 1995-2003 Spencer Kimball, Peter Mattis, and others
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -45,32 +45,48 @@
 
 #include "gimp-intl.h"
 
+#define MIN4(a,b,c,d) MIN(MIN(a,b),MIN(c,d))
+#define MAX4(a,b,c,d) MAX(MAX(a,b),MAX(c,d))
 
-#define BILINEAR(jk,j1k,jk1,j1k1,dx,dy) \
-                ((1-dy) * (jk + dx * (j1k - jk)) + \
-		    dy  * (jk1 + dx * (j1k1 - jk1)))
-
-/* access interleaved pixels */
-#define CUBIC_ROW(dx, row, step) \
-  gimp_drawable_transform_cubic(dx, (row)[0], (row)[step], (row)[step+step], (row)[step+step+step])
-#define CUBIC_SCALED_ROW(dx, row, step, i) \
-  gimp_drawable_transform_cubic(dx, (row)[0] * (row)[i], \
-            (row)[step] * (row)[step + i], \
-            (row)[step+step]* (row)[step+step + i], \
-            (row)[step+step+step] * (row)[step+step+step + i])
-
-#define REF_TILE(i,x,y) \
-     tile[i] = tile_manager_get_tile (float_tiles, x, y, TRUE, FALSE); \
-     src[i] = tile_data_pointer (tile[i], (x) % TILE_WIDTH, (y) % TILE_HEIGHT);
-
+/* recursion level should be a usersettable parameter,
+   3 seems to be a reasonable default */
+#define RECURSION_LEVEL 3
 
 /*  forward function prototypes  */
 
-static gdouble   gimp_drawable_transform_cubic (gdouble dx,
-					        gint    jm1,
-					        gint    j,
-					        gint    jp1,
-					        gint    jp2);
+static gint 
+supersample_dtest (gdouble u0, gdouble v0,
+                   gdouble u1, gdouble v1,
+                   gdouble u2, gdouble v2,
+                   gdouble u3, gdouble v3);
+
+static void sample_adapt (TileManager *tm,
+                          gdouble      uc,     gdouble     vc,
+                          gdouble      u0,     gdouble     v0,
+                          gdouble      u1,     gdouble     v1,
+                          gdouble      u2,     gdouble     v2,
+                          gdouble      u3,     gdouble     v3,
+                          gint         level,
+                          guchar      *color,
+                          guchar      *bg_color,
+                          gint         bpp, 
+                          gint         alpha);
+
+static void 
+sample_cubic (PixelSurround *surround, 
+              gdouble        u, 
+              gdouble        v, 
+              guchar        *color,
+              gint           bytes, 
+              gint           alpha);
+
+static void 
+sample_linear(PixelSurround *surround, 
+              gdouble        u,
+              gdouble        v, 
+              guchar        *color,
+              gint           bytes,
+              gint           alpha);
 
 
 /*  public functions  */
@@ -90,28 +106,31 @@
   TileManager *tiles;
   GimpMatrix3  m;
   GimpMatrix3  im;
-  gint         itx, ity;
-  gint         tx1, ty1, tx2, ty2;
-  gint         width, height;
-  gint         alpha;
-  gint         bytes, b;
-  gint         x, y;
-  gint         sx, sy;
-  gint         x1, y1, x2, y2;
-  gdouble      xinc, yinc, winc;
-  gdouble      tx, ty, tw;
-  gdouble      ttx = 0.0, tty = 0.0;
-  guchar      *dest;
-  guchar      *d;
-  guchar      *src[16];
-  Tile        *tile[16];
-  guchar       bg_col[MAX_CHANNELS];
-  gint         i;
-  gdouble      a_val, a_recip;
-  gint         newval;
-
   PixelSurround surround;
 
+
+  gint         x1, y1, x2, y2;        /* target bounding box */
+  gint         x, y;                  /* target coordinates */
+  gint         u1, v1, u2, v2;        /* source bounding box */
+  gdouble      uinc, vinc, winc;      /* increments in source coordinates 
+                                         pr horizontal target coordinate */
+
+  gdouble      u[5],v[5];             /* source coordinates,    
+                                           2
+                                         /  \   0 is sample in the centre of pixel
+                                        1 0 3   1..4 is offset 1 pixel in each
+                                         \  /   direction (in target space)
+                                          4
+                                      */
+
+  gdouble      tu[5],tv[5],tw[5];    /* undivided source coordinates and divisor */
+
+  gint         width;
+  gint         alpha;
+  gint         bytes;
+  guchar      *dest, *d;
+  guchar       bg_color[MAX_CHANNELS];
+
   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
   g_return_val_if_fail (float_tiles != NULL, NULL);
 
@@ -126,20 +145,21 @@
     interpolation_type = GIMP_INTERPOLATION_NONE;
 
   /*  Get the background color  */
-  gimp_image_get_background (gimage, drawable, bg_col);
+  gimp_image_get_background (gimage, drawable, bg_color);
+
 
   switch (GIMP_IMAGE_TYPE_BASE_TYPE (gimp_drawable_type (drawable)))
     {
     case GIMP_RGB:
-      bg_col[ALPHA_PIX] = TRANSPARENT_OPACITY;
+      bg_color[ALPHA_PIX] = TRANSPARENT_OPACITY;
       alpha = ALPHA_PIX;
       break;
     case GIMP_GRAY:
-      bg_col[ALPHA_G_PIX] = TRANSPARENT_OPACITY;
+      bg_color[ALPHA_G_PIX] = TRANSPARENT_OPACITY;
       alpha = ALPHA_G_PIX;
       break;
     case GIMP_INDEXED:
-      bg_col[ALPHA_I_PIX] = TRANSPARENT_OPACITY;
+      bg_color[ALPHA_I_PIX] = TRANSPARENT_OPACITY;
       alpha = ALPHA_I_PIX;
       /*  If the gimage is indexed color, ignore interpolation value  */
       interpolation_type = GIMP_INTERPOLATION_NONE;
@@ -152,7 +172,7 @@
   /*  enable rotating un-floated non-layers  */
   if (tile_manager_bpp (float_tiles) == 1)
     {
-      bg_col[0] = OPAQUE_OPACITY;
+      bg_color[0] = OPAQUE_OPACITY;
 
       /*  setting alpha = 0 will cause the channel's value to be treated
        *  as alpha and the color channel loops never to be entered
@@ -182,17 +202,17 @@
   path_transform_current_path (gimage, matrix, FALSE);
 #endif
 
-  tile_manager_get_offsets (float_tiles, &x1, &y1);
-  x2 = x1 + tile_manager_width (float_tiles);
-  y2 = y1 + tile_manager_height (float_tiles);
+  tile_manager_get_offsets (float_tiles, &u1, &v1);
+  u2 = u1 + tile_manager_width (float_tiles);
+  v2 = v1 + tile_manager_height (float_tiles);
 
-  /*  Find the bounding coordinates  */
+  /*  Find the bounding coordinates of target */
   if (alpha == 0 || clip_result)
     {
-      tx1 = x1;
-      ty1 = y1;
-      tx2 = x2;
-      ty2 = y2;
+      x1 = u1;
+      y1 = v1;
+      x2 = u2;
+      y2 = v2;
     }
   else
     {
@@ -201,65 +221,54 @@
       gdouble dx3, dy3;
       gdouble dx4, dy4;
 
-      gimp_matrix3_transform_point (matrix, x1, y1, &dx1, &dy1);
-      gimp_matrix3_transform_point (matrix, x2, y1, &dx2, &dy2);
-      gimp_matrix3_transform_point (matrix, x1, y2, &dx3, &dy3);
-      gimp_matrix3_transform_point (matrix, x2, y2, &dx4, &dy4);
-
-#define MIN4(a,b,c,d) MIN(MIN(MIN(a,b),c),d)
-#define MAX4(a,b,c,d) MAX(MAX(MAX(a,b),c),d)
+      gimp_matrix3_transform_point (matrix, u1, v1, &dx1, &dy1);
+      gimp_matrix3_transform_point (matrix, u2, v1, &dx2, &dy2);
+      gimp_matrix3_transform_point (matrix, u1, v2, &dx3, &dy3);
+      gimp_matrix3_transform_point (matrix, u2, v2, &dx4, &dy4);
 
-      tx1 = ROUND (MIN4 (dx1, dx2, dx3, dx4));
-      ty1 = ROUND (MIN4 (dy1, dy2, dy3, dy4));
+      x1 = ROUND (MIN4 (dx1, dx2, dx3, dx4));
+      y1 = ROUND (MIN4 (dy1, dy2, dy3, dy4));
 
-      tx2 = ROUND (MAX4 (dx1, dx2, dx3, dx4));
-      ty2 = ROUND (MAX4 (dy1, dy2, dy3, dy4));
-
-#undef MIN2
-#undef MAX4
+      x2 = ROUND (MAX4 (dx1, dx2, dx3, dx4));
+      y2 = ROUND (MAX4 (dy1, dy2, dy3, dy4));
     }
 
   /*  Get the new temporary buffer for the transformed result  */
-  tiles = tile_manager_new ((tx2 - tx1), (ty2 - ty1),
-			    tile_manager_bpp (float_tiles));
-  pixel_region_init (&destPR, tiles, 0, 0, (tx2 - tx1), (ty2 - ty1), TRUE);
-  tile_manager_set_offsets (tiles, tx1, ty1);
+  tiles = tile_manager_new ((x2 - x1), (y2 - y1),
+                tile_manager_bpp (float_tiles));
+  pixel_region_init (&destPR, tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE);
+  tile_manager_set_offsets (tiles, x1, y1);
 
-  /* initialise the pixel_surround accessor */
+  /* initialise the pixel_surround and pixel_cache accessors */
   switch (interpolation_type)
     {
     case GIMP_INTERPOLATION_NONE:
-      /* not actually useful, keeps the code cleaner */
-      pixel_surround_init (&surround, float_tiles, 1, 1, bg_col);
       break;
-
     case GIMP_INTERPOLATION_CUBIC:
-      pixel_surround_init (&surround, float_tiles, 4, 4, bg_col);
+      pixel_surround_init (&surround, float_tiles, 4, 4, bg_color);
       break;
-
     case GIMP_INTERPOLATION_LINEAR:
-      pixel_surround_init (&surround, float_tiles, 2, 2, bg_col);
+      pixel_surround_init (&surround, float_tiles, 2, 2, bg_color);
       break;
     }
 
   width  = tile_manager_width (tiles);
-  height = tile_manager_height (tiles);
   bytes  = tile_manager_bpp (tiles);
 
-  dest = g_new (guchar, width * bytes);
+  dest = g_new (guchar, tile_manager_width(tiles) * bytes);
 
-  xinc = m[0][0];
-  yinc = m[1][0];
+  uinc = m[0][0];
+  vinc = m[1][0];
   winc = m[2][0];
 
   /* these loops could be rearranged, depending on which bit of code
    * you'd most like to write more than once.
    */
 
-  for (y = ty1; y < ty2; y++)
+  for (y = y1; y < y2; y++)
     {
       if (progress_callback && !(y & 0xf))
-	(* progress_callback) (ty1, ty2, y, progress_data);
+    (* progress_callback) (y1, y2, y, progress_data);
 
       /* set up inverse transform steps */
       if (interpolation_type == GIMP_INTERPOLATION_NONE)
@@ -268,265 +277,160 @@
            *  as we end up at discrete pixel positions and are not aware of
            *  errors in the algorithm below
            */
-          tx = xinc * (tx1 + 0.5) + m[0][1] * (y + 0.5) + m[0][2];
-          ty = yinc * (tx1 + 0.5) + m[1][1] * (y + 0.5) + m[1][2];
-          tw = winc * (tx1 + 0.5) + m[2][1] * (y + 0.5) + m[2][2];
+          tu[0] = uinc * (x1 + 0.5) + m[0][1] * (y + 0.5) + m[0][2];
+          tv[0] = vinc * (x1 + 0.5) + m[1][1] * (y + 0.5) + m[1][2];
+          tw[0] = winc * (x1 + 0.5) + m[2][1] * (y + 0.5) + m[2][2];
         }
       else
         {
-          tx = xinc * tx1 + m[0][1] * y + m[0][2];
-          ty = yinc * tx1 + m[1][1] * y + m[1][2];
-          tw = winc * tx1 + m[2][1] * y + m[2][2];
+          tu[0] = uinc * (x1  ) + m[0][1] * (y  ) + m[0][2];
+          tv[0] = vinc * (x1  ) + m[1][1] * (y  ) + m[1][2];
+          tw[0] = winc * (x1  ) + m[2][1] * (y  ) + m[2][2];
+
+          tu[1] = uinc * (x1-1) + m[0][1] * (y  ) + m[0][2];
+          tv[1] = vinc * (x1-1) + m[1][1] * (y  ) + m[1][2];
+          tw[1] = winc * (x1-1) + m[2][1] * (y  ) + m[2][2];
+
+          tu[2] = uinc * (x1  ) + m[0][1] * (y-1) + m[0][2];
+          tv[2] = vinc * (x1  ) + m[1][1] * (y-1) + m[1][2];
+          tw[2] = winc * (x1  ) + m[2][1] * (y-1) + m[2][2];
+
+          tu[3] = uinc * (x1  ) + m[0][1] * (y+1) + m[0][2];
+          tv[3] = vinc * (x1  ) + m[1][1] * (y+1) + m[1][2];
+          tw[3] = winc * (x1  ) + m[2][1] * (y+1) + m[2][2];
+
+          tu[4] = uinc * (x1-1) + m[0][1] * (y  ) + m[0][2];
+          tv[4] = vinc * (x1-1) + m[1][1] * (y  ) + m[1][2];
+          tw[4] = winc * (x1-1) + m[2][1] * (y  ) + m[2][2];
         }
 
       d = dest;
 
-      for (x = tx1; x < tx2; x++)
-	{
-	  /*  normalize homogeneous coords  */
-	  if (tw == 0.0)
-	    {
-	      g_warning ("homogeneous coordinate = 0...\n");
-	    }
-	  else if (tw != 1.0)
-	    {
-	      ttx = tx / tw;
-	      tty = ty / tw;
-	    }
-	  else
-	    {
-	      ttx = tx;
-	      tty = ty;
-	    }
-
-          /*  Set the destination pixels  */
+      for (x = x1; x < x2; x++)
+    {
 
-          switch (interpolation_type)
+      {
+        gint i;        /*  normalize homogeneous coords  */
+        gint coords=1;
+        if (!interpolation_type==GIMP_INTERPOLATION_NONE)
+            coords=5;
+        for(i=0;i<coords;i++){
+          if (tw[i] == 0.0)
+            {
+              g_warning ("homogeneous coordinate = 0...\n");
+            }
+          else if (tw[i] != 1.0)
             {
-            case GIMP_INTERPOLATION_CUBIC:
-              /*  ttx & tty are the subpixel coordinates of the point in
-               *  the original selection's floating buffer.
-               *  We need the four integer pixel coords around them:
-               *  itx to itx + 3, ity to ity + 3
-               */
-              itx = floor (ttx);
-              ity = floor (tty);
+              u[i] = tu[i] / tw[i];
+              v[i] = tv[i] / tw[i];
+            }
+          else
+            {
+              u[i] = tu[i];
+              v[i] = tv[i];
+            }
+        }
+      }
 
-              /* check if any part of our region overlaps the buffer */
+          /*  Set the destination pixels  */
+        if(interpolation_type==GIMP_INTERPOLATION_NONE)
+          {
+              guchar color[MAX_CHANNELS];
+              gint iu = floor (u[0]);
+              gint iv = floor (v[0]);
+              gint b;
 
-              if ((itx + 2) >= x1 && (itx - 1) < x2 &&
-                  (ity + 2) >= y1 && (ity - 1) < y2)
+              if (iu >= u1 && iu < u2 &&
+                  iv >= v1 && iv < v2)
                 {
-                  guchar  *data;
-                  gint     row;
-                  gdouble  dx, dy;
-                  guchar  *start;
-
-                  /* lock the pixel surround */
-                  data = pixel_surround_lock (&surround,
-                                              itx - 1 - x1, ity - 1 - y1);
-
-                  row = pixel_surround_rowstride (&surround);
-
-                  /* the fractional error */
-                  dx = ttx - itx;
-                  dy = tty - ity;
-
-                  /* calculate alpha of result */
-                  start = &data[alpha];
-                  a_val = gimp_drawable_transform_cubic
-                    (dy,
-                     CUBIC_ROW (dx, start, bytes),
-                     CUBIC_ROW (dx, start + row, bytes),
-                     CUBIC_ROW (dx, start + row + row, bytes),
-                     CUBIC_ROW (dx, start + row + row + row, bytes));
-
-                  if (a_val <= 0.0)
-                    {
-                      a_recip = 0.0;
-                      d[alpha] = 0;
-                    }
-                  else if (a_val > 255.0)
-                    {
-                      a_recip = 1.0 / a_val;
-                      d[alpha] = 255;
-                    }
-                  else
-                    {
-                      a_recip = 1.0 / a_val;
-                      d[alpha] = RINT(a_val);
-                    }
-
-                  /*  for colour channels c,
-                   *  result = bicubic (c * alpha) / bicubic (alpha)
-                   *
-                   *  never entered for alpha == 0
-                   */
-                  for (i = -alpha; i < 0; ++i)
-                    {
-                      start = &data[alpha];
-                      newval =
-                        RINT (a_recip *
-                              gimp_drawable_transform_cubic
-                              (dy,
-                               CUBIC_SCALED_ROW (dx, start, bytes, i),
-                               CUBIC_SCALED_ROW (dx, start + row, bytes, i),
-                               CUBIC_SCALED_ROW (dx, start + row + row, bytes, i),
-                               CUBIC_SCALED_ROW (dx, start + row + row + row, bytes, i)));
-                      if (newval <= 0)
-                        {
-                          *d++ = 0;
-                        }
-                      else if (newval > 255)
-                        {
-                          *d++ = 255;
-                        }
-                      else
-                        {
-                          *d++ = newval;
-                        }
-                    }
+                  /*  u, v coordinates into source tiles  */
+                  gint u = iu - u1;
+                  gint v = iv - v1;
 
-                  /*  alpha already done  */
-                  d++;
+                  read_pixel_data_1 (float_tiles, u, v, color);
 
-                  pixel_surround_release (&surround);
-                }
-              else /* not in source range */
-                {
-                  /*  increment the destination pointers  */
                   for (b = 0; b < bytes; b++)
-                    *d++ = bg_col[b];
-                }
-              break;
-
-            case GIMP_INTERPOLATION_LINEAR:
-              itx = floor (ttx);
-              ity = floor (tty);
-
-              /*  expand source area to cover interpolation region
-               *  (which runs from itx to itx + 1, same in y)
-               */
-              if ((itx + 1) >= x1 && itx < x2 &&
-                  (ity + 1) >= y1 && ity < y2)
-                {
-                  guchar  *data;
-                  gint     row;
-                  double   dx, dy;
-                  guchar  *chan;
-
-                  /* lock the pixel surround */
-                  data = pixel_surround_lock (&surround, itx - x1, ity - y1);
-
-                  row = pixel_surround_rowstride (&surround);
-
-                  /* the fractional error */
-                  dx = ttx - itx;
-                  dy = tty - ity;
-
-                  /* calculate alpha value of result pixel */
-                  chan = &data[alpha];
-                  a_val = BILINEAR (chan[0], chan[bytes], chan[row],
-                                    chan[row+bytes], dx, dy);
-                  if (a_val <= 0.0)
-                    {
-                      a_recip = 0.0;
-                      d[alpha] = 0.0;
-                    }
-                  else if (a_val >= 255.0)
-                    {
-                      a_recip = 1.0 / a_val;
-                      d[alpha] = 255;
-                    }
-                  else
-                    {
-                      a_recip = 1.0 / a_val;
-                      d[alpha] = RINT (a_val);
-                    }
-
-                  /*  for colour channels c,
-                   *  result = bilinear (c * alpha) / bilinear (alpha)
-                   *
-                   *  never entered for alpha == 0
-                   */
-                  for (i = -alpha; i < 0; ++i)
-                    {
-                      chan = &data[alpha];
-                      newval =
-                        RINT (a_recip *
-                              BILINEAR (chan[0] * chan[i],
-                                        chan[bytes] * chan[bytes+i],
-                                        chan[row] * chan[row+i],
-                                        chan[row+bytes] * chan[row+bytes+i],
-                                        dx, dy));
-                      if (newval <= 0)
-                        {
-                          *d++ = 0;
-                        }
-                      else if (newval > 255)
-                        {
-                          *d++ = 255;
-                        }
-                      else
-                        {
-                          *d++ = newval;
-                        }
-                    }
-
-                  /*  alpha already done  */
-                  d++;
-
-                  pixel_surround_release (&surround);
+                    *d++ = color[b];
                 }
               else /* not in source range */
                 {
                   /*  increment the destination pointers  */
+                  gint b;
+                  
                   for (b = 0; b < bytes; b++)
-                    *d++ = bg_col[b];
-                }
-              break;
-
-            case GIMP_INTERPOLATION_NONE:
-              itx = floor (ttx);
-              ity = floor (tty);
-
-              if (itx >= x1 && itx < x2 &&
-                  ity >= y1 && ity < y2)
+                    *d++ = bg_color[b];
+                }            
+          }
+        else 
+          {
+                                
+                if (MAX4 (u[1], u[2], u[3], u[4]) < u1 ||
+                    MAX4 (v[1], v[2], v[3], v[4]) < v1 ||
+                    MIN4 (u[1], u[2], u[3], u[4]) >= u2 ||
+                    MIN4 (v[1], v[2], v[3], v[4]) >= v2)
+                
+                {    /* not in source range */
+                     /*  increment the destination pointers  */
+                  gint b;
+                  for (b = 0; b < bytes; b++)
+                     *d++ = bg_color[b];
+                } 
+              else 
                 {
-                  /*  x, y coordinates into source tiles  */
-                  sx = itx - x1;
-                  sy = ity - y1;
+                   guchar color[MAX_CHANNELS];
 
-                  REF_TILE (0, sx, sy);
+                   if (RECURSION_LEVEL && 
+                       supersample_dtest (u[1], v[1], u[2], v[2], u[3], v[3], u[4], v[4])){
 
-                  for (b = 0; b < bytes; b++)
-                    *d++ = src[0][b];
+                      sample_adapt(float_tiles,
+                          u[0]-u1, v[0]-v1,
+                          u[1]-u1, v[1]-v1,
+                          u[2]-u1, v[2]-v1,
+                          u[3]-u1, v[3]-v1,
+                          u[4]-u1, v[4]-v1,
+                          RECURSION_LEVEL, color, bg_color, bytes, alpha);
+                   } 
+                 else 
+                   { 
+                      /* cubic only needs to be done if no supersampling is needed */
+                   
+                      if (interpolation_type == GIMP_INTERPOLATION_LINEAR)
+                          sample_linear (&surround, u[0] - u1, v[0] - v1, color, bytes, alpha);
+                      else
+                          sample_cubic (&surround, u[0] - u1, v[0] - v1, color, bytes, alpha);
 
-                  tile_release (tile[0], FALSE);
-                }
-              else /* not in source range */
-                {
-                  /*  increment the destination pointers  */
-                  for (b = 0; b < bytes; b++)
-                    *d++ = bg_col[b];
-                }
-              break;
-            }
+                   }
 
-	  /*  increment the transformed coordinates  */
-	  tx += xinc;
-	  ty += yinc;
-	  tw += winc;
-	}
+                  {  /*  Set the destination pixel  */
+                    gint b;
+                    for (b=0; b<bytes; b++)
+                      *d++ = color[b];
+                  }
+               }      
+          }
+
+      {
+        gint i;        /*  increment the transformed coordinates  */
+        gint coords=1;
+        if (!interpolation_type==GIMP_INTERPOLATION_NONE)
+            coords=5;
+        for (i=0; i<coords; i++)
+        {
 
+          tu[i] += uinc;
+          tv[i] += vinc;
+          tw[i] += winc;
+        }
+      }
+    }
       /*  set the pixel region row  */
-      pixel_region_set_row (&destPR, 0, (y - ty1), width, dest);
+      pixel_region_set_row (&destPR, 0, (y - y1), width, dest);
     }
-
-  pixel_surround_clear (&surround);
+  
+  if(interpolation_type!=GIMP_INTERPOLATION_NONE)
+      pixel_surround_clear (&surround);
 
   g_free (dest);
-
   return tiles;
 }
 
@@ -716,9 +620,9 @@
   else
     {
       if (GIMP_IS_LAYER (drawable))
-	tiles = gimp_image_mask_extract (gimage, drawable, FALSE, TRUE, TRUE);
+        tiles = gimp_image_mask_extract (gimage, drawable, FALSE, TRUE, TRUE);
       else
-	tiles = gimp_image_mask_extract (gimage, drawable, FALSE, TRUE, FALSE);
+        tiles = gimp_image_mask_extract (gimage, drawable, FALSE, TRUE, FALSE);
 
       *new_layer = FALSE;
     }
@@ -746,21 +650,21 @@
   if (new_layer)
     {
       layer =
-	gimp_layer_new_from_tiles (tiles,
+        gimp_layer_new_from_tiles (tiles,
                                    gimage,
                                    gimp_drawable_type_with_alpha (drawable),
-				   _("Transformation"),
-				   GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
+                   _("Transformation"),
+                   GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
       if (! layer)
         {
           g_warning ("%s: gimp_layer_new_frome_tiles() failed",
-		     G_GNUC_FUNCTION);
+             G_GNUC_FUNCTION);
           return FALSE;
         }
 
       tile_manager_get_offsets (tiles, 
-				&(GIMP_DRAWABLE (layer)->offset_x),
-				&(GIMP_DRAWABLE (layer)->offset_y));
+                &(GIMP_DRAWABLE (layer)->offset_x),
+                &(GIMP_DRAWABLE (layer)->offset_y));
 
       /*  Start a group undo  */
       gimp_image_undo_group_start (gimage, GIMP_UNDO_GROUP_EDIT_PASTE,
@@ -779,19 +683,19 @@
   else
     {
       if (GIMP_IS_LAYER (drawable))
-	layer = GIMP_LAYER (drawable);
+        layer = GIMP_LAYER (drawable);
       else if (GIMP_IS_CHANNEL (drawable))
-	channel = GIMP_CHANNEL (drawable);
+        channel = GIMP_CHANNEL (drawable);
       else
-	return FALSE;
+        return FALSE;
 
       if (layer)
-	gimp_layer_add_alpha (layer);
+        gimp_layer_add_alpha (layer);
 
       floating_layer = gimp_image_floating_sel (gimage);
 
       if (floating_layer)
-	floating_sel_relax (floating_layer, TRUE);
+        floating_sel_relax (floating_layer, TRUE);
 
       gimp_image_update (gimage,
                          drawable->offset_x,
@@ -801,10 +705,10 @@
 
       /*  Push an undo  */
       if (layer)
-	gimp_image_undo_push_layer_mod (gimage, _("Transform Layer"),
+        gimp_image_undo_push_layer_mod (gimage, _("Transform Layer"),
                                         layer);
       else if (channel)
-	gimp_image_undo_push_channel_mod (gimage, _("Transform Channel"),
+        gimp_image_undo_push_channel_mod (gimage, _("Transform Channel"),
                                           channel);
 
       /*  set the current layer's data  */
@@ -815,26 +719,343 @@
       drawable->height   = tile_manager_height (tiles);
       drawable->bytes    = tile_manager_bpp (tiles);
       tile_manager_get_offsets (tiles, 
-				&drawable->offset_x, &drawable->offset_y);
+                &drawable->offset_x, &drawable->offset_y);
 
       if (floating_layer)
-	floating_sel_rigor (floating_layer, TRUE);
+        floating_sel_rigor (floating_layer, TRUE);
 
       gimp_drawable_update (drawable,
-			    0, 0,
-			    gimp_drawable_width (drawable),
-			    gimp_drawable_height (drawable));
+                0, 0,
+                gimp_drawable_width (drawable),
+                gimp_drawable_height (drawable));
 
       /*  if we were operating on the floating selection, then it's boundary 
        *  and previews need invalidating
        */
       if (drawable == (GimpDrawable *) floating_layer)
-	floating_sel_invalidate (floating_layer);
+        floating_sel_invalidate (floating_layer);
 
       return TRUE;
     }
 }
 
+#define BILINEAR(jk,j1k,jk1,j1k1,dx,dy) \
+                ((1-dy) * (jk + dx * (j1k - jk)) + \
+                   dy  * (jk1 + dx * (j1k1 - jk1)))
+
+  /*  u & v are the subpixel coordinates of the point in
+   *  the original selection's floating buffer.
+   *  We need the two pixel coords around them:
+   *  iu to iu + 1, iv to iv + 1
+   */
+static void 
+sample_linear (PixelSurround *surround, 
+               gdouble        u,
+               gdouble        v, 
+               guchar        *color,
+               gint           bytes,
+               gint           alpha)
+{
+    gdouble  a_val, a_recip;
+    gint     i;
+    gint     iu=floor (u);
+    gint     iv=floor (v);
+    gint     row;
+    gdouble  du,dv;
+    guchar  *alphachan;
+    guchar  *data;
+    
+    /* lock the pixel surround */
+    data = pixel_surround_lock (surround, iu, iv);
+
+    row = pixel_surround_rowstride (surround);
+
+    /* the fractional error */
+    du = u - iu;
+    dv = v - iv;
+
+    /* calculate alpha value of result pixel */
+    alphachan = &data[alpha];
+    a_val = BILINEAR (alphachan[0],   alphachan[bytes], 
+                      alphachan[row], alphachan[row+bytes], du, dv);
+    if (a_val <= 0.0)
+      {
+        a_recip = 0.0;
+        color[alpha] = 0.0;
+      }
+    else if (a_val >= 255.0)
+      {
+        a_recip = 1.0 / a_val;
+        color[alpha] = 255;
+      }
+    else
+      {
+        a_recip = 1.0 / a_val;
+        color[alpha] = RINT (a_val);
+      }
+
+    /*  for colour channels c,
+     *  result = bilinear (c * alpha) / bilinear (alpha)
+     *
+     *  never entered for alpha == 0
+     */
+    for (i = 0; i < alpha; ++i)
+      {
+        gint newval=a_recip *  BILINEAR (
+                      alphachan[0]         * data[i],
+                      alphachan[bytes]     * data[bytes+i],
+                      alphachan[row]       * data[row+i],
+                      alphachan[row+bytes] * data[row+bytes+i], du, dv);
+        color[i] = CLAMP (newval ,0,255);
+      }
+
+    pixel_surround_release (surround);
+}
+
+
+/*
+    bilinear interpolation of a 16.16 pixel
+*/
+static void 
+sample_bi (TileManager *tm,
+           gint         x,
+           gint         y,
+           guchar      *color,
+           guchar      *bg_color,
+           gint         bpp,
+           gint         alpha)
+{
+    guchar C[4][4];
+    gint i;
+    gint xscale = (x & 65535);
+    gint yscale = (y & 65535);
+
+    gint x0 = x>>16;
+    gint y0 = y>>16;
+    gint x1 = x0+1;
+    gint y1 = y0+1;
+
+
+    /*  fill the color with default values, since read_pixel_data_1 does nothing,
+     *  when accesses are out of bounds.
+     */
+    for (i=0; i<4; i++) 
+        *(guint*)(&C[i])=*(guint*)(bg_color);
+    
+    read_pixel_data_1 (tm, x0, y0, C[0]);
+    read_pixel_data_1 (tm, x1, y0, C[2]);
+    read_pixel_data_1 (tm, x0, y1, C[1]);
+    read_pixel_data_1 (tm, x1, y1, C[3]);
+    
+#define lerp(v1,v2,r) (((guint)(v1)*(65536-(guint)(r))+(guint)(v2)*(guint)(r))/65536)
+
+    {
+        gint i;
+        
+        color[alpha]= lerp( lerp (C[0][alpha], C[1][alpha], yscale),
+                            lerp (C[2][alpha], C[3][alpha], yscale), xscale);
+
+          if(color[alpha])
+            { /* to avoid problems, calculate with premultiplied alpha */
+             for (i=0; i<alpha; i++)
+               {
+                 C[0][i] = (C[0][i] * C[0][alpha] / 255);
+                 C[1][i] = (C[1][i] * C[1][alpha] / 255);
+                 C[2][i] = (C[2][i] * C[2][alpha] / 255);
+                 C[3][i] = (C[3][i] * C[3][alpha] / 255);
+               }
+          
+            for (i=0; i<alpha; i++)
+               color[i] = lerp( lerp (C[0][i], C[1][i], yscale),
+                              lerp (C[2][i], C[3][i], yscale), xscale);
+          } 
+        else 
+          {
+            for (i=0; i<alpha; i++)     
+               color[i] = 0;
+          }
+          
+    }
+#undef lerp
+}
+
+/* 
+    returns true if one of the deltas of the quad edge is >1.0 (16.16 fixed vaues) 
+*/
+static gint         
+supersample_test (gint x0, gint y0,
+                  gint x1, gint y1,
+                  gint x2, gint y2,
+                  gint x3, gint y3)
+{
+    if (abs(x0-x1) > 65535) return 1;
+    if (abs(x1-x2) > 65535) return 1;
+    if (abs(x2-x3) > 65535) return 1;
+    if (abs(x3-x0) > 65535) return 1;
+
+    if (abs(y0-y1) > 65535) return 1;
+    if (abs(y1-y2) > 65535) return 1;
+    if (abs(y2-y3) > 65535) return 1;
+    if (abs(y3-y0) > 65535) return 1;
+
+    return 0;
+}
+
+/* 
+    returns true if one of the deltas of the quad edge is >1.0 (double values) 
+*/
+static gint         
+supersample_dtest ( gdouble x0, gdouble y0,
+                    gdouble x1, gdouble y1,
+                    gdouble x2, gdouble y2,
+                    gdouble x3, gdouble y3)
+{
+    if (fabs(x0-x1)> 1.0) return 1;
+    if (fabs(x1-x2)> 1.0) return 1;
+    if (fabs(x2-x3)> 1.0) return 1;
+    if (fabs(x3-x0)> 1.0) return 1;
+
+    if (fabs(y0-y1)> 1.0) return 1;
+    if (fabs(y1-y2)> 1.0) return 1;
+    if (fabs(y2-y3)> 1.0) return 1;
+    if (fabs(y3-y0)> 1.0) return 1;
+
+    return 0;
+}
+
+/*
+    sample a grid that is spaced according to the quadraliteral's edges,
+    it subdivides a maximum of level times before sampling.
+    0..3 is a cycle around the quad
+*/
+static void
+get_sample (TileManager *tm,
+            gint         xc,  gint yc,
+            gint         x0,  gint y0,
+            gint         x1,  gint y1,
+            gint         x2,  gint y2,
+            gint         x3,  gint y3,
+            gint        *cc,
+            gint         level,
+            guint       *color,
+            guchar      *bg_color,
+            gint         bpp,
+            gint         alpha)
+{
+  if( !level || !supersample_test (x0, y0, x1, y1, x2, y2, x3, y3))
+    {
+      int i;
+      guchar C[4];
+      sample_bi (tm, xc, yc, C, bg_color, bpp, alpha);
+
+      for(i=0;i<bpp;i++)
+          color[i]+= C[i];
+
+      (*cc)++;  /* increase number of samples taken */
+    } 
+  else 
+    {
+          gint tx,lx,rx,bx,tlx,trx,blx,brx;
+          gint ty,ly,ry,by,tly,try,bly,bry;
+
+          /* calculate subdivided corner coordinates (including centercoords
+             thus using a bilinear interpolation,. almost as good as 
+             doing the perspective transform for each subpixel coordinate*/
+
+          tx  = (x0+x1)/2;
+          tlx = (x0+xc)/2;
+          trx = (x1+xc)/2;
+          lx  = (x0+x3)/2;
+          rx  = (x1+x2)/2;
+          blx = (x3+xc)/2;
+          brx = (x2+xc)/2;
+          bx  = (x3+x2)/2;
+
+          ty  = (y0+y1)/2;
+          tly = (y0+yc)/2;
+          try = (y1+yc)/2;
+          ly  = (y0+y3)/2;
+          ry  = (y1+y2)/2;
+          bly = (y3+yc)/2;
+          bry = (y2+yc)/2;
+          by  = (y3+y2)/2;
+
+          get_sample (tm,
+              tlx,tly,         
+              x0,y0, tx,ty, xc,yc, lx,ly,
+              cc, level-1, color, bg_color, bpp, alpha);
+
+          get_sample (tm,
+              trx,try,
+              tx,ty, x1,y1, rx,ry, xc,yc,
+              cc, level-1, color, bg_color, bpp, alpha);
+
+          get_sample (tm,
+              brx,bry,
+              xc,yc, rx,ry, x2,y2, bx,by,
+              cc, level-1, color, bg_color, bpp, alpha);
+
+          get_sample (tm,
+              blx,bly,
+              lx,ly, xc,yc, bx,by, x3,y3,
+              cc, level-1, color, bg_color, bpp, alpha);
+    }    
+  return;
+}
+
+static void 
+sample_adapt (TileManager *tm,
+              gdouble     xc,  gdouble yc,
+              gdouble     x0,  gdouble y0,
+              gdouble     x1,  gdouble y1,
+              gdouble     x2,  gdouble y2,
+              gdouble     x3,  gdouble y3,
+              gint        level,
+              guchar     *color,
+              guchar     *bg_color,
+              gint        bpp,
+              gint        alpha)
+{
+    gint cc=0;
+    guint C[MAX_CHANNELS];
+    C[0]=C[1]=C[2]=C[3]=0;
+
+    get_sample (tm,
+        xc*65535,yc*65535,
+        x0*65535,y0*65535, x1*65535,y1*65535, x2*65535,y2*65535, x3*65535,y3*65535,
+        &cc, level, C, bg_color, bpp, alpha);
+        
+    if (!cc)
+        cc=1;
+    {
+        gint i;
+        color[alpha] = C[alpha]/cc;
+
+        if (color[alpha])
+          {
+           for (i=0; i<alpha; i++)         /* go from premultiplied to postmultiplied alpha */
+               color[i] = ((C[i]/cc)*255)/color[alpha];
+          }
+        else
+          {
+            for (i=0; i<alpha; i++)
+                color[i]=0;
+          }
+    }
+}
+
+/* access interleaved pixels */
+#define CUBIC_ROW(dx, row, step) \
+  gimp_drawable_transform_cubic(dx, (row)[0], (row)[step], (row)[step+step], (row)[step+step+step])
+
+#define CUBIC_SCALED_ROW(dx, row, arow, step) \
+  gimp_drawable_transform_cubic(dx, \
+            (arow)[0]              * (row)[0], \
+            (arow)[step]           * (row)[step], \
+            (arow)[step+step]      * (row)[step+step], \
+            (arow)[step+step+step] * (row)[step+step+step])
+
+
 /* Note: cubic function no longer clips result */
 static gdouble
 gimp_drawable_transform_cubic (gdouble dx,
@@ -862,4 +1083,79 @@
                ( - jm1 + jp1 ) ) * dx + (j + j) ) / 2.0;
 
   return result;
+}
+
+
+  /*  u & v are the subpixel coordinates of the point in
+   *  the original selection's floating buffer.
+   *  We need the four integer pixel coords around them:
+   *  iu to iu + 3, iv to iv + 3
+   */
+static void 
+sample_cubic (PixelSurround *surround, 
+              gdouble        u,
+              gdouble        v, 
+              guchar        *color,
+              gint           bytes,
+              gint           alpha)
+{
+    gdouble      a_val, a_recip;
+    gint i;
+    gint iu=floor(u);
+    gint iv=floor(v);
+    gint row;
+    gdouble du,dv;
+    guchar *data;
+
+    /* lock the pixel surround */
+    data = pixel_surround_lock (surround,
+                                iu - 1 , iv - 1 );
+
+    row = pixel_surround_rowstride (surround);
+
+    /* the fractional error */
+    du = u - iu;
+    dv = v - iv;
+
+    /* calculate alpha of result */
+    a_val = gimp_drawable_transform_cubic
+      (dv,
+       CUBIC_ROW (du, data + alpha + row * 0, bytes),
+       CUBIC_ROW (du, data + alpha + row * 1, bytes),
+       CUBIC_ROW (du, data + alpha + row * 2, bytes),
+       CUBIC_ROW (du, data + alpha + row * 3, bytes));
+
+    if (a_val <= 0.0)
+      {
+        a_recip = 0.0;
+        color[alpha] = 0;
+      }
+    else if (a_val > 255.0)
+      {
+        a_recip = 1.0 / a_val;
+        color[alpha] = 255;
+      }
+    else
+      {
+        a_recip = 1.0 / a_val;
+        color[alpha] = RINT(a_val);
+      }
+
+    /*  for colour channels c,
+     *  result = bicubic (c * alpha) / bicubic (alpha)
+     *
+     *  never entered for alpha == 0
+     */
+    for (i = 0;i<alpha; ++i)
+      {
+        gint newval=a_recip * gimp_drawable_transform_cubic
+                  (dv,
+                    CUBIC_SCALED_ROW (du, i+ data + row *0, data + alpha + row * 0, bytes),
+                    CUBIC_SCALED_ROW (du, i+ data + row *1, data + alpha + row * 1, bytes),
+                    CUBIC_SCALED_ROW (du, i+ data + row *2, data + alpha + row * 2, bytes),
+                    CUBIC_SCALED_ROW (du, i+ data + row *3, data + alpha + row * 3, bytes));
+        color[i]=CLAMP(newval,0,255);
+      }
+
+    pixel_surround_release (surround);
 }
