Index: scale-funcs.c
===================================================================
--- scale-funcs.c	(revision 23123)
+++ scale-funcs.c	(working copy)
@@ -32,6 +32,29 @@
 
 
 #define EPSILON          (0.0001)  /* arbitary small number for avoiding zero */
+static void  reduce_region            (PixelRegion           *srcPR,
+                                       PixelRegion           *destPR);
+static void  reduce_pixel_region      (PixelRegion           *srcPR,
+                                       guchar                *dst,
+                                       gint                   bytes);
+static void  reduce_buffer            (guchar                *src,
+                                       guchar                *dst,
+                                       gint                   bytes,
+                                       gint                   width,
+                                       gint                   height,
+                                       gint                   alpha);
+static void  reduce_row               (guchar                *src,
+                                       guchar                *dst,
+                                       gint                   width,
+                                       gint                   bytes,
+                                       gint                   alpha);
+static void  reduce_bilinear_pr       (PixelRegion           *dstPR,
+                                       PixelRegion           *srcPR);
+static void  reduce_bilinear          (PixelRegion           *dstPR,
+                                       guchar                *src,
+                                       gint                   source_w,
+                                       gint                   source_h,
+                                       gint                   bytes);
 
 static void  scale_region_no_resample (PixelRegion           *srcPR,
                                        PixelRegion           *destPR);
@@ -443,7 +466,8 @@
   gint     i;
   gint     old_y = -4;
   gint     x, y;
-
+  gdouble scale;
+  
   switch (interpolation)
     {
     case GIMP_INTERPOLATION_NONE:
@@ -467,6 +491,14 @@
   width  = destPR->w;
   height = destPR->h;
 
+  /* Use scale by half for scaling down */
+  scale = (gdouble)width / (gdouble)orig_width;
+  if ( scale < 1.0 ) {
+    reduce_region (srcPR, destPR);
+    return;
+  }
+
+
 #if 0
   g_printerr ("scale_region: (%d x %d) -> (%d x %d)\n",
               orig_width, orig_height, width, height);
@@ -1009,3 +1041,317 @@
   g_free (win_buf);
   g_free (kernel_lookup);
 }
+
+
+
+static void
+reduce_region (PixelRegion           *srcPR,
+               PixelRegion           *dstPR)
+{
+  guchar  *dst;
+  guchar  *src;
+  gdouble  scale;
+  gint     level  = 0;
+  gint     width, height;
+  gint     src_width, src_height;
+  gint     dst_width, dst_height;
+  gint     bytes;
+  gint     alpha = 0;
+  
+  bytes = dstPR->bytes;
+  if (pixel_region_has_alpha (srcPR)) {
+    alpha = bytes - 1;
+  }
+
+  scale = (gdouble)dstPR->h / (gdouble)srcPR->h ;
+  dst_width  = dstPR->w;  
+  dst_height = dstPR->h;
+
+  while (scale <= 0.5) 
+    {
+      scale  *= 2;
+      level++;
+    }
+
+  if ( level > 0 ) {
+    width  = srcPR->w/2;  
+    height = srcPR->h/2;
+    dst = g_new (guchar, width * height * bytes);
+    reduce_pixel_region (srcPR, dst, bytes);
+    level--;
+    if ( level > 0 ) {
+      src_width  = width;  
+      src_height = height;
+      while (level){
+        width  = width/2;  
+        height = height/2;
+        src = dst;
+        dst = g_new (guchar, width * height * bytes);
+        reduce_buffer (src, dst, bytes, width*2, height*2, alpha);
+        g_free(src);    
+        level--;
+      }
+    }  
+    reduce_bilinear (dstPR, dst, width, height, bytes);
+    g_free(dst);    
+  } else {
+    reduce_bilinear_pr (dstPR, srcPR);
+  }
+}
+
+
+
+
+static void
+reduce_pixel_region (PixelRegion *srcPR,
+                     guchar      *dst,
+                     gint         bytes)
+{
+  guchar     *src_buf, *row0, *row1, *row2;
+  gint y;
+  gint src_width  = srcPR->w;
+  gint dst_width  = srcPR->w/2;
+  guchar *dst_ptr;
+  gint alpha = 0;
+  
+  if (pixel_region_has_alpha (srcPR)) {
+    alpha = bytes - 1;
+  }
+
+  src_buf  = g_new (guchar, src_width * bytes * 3);
+  row0 = src_buf;
+  row1 = row0 + src_width * bytes;
+  row2 = row1 + src_width * bytes;
+
+  dst_ptr = dst;
+  for (y = 0 ; y < srcPR->h ; y+=2) {
+    pixel_region_get_row (srcPR, 0, ABS(y-1), src_width, row0, 1);
+    pixel_region_get_row (srcPR, 0, y       , src_width, row1, 1);
+    pixel_region_get_row (srcPR, 0, y+1     , src_width, row2, 1);
+    reduce_row(src_buf, dst_ptr, src_width, bytes, alpha);
+    dst_ptr += dst_width * bytes;
+  }
+  g_free(src_buf);
+}
+
+static void
+reduce_buffer (guchar *src,
+               guchar *dst,
+               gint    bytes,
+               gint    width,
+               gint    height,
+               gint    alpha)
+{
+  guchar *src_buf, *row0, *row1, *row2;
+  gint    y, pos;
+  gint dst_width  = width/2;
+  guchar     *dstptr;
+  gint        rowstride = width * bytes;
+
+  src_buf  = g_new (guchar, rowstride * 3);
+  row0 = src_buf;
+  row1 = row0 + rowstride;
+  row2 = row1 + rowstride;
+  dstptr=dst;
+  for (y = 0 ; y < height ; y+=2) {
+    pos= ABS(y-1) * rowstride;
+    memcpy(row0, src+pos, rowstride); 
+    pos= y * rowstride;
+    memcpy(row1, src+pos, rowstride); 
+    pos= (y+1) * rowstride;
+    memcpy(row2, src+pos, rowstride); 
+    reduce_row(src_buf, dstptr, width, bytes, alpha);
+    dstptr += dst_width * bytes;
+  }
+  g_free(src_buf);
+}
+
+
+
+static void
+reduce_row (guchar *src,
+            guchar *dst,
+            gint    width,
+            gint    bytes,
+            gint    alpha)
+{
+
+  gint     x, b, start;
+  gdouble  alpha_sum;
+  gdouble  sum;
+  
+  guchar  *src0;
+  guchar  *src1;
+  guchar  *src2;
+  guchar  *dstptr;
+
+
+  /*  Pre calculated gausian matrix */
+  /*  Standard deviation = 0.71     
+  
+      12 32 12
+      32 86 32
+      12 32 12
+      
+      Matrix sum is = 262
+      Normalize by dividing with 273
+      
+  */
+
+  dstptr = dst;
+  
+  src0 = src;
+  src1 = src + width * bytes;
+  src2 = src1 + width * bytes;
+  
+  start = bytes -1;
+  sum = 0.0;
+  alpha_sum = 0.0;  
+  
+  for (x=0; x < width ; x+=2) {        
+    for (b = start ; b >= 0 ; b--) {    
+      sum += src0[ABS(x-1) * bytes + b] * 12;
+      sum += src0[ABS(x)   * bytes + b] * 32;
+      sum += src0[ABS(x+1) * bytes + b] * 12;
+      
+      sum += src1[ABS(x-1) * bytes + b] * 32;
+      sum += src1[ABS(x)   * bytes + b] * 86;
+      sum += src1[ABS(x+1) * bytes + b] * 32;
+
+      sum += src2[ABS(x-1) * bytes + b] * 12;
+      sum += src2[ABS(x)   * bytes + b] * 32;
+      sum += src2[ABS(x+1) * bytes + b] * 12;
+
+      sum /= 273.0;
+        
+      if ( alpha && (b == alpha)) { 
+        alpha_sum = sum;
+        dstptr[b] = (guchar)alpha_sum;
+      } else {
+        if ( alpha ) {
+          if (alpha_sum != 0.0)
+            sum = sum * ( 262.0 / alpha_sum );
+          else
+            sum = 0.0;
+        }
+        dstptr[b] = (guchar)sum;                                     
+      }      
+    }
+    dstptr+=bytes;
+  }
+}
+
+
+static void 
+reduce_bilinear (PixelRegion *dstPR,
+                    guchar *src,
+                    gint    source_w,
+                    gint    source_h,
+                    gint    bytes)
+{
+  gint     x, y, b;
+  gint     x0, x1, y0, y1;
+
+  gdouble  scale;
+  gdouble  xfrac, yfrac;           
+  gdouble  s00, s01, s10, s11;
+
+  guchar   value;
+  guchar  *dst;
+  
+  scale =  (gdouble)dstPR->h / (gdouble)source_h;   
+  dst = g_new (guchar, dstPR->w * dstPR->h * bytes);
+  
+  for (y = 0; y < dstPR->h; y++)
+    {
+      yfrac = ( y / scale ); 
+      y0 = (gint)floor(yfrac);
+      y1 = (gint)ceil(yfrac);
+      yfrac =  yfrac - y0;
+
+      for (x = 0; x < dstPR->w; x++)
+        {
+           xfrac = (x / scale); 
+           x0 = (gint)floor(xfrac);
+           x1 = (gint)ceil(xfrac);
+           xfrac =  xfrac - x0;
+
+           for (b = 0 ; b < bytes ; b++) {             
+             s00 = src[(x0 + y0 * source_w) * bytes + b];
+             s10 = src[(x1 + y0 * source_w) * bytes + b];
+             s01 = src[(x0 + y1 * source_w) * bytes + b];
+             s11 = src[(x1 + y1 * source_w) * bytes + b];
+             value =  (gint)((1 - yfrac) * ((1 - xfrac) * s00 + xfrac * s01 ) 
+                               +  yfrac  * ((1 - xfrac) * s10 + xfrac * s11 ));                  
+             dst[x*bytes+b] = value;
+           }
+        }
+        pixel_region_set_row (dstPR, 0, y, dstPR->w, dst);
+    }    
+}
+
+static void 
+reduce_bilinear_pr (PixelRegion      *dstPR,
+                       PixelRegion      *srcPR)
+{
+  gint     bytes;
+  gint     x, y, b;
+  gint     x0, x1, y0, y1;
+
+  gdouble  scale;
+  gdouble  xfrac, yfrac;           
+  gdouble  s00, s01, s10, s11;
+
+  guchar   value;
+  guchar  *dst;
+  guchar  *src0;
+  guchar  *src1;
+
+  scale = (gdouble) dstPR->h / (gdouble) srcPR->h;
+  bytes = dstPR->bytes;
+  
+  /* Copy and return if scale = 1.0 */
+  src0 = g_new0 (guchar, srcPR->w*bytes);
+  if ( srcPR->h == dstPR->h ) {
+    for (y = 0; y < dstPR->h; y++) {
+       pixel_region_get_row (srcPR, 0, y, srcPR->w, src0,1);
+       pixel_region_set_row (dstPR, 0, y, dstPR->w, src0);
+    }    
+    g_free(src0);
+    return;	    
+  }
+
+  /* Interpolate */  
+  src1 = g_new0 (guchar, srcPR->w*bytes);
+  dst  = g_new0 (guchar, dstPR->w*bytes);
+  for (y = 0; y < dstPR->h; y++)
+    {
+      yfrac = ( y / scale ); 
+      y0 = (gint)floor(yfrac);
+      y1 = (gint)ceil(yfrac);
+      yfrac =  yfrac - y0;
+
+      pixel_region_get_row (srcPR, 0, y0, srcPR->w, src0,1);
+      pixel_region_get_row (srcPR, 0, y1, srcPR->w, src1, 1);      
+      for (x = 0; x < dstPR->w; x++)
+        {
+          xfrac = (x / scale); 
+          x0 = (gint)floor(xfrac);
+          x1 = (gint)ceil(xfrac);
+          xfrac =  xfrac - x0;
+          for (b = 0 ; b < bytes ; b++) {
+             s00 = src0[x0 * bytes + b];
+             s01 = src0[x1 * bytes + b];
+             s10 = src1[x0 * bytes + b];
+             s11 = src1[x1 * bytes + b];
+             value =  (1 - yfrac) * ( (1 - xfrac) * s00 + xfrac * s01 )
+                        +  yfrac  * ( (1 - xfrac) * s10 + xfrac * s11 );                  
+             dst[x*bytes + b] = (guchar)value;
+           }
+        }
+      pixel_region_set_row (dstPR, 0, y, dstPR->w, dst);
+    }
+  g_free(src0);
+  g_free(src1);
+  g_free(dst);
+}
