]> git.imager.perl.org - imager.git/blobdiff - filters.c
- more information on gif library versions in README and Makefile.PL
[imager.git] / filters.c
index 4ee39879e79600e286b0655a73ca25f4a4614fc2..e7d58e3aab240eb4508ae610392e856d8ae9e19e 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,5 @@
 #include "image.h"
+#include "imagei.h"
 #include <stdlib.h>
 #include <math.h>
 
@@ -13,6 +14,7 @@ filters.c - implements filters that operate on images
   
   i_contrast(im, 0.8);
   i_hardinvert(im);
+  i_unsharp_mask(im, 2.0, 1.0);
   // and more
 
 =head1 DESCRIPTION
@@ -25,7 +27,7 @@ the pod for Imager.
 
 Some of these functions are internal.
 
-=over 4
+=over
 
 =cut
 */
@@ -33,6 +35,23 @@ Some of these functions are internal.
 
 
 
+/*
+=item saturate(in) 
+
+Clamps the input value between 0 and 255. (internal)
+
+  in - input integer
+
+=cut
+*/
+
+static
+unsigned char
+saturate(int in) {
+  if (in>255) { return 255; }
+  else if (in>0) return in;
+  return 0;
+}
 
 
 
@@ -262,12 +281,12 @@ i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st)
     mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels));
     return;
   }
-  
+
   mx = (bump->xsize <= im->xsize) ? bump->xsize : im->xsize;
   my = (bump->ysize <= im->ysize) ? bump->ysize : im->ysize;
 
   i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
+  
   aX = (light_x > (mx >> 1)) ? light_x : mx - light_x;
   aY = (light_y > (my >> 1)) ? light_y : my - light_y;
 
@@ -317,6 +336,213 @@ i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st)
 
 
 
+
+typedef struct {
+  float x,y,z;
+} fvec;
+
+
+static
+float
+dotp(fvec *a, fvec *b) {
+  return a->x*b->x+a->y*b->y+a->z*b->z;
+}
+
+static
+void
+normalize(fvec *a) {
+  double d = sqrt(dotp(a,a));
+  a->x /= d;
+  a->y /= d;
+  a->z /= d;
+}
+
+
+/*
+  positive directions:
+  
+  x - right, 
+  y - down
+  z - out of the plane
+  
+  I = Ia + Ip*( cd*Scol(N.L) + cs*(R.V)^n )
+  
+  Here, the variables are:
+  
+  * Ia   - ambient colour
+  * Ip   - intensity of the point light source
+  * cd   - diffuse coefficient
+  * Scol - surface colour
+  * cs   - specular coefficient
+  * n    - objects shinyness
+  * N    - normal vector
+  * L    - lighting vector
+  * R    - reflection vector
+  * V    - vision vector
+
+  static void fvec_dump(fvec *x) {
+    printf("(%.2f %.2f %.2f)", x->x, x->y, x->z);
+  }
+*/
+
+/* XXX: Should these return a code for success? */
+
+
+
+
+/* 
+=item i_bumpmap_complex(im, bump, channel, tx, ty, Lx, Ly, Lz, Ip, cd, cs, n, Ia, Il, Is)
+
+Makes a bumpmap on image im using the bump image as the elevation map.
+
+  im      - target image
+  bump    - image that contains the elevation info
+  channel - to take the elevation information from
+  tx      - shift in x direction of where to start applying bumpmap
+  ty      - shift in y direction of where to start applying bumpmap
+  Lx      - x position/direction of light
+  Ly      - y position/direction of light
+  Lz      - z position/direction of light
+  Ip      - light intensity
+  cd      - diffuse coefficient
+  cs      - specular coefficient
+  n       - surface shinyness
+  Ia      - ambient colour
+  Il      - light colour
+  Is      - specular colour
+
+if z<0 then the L is taken to be the direction the light is shining in.  Otherwise
+the L is taken to be the position of the Light, Relative to the image.
+
+=cut
+*/
+
+
+void
+i_bumpmap_complex(i_img *im,
+                 i_img *bump,
+                 int channel,
+                 int tx,
+                 int ty,
+                 float Lx,
+                 float Ly,
+                 float Lz,
+                 float cd,
+                 float cs,
+                 float n,
+                 i_color *Ia,
+                 i_color *Il,
+                 i_color *Is) {
+  i_img new_im;
+  
+  int inflight;
+  int x, y, ch;
+  int mx, Mx, my, My;
+  
+  float cdc[MAXCHANNELS];
+  float csc[MAXCHANNELS];
+
+  i_color x1_color, y1_color, x2_color, y2_color;
+
+  i_color Scol;   /* Surface colour       */
+
+  fvec L;         /* Light vector */
+  fvec N;         /* surface normal       */
+  fvec R;         /* Reflection vector    */
+  fvec V;         /* Vision vector        */
+
+  mm_log((1, "i_bumpmap_complex(im %p, bump %p, channel %d, tx %d, ty %d, Lx %.2f, Ly %.2f, Lz %.2f, cd %.2f, cs %.2f, n %.2f, Ia %p, Il %p, Is %p)\n",
+         im, bump, channel, tx, ty, Lx, Ly, Lz, cd, cs, n, Ia, Il, Is));
+  
+  if (channel >= bump->channels) {
+    mm_log((1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels));
+    return;
+  }
+
+  for(ch=0; ch<im->channels; ch++) {
+    cdc[ch] = (float)Il->channel[ch]*cd/255.f;
+    csc[ch] = (float)Is->channel[ch]*cs/255.f;
+  }
+
+  mx = 1;
+  my = 1;
+  Mx = bump->xsize-1;
+  My = bump->ysize-1;
+  
+  V.x = 0;
+  V.y = 0;
+  V.z = 1;
+  
+  if (Lz < 0) { /* Light specifies a direction vector, reverse it to get the vector from surface to light */
+    L.x = -Lx;
+    L.y = -Ly;
+    L.z = -Lz;
+    normalize(&L);
+  } else {      /* Light is the position of the light source */
+    inflight = 0;
+    L.x = -0.2;
+    L.y = -0.4;
+    L.z =  1;
+    normalize(&L);
+  }
+
+  i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
+
+  for(y = 0; y < im->ysize; y++) {             
+    for(x = 0; x < im->xsize; x++) {
+      double dp1, dp2;
+      double dx = 0, dy = 0;
+
+      /* Calculate surface normal */
+      if (mx<x && x<Mx && my<y && y<My) {
+       i_gpix(bump, x + 1, y,     &x1_color);
+       i_gpix(bump, x - 1, y,     &x2_color);
+       i_gpix(bump, x,     y + 1, &y1_color);
+       i_gpix(bump, x,     y - 1, &y2_color);
+       dx = x2_color.channel[channel] - x1_color.channel[channel];
+       dy = y2_color.channel[channel] - y1_color.channel[channel];
+      } else {
+       dx = 0;
+       dy = 0;
+      }
+      N.x = -dx * 0.015;
+      N.y = -dy * 0.015;
+      N.z = 1;
+      normalize(&N);
+
+      /* Calculate Light vector if needed */
+      if (Lz>=0) {
+       L.x = Lx - x;
+       L.y = Ly - y;
+       L.z = Lz;
+       normalize(&L);
+      }
+      
+      dp1 = dotp(&L,&N);
+      R.x = -L.x + 2*dp1*N.x;
+      R.y = -L.y + 2*dp1*N.y;
+      R.z = -L.z + 2*dp1*N.z;
+      
+      dp2 = dotp(&R,&V);
+
+      dp1 = dp1<0 ?0 : dp1;
+      dp2 = pow(dp2<0 ?0 : dp2,n);
+
+      i_gpix(im, x, y, &Scol);
+
+      for(ch = 0; ch < im->channels; ch++)
+       Scol.channel[ch] = 
+         saturate( Ia->channel[ch] + cdc[ch]*Scol.channel[ch]*dp1 + csc[ch]*dp2 );
+      
+      i_ppix(&new_im, x, y, &Scol);
+    }
+  }
+  
+  i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0);
+  i_img_exorcise(&new_im);
+}
+
+
 /* 
 =item i_postlevels(im, levels)
 
@@ -403,24 +629,6 @@ i_mosaic(i_img *im, int size) {
   }
 }
 
-/*
-=item saturate(in) 
-
-Clamps the input value between 0 and 255. (internal)
-
-  in - input integer
-
-=cut
-*/
-
-static
-unsigned char
-saturate(int in) {
-  if (in>255) { return 255; }
-  else if (in>0) return in;
-  return 0;
-}
-
 
 /*
 =item i_watermark(im, wmark, tx, ty, pixdiff) 
@@ -441,7 +649,10 @@ i_watermark(i_img *im, i_img *wmark, int tx, int ty, int pixdiff) {
   int vx, vy, ch;
   i_color val, wval;
 
-  for(vx=0;vx<128;vx++) for(vy=0;vy<110;vy++) {
+       int mx = wmark->xsize;
+       int my = wmark->ysize;
+
+  for(vx=0;vx<mx;vx++) for(vy=0;vy<my;vy++) {
     
     i_gpix(im,    tx+vx, ty+vy,&val );
     i_gpix(wmark, vx,    vy,   &wval);
@@ -752,7 +963,7 @@ i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
        fdist[p]  = xd*xd + yd*yd; /* euclidean distance */
        break;
       case 2: /* euclidean squared */
-       fdist[p]  = max(xd*xd, yd*yd); /* manhattan distance */
+       fdist[p]  = i_max(xd*xd, yd*yd); /* manhattan distance */
        break;
       default:
        m_fatal(3,"i_gradgen: Unknown distance measure\n");
@@ -771,6 +982,7 @@ i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
     }
     i_ppix(im, x, y, &val); 
   }
+  myfree(fdist);
   
 }
 
@@ -804,7 +1016,7 @@ i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dme
       mindist = xd*xd + yd*yd; /* euclidean distance */
       break;
     case 2: /* euclidean squared */
-      mindist = max(xd*xd, yd*yd); /* manhattan distance */
+      mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
       break;
     default:
       m_fatal(3,"i_nearest_color: Unknown distance measure\n");
@@ -821,7 +1033,7 @@ i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dme
        curdist = xd*xd + yd*yd; /* euclidean distance */
        break;
       case 2: /* euclidean squared */
-       curdist = max(xd*xd, yd*yd); /* manhattan distance */
+       curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
        break;
       default:
        m_fatal(3,"i_nearest_color: Unknown distance measure\n");
@@ -874,7 +1086,7 @@ i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasur
       mindist = xd*xd + yd*yd; /* euclidean distance */
       break;
     case 2: /* euclidean squared */
-      mindist = max(xd*xd, yd*yd); /* manhattan distance */
+      mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
       break;
     default:
       m_fatal(3,"i_nearest_color: Unknown distance measure\n");
@@ -891,7 +1103,7 @@ i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasur
        curdist = xd*xd + yd*yd; /* euclidean distance */
        break;
       case 2: /* euclidean squared */
-       curdist = max(xd*xd, yd*yd); /* manhattan distance */
+       curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
        break;
       default:
        m_fatal(3,"i_nearest_color: Unknown distance measure\n");
@@ -919,21 +1131,180 @@ i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasur
 }
 
 /*
-  Keep state information used by each type of fountain fill
+=item i_unsharp_mask(im, stddev, scale)
+
+Perform an usharp mask, which is defined as subtracting the blurred
+image from double the original.
+
+=cut
 */
-struct fount_state {
-  /* precalculated for the equation of the line perpendicular to the line AB */
-  double lA, lB, lC;
-  double AB;
-  double sqrtA2B2;
-  double mult;
-  double cos;
-  double sin;
-  double theta;
-  int xa, ya;
-  void *ssample_data;
-};
+void i_unsharp_mask(i_img *im, double stddev, double scale) {
+  i_img copy;
+  int x, y, ch;
+
+  if (scale < 0)
+    return;
+  /* it really shouldn't ever be more than 1.0, but maybe ... */
+  if (scale > 100)
+    scale = 100;
+
+  i_copy(&copy, im);
+  i_gaussian(&copy, stddev);
+  if (im->bits == i_8_bits) {
+    i_color *blur = mymalloc(im->xsize * sizeof(i_color) * 2);
+    i_color *out = blur + im->xsize;
+
+    for (y = 0; y < im->ysize; ++y) {
+      i_glin(&copy, 0, copy.xsize, y, blur);
+      i_glin(im, 0, im->xsize, y, out);
+      for (x = 0; x < im->xsize; ++x) {
+        for (ch = 0; ch < im->channels; ++ch) {
+          /*int temp = out[x].channel[ch] + 
+            scale * (out[x].channel[ch] - blur[x].channel[ch]);*/
+          int temp = out[x].channel[ch] * 2 - blur[x].channel[ch];
+          if (temp < 0)
+            temp = 0;
+          else if (temp > 255)
+            temp = 255;
+          out[x].channel[ch] = temp;
+        }
+      }
+      i_plin(im, 0, im->xsize, y, out);
+    }
+
+    myfree(blur);
+  }
+  else {
+    i_fcolor *blur = mymalloc(im->xsize * sizeof(i_fcolor) * 2);
+    i_fcolor *out = blur + im->xsize;
+
+    for (y = 0; y < im->ysize; ++y) {
+      i_glinf(&copy, 0, copy.xsize, y, blur);
+      i_glinf(im, 0, im->xsize, y, out);
+      for (x = 0; x < im->xsize; ++x) {
+        for (ch = 0; ch < im->channels; ++ch) {
+          double temp = out[x].channel[ch] +
+            scale * (out[x].channel[ch] - blur[x].channel[ch]);
+          if (temp < 0)
+            temp = 0;
+          else if (temp > 1.0)
+            temp = 1.0;
+          out[x].channel[ch] = temp;
+        }
+      }
+      i_plinf(im, 0, im->xsize, y, out);
+    }
+
+    myfree(blur);
+  }
+  i_img_exorcise(&copy);
+}
+
+/*
+=item i_diff_image(im1, im2, mindiff)
+
+Creates a new image that is transparent, except where the pixel in im2
+is different from im1, where it is the pixel from im2.
+
+The samples must differ by at least mindiff to be considered different.
 
+=cut
+*/
+
+i_img *
+i_diff_image(i_img *im1, i_img *im2, int mindiff) {
+  i_img *out;
+  int outchans, diffchans;
+  int xsize, ysize;
+  i_img temp;
+
+  i_clear_error();
+  if (im1->channels != im2->channels) {
+    i_push_error(0, "different number of channels");
+    return NULL;
+  }
+
+  outchans = diffchans = im1->channels;
+  if (outchans == 1 || outchans == 3)
+    ++outchans;
+
+  xsize = i_min(im1->xsize, im2->xsize);
+  ysize = i_min(im1->ysize, im2->ysize);
+
+  out = i_sametype_chans(im1, xsize, ysize, outchans);
+  
+  if (im1->bits == i_8_bits && im2->bits == i_8_bits) {
+    i_color *line1 = mymalloc(2 * xsize * sizeof(*line1));
+    i_color *line2 = line1 + xsize;
+    i_color empty;
+    int x, y, ch;
+
+    for (ch = 0; ch < MAXCHANNELS; ++ch)
+      empty.channel[ch] = 0;
+
+    for (y = 0; y < ysize; ++y) {
+      i_glin(im1, 0, xsize, y, line1);
+      i_glin(im2, 0, xsize, y, line2);
+      if (outchans != diffchans) {
+        /* give the output an alpha channel since it doesn't have one */
+        for (x = 0; x < xsize; ++x)
+          line2[x].channel[diffchans] = 255;
+      }
+      for (x = 0; x < xsize; ++x) {
+        int diff = 0;
+        for (ch = 0; ch < diffchans; ++ch) {
+          if (line1[x].channel[ch] != line2[x].channel[ch]
+              && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
+            diff = 1;
+            break;
+          }
+        }
+        if (!diff)
+          line2[x] = empty;
+      }
+      i_plin(out, 0, xsize, y, line2);
+    }
+    myfree(line1);
+  }
+  else {
+    i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
+    i_fcolor *line2 = line1 + xsize;
+    i_fcolor empty;
+    int x, y, ch;
+    double dist = mindiff / 255;
+
+    for (ch = 0; ch < MAXCHANNELS; ++ch)
+      empty.channel[ch] = 0;
+
+    for (y = 0; y < ysize; ++y) {
+      i_glinf(im1, 0, xsize, y, line1);
+      i_glinf(im2, 0, xsize, y, line2);
+      if (outchans != diffchans) {
+        /* give the output an alpha channel since it doesn't have one */
+        for (x = 0; x < xsize; ++x)
+          line2[x].channel[diffchans] = 1.0;
+      }
+      for (x = 0; x < xsize; ++x) {
+        int diff = 0;
+        for (ch = 0; ch < diffchans; ++ch) {
+          if (line1[x].channel[ch] != line2[x].channel[ch]
+              && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
+            diff = 1;
+            break;
+          }
+        }
+        if (!diff)
+          line2[x] = empty;
+      }
+      i_plinf(out, 0, xsize, y, line2);
+    }
+    myfree(line1);
+  }
+
+  return out;
+}
+
+struct fount_state;
 static double linear_fount_f(double x, double y, struct fount_state *state);
 static double bilinear_fount_f(double x, double y, struct fount_state *state);
 static double radial_fount_f(double x, double y, struct fount_state *state);
@@ -993,22 +1364,14 @@ static fount_repeat fount_repeats[] =
   fount_r_tri_both,
 };
 
-static int simple_ssample(i_fcolor *out, double parm, double x, double y, 
-                           struct fount_state *state, 
-                           fount_func ffunc, fount_repeat rpfunc,
-                           i_fountain_seg *segs, int count);
-static int random_ssample(i_fcolor *out, double parm, double x, double y, 
-                           struct fount_state *state, 
-                           fount_func ffunc, fount_repeat rpfunc,
-                           i_fountain_seg *segs, int count);
-static int circle_ssample(i_fcolor *out, double parm, double x, double y, 
-                           struct fount_state *state, 
-                           fount_func ffunc, fount_repeat rpfunc,
-                           i_fountain_seg *segs, int count);
-typedef int (*fount_ssample)(i_fcolor *out, double parm, double x, double y, 
-                              struct fount_state *state,
-                              fount_func ffunc, fount_repeat rpfunc,
-                              i_fountain_seg *segs, int count);
+static int simple_ssample(i_fcolor *out, double x, double y, 
+                           struct fount_state *state);
+static int random_ssample(i_fcolor *out, double x, double y, 
+                           struct fount_state *state);
+static int circle_ssample(i_fcolor *out, double x, double y, 
+                           struct fount_state *state);
+typedef int (*fount_ssample)(i_fcolor *out, double x, double y, 
+                              struct fount_state *state);
 static fount_ssample fount_ssamples[] =
 {
   NULL,
@@ -1018,9 +1381,38 @@ static fount_ssample fount_ssamples[] =
 };
 
 static int
-fount_getat(i_fcolor *out, double x, double y, fount_func ffunc, 
-            fount_repeat rpfunc, struct fount_state *state,
-            i_fountain_seg *segs, int count);
+fount_getat(i_fcolor *out, double x, double y, struct fount_state *state);
+
+/*
+  Keep state information used by each type of fountain fill
+*/
+struct fount_state {
+  /* precalculated for the equation of the line perpendicular to the line AB */
+  double lA, lB, lC;
+  double AB;
+  double sqrtA2B2;
+  double mult;
+  double cos;
+  double sin;
+  double theta;
+  int xa, ya;
+  void *ssample_data;
+  fount_func ffunc;
+  fount_repeat rpfunc;
+  fount_ssample ssfunc;
+  double parm;
+  i_fountain_seg *segs;
+  int count;
+};
+
+static void
+fount_init_state(struct fount_state *state, double xa, double ya, 
+                 double xb, double yb, i_fountain_type type, 
+                 i_fountain_repeat repeat, int combine, int super_sample, 
+                 double ssample_param, int count, i_fountain_seg *segs);
+
+static void
+fount_finish_state(struct fount_state *state);
 
 #define EPSILON (1e-6)
 
@@ -1135,23 +1527,119 @@ i_fountain(i_img *im, double xa, double ya, double xb, double yb,
            int combine, int super_sample, double ssample_param, 
            int count, i_fountain_seg *segs) {
   struct fount_state state;
-  fount_func ffunc;
-  fount_ssample ssfunc;
-  fount_repeat rpfunc;
   int x, y;
   i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
+  i_fcolor *work = NULL;
+
+  i_fountain_seg *my_segs;
+  i_fill_combine_f combine_func = NULL;
+  i_fill_combinef_f combinef_func = NULL;
+
+  i_get_combine(combine, &combine_func, &combinef_func);
+  if (combinef_func)
+    work = mymalloc(sizeof(i_fcolor) * im->xsize);
+
+  fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine, 
+                   super_sample, ssample_param, count, segs);
+  my_segs = state.segs;
+
+  for (y = 0; y < im->ysize; ++y) {
+    i_glinf(im, 0, im->xsize, y, line);
+    for (x = 0; x < im->xsize; ++x) {
+      i_fcolor c;
+      int got_one;
+      if (super_sample == i_fts_none)
+        got_one = fount_getat(&c, x, y, &state);
+      else
+        got_one = state.ssfunc(&c, x, y, &state);
+      if (got_one) {
+        if (combine)
+          work[x] = c;
+        else 
+          line[x] = c;
+      }
+    }
+    if (combine)
+      combinef_func(line, work, im->channels, im->xsize);
+    i_plinf(im, 0, im->xsize, y, line);
+  }
+  fount_finish_state(&state);
+  if (work) myfree(work);
+  myfree(line);
+}
+
+typedef struct {
+  i_fill_t base;
+  struct fount_state state;
+} i_fill_fountain_t;
+
+static void
+fill_fountf(i_fill_t *fill, int x, int y, int width, int channels, 
+            i_fcolor *data);
+static void
+fount_fill_destroy(i_fill_t *fill);
+
+/*
+=item i_new_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
+
+Creates a new general fill which fills with a fountain fill.
+
+=cut
+*/
+
+i_fill_t *
+i_new_fill_fount(double xa, double ya, double xb, double yb, 
+                 i_fountain_type type, i_fountain_repeat repeat, 
+                 int combine, int super_sample, double ssample_param, 
+                 int count, i_fountain_seg *segs) {
+  i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
+  
+  fill->base.fill_with_color = NULL;
+  fill->base.fill_with_fcolor = fill_fountf;
+  fill->base.destroy = fount_fill_destroy;
+  if (combine)
+    i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
+  else {
+    fill->base.combine = NULL;
+    fill->base.combinef = NULL;
+  }
+  fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine, 
+                   super_sample, ssample_param, count, segs);
+
+  return &fill->base;
+}
+
+/*
+=back
+
+=head1 INTERNAL FUNCTIONS
+
+=over
+
+=item fount_init_state(...)
+
+Used by both the fountain fill filter and the fountain fill.
+
+=cut
+*/
+
+static void
+fount_init_state(struct fount_state *state, double xa, double ya, 
+                 double xb, double yb, i_fountain_type type, 
+                 i_fountain_repeat repeat, int combine, int super_sample, 
+                 double ssample_param, int count, i_fountain_seg *segs) {
   int i, j;
   i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count);
-  int have_alpha = im->channels == 2 || im->channels == 4;
-  int ch;
-
+  /*int have_alpha = im->channels == 2 || im->channels == 4;*/
+  
+  memset(state, 0, sizeof(*state));
   /* we keep a local copy that we can adjust for speed */
   for (i = 0; i < count; ++i) {
     i_fountain_seg *seg = my_segs + i;
 
     *seg = segs[i];
-    if (seg->type < 0 || type >= i_ft_end)
-      seg->type = i_ft_linear;
+    if (seg->type < 0 || seg->type >= i_fst_end)
+      seg->type = i_fst_linear;
     if (seg->color < 0 || seg->color >= i_fc_end)
       seg->color = i_fc_direct;
     if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
@@ -1178,103 +1666,78 @@ i_fountain(i_img *im, double xa, double ya, double xb, double yb,
 
   /* initialize each engine */
   /* these are so common ... */
-  state.lA = xb - xa;
-  state.lB = yb - ya;
-  state.AB = sqrt(state.lA * state.lA + state.lB * state.lB);
-  state.xa = xa;
-  state.ya = ya;
+  state->lA = xb - xa;
+  state->lB = yb - ya;
+  state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
+  state->xa = xa;
+  state->ya = ya;
   switch (type) {
   default:
     type = i_ft_linear; /* make the invalid value valid */
   case i_ft_linear:
   case i_ft_bilinear:
-    state.lC = ya * ya - ya * yb + xa * xa - xa * xb;
-    state.mult = 1;
-    state.mult = 1/linear_fount_f(xb, yb, &state);
+    state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
+    state->mult = 1;
+    state->mult = 1/linear_fount_f(xb, yb, state);
     break;
 
   case i_ft_radial:
-    state.mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa) 
-                            + (double)(yb-ya)*(yb-ya));
+    state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa) 
+                             + (double)(yb-ya)*(yb-ya));
     break;
 
   case i_ft_radial_square:
-    state.cos = state.lA / state.AB;
-    state.sin = state.lB / state.AB;
-    state.mult = 1.0 / state.AB;
+    state->cos = state->lA / state->AB;
+    state->sin = state->lB / state->AB;
+    state->mult = 1.0 / state->AB;
     break;
 
   case i_ft_revolution:
-    state.theta = atan2(yb-ya, xb-xa);
-    state.mult = 1.0 / (PI * 2);
+    state->theta = atan2(yb-ya, xb-xa);
+    state->mult = 1.0 / (PI * 2);
     break;
 
   case i_ft_conical:
-    state.theta = atan2(yb-ya, xb-xa);
-    state.mult = 1.0 / PI;
+    state->theta = atan2(yb-ya, xb-xa);
+    state->mult = 1.0 / PI;
     break;
   }
-  ffunc = fount_funcs[type];
+  state->ffunc = fount_funcs[type];
   if (super_sample < 0 
-      || super_sample >= (sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
+      || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
     super_sample = 0;
   }
-  state.ssample_data = NULL;
+  state->ssample_data = NULL;
   switch (super_sample) {
   case i_fts_grid:
     ssample_param = floor(0.5 + sqrt(ssample_param));
-    state.ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
+    state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
     break;
 
   case i_fts_random:
   case i_fts_circle:
     ssample_param = floor(0.5+ssample_param);
-    state.ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
+    state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
     break;
   }
-  ssfunc = fount_ssamples[super_sample];
+  state->parm = ssample_param;
+  state->ssfunc = fount_ssamples[super_sample];
   if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
     repeat = 0;
-  rpfunc = fount_repeats[repeat];
-
-  for (y = 0; y < im->ysize; ++y) {
-    i_glinf(im, 0, im->xsize, y, line);
-    for (x = 0; x < im->xsize; ++x) {
-      i_fcolor c;
-      int got_one;
-      double v;
-      if (super_sample == i_fts_none)
-        got_one = fount_getat(&c, x, y, ffunc, rpfunc, &state, my_segs, count);
-      else
-        got_one = ssfunc(&c, ssample_param, x, y, &state, ffunc, rpfunc, 
-                         my_segs, count);
-      if (got_one) {
-        i_fountain_seg *seg = my_segs + i;
-        if (combine) {
-          for (ch = 0; ch < im->channels; ++ch) {
-            line[x].channel[ch] = line[x].channel[ch] * (1.0 - c.channel[3])
-              + c.channel[ch] * c.channel[3];
-          }
-        }
-        else 
-          line[x] = c;
-      }
-    }
-    i_plinf(im, 0, im->xsize, y, line);
-  }
-  myfree(line);
-  myfree(my_segs);
-  if (state.ssample_data)
-    myfree(state.ssample_data);
+  state->rpfunc = fount_repeats[repeat];
+  state->segs = my_segs;
+  state->count = count;
 }
 
-/*
-=back
-
-=head1 INTERNAL FUNCTIONS
+static void
+fount_finish_state(struct fount_state *state) {
+  if (state->ssample_data)
+    myfree(state->ssample_data);
+  myfree(state->segs);
+}
 
-=over
 
+/*
 =item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
 
 Evaluates the fountain fill at the given point.
@@ -1288,19 +1751,18 @@ instead, and combine those, but this breaks badly.
 */
 
 static int
-fount_getat(i_fcolor *out, double x, double y, fount_func ffunc, 
-            fount_repeat rpfunc, struct fount_state *state, 
-            i_fountain_seg *segs, int count) {
-  double v = rpfunc(ffunc(x, y, state));
+fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
+  double v = (state->rpfunc)((state->ffunc)(x, y, state));
   int i;
 
   i = 0;
-  while (i < count && (v < segs[i].start || v > segs[i].end)) {
+  while (i < state->count 
+         && (v < state->segs[i].start || v > state->segs[i].end)) {
     ++i;
   }
-  if (i < count) {
-    v = (fount_interps[segs[i].type])(v, segs+i);
-    (fount_cinterps[segs[i].color])(out, v, segs+i);
+  if (i < state->count) {
+    v = (fount_interps[state->segs[i].type])(v, state->segs+i);
+    (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
     return 1;
   }
   else
@@ -1541,13 +2003,10 @@ Simple grid-based super-sampling.
 =cut
 */
 static int
-simple_ssample(i_fcolor *out, double parm, double x, double y, 
-               struct fount_state *state, 
-               fount_func ffunc, fount_repeat rpfunc, i_fountain_seg *segs,
-               int count) {
+simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
   i_fcolor *work = state->ssample_data;
   int dx, dy;
-  int grid = parm;
+  int grid = state->parm;
   double base = -0.5 + 0.5 / grid;
   double step = 1.0 / grid;
   int ch, i;
@@ -1556,8 +2015,7 @@ simple_ssample(i_fcolor *out, double parm, double x, double y,
   for (dx = 0; dx < grid; ++dx) {
     for (dy = 0; dy < grid; ++dy) {
       if (fount_getat(work+samp_count, x + base + step * dx, 
-                      y + base + step * dy, ffunc, rpfunc, state, 
-                      segs, count)) {
+                      y + base + step * dy, state)) {
         ++samp_count;
       }
     }
@@ -1582,19 +2040,16 @@ Random super-sampling.
 =cut
 */
 static int
-random_ssample(i_fcolor *out, double parm, double x, double y, 
-               struct fount_state *state, 
-               fount_func ffunc, fount_repeat rpfunc, i_fountain_seg *segs,
-               int count) {
+random_ssample(i_fcolor *out, double x, double y, 
+               struct fount_state *state) {
   i_fcolor *work = state->ssample_data;
   int i, ch;
-  int maxsamples = parm;
+  int maxsamples = state->parm;
   double rand_scale = 1.0 / RAND_MAX;
   int samp_count = 0;
   for (i = 0; i < maxsamples; ++i) {
     if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale, 
-                    y - 0.5 + rand() * rand_scale, ffunc, rpfunc, state, 
-                    segs, count)) {
+                    y - 0.5 + rand() * rand_scale, state)) {
       ++samp_count;
     }
   }
@@ -1622,20 +2077,17 @@ much.
 =cut
  */
 static int
-circle_ssample(i_fcolor *out, double parm, double x, double y, 
-               struct fount_state *state, 
-               fount_func ffunc, fount_repeat rpfunc, i_fountain_seg *segs,
-               int count) {
+circle_ssample(i_fcolor *out, double x, double y, 
+               struct fount_state *state) {
   i_fcolor *work = state->ssample_data;
   int i, ch;
-  int maxsamples = parm;
+  int maxsamples = state->parm;
   double angle = 2 * PI / maxsamples;
   double radius = 0.3; /* semi-random */
   int samp_count = 0;
   for (i = 0; i < maxsamples; ++i) {
     if (fount_getat(work+samp_count, x + radius * cos(angle * i), 
-                    y + radius * sin(angle * i), ffunc, rpfunc, state, 
-                    segs, count)) {
+                    y + radius * sin(angle * i), state)) {
       ++samp_count;
     }
   }
@@ -1725,6 +2177,44 @@ fount_r_tri_both(double v) {
   return v > 1.0 ? 2.0 - v : v;
 }
 
+/*
+=item fill_fountf(fill, x, y, width, channels, data)
+
+The fill function for fountain fills.
+
+=cut
+*/
+static void
+fill_fountf(i_fill_t *fill, int x, int y, int width, int channels, 
+            i_fcolor *data) {
+  i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
+  
+  while (width--) {
+    i_fcolor c;
+    int got_one;
+    
+    if (f->state.ssfunc)
+      got_one = f->state.ssfunc(&c, x, y, &f->state);
+    else
+      got_one = fount_getat(&c, x, y, &f->state);
+    
+    *data++ = c;
+    
+    ++x;
+  }
+}
+
+/*
+=item fount_fill_destroy(fill)
+
+=cut
+*/
+static void
+fount_fill_destroy(i_fill_t *fill) {
+  i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
+  fount_finish_state(&f->state);
+}
+
 /*
 =back