]> git.imager.perl.org - imager.git/commitdiff
image based fills
authorTony Cook <tony@develop=help.com>
Wed, 19 Sep 2001 02:15:08 +0000 (02:15 +0000)
committerTony Cook <tony@develop=help.com>
Wed, 19 Sep 2001 02:15:08 +0000 (02:15 +0000)
Changes
Imager.xs
fills.c
image.h
lib/Imager/Fill.pm
t/t20fill.t

diff --git a/Changes b/Changes
index 1b1d1d240f3e989dc4bf768a4f6343b17cc5c55b..e94d7bce5ef29af55bb97221d7e0b598b529f0b4 100644 (file)
--- a/Changes
+++ b/Changes
@@ -513,6 +513,7 @@ Revision history for Perl extension Imager.
           if it's small enough
         - $img->arc() now calls i_circle_aa() if a complete circle is
           being drawn in a plain color
           if it's small enough
         - $img->arc() now calls i_circle_aa() if a complete circle is
           being drawn in a plain color
+        - image based fills
 
 =================================================================
 
 
 =================================================================
 
index 01d146e128a4854442a7d3c0ee2c93f3c590e61f..ea122a14205f07c7194896e56951a7cf9b7ccf19 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -3189,3 +3189,38 @@ i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
       OUTPUT:
         RETVAL
 
       OUTPUT:
         RETVAL
 
+Imager::FillHandle
+i_new_fill_image(src, matrix, xoff, yoff, combine)
+        Imager::ImgRaw src
+        int xoff
+        int yoff
+        int combine
+      PREINIT:
+        double matrix[9];
+        double *matrixp;
+        AV *av;
+        IV len;
+        SV *sv1;
+        int i;
+      CODE:
+        if (!SvOK(ST(1))) {
+          matrixp = NULL;
+        }
+        else {
+          if (!SvROK(ST(1)) || SvTYPE(SvRV(ST(1))) != SVt_PVAV)
+            croak("i_new_fill_image: parameter must be an arrayref");
+         av=(AV*)SvRV(ST(1));
+         len=av_len(av)+1;
+          if (len > 9)
+            len = 9;
+          for (i = 0; i < len; ++i) {
+           sv1=(*(av_fetch(av,i,0)));
+           matrix[i] = SvNV(sv1);
+          }
+          for (; i < 9; ++i)
+            matrix[i] = 0;
+          matrixp = matrix;
+        }
+        RETVAL = i_new_fill_image(src, matrixp, xoff, yoff, combine);
+      OUTPUT:
+        RETVAL
diff --git a/fills.c b/fills.c
index d91fb9804c86edc65d5e220d8c86217f35664dd0..196d91ca616c874d013598a884ed8ff68282d4f0 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_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
   i_fill_destroy(fill);
 
 =head1 DESCRIPTION
@@ -462,6 +463,58 @@ i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch,
                          dx, dy);
 }
 
                          dx, dy);
 }
 
+static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_color *data, i_color *work);
+static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_fcolor *data, i_fcolor *work);
+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))
 
 /*
 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
 
 /*
@@ -654,6 +707,208 @@ static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
   }
 }
 
   }
 }
 
+/* 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, i_color *work) {
+  struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
+  i_color *out = fill->combine ? work : data;
+  int i = 0;
+  i_color c;
+  
+  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;
+    }
+  }
+  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_gpix(f->src, rx, ry, out);
+      ++out;
+      ++i;
+    }
+  }
+
+  if (fill->combine) {
+    (fill->combine)(data, work, channels, width);
+  }
+}
+
+/*
+=item fill_image(fill, x, y, width, channels, data, work)
+
+=cut
+*/
+static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
+                       i_fcolor *data, i_fcolor *work) {
+  struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
+  i_fcolor *out = fill->combine ? work : data;
+  int i = 0;
+  i_fcolor c;
+  
+  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);
+      }
+      *out++ = 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, out);
+      ++out;
+      ++i;
+    }
+  }
+
+  if (fill->combinef) {
+    (fill->combinef)(data, work, channels, 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_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);
diff --git a/image.h b/image.h
index 3e74e40bfceb7319cdb7ec334d16826378fb40e9..d3ed9c5485f9612bf6a4cb4597a65694180a64d5 100644 (file)
--- a/image.h
+++ b/image.h
@@ -181,6 +181,8 @@ i_new_fill_hatch(i_color *fg, i_color *bg, int combine, int hatch,
 extern i_fill_t *
 i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch, 
                   unsigned char *cust_hatch, int dx, int dy);
 extern i_fill_t *
 i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch, 
                   unsigned char *cust_hatch, int dx, int dy);
+extern i_fill_t *
+i_new_fill_image(i_img *im, double *matrix, int xoff, int yoff, int combine);
 extern void i_fill_destroy(i_fill_t *fill);
 
 float i_gpix_pch(i_img *im,int x,int y,int ch);
 extern void i_fill_destroy(i_fill_t *fill);
 
 float i_gpix_pch(i_img *im,int x,int y,int ch);
index 229fbd2c56a6b7628f5156d3dc1edaab974857c4..037f0d6f35b507312b9ea87ccf81b1bfad567bee 100644 (file)
@@ -149,6 +149,14 @@ sub new {
                   $hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
                   $hsh{ssample_param}, $hsh{segments});
   }
                   $hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
                   $hsh{ssample_param}, $hsh{segments});
   }
+  elsif (defined $hsh{image}) {
+    $hsh{xoff} ||= 0;
+    $hsh{yoff} ||= 0;
+    $self->{fill} =
+      Imager::i_new_fill_image($hsh{image}{IMG}, $hsh{matrix}, $hsh{xoff}, 
+                               $hsh{yoff}, $hsh{combine});
+    $self->{DEPS} = [ $hsh{image}{IMG} ];
+  }
   else {
     $Imager::ERRSTR = "No fill type specified";
     warn "No fill type!";
   else {
     $Imager::ERRSTR = "No fill type specified";
     warn "No fill type!";
@@ -177,6 +185,8 @@ sub combines {
   my $fill1 = Imager::Fill->new(solid=>$color, combine=>$combine);
   my $fill2 = Imager::Fill->new(hatch=>'vline2', fg=>$color1, bg=>$color2,
                                 dx=>$dx, dy=>$dy);
   my $fill1 = Imager::Fill->new(solid=>$color, combine=>$combine);
   my $fill2 = Imager::Fill->new(hatch=>'vline2', fg=>$color1, bg=>$color2,
                                 dx=>$dx, dy=>$dy);
+  my $fill3 = Imager::Fill->new(fountain=>$type, ...);
+  my $fill4 = Imager::Fill->new(image=>$img, ...);
 
 =head1 DESCRIPTION 
 
 
 =head1 DESCRIPTION 
 
@@ -402,6 +412,22 @@ same fill as the C<fountain> filter, but is restricted to the shape
 you are drawing, and the fountain parameter supplies the fill type,
 and is required.
 
 you are drawing, and the fountain parameter supplies the fill type,
 and is required.
 
+=head2 Image Fills
+
+  my $fill = Imager::Fill->new(image=>$src, xoff=>$xoff, yoff=>$yoff,
+                               matrix=>$matrix, $combine);
+
+Fills the given image with a tiled version of the given image.  The
+first non-zero value of xoff or yoff will provide an offset along the
+given axis between rows or columns of tiles respectively.
+
+The matrix parameter performs a co-ordinate transformation from the
+co-ordinates in the target image to the fill image co-ordinates.
+Linear interpolation is used to determine the fill pixel.  You can use
+the L<Imager::Matrix2d> class to create transformation matrices.
+
+The matrix parameter will significantly slow down the fill.
+
 =head1 OTHER METHODS
 
 =over
 =head1 OTHER METHODS
 
 =over
@@ -422,10 +448,6 @@ I'm planning on adding the following types of fills:
 
 =over
 
 
 =over
 
-=item image
-
-tiled image fill
-
 =item checkerboard
 
 combines 2 other fills in a checkerboard
 =item checkerboard
 
 combines 2 other fills in a checkerboard
index d47a4508eb073ae41ef69696ad4ba0c5e6031f8b..ae84303a1323c4e3002f4159f181ee5d72d14107 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 use strict;
 
 #!perl -w
 use strict;
 
-print "1..35\n";
+print "1..37\n";
 
 use Imager ':handy';
 use Imager::Fill;
 
 use Imager ':handy';
 use Imager::Fill;
@@ -198,6 +198,38 @@ for my $comb (Imager::Fill->combines) {
 ok($testnum++, $ffim->arc(r=>45, color=>$blue, aa=>1), "aa circle");
 $ffim->write(file=>"testout/t20_aacircle.ppm");
 
 ok($testnum++, $ffim->arc(r=>45, color=>$blue, aa=>1), "aa circle");
 $ffim->write(file=>"testout/t20_aacircle.ppm");
 
+# image based fills
+my $green = NC(0, 255, 0);
+my $fillim = Imager->new(xsize=>40, ysize=>40, channels=>4);
+$fillim->box(filled=>1, xmin=>5, ymin=>5, xmax=>35, ymax=>35, 
+             color=>NC(0, 0, 255, 128));
+$fillim->arc(filled=>1, r=>10, color=>$green, aa=>1);
+my $ooim = Imager->new(xsize=>150, ysize=>150);
+$ooim->box(filled=>1, color=>$green, xmin=>70, ymin=>25, xmax=>130, ymax=>125);
+$ooim->box(filled=>1, color=>$blue, xmin=>20, ymin=>25, xmax=>80, ymax=>125);
+$ooim->arc(r=>30, color=>$red, aa=>1);
+
+my $oocopy = $ooim->copy();
+ok($testnum++, 
+   $oocopy->arc(fill=>{image=>$fillim, 
+                       combine=>'normal',
+                       xoff=>5}, r=>40),
+   "image based fill");
+$oocopy->write(file=>'testout/t20_image.ppm');
+
+# a more complex version
+use Imager::Matrix2d ':handy';
+$oocopy = $ooim->copy;
+ok($testnum++,
+   $oocopy->arc(fill=>{
+                       image=>$fillim,
+                       combine=>'normal',
+                       matrix=>m2d_rotate(degrees=>30),
+                       xoff=>5
+                       }, r=>40),
+   "transformed image based fill");
+$oocopy->write(file=>'testout/t20_image_xform.ppm');
+
 sub ok ($$$) {
   my ($num, $test, $desc) = @_;
 
 sub ok ($$$) {
   my ($num, $test, $desc) = @_;