Index: plug-ins/common/gauss_iir.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/common/gauss_iir.c,v
retrieving revision 1.57
diff -u -p -r1.57 gauss_iir.c
--- plug-ins/common/gauss_iir.c	18 Apr 2004 17:29:43 -0000	1.57
+++ plug-ins/common/gauss_iir.c	19 Apr 2004 01:05:47 -0000
@@ -53,7 +53,8 @@ static void      run    (const gchar    
                          gint             *nreturn_vals,
                          GimpParam       **return_vals);
 
-static void      gauss_iir         (GimpDrawable *drawable,
+static void      gauss_iir         (gint32        image_ID,
+                                    GimpDrawable *drawable,
                                     gdouble       horizontal,
                                     gdouble       vertical);
 
@@ -74,11 +75,6 @@ static void      find_constants    (gdou
                                     gdouble  bd_p[],
                                     gdouble  bd_m[],
                                     gdouble  std_dev);
-static void      transfer_pixels   (gdouble *src1,
-                                    gdouble *src2,
-                                    guchar  *dest,
-                                    gint     bytes,
-                                    gint     width);
 
 
 GimpPlugInInfo PLUG_IN_INFO =
@@ -292,8 +288,9 @@ run (const gchar      *name,
           /*  run the gaussian blur  */
           if (strcmp (name, "plug_in_gauss_iir") == 0)
             {
-              gauss_iir (drawable, (bvals.horizontal ? bvals.radius : 0.0),
-                                   (bvals.vertical ? bvals.radius : 0.0));
+              gauss_iir (image_ID, drawable,
+                         (bvals.horizontal ? bvals.radius : 0.0),
+                         (bvals.vertical ? bvals.radius : 0.0));
 
               /*  Store data  */
               if (run_mode == GIMP_RUN_INTERACTIVE)
@@ -302,7 +299,8 @@ run (const gchar      *name,
             }
           else
             {
-              gauss_iir (drawable, b2vals.horizontal, b2vals.vertical);
+              gauss_iir (image_ID, drawable,
+                         b2vals.horizontal, b2vals.vertical);
 
               /*  Store data  */
               if (run_mode == GIMP_RUN_INTERACTIVE)
@@ -473,74 +471,278 @@ gauss_iir2_dialog (gint32        image_I
   return run;
 }
 
+/* Convert from separated to premultiplied alpha. Input is a gdouble
+   buffer in (RGB|V)[A][S] format. Output goes to the same buffer, with
+   the RGB or V components multiplied by A*S when present. bpp stands
+   for bytes per pixel (not counting alpha/selection).  */
 
-/* Convert from separated to premultiplied alpha, on a single scan line. */
+/* Alpha only: */
 static void
-multiply_alpha (guchar *buf,
-                gint    width,
-                gint    bytes)
+multiply_alpha (gdouble *buf,
+                gint     width,
+                gint     bpp)
 {
-  gint i, j;
+  gint    i, j;
   gdouble alpha;
+  gint    alpha_pos = bpp;
+  gint    pix_size = bpp + 1;
+  gint    tot_size = width * pix_size;
 
-  for (i = 0; i < width * bytes; i += bytes)
+  for (i = 0; i < tot_size; i += pix_size)
     {
-      alpha = buf[i + bytes - 1] * (1.0 / 255.0);
-      for (j = 0; j < bytes - 1; j++)
+      alpha = buf[i + alpha_pos] * (1.0 / 255.0);
+      for (j = 0; j < bpp; j++)
         buf[i + j] *= alpha;
     }
 }
 
-/* Convert from premultiplied to separated alpha, on a single scan
-   line. */
+/* Alpha and selection: */
 static void
-separate_alpha (guchar *buf,
-                gint    width,
-                gint    bytes)
+multiply_alpha_sel (gdouble *buf,
+                    gint     width,
+                    gint     bpp)
+{
+  gint    j;
+  gdouble factor;
+  gint    pos = 0;
+  gint    alpha_pos = bpp;
+  gint    sel_pos  = bpp + 1;
+  gint    pix_size = bpp + 2;
+  gint    tot_size = width * pix_size;
+
+  for (pos = 0; pos < tot_size; pos += pix_size)
+    {
+      factor = buf[pos + alpha_pos] * buf[pos + sel_pos] * (1.0 / (255.0 * 255.0));
+      for (j = 0; j < bpp; j++)
+        buf[pos + j] *= factor;
+    }
+}
+
+/* Selection only: */
+static void
+multiply_sel (gdouble *buf,
+              gint     width,
+              gint     bpp)
+{
+  gint    j;
+  gdouble factor;
+  gint    pos = 0;
+  gint    sel_pos  = bpp;
+  gint    pix_size = bpp + 1;
+  gint    tot_size = width * pix_size;
+
+  for (pos = 0; pos < tot_size; pos += pix_size)
+    {
+      factor = buf[pos + sel_pos] * (1.0 / 255.0);
+      for (j = 0; j < bpp; j++)
+        buf[pos + j] *= factor;
+    }
+}
+
+/* Separate functions - convert from premultiplied to separated alpha and
+   selection. The input is the two (RGB|V)[A][S] gdouble buffers which
+   must be added to obtain the final result; the output is a (RGB|V)[A]
+   guchar buffer with the RGB or V component(s) divided by A, S or A*S
+   depending on which are present. */
+
+/* Transfer only, no division (no-alpha no-selection images): */
+static void
+separate_transfer (gdouble *src1,
+                   gdouble *src2,
+                   guchar  *dest,
+                   gint     width,
+                   gint     bpp)
+{
+  gdouble new_val;
+  gint    pos;
+  gint    size = width * bpp;
+
+  for (pos = 0; pos < size; pos++)
+    {
+      new_val = *src1++ + *src2++;
+      *dest++ = (guchar) CLAMP (new_val, 0.0, 255.0);
+    }
+}
+
+/* Alpha only: */
+static void
+separate_alpha (gdouble *src1,
+                gdouble *src2,
+                guchar  *dest,
+                gint     width,
+                gint     bpp)
 {
-  gint i, j;
-  guchar alpha;
+  gint    j;
+  gdouble alpha;
   gdouble recip_alpha;
-  gint new_val;
+  gdouble new_val;
+  gint    alpha_pos = bpp;
+  gint    pix_size = bpp + 1;
+  gint    tot_size = width * pix_size;
+  gint    pos;
 
-  for (i = 0; i < width * bytes; i += bytes)
+  for (pos = 0; pos < tot_size; pos += pix_size)
     {
-      alpha = buf[i + bytes - 1];
-      if (alpha != 0 && alpha != 255)
+      alpha = src1[pos + alpha_pos] + src2[pos + alpha_pos];
+
+      if (alpha != 0.0)
         {
           recip_alpha = 255.0 / alpha;
-          for (j = 0; j < bytes - 1; j++)
+
+          for (j = 0; j < bpp; j++)
+            {
+              new_val = (src1[pos + j] + src2[pos + j]) * recip_alpha;
+              dest[pos + j] = (guchar) CLAMP (new_val, 0.0, 255.0);
+            }
+          dest[pos + alpha_pos] = (guchar) CLAMP (alpha, 0.0, 255.0);
+        }
+      else
+        {
+          for (j = 0; j < pix_size; j++)
+            {
+              new_val = (src1[pos + j] + src2[pos + j]);
+              dest[pos + j] = (guchar) CLAMP (new_val, 0.0, 255.0);
+            }
+        }
+    }
+}
+
+/* Alpha and selection: */
+static void
+separate_alpha_sel (gdouble *src1,
+                    gdouble *src2,
+                    guchar  *dest,
+                    gint     width,
+                    gint     bpp)
+{
+  gint    j;
+  gdouble factor;
+  gdouble recip_factor;
+  gdouble new_val;
+  gint    alpha_pos = bpp;
+  gint    sel_pos  = bpp + 1;
+  gint    src_pixsize = bpp + 2;
+  gint    dst_pixsize = bpp + 1;
+  gint    src_pos = 0;
+  gint    dst_pos;
+  gint    dst_totsize = width * dst_pixsize;
+
+  for (dst_pos = 0; dst_pos < dst_totsize; dst_pos += dst_pixsize)
+    {
+      /* Multiply together the alpha and selection values for separation */
+      factor = ((src1[src_pos + alpha_pos] + src2[src_pos + alpha_pos]) *
+                (src1[src_pos + sel_pos] + src2[src_pos + sel_pos]));
+
+      if (factor != 0.0)
+        {
+          recip_factor = 255.0 * 255.0 / factor;
+
+          for (j = 0; j < bpp; j++)
+            {
+              new_val = (src1[src_pos + j] + src2[src_pos + j]) * recip_factor;
+              dest[dst_pos + j] = (guchar) CLAMP (new_val, 0.0, 255.0);
+            }
+          /* copy the alpha channel unchanged */
+          new_val = src1[src_pos + alpha_pos] + src2[src_pos + alpha_pos];
+          dest[dst_pos + alpha_pos] = (guchar) CLAMP (new_val, 0.0, 255.0);
+        }
+      else
+        {
+          /* transfer all except selection */
+          for (j = 0; j < dst_pixsize; j++)
             {
-              new_val = buf[i + j] * recip_alpha;
-              buf[i + j] = MIN (255, new_val);
+              new_val = src1[src_pos + j] + src2[src_pos + j];
+              dest[dst_pos + j] = (guchar) CLAMP (new_val, 0.0, 255.0);
             }
         }
+
+      src_pos += src_pixsize;
+    }
+}
+
+/* Selection only: */
+static void
+separate_sel (gdouble *src1,
+              gdouble *src2,
+              guchar  *dest,
+              gint     width,
+              gint     bpp)
+{
+  gint    j;
+  gdouble factor;
+  gdouble recip_factor;
+  gdouble new_val;
+  gint    sel_pos = bpp;
+  gint    src_pixsize = bpp + 1;
+  gint    dst_pixsize = bpp;
+  gint    src_pos = 0;
+  gint    dst_pos;
+  gint    dst_totsize = width * dst_pixsize;
+
+  for (dst_pos = 0; dst_pos < dst_totsize; dst_pos += dst_pixsize)
+    {
+      /* Use the selection value for separation */
+      factor = src1[src_pos + sel_pos] + src2[src_pos + sel_pos];
+
+      if (factor != 0.0)
+        {
+          recip_factor = 255.0 / factor;
+          for (j = 0; j < bpp; j++)
+            {
+              new_val = (src1[src_pos + j] + src2[src_pos + j]) * recip_factor;
+              dest[dst_pos + j] = (guchar) CLAMP (new_val, 0.0, 255.0);
+            }
+        }
+      else
+        {
+          for (j = 0; j < bpp; j++)
+            {
+              new_val = src1[src_pos + j] + src2[src_pos + j];
+              dest[dst_pos + j] = (guchar) CLAMP (new_val, 0.0, 255.0);
+            }
+        }
+
+      src_pos += src_pixsize;
     }
 }
 
 static void
-gauss_iir (GimpDrawable *drawable,
+gauss_iir (gint32        image_ID,
+           GimpDrawable *drawable,
            gdouble       horz,
            gdouble       vert)
 {
-  GimpPixelRgn src_rgn, dest_rgn;
-  gint width, height;
-  gint bytes;
-  gint has_alpha;
-  guchar *dest;
-  guchar *src, *sp_p, *sp_m;
-  gdouble n_p[5], n_m[5];
-  gdouble d_p[5], d_m[5];
-  gdouble bd_p[5], bd_m[5];
-  gdouble *val_p, *val_m, *vp, *vm;
-  gint x1, y1, x2, y2;
-  gint i, j;
-  gint row, col, b;
-  gint terms;
-  gdouble progress, max_progress;
-  gint initial_p[4];
-  gint initial_m[4];
-  gdouble std_dev;
+  GimpPixelRgn  src_rgn, dest_rgn, sel_rgn;
+  GimpDrawable *selDrawable = NULL;
+  gint          width, height;
+  gint          bpp;
+  gint          bppa;
+  gint          buf_bpp;
+  gint          ofsx, ofsy;
+  gint          has_alpha;
+  gboolean      has_sel;
+  guchar       *dest;
+  guchar       *srci;
+  guchar       *sel;
+  gdouble      *srcf, *sp_p, *sp_m;
+  gdouble       n_p[5], n_m[5];
+  gdouble       d_p[5], d_m[5];
+  gdouble       bd_p[5], bd_m[5];
+  gdouble      *val_p, *val_m, *vp, *vm;
+  gint          x1, y1, x2, y2;
+  gint          i;
+  gint          row, col, b;
+  gint          terms;
+  gdouble       progress, max_progress;
+  gint          initial_p[5];
+  gint          initial_m[5];
+  gdouble       std_dev;
+  void          (*multiply) (gdouble *buf, gint width, gint bpp);
+  void          (*separate) (gdouble *src1,
+                             gdouble *src2,
+                             guchar  *dest,
+                             gint     width,
+                             gint     bpp);
 
   if (horz <= 0.0 && vert <= 0.0)
     return;
@@ -553,14 +755,31 @@ gauss_iir (GimpDrawable *drawable,
   if (width < 1 || height < 1)
     return;
 
-  bytes = drawable->bpp;
-  has_alpha = gimp_drawable_has_alpha(drawable->drawable_id);
+  bpp = bppa = drawable->bpp;
+  has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+  has_sel = !gimp_selection_is_empty (image_ID);
+  gimp_drawable_offsets (drawable->drawable_id, &ofsx, &ofsy);
+
+  buf_bpp = bpp;
+
+  if (has_sel)
+    {
+      sel = g_new (guchar, MAX (width, height));
+      selDrawable = gimp_drawable_get (gimp_image_get_selection (image_ID));
+      gimp_pixel_rgn_init (&sel_rgn, selDrawable,
+                           0, 0, drawable->width, drawable->height,
+                           FALSE, FALSE);
+      buf_bpp++;
+    }
+  else
+    sel = NULL;
 
-  val_p = g_new (gdouble, MAX (width, height) * bytes);
-  val_m = g_new (gdouble, MAX (width, height) * bytes);
+  val_p = g_new (gdouble, MAX (width, height) * buf_bpp);
+  val_m = g_new (gdouble, MAX (width, height) * buf_bpp);
 
-  src =  g_new (guchar, MAX (width, height) * bytes);
-  dest = g_new (guchar, MAX (width, height) * bytes);
+  srci = g_new (guchar,  MAX (width, height) * buf_bpp);
+  srcf = g_new (gdouble, MAX (width, height) * buf_bpp);
+  dest = g_new (guchar,  MAX (width, height) * buf_bpp);
 
   gimp_pixel_rgn_init (&src_rgn,
                        drawable, 0, 0, drawable->width, drawable->height,
@@ -573,6 +792,32 @@ gauss_iir (GimpDrawable *drawable,
   max_progress  = (horz <= 0.0) ? 0 : width * height * horz;
   max_progress += (vert <= 0.0) ? 0 : width * height * vert;
 
+  /* Select the function to use for premultiplication / separation */
+  switch ((has_alpha ? 2 : 0) + (has_sel ? 1 : 0))
+    {
+    case 0:               /* no alpha no selection */
+      multiply = NULL;
+      separate = separate_transfer;
+      break;
+    case 1:               /* selection only */
+      multiply = multiply_sel;
+      separate = separate_sel;
+      break;
+    case 2:               /* alpha only */
+      multiply = multiply_alpha;
+      separate = separate_alpha;
+      bpp--;
+      break;
+    case 3:               /* alpha and selection */
+      multiply = multiply_alpha_sel;
+      separate = separate_alpha_sel;
+      bpp--;
+      break;
+    default:
+      multiply = NULL;
+      separate = NULL;
+      g_assert_not_reached ();
+    }
 
   /*  First the vertical pass  */
   if (vert > 0.0)
@@ -585,20 +830,34 @@ gauss_iir (GimpDrawable *drawable,
 
       for (col = 0; col < width; col++)
         {
-          memset(val_p, 0, height * bytes * sizeof (gdouble));
-          memset(val_m, 0, height * bytes * sizeof (gdouble));
+          memset (val_p, 0, height * buf_bpp * sizeof (gdouble));
+          memset (val_m, 0, height * buf_bpp * sizeof (gdouble));
 
-          gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, (y2 - y1));
-          if (has_alpha)
-            multiply_alpha (src, height, bytes);
+          gimp_pixel_rgn_get_col (&src_rgn, srci, col + x1, y1, (y2 - y1));
+          if (has_sel)
+            gimp_pixel_rgn_get_col (&sel_rgn, sel,
+                                    col + x1 + ofsx, y1 + ofsy, (y2 - y1));
 
-          sp_p = src;
-          sp_m = src + (height - 1) * bytes;
+          /* put the integer data into the float array, inserting selection
+             data if needed */
+          for (row = 0; row < height; row++)
+            {
+              for (b = 0; b < bppa; b++)
+                srcf[row * buf_bpp + b] = srci[row * bppa + b];
+              if (has_sel)
+                srcf[row * buf_bpp + bppa] = sel[row];
+            }
+
+          if (multiply)
+            (*multiply) (srcf, height, bpp);
+
+          sp_p = srcf;
+          sp_m = srcf + (height - 1) * buf_bpp;
           vp = val_p;
-          vm = val_m + (height - 1) * bytes;
+          vm = val_m + (height - 1) * buf_bpp;
 
           /*  Set up the first vals  */
-          for (i = 0; i < bytes; i++)
+          for (i = 0; i < buf_bpp; i++)
             {
               initial_p[i] = sp_p[i];
               initial_m[i] = sp_m[i];
@@ -609,32 +868,31 @@ gauss_iir (GimpDrawable *drawable,
               gdouble *vpptr, *vmptr;
               terms = (row < 4) ? row : 4;
 
-              for (b = 0; b < bytes; b++)
+              for (b = 0; b < buf_bpp; b++)
                 {
-                  vpptr = vp + b; vmptr = vm + b;
+                  vpptr = vp + b;
+                  vmptr = vm + b;
                   for (i = 0; i <= terms; i++)
                     {
-                      *vpptr += n_p[i] * sp_p[(-i * bytes) + b] -
-                        d_p[i] * vp[(-i * bytes) + b];
-                      *vmptr += n_m[i] * sp_m[(i * bytes) + b] -
-                        d_m[i] * vm[(i * bytes) + b];
+                      *vpptr += n_p[i] * sp_p[(-i * buf_bpp) + b] -
+                        d_p[i] * vp[(-i * buf_bpp) + b];
+                      *vmptr += n_m[i] * sp_m[(i * buf_bpp) + b] -
+                        d_m[i] * vm[(i * buf_bpp) + b];
                     }
-                  for (j = i; j <= 4; j++)
+                  for (; i <= 4; i++)
                     {
-                      *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
-                      *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
+                      *vpptr += (n_p[i] - bd_p[i]) * initial_p[b];
+                      *vmptr += (n_m[i] - bd_m[i]) * initial_m[b];
                     }
                 }
 
-              sp_p += bytes;
-              sp_m -= bytes;
-              vp += bytes;
-              vm -= bytes;
+              sp_p += buf_bpp;
+              sp_m -= buf_bpp;
+              vp += buf_bpp;
+              vm -= buf_bpp;
             }
 
-          transfer_pixels (val_p, val_m, dest, bytes, height);
-          if (has_alpha)
-            separate_alpha (src, height, bytes);
+          (*separate) (val_p, val_m, dest, height, bpp);
 
           gimp_pixel_rgn_set_col (&dest_rgn, dest, col + x1, y1, (y2 - y1));
 
@@ -664,20 +922,34 @@ gauss_iir (GimpDrawable *drawable,
 
       for (row = 0; row < height; row++)
         {
-          memset(val_p, 0, width * bytes * sizeof (gdouble));
-          memset(val_m, 0, width * bytes * sizeof (gdouble));
+          memset (val_p, 0, width * buf_bpp * sizeof (gdouble));
+          memset (val_m, 0, width * buf_bpp * sizeof (gdouble));
+
+          gimp_pixel_rgn_get_row (&src_rgn, srci, x1, row + y1, (x2 - x1));
+          if (has_sel)
+            gimp_pixel_rgn_get_row (&sel_rgn, sel,
+                                    x1 + ofsx, row + y1 + ofsy, (x2 - x1));
+
+          /* put the integer data into the float array, inserting selection
+             data if needed */
+          for (col = 0; col < width; col++)
+            {
+              for (b = 0; b < bppa; b++)
+                srcf[col * buf_bpp + b] = srci[col * bppa + b];
+              if (has_sel)
+                srcf[col * buf_bpp + bppa] = sel[col];
+            }
 
-          gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, (x2 - x1));
-          if (has_alpha)
-            multiply_alpha (dest, width, bytes);
+          if (multiply)
+            (*multiply) (srcf, width, bpp);
 
-          sp_p = src;
-          sp_m = src + (width - 1) * bytes;
+          sp_p = srcf;
+          sp_m = srcf + (width - 1) * buf_bpp;
           vp = val_p;
-          vm = val_m + (width - 1) * bytes;
+          vm = val_m + (width - 1) * buf_bpp;
 
           /*  Set up the first vals  */
-          for (i = 0; i < bytes; i++)
+          for (i = 0; i < buf_bpp; i++)
             {
               initial_p[i] = sp_p[i];
               initial_m[i] = sp_m[i];
@@ -688,32 +960,30 @@ gauss_iir (GimpDrawable *drawable,
               gdouble *vpptr, *vmptr;
               terms = (col < 4) ? col : 4;
 
-              for (b = 0; b < bytes; b++)
+              for (b = 0; b < buf_bpp; b++)
                 {
                   vpptr = vp + b; vmptr = vm + b;
                   for (i = 0; i <= terms; i++)
                     {
-                      *vpptr += n_p[i] * sp_p[(-i * bytes) + b] -
-                        d_p[i] * vp[(-i * bytes) + b];
-                      *vmptr += n_m[i] * sp_m[(i * bytes) + b] -
-                        d_m[i] * vm[(i * bytes) + b];
+                      *vpptr += n_p[i] * sp_p[(-i * buf_bpp) + b] -
+                        d_p[i] * vp[(-i * buf_bpp) + b];
+                      *vmptr += n_m[i] * sp_m[(i * buf_bpp) + b] -
+                        d_m[i] * vm[(i * buf_bpp) + b];
                     }
-                  for (j = i; j <= 4; j++)
+                  for (; i <= 4; i++)
                     {
-                      *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
-                      *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
+                      *vpptr += (n_p[i] - bd_p[i]) * initial_p[b];
+                      *vmptr += (n_m[i] - bd_m[i]) * initial_m[b];
                     }
                 }
 
-              sp_p += bytes;
-              sp_m -= bytes;
-              vp += bytes;
-              vm -= bytes;
+              sp_p += buf_bpp;
+              sp_m -= buf_bpp;
+              vp += buf_bpp;
+              vm -= buf_bpp;
             }
 
-          transfer_pixels (val_p, val_m, dest, bytes, width);
-          if (has_alpha)
-            separate_alpha (dest, width, bytes);
+          (*separate) (val_p, val_m, dest, width, bpp);
 
           gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, (x2 - x1));
 
@@ -729,28 +999,16 @@ gauss_iir (GimpDrawable *drawable,
   gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
 
   /*  free up buffers  */
+  if (has_sel)
+    {
+      gimp_drawable_detach (selDrawable);
+      g_free (sel);
+    }
   g_free (val_p);
   g_free (val_m);
-  g_free (src);
+  g_free (srci);
+  g_free (srcf);
   g_free (dest);
-}
-
-static void
-transfer_pixels (gdouble *src1,
-                 gdouble *src2,
-                 guchar  *dest,
-                 gint     bytes,
-                 gint     width)
-{
-  gint b;
-  gint bend = bytes * width;
-  gdouble sum;
-
-  for(b = 0; b < bend; b++)
-    {
-      sum = *src1++ + *src2++;
-      *dest++ = (guchar) CLAMP (sum, 0.0, 255.0);
-    }
 }
 
 static void
