]> git.imager.perl.org - imager.git/blobdiff - fills.c
- convert t/t15color.t to Test::More
[imager.git] / fills.c
diff --git a/fills.c b/fills.c
index d91fb9804c86edc65d5e220d8c86217f35664dd0..ed2a205c36aea4eb7fdb5d28c59417335a9e1d54 100644 (file)
--- a/fills.c
+++ b/fills.c
@@ -16,6 +16,7 @@ fills.c - implements the basic general fills
   fill = i_new_fill_solid(&c1, combine);
   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);
   i_fill_destroy(fill);
 
 =head1 DESCRIPTION
@@ -162,13 +163,13 @@ typedef struct
 } i_fill_solid_t;
 
 static void fill_solid(i_fill_t *, int x, int y, int width, int channels, 
-                       i_color *, i_color *);
+                       i_color *);
 static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, 
-                        i_fcolor *, i_fcolor *);
+                        i_fcolor *);
 static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels, 
-                            i_color *, i_color *);
+                            i_color *);
 static void fill_solidf_comb(i_fill_t *, int x, int y, int width, 
-                             int channels, i_fcolor *, i_fcolor *);
+                             int channels, i_fcolor *);
 
 static i_fill_solid_t base_solid_fill =
 {
@@ -407,9 +408,9 @@ typedef struct
 } i_fill_hatch_t;
 
 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
-                       i_color *data, i_color *work);
+                       i_color *data);
 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
-                        i_fcolor *data, i_fcolor *work);
+                        i_fcolor *data);
 static
 i_fill_t *
 i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, 
@@ -462,6 +463,58 @@ i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch,
                          dx, dy);
 }
 
+static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_color *data);
+static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_fcolor *data);
+struct i_fill_image_t {
+  i_fill_t base;
+  i_img *src;
+  int xoff, yoff;
+  int has_matrix;
+  double matrix[9];
+};
+
+/*
+=item i_new_fill_image(im, matrix, xoff, yoff, combine)
+
+Create an image based fill.
+
+=cut
+*/
+i_fill_t *
+i_new_fill_image(i_img *im, double *matrix, int xoff, int yoff, int combine) {
+  struct i_fill_image_t *fill = mymalloc(sizeof(*fill));
+
+  fill->base.fill_with_color = fill_image;
+  fill->base.fill_with_fcolor = fill_imagef;
+  fill->base.destroy = NULL;
+
+  if (combine) {
+    i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
+  }
+  else {
+    fill->base.combine = NULL;
+    fill->base.combinef = NULL;
+  }
+  fill->src = im;
+  if (xoff < 0)
+    xoff += im->xsize;
+  fill->xoff = xoff;
+  if (yoff < 0)
+    yoff += im->ysize;
+  fill->yoff = yoff;
+  if (matrix) {
+    fill->has_matrix = 1;
+    memcpy(fill->matrix, matrix, sizeof(fill->matrix));
+  }
+  else
+    fill->has_matrix = 0;
+
+  return &fill->base;
+}
+
+
 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
 
 /*
@@ -479,7 +532,7 @@ 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 *work) {
+           i_color *data) {
   while (width-- > 0) {
     *data++ = T_SOLID_FILL(fill)->c;
   }
@@ -494,7 +547,7 @@ 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, i_fcolor *work) {
+           i_fcolor *data) {
   while (width-- > 0) {
     *data++ = T_SOLID_FILL(fill)->fc;
   }
@@ -509,15 +562,12 @@ The 8-bit sample fill function for combining solid fills.
 */
 static void
 fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, 
-                i_color *data, i_color *work) {
+                i_color *data) {
   i_color c = T_SOLID_FILL(fill)->c;
-  int count = width;
-  i_color *wstart = work;
 
   while (width-- > 0) {
-    *work++ = c;
+    *data++ = c;
   }
-  (fill->combine)(data, wstart, channels, count);
 }
 
 /*
@@ -529,15 +579,12 @@ The floating sample fill function for combining solid fills.
 */
 static void
 fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, 
-           i_fcolor *data, i_fcolor *work) {
+           i_fcolor *data) {
   i_fcolor c = T_SOLID_FILL(fill)->fc;
-  int count = width;
-  i_fcolor *wstart = work;
 
   while (width-- > 0) {
-    *work++ = c;
+    *data++ = c;
   }
-  (fill->combinef)(data, wstart, channels, count);
 }
 
 /*
@@ -587,34 +634,20 @@ i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg,
 
 The 8-bit sample fill function for hatched fills.
 
-=back
+=cut
 */
 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
-                       i_color *data, i_color *work) {
+                       i_color *data) {
   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
   int byte = f->hatch[(y + f->dy) & 7];
   int xpos = (x + f->dx) & 7;
   int mask = 128 >> xpos;
 
-  if (fill->combine) {
-    int count = width;
-    i_color *wstart = work;
-
-    while (count-- > 0) {
-      *work++ = (byte & mask) ? f->fg : f->bg;
-      
-      if ((mask >>= 1) == 0)
-        mask = 128;
-    }
-    (fill->combine)(data, wstart, channels, width);
-  }
-  else {
-    while (width-- > 0) {
-      *data++ = (byte & mask) ? f->fg : f->bg;
-
-      if ((mask >>= 1) == 0)
-        mask = 128;
-    }
+  while (width-- > 0) {
+    *data++ = (byte & mask) ? f->fg : f->bg;
+    
+    if ((mask >>= 1) == 0)
+      mask = 128;
   }
 }
 
@@ -626,30 +659,253 @@ The floating sample fill function for hatched fills.
 =back
 */
 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
-                        i_fcolor *data, i_fcolor *work) {
+                        i_fcolor *data) {
   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
   int byte = f->hatch[(y + f->dy) & 7];
   int xpos = (x + f->dx) & 7;
   int mask = 128 >> xpos;
   
-  if (fill->combinef) {
-    int count = width;
-    i_fcolor *wstart = work;
-
-    while (count-- > 0) {
-      *work++ = (byte & mask) ? f->ffg : f->fbg;
-      
-      if ((mask >>= 1) == 0)
-        mask = 128;
+  while (width-- > 0) {
+    *data++ = (byte & mask) ? f->ffg : f->fbg;
+    
+    if ((mask >>= 1) == 0)
+      mask = 128;
+  }
+}
+
+/* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
+/* linear interpolation */
+static i_color interp_i_color(i_color before, i_color after, double pos,
+                              int channels) {
+  i_color out;
+  int ch;
+
+  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])
+    for (ch = 0; ch < channels; ++ch)
+      if (ch != 3) {
+        int temp = out.channel[ch] * 255 / out.channel[3];
+        if (temp > 255)
+          temp = 255;
+        out.channel[ch] = temp;
+      }
+
+  return out;
+}
+
+/* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
+/* linear interpolation */
+static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
+                                int channels) {
+  i_fcolor out;
+  int ch;
+
+  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])
+    for (ch = 0; ch < channels; ++ch)
+      if (ch != 3) {
+        int temp = out.channel[ch] / out.channel[3];
+        if (temp > 1.0)
+          temp = 1.0;
+        out.channel[ch] = temp;
+      }
+
+  return out;
+}
+
+/*
+=item fill_image(fill, x, y, width, channels, data, work)
+
+=cut
+*/
+static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_color *data) {
+  struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
+  int i = 0;
+  i_color *out = data;
+  
+  if (f->has_matrix) {
+    /* the hard way */
+    while (i < width) {
+      double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
+      double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
+      double ix = floor(rx / f->src->xsize);
+      double iy = floor(ry / f->src->ysize);
+      i_color c[2][2];
+      i_color c2[2];
+      int dy;
+
+      if (f->xoff) {
+        rx += iy * f->xoff;
+        ix = floor(rx / f->src->xsize);
+      }
+      else if (f->yoff) {
+        ry += ix * f->yoff;
+        iy = floor(ry / f->src->ysize);
+      }
+      rx -= ix * f->src->xsize;
+      ry -= iy * f->src->ysize;
+
+      for (dy = 0; dy < 2; ++dy) {
+        if ((int)rx == f->src->xsize-1) {
+          i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
+          i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
+        }
+        else {
+          i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
+                 c[dy]);
+        }
+        c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
+      }
+      *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
+      ++i;
     }
-    (fill->combinef)(data, wstart, channels, width);
   }
   else {
-    while (width-- > 0) {
-      *data++ = (byte & mask) ? f->ffg : f->fbg;
+    /* the easy way */
+    /* this should be possible to optimize to use i_glin() */
+    while (i < width) {
+      int rx = x+i;
+      int ry = y;
+      int ix = rx / f->src->xsize;
+      int iy = ry / f->src->ysize;
+
+      if (f->xoff) {
+        rx += iy * f->xoff;
+        ix = rx / f->src->xsize;
+      }
+      else if (f->yoff) {
+        ry += ix * f->yoff;
+        iy = ry / f->src->xsize;
+      }
+      rx -= ix * f->src->xsize;
+      ry -= iy * f->src->ysize;
+      i_gpix(f->src, rx, ry, out);
+      ++out;
+      ++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++;
+    }
+  }
+}
+
+/*
+=item fill_image(fill, x, y, width, channels, data, work)
 
-      if ((mask >>= 1) == 0)
-        mask = 128;
+=cut
+*/
+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;
+  
+  if (f->has_matrix) {
+    /* the hard way */
+    while (i < width) {
+      double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
+      double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
+      double ix = floor(rx / f->src->xsize);
+      double iy = floor(ry / f->src->ysize);
+      i_fcolor c[2][2];
+      i_fcolor c2[2];
+      int dy;
+
+      if (f->xoff) {
+        rx += iy * f->xoff;
+        ix = floor(rx / f->src->xsize);
+      }
+      else if (f->yoff) {
+        ry += ix * f->yoff;
+        iy = floor(ry / f->src->ysize);
+      }
+      rx -= ix * f->src->xsize;
+      ry -= iy * f->src->ysize;
+
+      for (dy = 0; dy < 2; ++dy) {
+        if ((int)rx == f->src->xsize-1) {
+          i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
+          i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
+        }
+        else {
+          i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
+                 c[dy]);
+        }
+        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);
+      ++i;
+    }
+  }
+  else {
+    /* the easy way */
+    /* this should be possible to optimize to use i_glin() */
+    while (i < width) {
+      int rx = x+i;
+      int ry = y;
+      int ix = rx / f->src->xsize;
+      int iy = ry / f->src->ysize;
+
+      if (f->xoff) {
+        rx += iy * f->xoff;
+        ix = rx / f->src->xsize;
+      }
+      else if (f->yoff) {
+        ry += ix * f->yoff;
+        iy = ry / f->src->xsize;
+      }
+      rx -= ix * f->src->xsize;
+      ry -= iy * f->src->ysize;
+      i_gpixf(f->src, rx, ry, data);
+      ++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++;
     }
   }
 }
@@ -681,7 +937,7 @@ 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);
 
-struct i_combines {
+static struct i_combines {
   i_fill_combine_f combine;
   i_fill_combinef_f combinef;
 } combines[] =
@@ -792,7 +1048,6 @@ static void combine_mult(i_color *out, i_color *in, int channels, int count) {
   int ch;
 
   while (count--) {
-    i_color c = *in;
     double mult[MAXCHANNELS];
     mult[3] = in->channel[3];
     for (ch = 0; ch < (channels); ++ch) { 
@@ -821,8 +1076,6 @@ static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count)
 }
 
 static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
-  int ch;
-
   while (count--) {
     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
       COMBINE(*out, *in, channels);
@@ -832,8 +1085,6 @@ static void combine_dissolve(i_color *out, i_color *in, int channels, int count)
 }
 
 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
-  int ch;
-
   while (count--) {
     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
       COMBINEF(*out, *in, channels);