# Implementing the flood fill algorithm

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

The flood fill algorithm is a method of determining connected regions in an array (e.g. for filling an area of pixels with a colour).

## Implementations

### Pseudocode

One implicitly stack-based recursion flood-fill implementation (for a two-dimensional array) goes as follows:

```Flood-fill (node, target-color, replacement-color):
1. If the color of node is not equal to target-color, return.
2. Set the color of node to replacement-color.
3. Perform Flood-fill (one step to the west of node, target-color, replacement-color).
Perform Flood-fill (one step to the east of node, target-color, replacement-color).
Perform Flood-fill (one step to the north of node, target-color, replacement-color).
Perform Flood-fill (one step to the south of node, target-color, replacement-color).
4. Return.
```

An explicitly queue-based implementation might resemble the following:

```Flood-fill (node, target-color, replacement-color):
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to the end of Q.
4. For each element n of Q:
5.  Set the color of n to replacement-color.
6.  If the color of the node to the west of n is target-color, add that node to the end of Q.
If the color of the node to the east of n is target-color, add that node to the end of Q.
If the color of the node to the north of n is target-color, add that node to the end of Q.
If the color of the node to the south of n is target-color, add that node to the end of Q.
7. Continue looping until Q is exhausted.
8. Return.
```

Most practical implementations use a loop for the west and east directions as an optimization to avoid the overhead of queue management:

```Flood-fill (node, target-color, replacement-color):
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to the end of Q.
4. For each element n of Q:
5.  Set w and e equal to n.
6.  Move w to the west until the color of the node to the west of w no longer matches target-color.
7.  Move e to the east until the color of the node to the east of e no longer matches target-color.
8.  Set the color of nodes between w and e to replacement-color.
9.  For each node n between w and e:
10.   If the color of the node to the north of n is target-color, add that node to the end of Q.
If the color of the node to the south of n is target-color, add that node to the end of Q.
11. Continue looping until Q is exhausted.
12. Return.
```

Adapting the algorithm to use an additional array to store the shape of the region allows generalization to cover "fuzzy" flood filling, where an element can differ by up to a specified threshold from the source symbol. Using this additional array as an alpha channel allows the edges of the filled region to blend somewhat smoothly with the not-filled region.

### ActionScript (Flash)

```      fill_map = new Array();
fill_map = [1, 1, 1, 1, 1, 1, 1, 1, 1];
fill_map = [1, 0, 0, 0, 1, 0, 0, 0, 1];
fill_map = [1, 0, 0, 0, 1, 0, 0, 0, 1];
fill_map = [1, 0, 0, 1, 0, 0, 0, 0, 1];
fill_map = [1, 1, 1, 0, 0, 0, 1, 1, 1];
fill_map = [1, 0, 0, 0, 0, 1, 0, 0, 1];
fill_map = [1, 0, 0, 0, 1, 0, 0, 0, 1];
fill_map = [1, 0, 0, 0, 1, 0, 0, 0, 1];
fill_map = [1, 1, 1, 1, 1, 1, 1, 1, 1];
for (y=0; y<9; y++) {
for (x=0; x<9; x++) {
tile = _root.attachMovie("tile", "tile_"+(y+x*9), _root.getNextHighestDepth(), {_x:y*50, _y:x*50});
tile.gotoAndStop(1+fill_map[x][y]);
}
}
_root.attachMovie("cursor", "cursor", _root.getNextHighestDepth());
cursor.onEnterFrame = function() {
this._x = 50*Math.floor(_root._xmouse/50);
this._y = 50*Math.floor(_root._ymouse/50);
};
_root.onMouseDown = function() {
pos_x = Math.floor(_root._xmouse/50);
pos_y = Math.floor(_root._ymouse/50);
flood_fill(pos_x, pos_y);
};
function flood_fill(x, y) {
pos = x+y*9;
if (fill_map[y][x] == 0) {
fill_map[y][x] = 2;
_root["tile_"+(x+y*9)].gotoAndStop(3);
flood_fill(x+1,y);
flood_fill(x-1,y);
flood_fill(x,y+1);
flood_fill(x,y-1);
}
}
```

### C

``` #define BOARD_WIDTH  10
#define BOARD_HEIGHT 20

typedef struct MAP
{
unsigned char b[BOARD_HEIGHT][BOARD_WIDTH];
} MAP;
static void flood_loop(MAP *map, int x, int y,
unsigned int dst_c, unsigned  int src_c)
{
int fillL, fillR, i;
int in_line = 1;
//unsigned char c = src_c, fillC = dst_c;

/* find left side, filling along the way */
fillL = fillR = x;
while( in_line )
{
map->b[y][fillL] = dst_c;
fillL--;
in_line = (fillL < 0) ? 0 : (map->b[y][fillL] == src_c);
}
fillL++;
/* find right side, filling along the way */
in_line = 1;
while( in_line )
{
map->b[y][fillR] = dst_c;
fillR++;
in_line = (fillR > BOARD_WIDTH-1) ? 0 : (map->b[y][fillR] == src_c);
}
fillR--;
/* search top and bottom */
for(i = fillL; i <= fillR; i++)
{
if( y > 0 && map->b[y - 1][i] == src_c )
flood_loop(map, i, y - 1, dst_c, src_c);
if( y < BOARD_HEIGHT-1 && map->b[y + 1][i] == src_c )
flood_loop(map, i, y + 1, dst_c, src_c);
}
}
void flood_fill(MAP *map, int x, int y, unsigned int c)
{
flood_loop(map, x, y, c, map->b[y][x]);
map->b[y][x] = c;  /* some buggy optimizers needed this line */
}
```

#### Micromouse

Implemented in C

```    #define NORTH      1
#define EAST       2
#define SOUTH      4
#define WEST       8
#define VISITED      16
#define ONROUTE      32

#define NOTNORTH  (255-NORTH)
#define NOTEAST   (255-EAST)
#define NOTSOUTH  (255-SOUTH)
#define NOTWEST   (255-WEST)
#define ALLWALLS  (NORTH|EAST|SOUTH|WEST)

// maze storage
#define NUMCELLS 256
extern CELL maze[NUMCELLS] ;
extern CELL map[NUMCELLS];

/*
floodMaze solves the maze using wall data found in the array maze
and places the flooding data - effectively the routemap - into
the array map. The destination cell is passed in to the function
to allow solving for any location.
The state of map on entry is not important and it is not important
whether all the walls have been found. The algorithm simply performs
the flood based on what it knows at the time it is called.
At worst, up to 255 passes are made through the maze date in generating
the map. If that many passes are made, there has been an error and
the maze should be considered to be seriously in error.
Passes of the algorithm are made until either the limit has been reached
or the change flag indicates that none of the map data has changed in
that pass. At that point the maze can be considered to have been solved.
Clearly, unless all the walls have been observed, the map will contain
invalid paths. The mouse will discover new walls and call the solver again
as needed.
The function returns the number of passes required to solve the maze.
A returned value of 255 indicates an error.
Note that the array is allowed to wrap around in a most dangerous
manner by relying on the 8 bit pointer.
This should not be a real problem in as much as the existence of the outer
walls means we never have to make use of the value
*/

UCHAR floodMaze(UCHAR goal)
{
UCHAR i,j;
UCHAR now,next;
UCHAR passes;
UCHAR cellwalls;    // the wall data for a given cell
UCHAR changed;

i = 0;
do{
map[i--] = 255;
} while (i);

map[goal]=0;
passes = 0;
now = 0;
next = now+1;
do
{
changed = 0;
i = 0;
do
{
if (map[i]==now)
{
cellwalls=maze[i];
if ((cellwalls & NORTH) == 0)
{
j = i+1;
if (map[j] == 255){ map[j] = next; changed=1;}
}
if ((cellwalls & EAST) == 0)
{
j = i + 16;
if (map[j] == 255){ map[j] = next; changed=1;}
}
if ((cellwalls & SOUTH) == 0)
{
j = i + 255;
if (map[j] == 255){ map[j] = next; changed=1;}
}
if ((cellwalls & WEST) == 0)
{
j = i + 240;
if (map[j] == 255){ map[j] = next; changed=1;}
}
}
i--;
} while(i);
now  = now+1;
next = now+1;
passes++;
} while(changed);
return passes;
}
```

### Java

This is next algorithm adapted by Claudio Santana and Damian Johnson for Java:

```  /**
* Fills the selected pixel and all surrounding pixels of the same color with the fill color.
* @param img image on which operation is applied
* @param fillColor color to be filled in
* @param loc location at which to start fill
* @throws IllegalArgumentException if loc is out of bounds of the image
*/
public static void floodFill(BufferedImage img, Color fillColor, Point loc) {
if (loc.x < 0 || loc.x >= img.getWidth() || loc.y < 0 || loc.y >= img.getHeight()) throw new IllegalArgumentException();

WritableRaster raster = img.getRaster();
int[] fill =
new int[] {fillColor.getRed(), fillColor.getGreen(), fillColor.getBlue(),
fillColor.getAlpha()};
int[] old = raster.getPixel(loc.x, loc.y, new int);

// Checks trivial case where loc is of the fill color
if (isEqualRgba(fill, old)) return;

floodLoop(raster, loc.x, loc.y, fill, old);
}

// Recursively fills surrounding pixels of the old color
private static void floodLoop(WritableRaster raster, int x, int y, int[] fill, int[] old) {
Rectangle bounds = raster.getBounds();
int[] aux = {255, 255, 255, 255};

// finds the left side, filling along the way
int fillL = x;
do {
raster.setPixel(fillL, y, fill);
fillL--;
} while (fillL >= 0 && isEqualRgba(raster.getPixel(fillL, y, aux), old));
fillL++;

// find the right right side, filling along the way
int fillR = x;
do {
raster.setPixel(fillR, y, fill);
fillR++;
} while (fillR < bounds.width - 1 && isEqualRgba(raster.getPixel(fillR, y, aux), old));
fillR--;

// checks if applicable up or down
for (int i = fillL; i <= fillR; i++) {
if (y > 0 && isEqualRgba(raster.getPixel(i, y - 1, aux), old)) floodLoop(raster, i, y - 1,
fill, old);
if (y < bounds.height - 1 && isEqualRgba(raster.getPixel(i, y + 1, aux), old)) floodLoop(
raster, i, y + 1, fill, old);
}
}

// Returns true if RGBA arrays are equivalent, false otherwise
// Could use Arrays.equals(int[], int[]), but this is probably a little faster...
private static boolean isEqualRgba(int[] pix1, int[] pix2) {
return pix1 == pix2 && pix1 == pix2 && pix1 == pix2 && pix1 == pix2;
}
```

### Matlab

```function flood_fill(xc,yc,x,y,new,old,width,height)
% flood fills area of matrix
% (xc,yc) = start point of flood fill
% (x,y) = current test location
% new = new value to be filled
% old = old value to replace
%
% example use:
% global fill
% width = 20;
% height = 20;
% fill = zeros(width,height);
% new = 1;
% old = 0;
% xc = 10;
% yc = 10;
% flood_fill(xc,yc,xc,yc,new,old,width,height);
%
% Results:
% The flood fill will alter all values of 0 to one flood filling from a
% start point (xc,yc)
% If the flood fill starts in an enclosed space it will fill up to the
% boundary
%
% Author: James Goodwin, June 2003
% email: JamesRichardGoodwin@lyocs.com

global fill;
global call;

call = call + 1;

if (x<2 || x>=width)
x = xc;
end

if (y<2 || y>=height)
y = yc;
end

if (fill(y,x) == old)
fill(y,x) = new;
flood_fill(xc,yc,x+1,y,new,old,width,height);
flood_fill(xc,yc,x,y+1,new,old,width,height);
flood_fill(xc,yc,x-1,y,new,old,width,height);
flood_fill(xc,yc,x,y-1,new,old,width,height);
end
```

### OCaml

Naive non-tail-recursive implementation (this is more than adequate in practice because OCaml only allocates tiny stack frames, allowing deep recursion):

```# let rec flood img targ repl (x, y) =
match try Some img.(x).(y) with _ -> None with
| Some color when color = targ ->
img.(x).(y) <- repl;
let f = flood img targ repl in
f(x-1, y); f(x+1, y); f(x, y-1); f(x, y+1)
| _ -> ();;
val flood : 'a array array -> 'a -> 'a -> int * int -> unit = <fun>
```

For example:

```# let test =
Array.init 10 (fun y -> Array.init 10 (fun x -> if (x+1)/(y+1)<2 then 1 else 0));;
val test : int array array =
[|[|1; 0; 0; 0; 0; 0; 0; 0; 0; 0|];
[|1; 1; 1; 0; 0; 0; 0; 0; 0; 0|];
[|1; 1; 1; 1; 1; 0; 0; 0; 0; 0|];
[|1; 1; 1; 1; 1; 1; 1; 0; 0; 0|];
[|1; 1; 1; 1; 1; 1; 1; 1; 1; 0|];
[|1; 1; 1; 1; 1; 1; 1; 1; 1; 1|];
[|1; 1; 1; 1; 1; 1; 1; 1; 1; 1|];
[|1; 1; 1; 1; 1; 1; 1; 1; 1; 1|];
[|1; 1; 1; 1; 1; 1; 1; 1; 1; 1|];
[|1; 1; 1; 1; 1; 1; 1; 1; 1; 1|]|]
# flood test 1 2 (0,0); test;;
- : int array array =
[|[|2; 0; 0; 0; 0; 0; 0; 0; 0; 0|];
[|2; 2; 2; 0; 0; 0; 0; 0; 0; 0|];
[|2; 2; 2; 2; 2; 0; 0; 0; 0; 0|];
[|2; 2; 2; 2; 2; 2; 2; 0; 0; 0|];
[|2; 2; 2; 2; 2; 2; 2; 2; 2; 0|];
[|2; 2; 2; 2; 2; 2; 2; 2; 2; 2|];
[|2; 2; 2; 2; 2; 2; 2; 2; 2; 2|];
[|2; 2; 2; 2; 2; 2; 2; 2; 2; 2|];
[|2; 2; 2; 2; 2; 2; 2; 2; 2; 2|];
[|2; 2; 2; 2; 2; 2; 2; 2; 2; 2|]|]
```

Queue-based version:

```# let rec flood img targ repl = function
| [] -> ()
| (x, y) :: t ->
match try Some img.(x).(y) with _ -> None with
| Some color when color = targ ->
img.(x).(y) <- repl;
flood img targ repl
((x-1, y) :: (x+1, y) :: (x, y-1) :: (x, y+1) :: t)
| _ -> flood img targ repl t;;
val flood : 'a array array -> 'a -> 'a -> (int * int) list -> unit = <fun>
```

Continuation passing style (CPS) uses a stack of closures which is shorter but slower than the explicit queue:

```# let rec flood img targ repl (x, y) k () =
match try Some img.(x).(y) with _ -> None with
| Some color when color = targ ->
img.(x).(y) <- repl;
let f = flood img targ repl in
f(x-1, y) (f(x+1, y) (f(x, y-1) (f(x, y+1) k))) ()
| _ -> k();;
val flood : 'a array array -> 'a -> 'a -> int * int -> unit = <fun>
```

### Perl

Assuming floodfill_*, col and the four directions are methods on some object whose instance is passed as first argument as per standard method call syntax.

```sub col;    # implementation
sub west;   # left
sub east;   # as exercise
sub north;  # for

sub floodfill_stack {
my \$self    = shift;
my \$target  = shift;
my \$repl    = shift;

return unless \$target eq \$self->col;    # get
=pod
One might either overload the eq resp. == operator
here if a colour cannot be expressed in primitive
types, or write an encapsulating method for
=cut

\$self->col(\$repl);      # set
\$self->\$_->floodfill_stack(\$target, \$repl)
for qw(west east north south);
return;
};
```
```sub floodfill_queue {
my \$self    = shift;
my \$target  = shift;
my \$repl    = shift;

my @q;
# thanks to Perl arrays, queue management is totally painless

return unless \$target eq \$self->col;    # get
push @q, \$self;
while (my \$n = shift @q) {
=pod
Be careful that the assignment to \$n evaluates true in
boolean context, else the loop ends prematurely. Since
\$n is supposed to be an object instance and not just a
primitive number, that accident will not happen. \$n is
only ever undef when the @q is actually emptied.
=cut
\$n->col(\$repl);     # set
for (qw(west east north south)) {
push @q, \$n->\$_ if \$target eq \$n->\$_->col;
};
};
};
```

### Python

Assuming floodfill_*, and the four directions are methods in some class, and color is an instance variable.

```class ?:
# implementation
def west(self): pass     # left
def east(self): pass     # as exercise
def north(self): pass    # for
def south(self): pass    # the reader

def floodfill_stack(self, target, repl):
if target != self.color:
return

self.color = repl
for x in [self.west(), self.east(), self.north(), self.south()]:
x.floodfill_stack(target, repl)
return
```
```    def floodfill_queue(self, target, repl):
if target != self.color:
return

q = [self]
while q:
n = q.pop(0)
n.color = repl
for x in [n.west(), n.east(), n.north(), n.south()]:
if x.color == target:
x.color = repl
q.append(x)
```