an extra stipple
authorTony Cook <tony@develop=help.com>
Sun, 2 Sep 2001 12:37:14 +0000 (12:37 +0000)
committerTony Cook <tony@develop=help.com>
Sun, 2 Sep 2001 12:37:14 +0000 (12:37 +0000)
expose flood_fill as a method
add a general fill version of flood fill

Changes
Imager.pm
Imager.xs
draw.c
fills.c
io.c
lib/Imager/Fill.pm
t/t20fill.t

diff --git a/Changes b/Changes
index 0a916ad..7ed61ce 100644 (file)
--- a/Changes
+++ b/Changes
@@ -499,6 +499,9 @@ Revision history for Perl extension Imager.
           - fountain generic fills
           - sample code to generate an examples page
         - improved the scale* hatches a bit
+        - add the flood_fill() method (using the existing i_flood_fill)
+        - implement i_flood_cfill() based on i_flood_fill, and 
+          add general fills to the flood_fill() method
 
 =================================================================
 
index ca368ae..ad053a6 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -1727,6 +1727,30 @@ sub polybezier {
   return $self;
 }
 
+sub flood_fill {
+  my $self = shift;
+  my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ );
+
+  unless (exists $opts{x} && exists $opts{'y'}) {
+    $self->{ERRSTR} = "missing seed x and y parameters";
+    return undef;
+  }
+  
+  if ($opts{fill}) {
+    unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+      # assume it's a hash ref
+      require 'Imager/Fill.pm';
+      $opts{fill} = Imager::Fill->new(%{$opts{fill}});
+    }
+    i_flood_cfill($self->{IMG}, $opts{x}, $opts{'y'}, $opts{fill}{fill});
+  }
+  else {
+    i_flood_fill($self->{IMG}, $opts{x}, $opts{'y'}, $opts{color});
+  }
+
+  $self;
+}
+
 # make an identity matrix of the given size
 sub _identity {
   my ($size) = @_;
@@ -2791,6 +2815,20 @@ The point set can either be specified as an arrayref to an array of
 array references (where each such array represents a point).  The
 other way is to specify two array references.
 
+You can fill a region that all has the same color using the
+flood_fill() method, for example:
+
+  $img->flood_fill(x=>50, y=>50, color=>$color);
+
+will fill all regions the same color connected to the point (50, 50).
+
+You can also use a general fill, so you could fill the same region
+with a check pattern using:
+
+  $img->flood_fill(x=>50, y=>50, fill=>{ hatch=>'check2x2' });
+
+See L<Imager::Fill> for more information on general fills.
+
 =head2 Text rendering
 
 Text rendering is described in the Imager::Font manpage.
index 2a48bfa..955cbc2 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -868,6 +868,13 @@ i_flood_fill(im,seedx,seedy,dcol)
               int     seedy
      Imager::Color     dcol
 
+void
+i_flood_cfill(im,seedx,seedy,fill)
+    Imager::ImgRaw     im
+              int     seedx
+              int     seedy
+     Imager::FillHandle     fill
+
 
 void
 i_copyto(im,src,x1,y1,x2,y2,tx,ty)
diff --git a/draw.c b/draw.c
index 50d1e07..ecf0bad 100644 (file)
--- a/draw.c
+++ b/draw.c
@@ -1088,3 +1088,193 @@ i_flood_fill(i_img *im,int seedx,int seedy,i_color *dcol) {
   btm_destroy(btm);
   llist_destroy(st);
 }
+
+static struct i_bitmap *
+i_flood_fill_low(i_img *im,int seedx,int seedy,
+                 int *bxminp, int *bxmaxp, int *byminp, int *bymaxp) {
+  int lx,rx;
+  int y;
+  int direction;
+  int dadLx,dadRx;
+
+  int wasIn=0;
+  int x=0;
+
+  /*  int tx,ty; */
+
+  int bxmin=seedx,bxmax=seedx,bymin=seedy,bymax=seedy;
+
+  struct llist *st;
+  struct i_bitmap *btm;
+
+  int channels,xsize,ysize;
+  i_color cval,val;
+
+  channels=im->channels;
+  xsize=im->xsize;
+  ysize=im->ysize;
+
+  btm=btm_new(xsize,ysize);
+  st=llist_new(100,sizeof(struct stack_element*));
+
+  /* Get the reference color */
+  i_gpix(im,seedx,seedy,&val);
+
+  /* Find the starting span and fill it */
+  lx=i_lspan(im,seedx,seedy,&val);
+  rx=i_rspan(im,seedx,seedy,&val);
+  
+  /* printf("span: %d %d \n",lx,rx); */
+
+  for(x=lx;x<=rx;x++) SET(x,seedy);
+
+  ST_PUSH(lx,rx,lx,rx,seedy+1,1);
+  ST_PUSH(lx,rx,lx,rx,seedy-1,-1);
+
+  while(st->count) {
+    ST_POP();
+    
+    if (y<0 || y>ysize-1) continue;
+
+    if (bymin > y) bymin=y; /* in the worst case an extra line */
+    if (bymax < y) bymax=y; 
+
+    /*     printf("start of scan - on stack : %d \n",st->count); */
+
+    
+    /*     printf("lx=%d rx=%d dadLx=%d dadRx=%d y=%d direction=%d\n",lx,rx,dadLx,dadRx,y,direction); */
+    
+    /*
+    printf(" ");
+    for(tx=0;tx<xsize;tx++) printf("%d",tx%10);
+    printf("\n");
+    for(ty=0;ty<ysize;ty++) {
+      printf("%d",ty%10);
+      for(tx=0;tx<xsize;tx++) printf("%d",!!btm_test(btm,tx,ty));
+      printf("\n");
+    }
+
+    printf("y=%d\n",y);
+    */
+
+
+    x=lx+1;
+    if ( (wasIn = INSIDE(lx,y)) ) {
+      SET(lx,y);
+      lx--;
+      while(INSIDE(lx,y) && lx > 0) {
+       SET(lx,y);
+       lx--;
+      }
+    }
+
+    if (bxmin > lx) bxmin=lx;
+    
+    while(x <= xsize-1) {
+      /*  printf("x=%d\n",x); */
+      if (wasIn) {
+       
+       if (INSIDE(x,y)) {
+         /* case 1: was inside, am still inside */
+         SET(x,y);
+       } else {
+         /* case 2: was inside, am no longer inside: just found the
+            right edge of a span */
+         ST_STACK(direction,dadLx,dadRx,lx,(x-1),y);
+
+         if (bxmax < x) bxmax=x;
+
+         wasIn=0;
+       }
+      } else {
+       if (x>rx) goto EXT;
+       if (INSIDE(x,y)) {
+         SET(x,y);
+         /* case 3: Wasn't inside, am now: just found the start of a new run */
+         wasIn=1;
+           lx=x;
+       } else {
+         /* case 4: Wasn't inside, still isn't */
+       }
+      }
+      x++;
+    }
+  EXT: /* out of loop */
+    if (wasIn) {
+      /* hit an edge of the frame buffer while inside a run */
+      ST_STACK(direction,dadLx,dadRx,lx,(x-1),y);
+      if (bxmax < x) bxmax=x;
+    }
+  }
+  
+  /*   printf("lx=%d rx=%d dadLx=%d dadRx=%d y=%d direction=%d\n",lx,rx,dadLx,dadRx,y,direction); 
+       printf("bounding box: [%d,%d] - [%d,%d]\n",bxmin,bymin,bxmax,bymax); */
+
+  llist_destroy(st);
+
+  *bxminp = bxmin;
+  *bxmaxp = bxmax;
+  *byminp = bymin;
+  *bymaxp = bymax;
+
+  return btm;
+}
+
+void
+i_flood_cfill(i_img *im, int seedx, int seedy, i_fill_t *fill) {
+  int bxmin, bxmax, bymin, bymax;
+  struct i_bitmap *btm;
+  int x, y;
+  int start;
+
+  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax);
+
+  if (im->bits == i_8_bits && fill->fill_with_color) {
+    i_color *line = mymalloc(sizeof(i_color) * (bxmax - bxmin));
+
+    for(y=bymin;y<=bymax;y++) {
+      x = bxmin;
+      while (x < bxmax) {
+        while (x < bxmax && !btm_test(btm, x, y)) {
+          ++x;
+        }
+        if (btm_test(btm, x, y)) {
+          start = x;
+          while (x < bxmax && btm_test(btm, x, y)) {
+            ++x;
+          }
+          if (fill->combines)
+            i_glin(im, start, x, y, line);
+          (fill->fill_with_color)(fill, start, y, x-start, im->channels, line);
+          i_plin(im, start, x, y, line);
+        }
+      }
+    }
+    myfree(line);
+  }
+  else {
+    i_fcolor *line = mymalloc(sizeof(i_fcolor) * (bxmax - bxmin));
+    
+    for(y=bymin;y<=bymax;y++) {
+      x = bxmin;
+      while (x < bxmax) {
+        while (x < bxmax && !btm_test(btm, x, y)) {
+          ++x;
+        }
+        if (btm_test(btm, x, y)) {
+          start = x;
+          while (x < bxmax && btm_test(btm, x, y)) {
+            ++x;
+          }
+          if (fill->combines)
+            i_glinf(im, start, x, y, line);
+          (fill->fill_with_fcolor)(fill, start, y, x-start, im->channels, line);
+          i_plinf(im, start, x, y, line);
+        }
+      }
+    }
+    myfree(line);
+  }
+
+  btm_destroy(btm);
+}
diff --git a/fills.c b/fills.c
index bb9700c..329d2a7 100644 (file)
--- a/fills.c
+++ b/fills.c
@@ -290,6 +290,10 @@ builtin_hatches[][8] =
     /* L-shaped tiles */
     0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
   },
+  {
+    /* wider stipple */
+    0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
+  },
 };
 
 typedef struct
diff --git a/io.c b/io.c
index bcc3f91..d8c0859 100644 (file)
--- a/io.c
+++ b/io.c
@@ -180,7 +180,7 @@ mymalloc(int size) {
 
   if ( (buf = malloc(size)) == NULL ) {
     mm_log((1, "mymalloc: unable to malloc %d\n", size));
-    fprintf(stderr,"Unable to malloc.\n"); exit(3);
+    fprintf(stderr,"Unable to malloc %d.\n", size); exit(3);
   }
   mm_log((1, "mymalloc(size %d) -> %p\n", size, buf));
   return buf;
index bd6a291..c727052 100644 (file)
@@ -6,7 +6,7 @@ my @hatch_types =
      hline1 hline2 hline4 slash1 slosh1 slash2 slosh2
      grid1 grid2 grid4 dots1 dots4 dots16 stipple weave cross1 cross2
      vlozenge hlozenge scalesdown scalesup scalesleft scalesright stipple2
-     tile_L/;
+     tile_L stipple3/;
 my %hatch_types;
 @hatch_types{@hatch_types} = 0..$#hatch_types;
 
index 5def8d7..cfeba5a 100644 (file)
@@ -7,7 +7,7 @@ use Imager::Color::Float;
 
 Imager::init_log("testout/t20fill.log", 1);
 
-print "1..18\n";
+print "1..21\n";
 
 my $blue = NC(0,0,255);
 my $red = NC(255, 0, 0);
@@ -114,6 +114,41 @@ $im->arc(r=>80, d1=>75, d2=>135,
          fill=>{ fountain=>'radial', xa=>100, ya=>100, xb=>20, yb=>100 });
 $im->write(file=>'testout/t20_sample.ppm');
 
+# flood fill tests
+my $rffimg = Imager::ImgRaw::new(100, 100, 3);
+# build a H 
+Imager::i_box_filled($rffimg, 10, 10, 20, 90, $blue);
+Imager::i_box_filled($rffimg, 80, 10, 90, 90, $blue);
+Imager::i_box_filled($rffimg, 20, 45, 80, 55, $blue);
+my $black = Imager::Color->new(0, 0, 0);
+Imager::i_flood_fill($rffimg, 15, 15, $red);
+my $rffcmp = Imager::ImgRaw::new(100, 100, 3);
+# build a H 
+Imager::i_box_filled($rffcmp, 10, 10, 20, 90, $red);
+Imager::i_box_filled($rffcmp, 80, 10, 90, 90, $red);
+Imager::i_box_filled($rffcmp, 20, 45, 80, 55, $red);
+$diff = Imager::i_img_diff($rffimg, $rffcmp);
+ok(19, !$diff, "flood fill difference");
+
+my $ffim = Imager->new(xsize=>100, ysize=>100);
+my $yellow = Imager::Color->new(255, 255, 0);
+$ffim->box(xmin=>10, ymin=>10, xmax=>20, ymax=>90, color=>$blue, filled=>1);
+$ffim->box(xmin=>20, ymin=>45, xmax=>80, ymax=>55, color=>$blue, filled=>1);
+$ffim->box(xmin=>80, ymin=>10, xmax=>90, ymax=>90, color=>$blue, filled=>1);
+ok(20, $ffim->flood_fill(x=>50, 'y'=>50, color=>$red), "flood fill");
+$diff = Imager::i_img_diff($rffcmp, $ffim->{IMG});
+ok(21, !$diff, "oo flood fill difference");
+$ffim->flood_fill(x=>50, 'y'=>50,
+                  fill=> {
+                          hatch => 'check2x2'
+                         });
+#                  fill=>{
+#                         fountain=>'radial',
+#                         xa=>50, ya=>50,
+#                         xb=>10, yb=>10,
+#                        });
+$ffim->write(file=>'testout/t20_ooflood.ppm');
+
 sub ok {
   my ($num, $test, $desc) = @_;