a region of the color at the start poiint.
from perl you can call flood_fill() with a border parameter to specify
a fill to border.
from the API call the i_flood_fill_border() or i_flood_cfill_border()
function.
return undef;
}
- if ($opts{fill}) {
- unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
- # assume it's a hash ref
- require 'Imager/Fill.pm';
- unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
- $self->{ERRSTR} = $Imager::ERRSTR;
- return;
+ if ($opts{border}) {
+ my $border = _color($opts{border});
+ unless ($border) {
+ $self->_set_error($Imager::ERRSTR);
+ return;
+ }
+ if ($opts{fill}) {
+ unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+ # assume it's a hash ref
+ require Imager::Fill;
+ unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ }
+ $rc = i_flood_cfill_border($self->{IMG}, $opts{'x'}, $opts{'y'},
+ $opts{fill}{fill}, $border);
+ }
+ else {
+ my $color = _color($opts{'color'});
+ unless ($color) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
}
+ $rc = i_flood_fill_border($self->{IMG}, $opts{'x'}, $opts{'y'},
+ $color, $border);
+ }
+ if ($rc) {
+ return $self;
+ }
+ else {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ return;
}
- $rc = i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
}
else {
- my $color = _color($opts{'color'});
- unless ($color) {
- $self->{ERRSTR} = $Imager::ERRSTR;
+ if ($opts{fill}) {
+ unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+ # assume it's a hash ref
+ require 'Imager/Fill.pm';
+ unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ }
+ $rc = i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
+ }
+ else {
+ my $color = _color($opts{'color'});
+ unless ($color) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ $rc = i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
+ }
+ if ($rc) {
+ return $self;
+ }
+ else {
+ $self->{ERRSTR} = $self->_error_as_msg();
return;
}
- $rc = i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
- }
- if ($rc) { $self; } else { $self->{ERRSTR} = $self->_error_as_msg(); return (); }
+ }
}
sub setpixel {
int seedy
Imager::FillHandle fill
+undef_int
+i_flood_fill_border(im,seedx,seedy,dcol, border)
+ Imager::ImgRaw im
+ int seedx
+ int seedy
+ Imager::Color dcol
+ Imager::Color border
+
+undef_int
+i_flood_cfill_border(im,seedx,seedy,fill, border)
+ Imager::ImgRaw im
+ int seedx
+ int seedy
+ Imager::FillHandle fill
+ Imager::Color border
+
void
i_copyto(im,src,x1,y1,x2,y2,tx,ty)
#include <limits.h>
+static void
+cfill_from_btm(i_img *im, i_fill_t *fill, struct i_bitmap *btm,
+ int bxmin, int bxmax, int bymin, int bymax);
+
void
i_mmarray_cr(i_mmarray *ar,int l) {
int i;
/* i_ccomp compares two colors and gives true if they are the same */
+typedef int (*ff_cmpfunc)(i_color const *c1, i_color const *c2, int channels);
+
static int
-i_ccomp(i_color *val1,i_color *val2,int ch) {
+i_ccomp_normal(i_color const *val1, i_color const *val2, int ch) {
int i;
- for(i=0;i<ch;i++) if (val1->channel[i] !=val2->channel[i]) return 0;
+ for(i = 0; i < ch; i++)
+ if (val1->channel[i] !=val2->channel[i])
+ return 0;
return 1;
}
+static int
+i_ccomp_border(i_color const *val1, i_color const *val2, int ch) {
+ int i;
+ for(i = 0; i < ch; i++)
+ if (val1->channel[i] !=val2->channel[i])
+ return 1;
+ return 0;
+}
static int
-i_lspan(i_img *im, int seedx, int seedy, i_color *val) {
+i_lspan(i_img *im, int seedx, int seedy, i_color const *val, ff_cmpfunc cmpfunc) {
i_color cval;
while(1) {
if (seedx-1 < 0) break;
i_gpix(im,seedx-1,seedy,&cval);
- if (!i_ccomp(val,&cval,im->channels)) break;
+ if (!cmpfunc(val,&cval,im->channels))
+ break;
seedx--;
}
return seedx;
}
static int
-i_rspan(i_img *im, int seedx, int seedy, i_color *val) {
+i_rspan(i_img *im, int seedx, int seedy, i_color const *val, ff_cmpfunc cmpfunc) {
i_color cval;
while(1) {
if (seedx+1 > im->xsize-1) break;
i_gpix(im,seedx+1,seedy,&cval);
- if (!i_ccomp(val,&cval,im->channels)) break;
+ if (!cmpfunc(val,&cval,im->channels)) break;
seedx++;
}
return seedx;
#define SET(x,y) btm_set(btm,x,y)
/* INSIDE returns true if pixel is correct color and we haven't set it before. */
-#define INSIDE(x,y) ((!btm_test(btm,x,y) && ( i_gpix(im,x,y,&cval),i_ccomp(&val,&cval,channels) ) ))
+#define INSIDE(x,y, seed) ((!btm_test(btm,x,y) && ( i_gpix(im,x,y,&cval),cmpfunc(seed,&cval,channels) ) ))
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 *bxminp, int *bxmaxp, int *byminp, int *bymaxp,
+ i_color const *seed, ff_cmpfunc cmpfunc) {
int ltx, rtx;
int tx = 0;
struct i_bitmap *btm;
int channels,xsize,ysize;
- i_color cval,val;
+ i_color cval;
channels = im->channels;
xsize = im->xsize;
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 */
- ltx = i_lspan(im, seedx, seedy, &val);
- rtx = i_rspan(im, seedx, seedy, &val);
+ ltx = i_lspan(im, seedx, seedy, seed, cmpfunc);
+ rtx = i_rspan(im, seedx, seedy, seed, cmpfunc);
for(tx=ltx; tx<=rtx; tx++) SET(tx, seedy);
ST_PUSH(ltx, rtx, ltx, rtx, seedy+1, 1);
x = lx+1;
- if ( lx >= 0 && (wasIn = INSIDE(lx, y)) ) {
+ if ( lx >= 0 && (wasIn = INSIDE(lx, y, seed)) ) {
SET(lx, y);
lx--;
- while(INSIDE(lx, y) && lx > 0) {
+ while(INSIDE(lx, y, seed) && lx > 0) {
SET(lx,y);
lx--;
}
/* printf("x=%d\n",x); */
if (wasIn) {
- if (INSIDE(x, y)) {
+ if (INSIDE(x, y, seed)) {
/* case 1: was inside, am still inside */
SET(x,y);
} else {
}
} else {
if (x > rx) goto EXT;
- if (INSIDE(x, y)) {
+ if (INSIDE(x, y, seed)) {
SET(x, y);
/* case 3: Wasn't inside, am now: just found the start of a new run */
wasIn = 1;
int bxmin, bxmax, bymin, bymax;
struct i_bitmap *btm;
int x, y;
+ i_color val;
i_clear_error();
if (seedx < 0 || seedx >= im->xsize ||
return 0;
}
- btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax);
+ /* Get the reference color */
+ i_gpix(im, seedx, seedy, &val);
+
+ btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+ &val, i_ccomp_normal);
for(y=bymin;y<=bymax;y++)
for(x=bxmin;x<=bxmax;x++)
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;
+ i_color val;
i_clear_error();
return 0;
}
- btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax);
+ /* Get the reference color */
+ i_gpix(im, seedx, seedy, &val);
+
+ btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+ &val, i_ccomp_normal);
+
+ cfill_from_btm(im, fill, btm, bxmin, bxmax, bymin, bymax);
+
+ btm_destroy(btm);
+ return 1;
+}
+
+/*
+=item i_flood_fill_border(im, seedx, seedy, color, border)
+
+=category Drawing
+=synopsis i_flood_fill_border(im, 50, 50, &color, &border);
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<color>, fill stops when the fill reaches a pixels with
+color I<border>.
+
+Returns false if (seedx, seedy) are outside the image.
+
+=cut
+*/
+
+undef_int
+i_flood_fill_border(i_img *im, int seedx, int seedy, const i_color *dcol,
+ const i_color *border) {
+ int bxmin, bxmax, bymin, bymax;
+ struct i_bitmap *btm;
+ int x, y;
+
+ i_clear_error();
+ if (seedx < 0 || seedx >= im->xsize ||
+ seedy < 0 || seedy >= im->ysize) {
+ i_push_error(0, "i_flood_cfill: Seed pixel outside of image");
+ return 0;
+ }
+
+ btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+ border, i_ccomp_border);
+
+ for(y=bymin;y<=bymax;y++)
+ for(x=bxmin;x<=bxmax;x++)
+ if (btm_test(btm,x,y))
+ i_ppix(im,x,y,dcol);
+ btm_destroy(btm);
+ return 1;
+}
+
+/*
+=item i_flood_cfill_border(im, seedx, seedy, fill, border)
+
+=category Drawing
+=synopsis i_flood_cfill_border(im, 50, 50, fill, border);
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<fill>, the fill stops when it reaches pixels of color
+I<border>.
+
+Returns false if (seedx, seedy) are outside the image.
+
+=cut
+*/
+
+undef_int
+i_flood_cfill_border(i_img *im, int seedx, int seedy, i_fill_t *fill,
+ const i_color *border) {
+ int bxmin, bxmax, bymin, bymax;
+ struct i_bitmap *btm;
+
+ i_clear_error();
+
+ if (seedx < 0 || seedx >= im->xsize ||
+ seedy < 0 || seedy >= im->ysize) {
+ i_push_error(0, "i_flood_cfill_border: Seed pixel outside of image");
+ return 0;
+ }
+
+ btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+ border, i_ccomp_border);
+
+ cfill_from_btm(im, fill, btm, bxmin, bxmax, bymin, bymax);
+
+ btm_destroy(btm);
+
+ return 1;
+}
+
+static void
+cfill_from_btm(i_img *im, i_fill_t *fill, struct i_bitmap *btm,
+ int bxmin, int bxmax, int bymin, int bymax) {
+ int x, y;
+ int start;
if (im->bits == i_8_bits && fill->fill_with_color) {
/* bxmax/bxmin are inside the image, hence this won't overflow */
if (work)
myfree(work);
}
-
- btm_destroy(btm);
- return 1;
}
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];
undef_int i_flood_fill (i_img *im,int seedx,int seedy, const i_color *dcol);
undef_int i_flood_cfill(i_img *im, int seedx, int seedy, i_fill_t *fill);
+undef_int i_flood_fill_border (i_img *im,int seedx,int seedy, const i_color *dcol, const i_color *border);
+undef_int i_flood_cfill_border(i_img *im, int seedx, int seedy, i_fill_t *fill, const i_color *border);
/* image processing functions */
i_set_image_file_limits,
i_get_image_file_limits,
i_int_check_image_file_limits,
+
+ i_flood_fill_border,
+ i_flood_cfill_border,
};
/* in general these functions aren't called by Imager internally, but
#define i_circle_aa(im, x, y, rad, val) ((im_extt->f_i_circle_aa)((im), (x), (y), (rad), (val)))
#define i_flood_fill(im, seedx, seedy, dcol) ((im_extt->f_i_flood_fill)((im), (seedx), (seedy), (dcol)))
#define i_flood_cfill(im, seedx, seedy, fill) ((im_extt->f_i_flood_cfill)((im), (seedx), (seedy), (fill)))
+#define i_flood_fill_border(im, seedx, seedy, dcol, border) ((im_extt->f_i_flood_fill_border)((im), (seedx), (seedy), (dcol), (border)))
+#define i_flood_cfill_border(im, seedx, seedy, fill, border) ((im_extt->f_i_flood_cfill_border)((im), (seedx), (seedy), (fill), (border)))
#define i_copyto(im, src, x1, y1, x2, y2, tx, ty) \
((im_extt->f_i_copyto)((im), (src), (x1), (y1), (x2), (y2), (tx), (ty)))
int (*f_i_set_image_file_limits)(int width, int height, int bytes);
int (*f_i_get_image_file_limits)(int *width, int *height, int *bytes);
int (*f_i_int_check_image_file_limits)(int width, int height, int channels, int sample_size);
+ int (*f_i_flood_fill_border)(i_img *im, int seedx, int seedy, const i_color *dcol, const i_color *border);
+ int (*f_i_flood_cfill_border)(i_img *im, int seedx, int seedy, i_fill_t *fill, const i_color *border);
/* IMAGER_API_LEVEL 3 functions will be added here */
} im_ext_funcs;
i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &color);
i_circle_aa(im, 50, 50, 45, &color);
i_flood_cfill(im, 50, 50, fill);
+ i_flood_cfill_border(im, 50, 50, fill, border);
i_flood_fill(im, 50, 50, &color);
+ i_flood_fill_border(im, 50, 50, &color, &border);
# Error handling
=for comment
-From: Line 205 in draw.c
+From: Line 209 in draw.c
=item i_arc_aa(im, x, y, rad, d1, d2, color)
=for comment
-From: Line 330 in draw.c
+From: Line 334 in draw.c
=item i_arc_aa_cfill(im, x, y, rad, d1, d2, fill)
=for comment
-From: Line 356 in draw.c
+From: Line 360 in draw.c
=item i_arc_cfill(im, x, y, rad, d1, d2, fill)
=for comment
-From: Line 230 in draw.c
+From: Line 234 in draw.c
=item i_box(im, x1, y1, x2, y2, color)
=for comment
-From: Line 516 in draw.c
+From: Line 520 in draw.c
=item i_box_cfill(im, x1, y1, x2, y2, fill)
=for comment
-From: Line 559 in draw.c
+From: Line 563 in draw.c
=item i_box_filled(im, x1, y1, x2, y2, color)
=for comment
-From: Line 541 in draw.c
+From: Line 545 in draw.c
=item i_circle_aa(im, x, y, rad, color)
=for comment
-From: Line 462 in draw.c
+From: Line 466 in draw.c
=item i_flood_cfill(im, seedx, seedy, fill)
=for comment
-From: Line 1317 in draw.c
+From: Line 1329 in draw.c
+
+=item i_flood_cfill_border(im, seedx, seedy, fill, border)
+
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<fill>, the fill stops when it reaches pixels of color
+I<border>.
+
+Returns false if (seedx, seedy) are outside the image.
+
+
+=for comment
+From: Line 1409 in draw.c
=item i_flood_fill(im, seedx, seedy, color)
=for comment
-From: Line 1280 in draw.c
+From: Line 1287 in draw.c
+
+=item i_flood_fill_border(im, seedx, seedy, color, border)
+
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<color>, fill stops when the fill reaches a pixels with
+color I<border>.
+
+Returns false if (seedx, seedy) are outside the image.
+
+
+=for comment
+From: Line 1369 in draw.c
=item i_glin(im, l, r, y, colors)
=for comment
-From: Line 202 in imext.c
+From: Line 210 in imext.c
=item i_glinf(im, l, r, y, colors)
=for comment
-From: Line 237 in imext.c
+From: Line 245 in imext.c
=item i_gpal(im, x, r, y, indexes)
=for comment
-From: Line 301 in imext.c
+From: Line 309 in imext.c
=item i_gpix(im, x, y, color)
=for comment
-From: Line 130 in imext.c
+From: Line 138 in imext.c
=item i_gpixf(im, x, y, fcolor)
=for comment
-From: Line 166 in imext.c
+From: Line 174 in imext.c
=item i_gsamp(im, l, r, y, samp, chans, chan_count)
=for comment
-From: Line 255 in imext.c
+From: Line 263 in imext.c
=item i_gsampf(im, l, r, y, samp, chans, chan_count)
=for comment
-From: Line 278 in imext.c
+From: Line 286 in imext.c
=item i_line(im, x1, y1, x2, y2, val, endp)
=for comment
-From: Line 630 in draw.c
+From: Line 634 in draw.c
=item i_line_aa(im, x1, x2, y1, y2, color, endp)
=for comment
-From: Line 834 in draw.c
+From: Line 838 in draw.c
=item i_plin(im, l, r, y, colors)
=for comment
-From: Line 184 in imext.c
+From: Line 192 in imext.c
=item i_plinf(im, l, r, fcolors)
=for comment
-From: Line 219 in imext.c
+From: Line 227 in imext.c
=item i_ppal(im, x, r, y, indexes)
=for comment
-From: Line 320 in imext.c
+From: Line 328 in imext.c
=item i_ppix(im, x, y, color)
=for comment
-From: Line 110 in imext.c
+From: Line 118 in imext.c
=item i_ppixf(im, x, y, fcolor)
=for comment
-From: Line 147 in imext.c
+From: Line 155 in imext.c
=back
=for comment
-From: Line 339 in imext.c
+From: Line 347 in imext.c
=item i_colorcount(im)
=for comment
-From: Line 385 in imext.c
+From: Line 393 in imext.c
=item i_findcolor(im, color, &entry)
=for comment
-From: Line 420 in imext.c
+From: Line 428 in imext.c
=item i_getcolors(im, index, colors, count)
=for comment
-From: Line 360 in imext.c
+From: Line 368 in imext.c
=item i_maxcolors(im)
=for comment
-From: Line 402 in imext.c
+From: Line 410 in imext.c
=item i_setcolors(im, index, colors, count)
=for comment
-From: Line 440 in imext.c
+From: Line 448 in imext.c
=back
Retrieve a tag specified by name or code as an integer.
-On success sets the i_color *I<value> to the color and returns true.
+On success sets the int *I<value> to the integer and returns true.
On failure returns false.
From: Line 634 in tags.c
+=back
+
+
+=head1 UNDOCUMENTED
+
+The following API functions are undocumented so far, hopefully this
+will change:
+
+=over
+
+=item *
+
+B<i_get_image_file_limits>
+
+=item *
+
+B<i_int_check_image_file_limits>
+
+=item *
+
+B<i_set_image_file_limits>
+
+
+
=back
=item flood_fill
-You can fill a region that all has the same color using the
-flood_fill() method, for example:
+X<flood_fill>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).
+Alternatively you can fill a region limited by a given border color:
+
+ # stop at the red border
+ $im->flood_fill(x=>50, y=>50, color=>$color, border=>"red");
+
You can also fill with a complex fill:
$img->flood_fill(x=>50, y=>50, fill=>{ hatch=>'cross1x1' });
fill - the fill for the filled area. See L<"Fill Parameters">
+=item *
+
+border - the border color of the region to be filled. If this
+parameter is supplied flood_fill() will stop when it finds this color.
+If this is not supplied then a normal fill is done. C<border> can be
+supplied as a L<"Color Parameter">.
+
=back
=item setpixel
#!perl -w
use strict;
use lib 't';
-use Test::More tests => 52;
+use Test::More tests => 56;
use Imager ':handy';
use Imager::Fill;
ok(!$diff, "oo flood fill difference");
$ffim->flood_fill('x'=>50, 'y'=>50,
fill=> {
- hatch => 'check2x2'
+ hatch => 'check2x2',
+ fg => '0000FF',
});
# fill=>{
# fountain=>'radial',
# });
$ffim->write(file=>'testout/t20_ooflood.ppm');
+my $copy = $ffim->copy;
+ok($ffim->flood_fill('x' => 50, 'y' => 50,
+ color => $red, border => '000000'),
+ "border solid flood fill");
+is(Imager::i_img_diff($ffim->{IMG}, $rffcmp), 0, "compare");
+ok($ffim->flood_fill('x' => 50, 'y' => 50,
+ fill => { hatch => 'check2x2', fg => '0000FF', },
+ border => '000000'),
+ "border cfill fill");
+is(Imager::i_img_diff($ffim->{IMG}, $copy->{IMG}), 0,
+ "compare");
+
# test combining modes
my $fill = NC(192, 128, 128, 128);
my $target = NC(64, 32, 64);
i_img *im = i_img_8_new(100, 100, 3);
i_img *fill_im = i_img_8_new(5, 5, 3);
i_img *testim;
- i_color red, blue, temp_color;
+ i_color red, blue, green, black, temp_color;
i_fcolor redf, bluef;
i_fill_t *hatch, *fhatch_fill;
i_fill_t *im_fill;
red.channel[3] = 255;
blue.channel[0] = 0; blue.channel[1] = 0; blue.channel[2] = 255;
blue.channel[3] = 255;
+ green.channel[0] = 0; green.channel[1] = 255; green.channel[2] = 0;
+ green.channel[3] = 255;
+ black.channel[0] = black.channel[1] = black.channel[2] = 0;
+ black.channel[3] = 255;
hatch = i_new_fill_hatch(&red, &blue, 0, 1, NULL, 0, 0);
i_box(im, 0, 0, 9, 9, &red);
i_box(im, 71, 11, 78, 18, &red);
i_flood_cfill(im, 75, 15, hatch);
+ i_box_filled(im, 1, 21, 9, 24, &red);
+ i_box_filled(im, 1, 25, 9, 29, &blue);
+ i_flood_fill_border(im, 5, 25, &green, &black);
+
+ i_box_filled(im, 11, 21, 19, 24, &red);
+ i_box_filled(im, 11, 25, 19, 29, &blue);
+ i_flood_cfill_border(im, 15, 25, hatch, &black);
+
i_fill_destroy(fount_fill);
i_fill_destroy(fhatch_fill);
i_fill_destroy(solid_fill);