- 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
=================================================================
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) = @_;
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.
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)
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);
+}
/* L-shaped tiles */
0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
},
+ {
+ /* wider stipple */
+ 0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
+ },
};
typedef struct
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;
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;
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);
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) = @_;