Define a region from a floodfill

LianaEnt

Member
Joined
Sep 15, 2003
Messages
12
Hey all you GDI+ gurus,

Im trying to define a region based on the results of a floodfill. The user draws freeform on a picturebox, then clicks in any enclosed area. I can fill the enclosed area rapidly just fine. However, I now want to define a region based on what was filled.

One idea was to collect the points during the floodfill that are on the original path. While this is slow, its even slower trying to rearrange the resulting collection of points into a new path (the collection is in a pseudo-random order). Another idea is to collect the points in the path that enclose the selected point, define that as a region, and just do a FillRegion. However, sometimes the region is not bordered by a path but by another region of another color.

Is it possible to collect all points that are filled, then define a region that encloses all those points? I dont think RegionData works for this.

Any help (in VB.Net) would be greatly appreciated!
Larry
 
Optimizing the region?

I found a way to rapidly create a region from the pixels that are filled by creating a rectangle for each pixel, and then unioning that with a region. Its actually fairly simple, and heres the heart of the code:

points = New Stack
Dim rc As Rectangle
Dim reg As New Region
reg.MakeEmpty()

points.Push(New Point(X, Y))

Do
Dim p As Point = New Point
p = points.Pop

If bmp.GetPixel(p.X, p.Y).ToArgb <> fillColor.ToArgb Then
bmp.SetPixel(p.X, p.Y, fillColor)
rc = New Rectangle(p.X, p.Y, 1, 1)
reg.Union(rc)

If (CanUp(p.X, p.Y)) Then
points.Push(New Point(p.X, p.Y - 1))
End If
If (CanRight(p.X, p.Y)) Then
points.Push(New Point(p.X + 1, p.Y))
End If
If (CanDown(p.X, p.Y)) Then
points.Push(New Point(p.X, p.Y + 1))
End If
If (CanLeft(p.X, p.Y)) Then
points.Push(New Point(p.X - 1, p.Y))
End If
End If

Loop While (points.Count > 0)


However, if the region is larger than about 25,000 pixels, I get a stack overflow when I try to do anything with it, like FillRegion(Brushes.Blue, reg).

Is there some way to optimize the region so only the necessary datapoints are left in it?

Larry
 
Heres a fuller version of the code:

Code:
    Private bmp As Bitmap
    Private clrFill As Color
    Private clrOrig As Color
    Private reg As Region

    Private Sub panel1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles panel1.MouseDown
        bmp = panel1.Image
        FloodFill(e.X, e.Y, clrFill)
        panel1.Invalidate()
    End Sub

    Private Function FloodFill(ByVal X As Integer, ByVal Y As Integer, ByVal fillColor As Color) As Boolean
        If ((X < 0) Or (X >= bmp.Width) Or (Y < 0) Or (Y >= bmp.Height)) Then
            Return False
        End If

        clrOrig = bmp.GetPixel(X, Y)
        If clrOrig.ToArgb = fillColor.ToArgb Then
            Return False
        End If

        points = New Stack
        Dim rc As Rectangle
        reg = New Region
        reg.MakeEmpty()

        points.Push(New Point(X, Y))

        Do
            Dim p As Point = New Point
            p = points.Pop

            If bmp.GetPixel(p.X, p.Y).ToArgb <> fillColor.ToArgb Then
                bmp.SetPixel(p.X, p.Y, fillColor)

                rc = New Rectangle(p.X, p.Y, 1, 1)
                reg.Union(rc)

                If (CanUp(p.X, p.Y)) Then
                    points.Push(New Point(p.X, p.Y - 1))
                End If
                If (CanRight(p.X, p.Y)) Then
                    points.Push(New Point(p.X + 1, p.Y))
                End If
                If (CanDown(p.X, p.Y)) Then
                    points.Push(New Point(p.X, p.Y + 1))
                End If
                If (CanLeft(p.X, p.Y)) Then
                    points.Push(New Point(p.X - 1, p.Y))
                End If
            End If
        Loop While (points.Count > 0)

        Return True
    End Function

    Private Function CanUp(ByVal X As Integer, ByVal Y As Integer) As Boolean
        Return ((Y > 0) AndAlso bmp.GetPixel(X, Y - 1).ToArgb = clrOrig.ToArgb)
    End Function

    Private Function CanRight(ByVal X As Integer, ByVal Y As Integer) As Boolean
        Return ((X < bmp.Width - 1) AndAlso bmp.GetPixel(X + 1, Y).ToArgb = clrOrig.ToArgb)
    End Function

    Private Function CanDown(ByVal X As Integer, ByVal Y As Integer) As Boolean
        Return ((Y < bmp.Height - 1) AndAlso bmp.GetPixel(X, Y + 1).ToArgb = clrOrig.ToArgb)
    End Function

    Private Function CanLeft(ByVal X As Integer, ByVal Y As Integer) As Boolean
        Return ((X > 0) AndAlso bmp.GetPixel(X - 1, Y).ToArgb = clrOrig.ToArgb)
    End Function

Before this section of code Im defining the bitmap from a picturebox, and drawing a freeform path on the picture box that can have many intersections, thus many small potential regions (think of a figure 8 - it has two regions). Id like to fill each enclosed section of the path and create a region based on it. Im also selecting the fill color - clrFill before this section of code.

When I click on a point inside an enclosed loop, the bitmap bmp takes a snapshot of the picturebox, and calls the FloodFill routine. The FloodFill pushes the first clicked point on the stack, then starts the loop by popping that point and checking if its already been changed. If not, then it changes it to the fill color and checks the points above, to the right, below, and to the left to see if any need to be changed. If they do, theyre pushed on the stack. The routine continues popping the points off the stack, checking, pushing new ones on the stack, until all points have been popped off the stack.

Whenever a new point is changed, I create a rectangle 1 pixel in size and union it with the growing region reg.

This is all fine and works fast. The stack overflow is not occuring with the points stack, but with the region. After the FloodFill routine finishes, panel1 is invalidated, calling the Paint routine, which tries to paint the region yellow.

e.Graphics.FillRegion(Brushes.Yellow, reg)

If the region has too many datapoints, this (or any other operation with reg such as reg.GetRegionData) causes the stack overflow.

The reason I say that theres unnecessary data points, is because I dont think you need all the interior datapoints to define a region, or wed be seeing stack overflows all over the place. Since I cant do anything with the region after the FloodFill is finished, I have to find some way to limit the datapoints while the region is being built. OR, I have to find an entirely different way of doing this.

Thanks (and Merry Christmas!),
Larry
 
Back
Top