Home
Search
 
What's New
Index
Books
Links
Q & A
Newsletter
Banners
 
Feedback
Tip Jar
 
C# Helper...
 
XML RSS Feed
Follow VBHelper on Twitter
 
 
 
MSDN Visual Basic Community
 
 
 
 
 
TitleFlood fill areas using safe and unsafe methods in Visual Basic .NET
DescriptionThis example shows how to flood fill areas using safe and unsafe methods in Visual Basic .NET.
Keywordsflood, floodfill, VB.NET, safe, unsafe
CategoriesGraphics, VB.NET
 

The following code performs a four-way flood fill on a Bitmap starting at a specific point. It continues the flood as long as the pixels it finds have the same color as the initial pixel's color. (A "four-way" flood looks up, down, left, and right from each pixel to see if it should color more pixels. In contrast, an "eight-way" flood looks in the upper, upper right, right, lower right, lower, lower left, left, and upper left directions.)

Subroutine SafeFloodFill starts by getting the color of the start pixel. It then creates a Stack to hold points that will be colored. (A Stack is a last-in-first-out (LIFO) data structure. Its Push method adds an item to the end of the Stack. Its Pop method removes the most recently added item from the Stack.) The program sets this pixel's color to the flood color.

Then, as long as the Stack is not empty, the program removes the most recent item from the Stack and calls subroutine SafeCheckPoint to see if its neighbors that are above, below, and to either side of it should be colored, too.

Subroutine SafeCheckPoint checks a point's color. If the point's color matches the original flood point's color, the routine adds the point to the Stack and gives it the flood color.

 
' Flood fill the point.
Public Sub SafeFloodFill(ByVal bm As Bitmap, ByVal x As _
    Integer, ByVal y As Integer, ByVal new_color As Color)
    ' Get the old and new colors.
    Dim old_color As Color = bm.GetPixel(x, y)

    ' The following "If Then" test was added by Reuben
    ' Jollif
    ' to protect the code in case the start pixel
    ' has the same color as the fill color.
    If old_color.ToArgb <> new_color.ToArgb Then
        ' Start with the original point in the stack.
        Dim pts As New Stack(1000)
        pts.Push(New Point(x, y))
        bm.SetPixel(x, y, new_color)

        ' While the stack is not empty, process a point.
        Do While pts.Count > 0
            Dim pt As Point = DirectCast(pts.Pop(), Point)
            If pt.X > 0 Then SafeCheckPoint(bm, pts, pt.X - _
                1, pt.Y, old_color, new_color)
            If pt.Y > 0 Then SafeCheckPoint(bm, pts, pt.X, _
                pt.Y - 1, old_color, new_color)
            If pt.X < bm.Width - 1 Then SafeCheckPoint(bm, _
                pts, pt.X + 1, pt.Y, old_color, new_color)
            If pt.Y < bm.Height - 1 Then SafeCheckPoint(bm, _
                pts, pt.X, pt.Y + 1, old_color, new_color)
        Loop
    End If
End Sub

' See if this point should be added to the stack.
Private Sub SafeCheckPoint(ByVal bm As Bitmap, ByVal pts As _
    Stack, ByVal x As Integer, ByVal y As Integer, ByVal _
    old_color As Color, ByVal new_color As Color)
    Dim clr As Color = bm.GetPixel(x, y)
    If clr.Equals(old_color) Then
        pts.Push(New Point(x, y))
        bm.SetPixel(x, y, new_color)
    End If
End Sub
 
The previous code works and is reasonably fast (much faster than similar VB 6 code) but there is a lot of overhead in the calls to GetPixel and SetPixel. The following code uses unsafe bitmap access to avoid these calls.

Subroutine UnsafeFloodFill begins much as the safe version does. After getting the initial pixel's color and adding it to the Stack, the routine creates a new BitmapBytesARGB32 object associated with the Bitmap. This object, described shortly, manages unsafe access to a bitmap.

The program locks the bitmap's data and then processes the Stack as before. This time, however, it uses subroutine UnsafeCheckPoint to handle the neighbors of points. When it is finished, UnsafeFloodFill unlocks the BitmapBytesARGB32 object's data.

The bm_bytes object provides access to the bitmap's pixel data through an array of bytes named ImageBytes. Each pixel in the array is represented by four bytes storing the pixel's blue, green, red, and alpha components (note the reversed order). Subroutine UnsafeCheckPoint calculates the location of a pixel's bytes in the array and compares its red, green, and blue components to those of the original flood point. If the components match, then the routine changes the pixel's color to the flood color.

 
' Flood the area at this point.
Public Sub UnsafeFloodFill(ByVal bm As Bitmap, ByVal x As _
    Integer, ByVal y As Integer, ByVal new_color As Color)
    ' Get the old and new colors' components.
    Dim old_r As Byte = bm.GetPixel(x, y).R
    Dim old_g As Byte = bm.GetPixel(x, y).G
    Dim old_b As Byte = bm.GetPixel(x, y).B

    Dim new_r As Byte = new_color.R
    Dim new_g As Byte = new_color.G
    Dim new_b As Byte = new_color.B

    ' Start with the original point in the stack.
    Dim pts As New Stack(1000)
    pts.Push(New Point(x, y))
    bm.SetPixel(x, y, new_color)

    ' Make a BitmapBytesARGB32 object.
    Dim bm_bytes As New BitmapBytesARGB32(bm)

    ' Lock the bitmap.
    bm_bytes.LockBitmap()

    ' While the stack is not empty, process a point.
    Dim pix As Integer
    Do While pts.Count > 0
        Dim pt As Point = DirectCast(pts.Pop(), Point)
        If pt.X > 0 Then UnsafeCheckPoint(bm_bytes, pts, _
            pt.X - 1, pt.Y, old_r, old_g, old_b, new_r, _
            new_g, new_b)
        If pt.Y > 0 Then UnsafeCheckPoint(bm_bytes, pts, _
            pt.X, pt.Y - 1, old_r, old_g, old_b, new_r, _
            new_g, new_b)
        If pt.X < bm.Width - 1 Then _
            UnsafeCheckPoint(bm_bytes, pts, pt.X + 1, pt.Y, _
            old_r, old_g, old_b, new_r, new_g, new_b)
        If pt.Y < bm.Height - 1 Then _
            UnsafeCheckPoint(bm_bytes, pts, pt.X, pt.Y + 1, _
            old_r, old_g, old_b, new_r, new_g, new_b)
    Loop

    ' Unlock the bitmap.
    bm_bytes.UnlockBitmap()
End Sub

' See if this point should be added to the stack.
Private Sub UnsafeCheckPoint(ByVal bm_bytes As _
    BitmapBytesARGB32, ByVal pts As Stack, ByVal x As _
    Integer, ByVal y As Integer, ByVal old_r As Byte, ByVal _
    old_g As Byte, ByVal old_b As Byte, ByVal new_r As _
    Byte, ByVal new_g As Byte, ByVal new_b As Byte)
    Dim pix As Integer = y * bm_bytes.RowSizeBytes + x * _
        bm_bytes.PixelSizeBytes
    Dim b As Byte = bm_bytes.ImageBytes(pix)
    Dim g As Byte = bm_bytes.ImageBytes(pix + 1)
    Dim r As Byte = bm_bytes.ImageBytes(pix + 2)

    If (r = old_r) AndAlso (g = old_g) AndAlso (b = old_b) _
        Then
        pts.Push(New Point(x, y))
        bm_bytes.ImageBytes(pix) = new_b
        bm_bytes.ImageBytes(pix + 1) = new_g
        bm_bytes.ImageBytes(pix + 2) = new_r
    End If
End Sub
 
Left-click on the example program to use the UnsafeFloodFill subroutine and right-click to use SafeFloodFill. The program displays the time each method took in the Output window. In one set of tests, SafeFloodFill took about 5 times as long as UnsafeFloodFill.

For more information about Stacks and unsafe bitmap operations, see my book Visual Basic 2005 Programmer's Reference.

 
 
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.
  Updated