]> git.imager.perl.org - imager.git/blobdiff - fills.c
note on the PNG / Imager::Probe change
[imager.git] / fills.c
diff --git a/fills.c b/fills.c
index 05db1aabd5a0cccadbf7d5d807a5a08e5a93d2c5..0e8f41790ecd1a6f1113d91d41244c0e0b3a070f 100644 (file)
--- a/fills.c
+++ b/fills.c
@@ -17,6 +17,7 @@ fills.c - implements the basic general fills
   fill = i_new_fill_hatchf(&fc1, &fc2, combine, hatch, cust_hash, dx, dy);
   fill = i_new_fill_hatch(&c1, &c2, combine, hatch, cust_hash, dx, dy);
   fill = i_new_fill_image(im, matrix, xoff, yoff, combine);
+  fill = i_new_fill_opacity(fill, alpha_mult);
   i_fill_destroy(fill);
 
 =head1 DESCRIPTION
@@ -166,10 +167,6 @@ static void fill_solid(i_fill_t *, int x, int y, int width, int channels,
                        i_color *);
 static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, 
                         i_fcolor *);
-static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels, 
-                            i_color *);
-static void fill_solidf_comb(i_fill_t *, int x, int y, int width, 
-                             int channels, i_fcolor *);
 
 static i_fill_solid_t base_solid_fill =
 {
@@ -181,21 +178,12 @@ static i_fill_solid_t base_solid_fill =
     NULL,
   },
 };
-static i_fill_solid_t base_solid_fill_comb =
-{
-  {
-    fill_solid_comb,
-    fill_solidf_comb,
-    NULL,
-    NULL,
-    NULL,
-  },
-};
 
 /*
 =item i_fill_destroy(fill)
-
+=order 90
 =category Fills
+=synopsis i_fill_destroy(fill);
 
 Call to destroy any fill object.
 
@@ -213,6 +201,7 @@ i_fill_destroy(i_fill_t *fill) {
 =item i_new_fill_solidf(color, combine)
 
 =category Fills
+=synopsis i_fill_t *fill = i_new_fill_solidf(&fcolor, combine);
 
 Create a solid fill based on a float color.
 
@@ -226,12 +215,11 @@ i_new_fill_solidf(const i_fcolor *c, int combine) {
   int ch;
   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
   
+  *fill = base_solid_fill;
   if (combine) {
-    *fill = base_solid_fill_comb;
     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
   }
-  else
-    *fill = base_solid_fill;
+
   fill->fc = *c;
   for (ch = 0; ch < MAXCHANNELS; ++ch) {
     fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
@@ -244,6 +232,7 @@ i_new_fill_solidf(const i_fcolor *c, int combine) {
 =item i_new_fill_solid(color, combine)
 
 =category Fills
+=synopsis i_fill_t *fill = i_new_fill_solid(&color, combine);
 
 Create a solid fill based on an 8-bit color.
 
@@ -257,12 +246,11 @@ i_new_fill_solid(const i_color *c, int combine) {
   int ch;
   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
 
+  *fill = base_solid_fill;
   if (combine) {
-    *fill = base_solid_fill_comb;
     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
   }
-  else
-    *fill = base_solid_fill;
+
   fill->c = *c;
   for (ch = 0; ch < MAXCHANNELS; ++ch) {
     fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
@@ -424,20 +412,23 @@ i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const
                 int dx, int dy);
 
 /*
-=item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
+=item i_new_fill_hatch(C<fg>, C<bg>, C<combine>, C<hatch>, C<cust_hatch>, C<dx>, C<dy>)
 
 =category Fills
+=synopsis i_fill_t *fill = i_new_fill_hatch(&fg_color, &bg_color, combine, hatch, custom_hatch, dx, dy);
 
-Creates a new hatched fill with the fg color used for the 1 bits in
-the hatch and bg for the 0 bits.  If combine is non-zero alpha values
-will be combined.
+Creates a new hatched fill with the C<fg> color used for the 1 bits in
+the hatch and C<bg> for the 0 bits.  If C<combine> is non-zero alpha
+values will be combined.
 
-If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
+If C<cust_hatch> is non-NULL it should be a pointer to 8 bytes of the
 hash definition, with the high-bits to the left.
 
-If cust_hatch is NULL then one of the standard hatches is used.
+If C<cust_hatch> is NULL then one of the standard hatches is used.
 
-(dx, dy) are an offset into the hatch which can be used to unalign adjoining areas, or to align the origin of a hatch with the the side of a filled area.
+(C<dx>, C<dy>) are an offset into the hatch which can be used to hatch
+adjoining areas out of alignment, or to align the origin of a hatch
+with the the side of a filled area.
 
 =cut
 */
@@ -449,20 +440,23 @@ i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch,
 }
 
 /*
-=item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
+=item i_new_fill_hatchf(C<fg>, C<bg>, C<combine>, C<hatch>, C<cust_hatch>, C<dx>, C<dy>)
 
 =category Fills
+=synopsis i_fill_t *fill = i_new_fill_hatchf(&fg_fcolor, &bg_fcolor, combine, hatch, custom_hatch, dx, dy);
 
-Creates a new hatched fill with the fg color used for the 1 bits in
-the hatch and bg for the 0 bits.  If combine is non-zero alpha values
-will be combined.
+Creates a new hatched fill with the C<fg> color used for the 1 bits in
+the hatch and C<bg> for the 0 bits.  If C<combine> is non-zero alpha
+values will be combined.
 
-If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
+If C<cust_hatch> is non-NULL it should be a pointer to 8 bytes of the
 hash definition, with the high-bits to the left.
 
-If cust_hatch is NULL then one of the standard hatches is used.
+If C<cust_hatch> is NULL then one of the standard hatches is used.
 
-(dx, dy) are an offset into the hatch which can be used to unalign adjoining areas, or to align the origin of a hatch with the the side of a filled area.
+(C<dx>, C<dy>) are an offset into the hatch which can be used to hatch
+adjoining areas out of alignment, or to align the origin of a hatch
+with the the side of a filled area.
 
 =cut
 */
@@ -485,16 +479,27 @@ struct i_fill_image_t {
   double matrix[9];
 };
 
+static struct i_fill_image_t
+image_fill_proto =
+  {
+    {
+      fill_image,
+      fill_imagef,
+      NULL
+    }
+  };
+
 /*
-=item i_new_fill_image(im, matrix, xoff, yoff, combine)
+=item i_new_fill_image(C<im>, C<matrix>, C<xoff>, C<yoff>, C<combine>)
 
 =category Fills
+=synopsis i_fill_t *fill = i_new_fill_image(src_img, matrix, x_offset, y_offset, combine);
 
 Create an image based fill.
 
 matrix is an array of 9 doubles representing a transformation matrix.
 
-xoff and yoff are the offset into the image to start filling from.
+C<xoff> and C<yoff> are the offset into the image to start filling from.
 
 =cut
 */
@@ -502,9 +507,7 @@ i_fill_t *
 i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combine) {
   struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */
 
-  fill->base.fill_with_color = fill_image;
-  fill->base.fill_with_fcolor = fill_imagef;
-  fill->base.destroy = NULL;
+  *fill = image_fill_proto;
 
   if (combine) {
     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
@@ -530,6 +533,43 @@ i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combin
   return &fill->base;
 }
 
+static void fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_color *data);
+static void fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_fcolor *data);
+
+struct i_fill_opacity_t {
+  i_fill_t base;
+  i_fill_t *other_fill;
+  double alpha_mult;
+};
+
+static struct i_fill_opacity_t
+opacity_fill_proto =
+  {
+    fill_opacity,
+    fill_opacityf,
+    NULL
+  };
+
+i_fill_t *
+i_new_fill_opacity(i_fill_t *base_fill, double alpha_mult) {
+  struct i_fill_opacity_t *fill = mymalloc(sizeof(*fill));
+  *fill = opacity_fill_proto;
+
+  fill->base.combine = base_fill->combine;
+  fill->base.combinef = base_fill->combinef;
+
+  fill->other_fill = base_fill;
+  fill->alpha_mult = alpha_mult;
+
+  if (!base_fill->f_fill_with_color) {
+    /* base fill only does floating, so we only do that too */
+    fill->base.f_fill_with_color = NULL;
+  }
+
+  return &fill->base;
+}
 
 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
 
@@ -549,8 +589,10 @@ The 8-bit sample fill function for non-combining solid fills.
 static void
 fill_solid(i_fill_t *fill, int x, int y, int width, int channels, 
            i_color *data) {
+  i_color c = T_SOLID_FILL(fill)->c;
+  i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
   while (width-- > 0) {
-    *data++ = T_SOLID_FILL(fill)->c;
+    *data++ = c;
   }
 }
 
@@ -564,45 +606,23 @@ The floating sample fill function for non-combining solid fills.
 static void
 fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, 
            i_fcolor *data) {
-  while (width-- > 0) {
-    *data++ = T_SOLID_FILL(fill)->fc;
-  }
-}
-
-/*
-=item fill_solid_comb(fill, x, y, width, channels, data)
-
-The 8-bit sample fill function for combining solid fills.
-
-=cut
-*/
-static void
-fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, 
-                i_color *data) {
-  i_color c = T_SOLID_FILL(fill)->c;
-
-  while (width-- > 0) {
-    *data++ = c;
-  }
-}
-
-/*
-=item fill_solidf_comb(fill, x, y, width, channels, data)
-
-The floating sample fill function for combining solid fills.
-
-=cut
-*/
-static void
-fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, 
-           i_fcolor *data) {
   i_fcolor c = T_SOLID_FILL(fill)->fc;
-
+  i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
   while (width-- > 0) {
     *data++ = c;
   }
 }
 
+static i_fill_hatch_t
+hatch_fill_proto =
+  {
+    {
+      fill_hatch,
+      fill_hatchf,
+      NULL
+    }
+  };
+
 /*
 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
 
@@ -618,13 +638,26 @@ i_new_hatch_low(const i_color *fg, const i_color *bg,
                 int dx, int dy) {
   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
 
-  fill->base.fill_with_color = fill_hatch;
-  fill->base.fill_with_fcolor = fill_hatchf;
-  fill->base.destroy = NULL;
-  fill->fg = fg ? *fg : fcolor_to_color(ffg);
-  fill->bg = bg ? *bg : fcolor_to_color(fbg);
-  fill->ffg = ffg ? *ffg : color_to_fcolor(fg);
-  fill->fbg = fbg ? *fbg : color_to_fcolor(bg);
+  *fill = hatch_fill_proto;
+  /* Some Sun C didn't like the condition expressions that were here.
+     See https://rt.cpan.org/Ticket/Display.html?id=21944
+   */
+  if (fg)
+    fill->fg = *fg;
+  else
+    fill->fg = fcolor_to_color(ffg);
+  if (bg)
+    fill->bg = *bg;
+  else
+    fill->bg = fcolor_to_color(fbg);
+  if (ffg) 
+    fill->ffg = *ffg;
+  else
+    fill->ffg = color_to_fcolor(fg);
+  if (fbg)
+    fill->fbg = *fbg;
+  else
+    fill->fbg = color_to_fcolor(bg);
   if (combine) {
     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
   }
@@ -659,9 +692,20 @@ static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
   int byte = f->hatch[(y + f->dy) & 7];
   int xpos = (x + f->dx) & 7;
   int mask = 128 >> xpos;
+  i_color fg = f->fg;
+  i_color bg = f->bg;
+  int want_channels = channels > 2 ? 4 : 2;
+
+  if (channels < 3) {
+    i_adapt_colors(2, 4, &fg, 1);
+    i_adapt_colors(2, 4, &bg, 1);
+  }
 
   while (width-- > 0) {
-    *data++ = (byte & mask) ? f->fg : f->bg;
+    if (byte & mask)
+      *data++ = fg;
+    else
+      *data++ = bg;
     
     if ((mask >>= 1) == 0)
       mask = 128;
@@ -681,9 +725,19 @@ static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
   int byte = f->hatch[(y + f->dy) & 7];
   int xpos = (x + f->dx) & 7;
   int mask = 128 >> xpos;
+  i_fcolor fg = f->ffg;
+  i_fcolor bg = f->fbg;
+
+  if (channels < 3) {
+    i_adapt_fcolors(2, 4, &fg, 1);
+    i_adapt_fcolors(2, 4, &bg, 1);
+  }
   
   while (width-- > 0) {
-    *data++ = (byte & mask) ? f->ffg : f->fbg;
+    if (byte & mask)
+      *data++ = fg;
+    else
+      *data++ = bg;
     
     if ((mask >>= 1) == 0)
       mask = 128;
@@ -700,7 +754,7 @@ static i_color interp_i_color(i_color before, i_color after, double pos,
   pos -= floor(pos);
   for (ch = 0; ch < channels; ++ch)
     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
-  if (out.channel[3])
+  if (channels > 3 && out.channel[3])
     for (ch = 0; ch < channels; ++ch)
       if (ch != 3) {
         int temp = out.channel[ch] * 255 / out.channel[3];
@@ -744,6 +798,7 @@ static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
   int i = 0;
   i_color *out = data;
+  int want_channels = channels > 2 ? 4 : 2;
   
   if (f->has_matrix) {
     /* the hard way */
@@ -806,33 +861,12 @@ static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
       ++i;
     }
   }
-  if (f->src->channels == 3) {
-    /* just set the alpha */
-    for (i = 0; i <  width; ++i) {
-      data->channel[3] = 255;
-      data++;
-    }
-  }
-  else if (f->src->channels == 2) {
-    /* copy the alpha to channel 3, duplicate the grey value */
-    for (i = 0; i <  width; ++i) {
-      data->channel[3] = data->channel[1];
-      data->channel[1] = data->channel[2] = data->channel[0];
-      data++;
-    }
-  }
-  else if (f->src->channels == 1) {
-    /* set the alpha, duplicate grey */
-    for (i = 0; i <  width; ++i) {
-      data->channel[3] = 255;
-      data->channel[1] = data->channel[2] = data->channel[0];
-      data++;
-    }
-  }
+  if (f->src->channels != want_channels)
+    i_adapt_colors(want_channels, f->src->channels, data, width);
 }
 
 /*
-=item fill_image(fill, x, y, width, channels, data, work)
+=item fill_imagef(fill, x, y, width, channels, data, work)
 
 =cut
 */
@@ -840,8 +874,10 @@ static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
                        i_fcolor *data) {
   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
   int i = 0;
+  int want_channels = channels > 2 ? 4 : 2;
   
   if (f->has_matrix) {
+    i_fcolor *work_data = data;
     /* the hard way */
     while (i < width) {
       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
@@ -874,11 +910,12 @@ static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
         }
         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
       }
-      *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
+      *work_data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
       ++i;
     }
   }
   else {
+    i_fcolor *work_data = data;
     /* the easy way */
     /* this should be possible to optimize to use i_glin() */
     while (i < width) {
@@ -897,498 +934,55 @@ static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
       }
       rx -= ix * f->src->xsize;
       ry -= iy * f->src->ysize;
-      i_gpixf(f->src, rx, ry, data);
-      ++data;
+      i_gpixf(f->src, rx, ry, work_data);
+      ++work_data;
       ++i;
     }
   }
-  if (f->src->channels == 3) {
-    /* just set the alpha */
-    for (i = 0; i <  width; ++i) {
-      data->channel[3] = 1.0;
-      data++;
-    }
-  }
-  else if (f->src->channels == 2) {
-    /* copy the alpha to channel 3, duplicate the grey value */
-    for (i = 0; i <  width; ++i) {
-      data->channel[3] = data->channel[1];
-      data->channel[1] = data->channel[2] = data->channel[0];
-      data++;
-    }
-  }
-  else if (f->src->channels == 1) {
-    /* set the alpha, duplicate grey */
-    for (i = 0; i <  width; ++i) {
-      data->channel[3] = 1.0;
-      data->channel[1] = data->channel[2] = data->channel[0];
-      data++;
-    }
-  }
+  if (f->src->channels != want_channels)
+    i_adapt_fcolors(want_channels, f->src->channels, data, width);
 }
 
-static void combine_replace(i_color *, i_color *, int, int);
-static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
-static void combine_alphablend(i_color *, i_color *, int, int);
-static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
-static void combine_mult(i_color *, i_color *, int, int);
-static void combine_multf(i_fcolor *, i_fcolor *, int, int);
-static void combine_dissolve(i_color *, i_color *, int, int);
-static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
-static void combine_add(i_color *, i_color *, int, int);
-static void combine_addf(i_fcolor *, i_fcolor *, int, int);
-static void combine_subtract(i_color *, i_color *, int, int);
-static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
-static void combine_diff(i_color *, i_color *, int, int);
-static void combine_difff(i_fcolor *, i_fcolor *, int, int);
-static void combine_darken(i_color *, i_color *, int, int);
-static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
-static void combine_lighten(i_color *, i_color *, int, int);
-static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
-static void combine_hue(i_color *, i_color *, int, int);
-static void combine_huef(i_fcolor *, i_fcolor *, int, int);
-static void combine_sat(i_color *, i_color *, int, int);
-static void combine_satf(i_fcolor *, i_fcolor *, int, int);
-static void combine_value(i_color *, i_color *, int, int);
-static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
-static void combine_color(i_color *, i_color *, int, int);
-static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
-
-static struct i_combines {
-  i_fill_combine_f combine;
-  i_fill_combinef_f combinef;
-} combines[] =
-{
-  { /* replace */
-    combine_replace,
-    combine_replacef,
-  },
-  { /* alpha blend */
-    combine_alphablend,
-    combine_alphablendf,
-  },
-  {
-    /* multiply */
-    combine_mult,
-    combine_multf,
-  },
-  {
-    /* dissolve */
-    combine_dissolve,
-    combine_dissolvef,
-  },
-  {
-    /* add */
-    combine_add,
-    combine_addf,
-  },
-  {
-    /* subtract */
-    combine_subtract,
-    combine_subtractf,
-  },
-  {
-    /* diff */
-    combine_diff,
-    combine_difff,
-  },
-  {
-    combine_lighten,
-    combine_lightenf,
-  },
-  {
-    combine_darken,
-    combine_darkenf,
-  },
-  {
-    combine_hue,
-    combine_huef,
-  },
-  {
-    combine_sat,
-    combine_satf,
-  },
-  {
-    combine_value,
-    combine_valuef,
-  },
-  {
-    combine_color,
-    combine_colorf,
-  },
-};
-
-/*
-=item i_get_combine(combine, color_func, fcolor_func)
-
-=cut
-*/
-
-void i_get_combine(int combine, i_fill_combine_f *color_func, 
-                   i_fill_combinef_f *fcolor_func) {
-  if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
-    combine = 0;
-
-  *color_func = combines[combine].combine;
-  *fcolor_func = combines[combine].combinef;
-}
-
-static void combine_replace(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    *out++ = *in++;
-  }
-}
-
-static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  while (count--) {
-    *out++ = *in++;
-  }
-}
-
-static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    COMBINE(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  while (count--) {
-    COMBINEF(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_mult(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    double mult[MAXCHANNELS];
-    mult[3] = in->channel[3];
-    for (ch = 0; ch < (channels); ++ch) { 
-      if (ch != 3)
-        mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
-    } 
-    COMBINEA(*out, mult, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_fcolor c = *in;
-    for (ch = 0; ch < channels; ++ch) { 
-      if (ch != 3)
-        c.channel[ch] = out->channel[ch] * in->channel[ch];
-    } 
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    if (in->channel[3] > rand() * (255.0 / RAND_MAX))
-      COMBINE(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  while (count--) {
-    if (in->channel[3] > rand() * (1.0 / RAND_MAX))
-      COMBINEF(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_add(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_color c = *in;
-    for (ch = 0; ch < (channels); ++ch) { 
-      if (ch != 3) {
-        int total = out->channel[ch] + in->channel[ch];
-        if (total > 255)
-          total = 255;
-        c.channel[ch] = total;
-      }
-    } 
-    COMBINE(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_fcolor c = *in;
-    for (ch = 0; ch < (channels); ++ch) { 
-      if (ch != 3) {
-        double total = out->channel[ch] + in->channel[ch];
-        if (total > 1.0)
-          total = 1.0;
-        out->channel[ch] = total;
-      }
-    } 
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_color c = *in;
-    for (ch = 0; ch < (channels); ++ch) { 
-      if (ch != 3) {
-        int total = out->channel[ch] - in->channel[ch];
-        if (total < 0)
-          total = 0;
-        c.channel[ch] = total;
-      }
-    } 
-    COMBINE(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_fcolor c = *in;
-    for (ch = 0; ch < channels; ++ch) { 
-      if (ch != 3) {
-        double total = out->channel[ch] - in->channel[ch];
-        if (total < 0)
-          total = 0;
-        c.channel[ch] = total;
-      }
-    } 
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_diff(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_color c = *in;
-    for (ch = 0; ch < (channels); ++ch) { 
-      if (ch != 3) 
-        c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
-    } 
-    COMBINE(*out, c, channels)
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    i_fcolor c = *in;
-    for (ch = 0; ch < (channels); ++ch) { 
-      if (ch != 3)
-        c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
-    }
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_darken(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    for (ch = 0; ch < channels; ++ch) { 
-      if (ch != 3 && out->channel[ch] < in->channel[ch])
-        in->channel[ch] = out->channel[ch];
-    } 
-    COMBINE(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    for (ch = 0; ch < channels; ++ch) { 
-      if (ch != 3 && out->channel[ch] < in->channel[ch])
-        in->channel[ch] = out->channel[ch];
-    } 
-    COMBINEF(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    for (ch = 0; ch < channels; ++ch) { 
-      if (ch != 3 && out->channel[ch] > in->channel[ch])
-        in->channel[ch] = out->channel[ch];
-    } 
-    COMBINE(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
-  while (count--) {
-    for (ch = 0; ch < channels; ++ch) { 
-      if (ch != 3 && out->channel[ch] > in->channel[ch])
-        in->channel[ch] = out->channel[ch];
-    } 
-    COMBINEF(*out, *in, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_hue(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    i_color c = *out;
-    i_rgb_to_hsv(&c);
-    i_rgb_to_hsv(in);
-    c.channel[0] = in->channel[0];
-    i_hsv_to_rgb(&c);
-    c.channel[3] = in->channel[3];
-    COMBINE(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  while (count--) {
-    i_fcolor c = *out;
-    i_rgb_to_hsvf(&c);
-    i_rgb_to_hsvf(in);
-    c.channel[0] = in->channel[0];
-    i_hsv_to_rgbf(&c);
-    c.channel[3] = in->channel[3];
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_sat(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    i_color c = *out;
-    i_rgb_to_hsv(&c);
-    i_rgb_to_hsv(in);
-    c.channel[1] = in->channel[1];
-    i_hsv_to_rgb(&c);
-    c.channel[3] = in->channel[3];
-    COMBINE(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  while (count--) {
-    i_fcolor c = *out;
-    i_rgb_to_hsvf(&c);
-    i_rgb_to_hsvf(in);
-    c.channel[1] = in->channel[1];
-    i_hsv_to_rgbf(&c);
-    c.channel[3] = in->channel[3];
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_value(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    i_color c = *out;
-    i_rgb_to_hsv(&c);
-    i_rgb_to_hsv(in);
-    c.channel[2] = in->channel[2];
-    i_hsv_to_rgb(&c);
-    c.channel[3] = in->channel[3];
-    COMBINE(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
-                           int count) {
-  while (count--) {
-    i_fcolor c = *out;
-    i_rgb_to_hsvf(&c);
-    i_rgb_to_hsvf(in);
-    c.channel[2] = in->channel[2];
-    i_hsv_to_rgbf(&c);
-    c.channel[3] = in->channel[3];
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
-
-static void combine_color(i_color *out, i_color *in, int channels, int count) {
-  while (count--) {
-    i_color c = *out;
-    i_rgb_to_hsv(&c);
-    i_rgb_to_hsv(in);
-    c.channel[0] = in->channel[0];
-    c.channel[1] = in->channel[1];
-    i_hsv_to_rgb(&c);
-    c.channel[3] = in->channel[3];
-    COMBINE(*out, c, channels);
-    ++out;
-    ++in;
-  }
-}
+static void 
+fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
+            i_color *data) {
+  struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
+  int alpha_chan = channels > 2 ? 3 : 1;
+  i_color *datap = data;
+  
+  (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data);
+  while (width--) {
+    double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
+    if (new_alpha < 0) 
+      datap->channel[alpha_chan] = 0;
+    else if (new_alpha > 255)
+      datap->channel[alpha_chan] = 255;
+    else datap->channel[alpha_chan] = (int)(new_alpha + 0.5);
+
+    ++datap;
+  }
+}
+static void 
+fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
+           i_fcolor *data) {
+  struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
+  int alpha_chan = channels > 2 ? 3 : 1;
+  i_fcolor *datap = data;
+  
+  (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data);
+  
+  while (width--) {
+    double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
+    if (new_alpha < 0) 
+      datap->channel[alpha_chan] = 0;
+    else if (new_alpha > 1.0)
+      datap->channel[alpha_chan] = 1.0;
+    else datap->channel[alpha_chan] = new_alpha;
 
-static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
-                           int count) {
-  while (count--) {
-    i_fcolor c = *out;
-    i_rgb_to_hsvf(&c);
-    i_rgb_to_hsvf(in);
-    c.channel[0] = in->channel[0];
-    c.channel[1] = in->channel[1];
-    i_hsv_to_rgbf(&c);
-    c.channel[3] = in->channel[3];
-    COMBINEF(*out, c, channels);
-    ++out;
-    ++in;
+    ++datap;
   }
 }
 
-
 /*
 =back