Introduction

The purpose of this article is to describe how to design an efficient flood fill algorithm.

In order to show the strengths of a good design, I must first: describe the basic types of flood filling and there individual strengths and weaknesses.

Note: By efficient, I mean creating an algorithm that reads as few pixels as possible and uses the least amount of memory allocations. Although we are now talking about flood filling a bitmap, historically speaking, flood fills read and wrote directly to the video, which was very slow. Therefore, it was a must to reduce the number of reads and writes; also the amount of memory was limited, so a reduction in memory usage was also a big plus.

What is the QuickFill algorithm?

The QuickFill algorithm is a non-recursive (seed fill) method of filling a 2D graphics image using a scan line search method and doubly linked to lists of nodes to reduce the amount of memory required. The scan line method used in combination with the linked lists, greatly increases the speed at which an image can be filled and allows for multiple fill types to be implemented: background, border, and pattern fills.

Note: The original QuickFill algorithm was written, as part of my personal DOS graphics library, to fill images displayed on a video monitor.

Basic 4 way recursive method

This is the most basic of all flood filling methods, as well as the simplest.

Recursive scan line method

The recursive scan line method is a type of 4 way method, but is more efficient than the 4 or 8 way single pixel recursive methods.

Its strength: complete lines are scanned.

Its weaknesses: repeated sampling of some lines and recursion (may overflow stack).

The diagram above shows how the flooding of a 3x3 image progresses. As you can see: each line was visited 1 to 3 times, and the depth of recursion was 2.

The line fill method below may be inefficient, but it reduces the number of times that a pixel is revisited and reduces the number of recursive calls made. It also has the advantage of being optimizable, that is it can be improved through optimization techniques.

Some techniques that can be applied are:

write scan and search line functions that read and write the image data directly,

The second, so called, optimization technique, appears as if it would slow down the filling process. If it was used in the same way as the stack array in the following code, then you would be correct. The fact is, a linked list allows us to apply other optimization techniques that more than compensate for the slight loss in efficiency. For example: we could pop items off the stack based on there line number, which reduces the number of items on the stack.

Note: Since most programmers now work with bitmaps, instead of reading and writing directly to video, the first optimization above may be all that is required to make this a truly fast solid flood fill algorithm.

The QuickFill algorithm

Finally, we get to the QuickFill method of flood filling, which is a type of 4 way method, but is more efficient than the simple non-recursive method (believe it or not).

Note: For the purposes of this article, I will be describing (mostly) the original QuickFill algorithm, since the included code does not use optimized scan, search, and line drawing functions (remember, the original code directly accessed the video).

Its strengths are:

supports 3 types of fills: background, border and pattern,

optimized scan, search and drawing functions,

no recursion,

use of doubly linked list, to allow for efficiently removing items from list based on line number,

efficient use of list for reverse line splitting, when pattern filling is required.

Its weakness: repeated sampling of some lines, during solid fills.

As you can see from the code below, this is the most complicate method of flood filling. It was derived, indirectly, from the ideas presented in the simple non-recursive method.

The steps taken to create the original code where as follows:

Wrote QuickFill function based on what I could remember about scan line filling.

Replaced stack array with two singly linked lists.

Modified PopLine function, so that lines would be removed based on line number.

Added SearchLeft, SearchRight, ScanLeft, ScanRight functions; in order to allow for optimization of searching and scanning.

Added the PushOpposite function, which reverse splits lines, in order to reduce the number of lines revisited. This required that the list be changed to a doubly linked list and that the list be x-sorted. The reason behind this function was to eliminate all line revisits, but instead it just reduced the number of visits (at a cost).

Optimization of all of the above.

Note: While testing the port of the code to Windows, I discovered a gapping hole in the single visit code, patterns and masks could not be empty or the code would get stuck looping for ever (hence the following).

In order to make this function more useful to Windows programmers, I added code to support flooding of image with a bitmap pattern. Since PushOpposite only reduces the number of lines revisited and does not prevent revisits, I had to add another linked list to keep track of which lines had already been visited. The PushOpposite function is still required as it still reduces the number of line revisits and, as a bonus, it reduces the number of items placed in the visited list. To optimize the visited list, I decided to use visited blocks (rectangles) instead of visited lines, this serves to reduce the number of items needed in the list, which means less memory allocations.

Note: The following is the QuickFill function taken from version 1.0 of the code.

Note: In the following diagram "Scanned" impiles next line to be scanned, when line being pushed is popped off the stack.

Reverse line splitting:

Note: In the following diagram the scanning/pushing starts at the top.

Note: In the following diagram the blue (arrowed) lines represent the lines being pushed.

Background

At the time that the QuickFill algorithm was created, I had seen only two types of flood fills used and wanted an algorithm that could handle both types. The first type used a border-color approach, which meant that the area that needed to be filled first had to be outlined with a border of a given color; this approach is the least desirable of the flood fills. The second type used the color at the seed-point, which meant that only pixels of that color where filled; this approach is much more versatile, since it allows for multiple image objects of varying color to be contained in the area being filled. Both of these flood fill types used a horizontal scan-line approach to solve the problem of flood filling an image.

When trying to find information on how to implement a fast flood fill algorithm, I discovered that there was almost no information on the subject. The only algorithms that I found were:

the basic recursive 4 way flood fill that has been mentioned in many publications (and still is), this is the worst (and slowest) method for filling an image,

a non-recursive scan-line version (ref. 1), that was faster than the 4 way version (still too slow for my purposes), and

a non-recursive scan-line version (ref. 2) that used two linked lists to implement a flood fill algorithm, faster (but still too slow).

The first two methods had the advantage of being very compact, and very slow. The third method on the other hand, had the potential of being very fast and complicated.

After placing the idea on the back burners for a while, since it was for my own personal graphics library and was not a priority, I had an epiphany. I was having a cup of coffee at the local coffee shop and thinking about how I could solve the problem, when it all came together in my head. I started with the ideas used in the simple scan-line algorithm (ref. 1) and proceeded to expand on the idea using a singly linked list (ref. 2) of nodes, representing a LIFO stack. Over the next nine days, I made incremental changes to the algorithm, each designed to increase the over all speed. When I was finished, I had an algorithm that was not only fast, but faster than every implementation that I could find, except for the one implemented in the Fastgraph library by Ted Gruber (written in assembly). Five months after the completion of the code, I finally went back and added the code necessary for single line visits so that the QuickFill function could also handle pattern fills.

References:

Using the code

Note: When running the demo program in slow mode, you can press the ESC key to stop it.

CDibData specific:

If you have not installed the Windows SDK from Microsoft, you will get some compilation errors when compiling in debug mode. The reason for this is that BITMAPV5HEADER was not defined in the SDK that came with Visual C++ 6.0. Since BITMAPV5HEADER is only used for displaying debugging information, it should be easy to comment out the code.

Points of Interest

The code included with this article relies on the CDibData class to provide direct access to bitmap pixels.

I see no barrier to rewriting the code for use with GDI+, the only reason I did not do that, is that, I do not know enough about GDI+ usage at this time. Besides, I have a personal paint program written using GDI and would like to add this code to it.

For those who want to compare the various flood fill algorithms, I suggest the following:

add some tracking variables and code (like the ones in the QuickFill code),

make the test function or functions private members of the QuickFill class,

after the QuickFill initialization, but before the first push, add a call to the test function. Then clean up after the test function call and return without entering the main QuickFill loop.

Note: The above is how I tested the recursive scan line code. Since it was written off the top of my head, I needed to be sure it would work.

If any one knows how to completely eliminate the need for the visited list, I would be very interested. When I discovered that the original method used to reduce revisits did not eliminate them, I felt insulted by my own code. At the time I wrote the QuickFill algorithm, I may have known of this problem and just forgot about it (doubt that).

Question: How would you optimize the QuickFill algorithm for bitmaps?

Answer: Modify the CDibData class so that it has optimized functions for ScanLeft, ScanRight, SearchLeft, SearchRight and horizontal line drawing. Of course, horizontal line drawing would be the hardest, since it would have to work the same as the DrawHorizontalLine member function.

History

Version 1.0

Jan. ??, 2004 : Converted from C (direct video access) to C++ for use with MS Windows bitmaps and added support for: pattern bitmaps to be used as fill and visit-block-list to eliminate revisiting durring pattern & mask fills.

Note: Since the origanal code had direct video access it could take advantage (with the correct write mode) of byte block copping and color-bit-mask reading (read mode 1). Both of which where hardware supported (VGA/EGA monitors) and much more efficeint than reading/writing single pixels directly from/to a bitmap.

Feb. 19, 2004 : Changed internal scan, search and line drawing routines to use pixel color values, in order to increase overall speed while working with palettized bitmaps (modified CDibData).

Mar. 5, 2004 : (1) Moved PushVisitedLine from QuickFill to PushOpposite, this increases the number of revisits and reduces the size of the visit list (8:1). (2) Changed visit list to use HLINE_NODE, since block checking is no longer required and the number of allocations are reduce because the free list can now be used by all. (Of course HLINE_NODE is larger than we need, since it is not a visit list specific node type)

Mar. 9, 2004 : (1) Added argument to QuickFill so that user can specify the rectangular area to be filled. (2) Added the GetInvalidRect function and associated code so that user can retrieve the rectangular coordinates of the bitmap area that has been modified, since lack of this information forces user to redraw whole bitmap.

Changes to demo program:

"Fill Mask" selection option.

"Show Revisits" option. This is used to show which lines were placed in the visit list, when pattern bitmaps or masks are used. Line types: Cyan->line in visit list, Yellow->revisited line direction = -1, Blue->revisited line direction = +1.

Fill area selection via mouse (left-click and drag to select area). To fill: left-click in selected area and release to fill.

Added elapsed fill time and revisit count to information displayed to right of bitmap.

Credits

Andrew J. McCauley for modifying CDibData debug code so that header type checking occurs based on WINVER rather than on header type only. This stopped compilation errors which occurred if the user did not have the new Windows SDK installed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

I am a senior software engineer who has been designing and developing software for many years, mostly in C/C++. You might say that I think in code; which is why I am passionate about my first rule of coding: “First do no harm”. So if I get carried away in my explanations, please realize that it is just part of my personality. I enjoy learning new things and, when I have the time, passing that knowledge onto others.

If the image is loaded into a CBitmap call QuickFill(pBitmap, pRect, x, y, fill_color, border_color); where pRect is rectangular region, of the image, that you wish to fill. (See QuickFillDemoView.cpp for example of usage).

If for some reason the image is not a CBitmap, but you are still using MFC, then you can derive from CQuickFill and override the QuickFill, GetPixel, SetPixel, DrawHorizontalLine, ScanLeft, SearchLeft, ScanRight, and SearchRight methods to read from and write to your particular image type.

The limitation is the image type specific read/write operations, the rest of the code is non-specific and can remain unchanged.

INTP"Program testing can be used to show the presence of bugs, but never to show their absence."Edsger Dijkstra

Hi! John Your algorithm and code are very excellent. I want to use that to implement the MagicWand Tool like in Photoshop. A very simple change is like the follow(for only color image with the 'm_wBitsPerPixel==24'):We add the 'Tolerance'When in the ScanLeft(ScanRight)function,the code changeed to be this case 24: for( ; x <= xmax; ++x, pPixel += 3 ) { clrSrc = 0x00FFFFFF & *(LPDWORD)pPixel; if(abs((BYTE)((WORD)(GetRValue(clrSrc))-(WORD) (GetRValue(dwValue)))) >m_nTolerance||abs((BYTE)((WORD)(GetGValue(clrSrc))-(WORD)(GetGValue(dwValue))))>m_nTolerance||abs((BYTE)((WORD)(GetBValue(clrSrc))-(WORD)(GetBValue(dwValue))))>m_nTolerance)//if( dwValue != (0x00FFFFFF & *(LPDWORD)pPixel) ) break; }break;//In the SearchLeft(SeachRight)function,the code changed to be that

Mosttimes the code works very well,but I found one problem that could not betackled by myself.sometims when the mouse clicked at some points on the image,the main memory busy working will increasing sharply.the computer is done! Then I use the "Slow Draw Mode",the computer stopped in the midway.I don't know why.Only find that there might be some problems in the big circle of '/* Now start flooding */ while( m_pLineList ) {}' in the code.My email address is:xiangjianjian0@hotmail.com

Off the top of my head:1) Make nMinX, nMaxX, nMinY, and nMaxY local variables.2) Make the current CDC object an argument.3) Make the bitmap to fill an argument.4) Create a compatible DC and select bitmap into it.5) Fill bitmap.6) select old bitmap into DC.7) return.

Your demo is very fast comparing to other algorithm .but when I am trying this algorithm for pocket PC application in c#. I am gating missing method exception .Is u have any way to apply this algorithm to pocket PC application.

Essentially, the only functions that need to be replaced are the the functions for reading and writing pixels.

I rewrote the code (from the original C code, I wrote years ago) to use C++ and MFC. It would have been better if I just converted it to a standard C++ class and provided vertual functions for reading and writing pixels. Instead of introducing dependents on MFC.

I do not know what the "missing method exception" is all about. That, fact, does does not change the fact that it can be addapted to any evironment, that requires this ability. You just need to understand, what the algorithm is doing.

Yes! But it is part my old graphics library and does not support backgound filling with a bitmap image (only solid colors or simple pattern). If you would like, I can send you a copy of the origanal file.

Notes:1) The origanal code was written in C.2) The minor fix in the push clipped back function, was also added to the origanal code.

P.S. I hope received my email explaning that you're now on my email list.

INTP"The more help VB provides VB programmers, the more miserable your life as a C++ programmer becomes."Andrew W. Troelsen

First the original QuickFill algorithm was not a rewrite, I had previously read the Paul Heckbert article a year before writing this alorithm as well as every other article I could find on the subject. The ideas expressed by Anton Treuenfels (based on some white paper he read) were more important than the simple algorithm presented by Paul Heckbert. Even without reading the article by Paul, I still would have arrived at the same place, as it is very similar to my previous attempts, made before searching for articles on the subject.

As for PUSH and POP, I am not sure what you mean. Yes, PUSH and POP work differently than they did in Pauls' algorithm (not LIFO). The changes, I made, increased the speed and reduced the stack size considerably. I no longer have my original notes stating the speed increases and stack reductions achieved by each of the changes.

I tried to present the information in a way that showed the differences between the algorithms. I did not go into as much detail (of my thinking) as I would have liked, but I wrote the original code years ago (on a DOS 386 machine). The algorithm is based on what I could remember from my research the subject (a year earlier), and lots of hand drawn diagrams.

You are welcome! It did not work as is because it was the one example I did not test. The other examples where written off the top of my head and therefore, needed testing. That piece of code was modified (from original) to be consistant with the other examples and, of course, I made a little mistake, by not testing it.

Click the horizontal or vertical line, the whole connected line can be filled and highlighted, but not for the tilted line as there are linked in diagonal direction rather than the up/down or left/right directions? Any way or possible to update it?

Diagonaly connected pixels are not considered connected for filling purposes. If a fill goes diaganly, it is considered a leak.

Note: S = seed point, B = border, . = background
[.][.][.][.][.][.][.]
[.][.][B][B][B][.][.]
[.][B][.][.][.][B][.]
[.][B][.][S][.][B][.]
[.][B][.][.][.][B][.]
[.][.][B][B][B][.][.]
[.][.][.][.][.][.][.]
If thisis filled starting at the seed point then
only the interior is filled the courners remain
unchanged.
If the diagonal pixes at the courners where
considered conected then filling would leak into
the surrounding area, which defeats the purpose
of the algorithm.

Could the algorithm be modified to allow for diagonals? Yes! Am I going to do it? Of course not! Why would I want a floodfill that leaks?

Your article is excellent : not only you are explaining your own algorithm, but also you present the existing ones, making a precious reference for floodfill algos.

I have a problem with your demo exe: when I click on a white area, I see Seconds and Maximum Stack Size (and sometime Current Line) numbers change, but the image doesn't change: no fill is visible (on Win98SE).

I changed the fill color (seems to be red by default), not improvement. I changed the Fill Mask, the Visit List Size and Pixel Revisited numbers change, but still no filling...

I had a quick look at the source, and saw rectangle tracking code. I don't see how it is used either.

I suppose I have to compile and debug the demo to see what is wrong...

BTW, it is not very convenient to have the test bitmap with the source, separated from the demo exe...

I tracked down to the call of system API ::SetDIBits, in CDibData::SetDIBits.It fails and return 0 (which isn't tested... )

I don't know why it fails. I suppose the given parameters are correct, since I am the only one to complain. I am not the only one to test this on Win98SE, isn't it? I have a i740 graphic card (yes, I have an old computer, a PII300...), I don't know if the driver is to blame.

I am struggling to make a simple C API program to test SetDIBits with other parameters, but I have some troubles to transform my handle returned by LoadImage into a DIB... Oh well, it is a good way to learn the DIB and bitmap functions, since I plan to make a Paint-like program!Still a long way to go...