DPrometheus
Regular
Hey there,
Lately I'm working on some paint program in vb.net but the floodfill algorithm (such as the bucket tool in MS paint) gives me some problems.
I do the tool as follows..
I create a new stack object, and from the point where the user clicks, it goes all the way to the top of the image, and checks colors accordingly. So it finds the boundary with another color or the boundary with the current selection. The it goes down in a while loop and checks if the right / left neighbours needs a repaint as well. If they do, we put them on the stack. The algorithm works nicely, but if I open up taskmanager the memory allocated by this component grows dramatically (sometimes over 1gb, starting from 30 mb) If I fire up CLR profiler it gives me more information and shows that the stack count with the points exceeds 4 million items.
The stack.Pop() function should free memory for the top most item when removing it and the stack itself has 0 items when the function finishes I really have no clue what I'm doing wrong? has someone got any idea's?
Also both the stack object and Drawing.Point don't implement IDisposable, so there is no need to call Dispose() explicitly, right? (Even if I would there ain't a member function.. )
thanks in advance,
M. Siroen
This is what I've got so far:
ArgbColor = custom color class with alpha, red, green and blue properties
MyImage = Drawing.Image object
MyImage.InSelection returns true if the given point is within the current selection, false otherwise
EDIT:
As I figured out today the stack grows just too much in size.
In this part:
I had to check not only the opposite direction (which would be sufficient if no selections were made) but also check the current direction and push those values onto the stack. i.e. If the stack was 'moving' left I had to check the values left of the current column as well, otherwise I'm left behind with gaps in the image.
If I remove this extra line however the memory leak seems to be gone (the program keeps around 31 MB). Still does someone has another workaround for those gaps? With the selections I can add or remove shapes to the current selection. Combinations of several primary shapes will make the fill tool more error-prone in words of leaving gaps.
Suggestions?
Lately I'm working on some paint program in vb.net but the floodfill algorithm (such as the bucket tool in MS paint) gives me some problems.
I do the tool as follows..
I create a new stack object, and from the point where the user clicks, it goes all the way to the top of the image, and checks colors accordingly. So it finds the boundary with another color or the boundary with the current selection. The it goes down in a while loop and checks if the right / left neighbours needs a repaint as well. If they do, we put them on the stack. The algorithm works nicely, but if I open up taskmanager the memory allocated by this component grows dramatically (sometimes over 1gb, starting from 30 mb) If I fire up CLR profiler it gives me more information and shows that the stack count with the points exceeds 4 million items.
The stack.Pop() function should free memory for the top most item when removing it and the stack itself has 0 items when the function finishes I really have no clue what I'm doing wrong? has someone got any idea's?
Also both the stack object and Drawing.Point don't implement IDisposable, so there is no need to call Dispose() explicitly, right? (Even if I would there ain't a member function.. )
thanks in advance,
M. Siroen
This is what I've got so far:
ArgbColor = custom color class with alpha, red, green and blue properties
MyImage = Drawing.Image object
MyImage.InSelection returns true if the given point is within the current selection, false otherwise
Code:
''' <summary>
''' Flood Fill function with tolerance
''' </summary>
''' <param name="point">start point</param>
''' <param name="newVal">new color</param>
''' <param name="Tolerance">Tolerance</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function InternalFill(ByVal point As Point, ByVal newVal As Color, ByVal Tolerance As Color) As Object
' Check if the given point is within the selection
If Not MyImage.InSelection(point) Then Return Nothing
' Original color value
Dim oldval As Color
Dim firstRun As Boolean = False
' Switch cursor type to indicate we are busy
Cursor.Current = Cursors.WaitCursor
' Determine the color underneath the mouse
oldval = MyImage.GetPixel(point)
' If the new color and the clicked pixel are the same return
If (ArgbColor.Compare(oldval, newVal, Tolerance)) Then Return Nothing
' First determine if a bitmap is loaded
If Not (Drawing.Bitmap.Equals(MyImage, Nothing)) Then
' Local variables
Dim c As Color
Dim st As Stack = New Stack()
Dim y1 As Int32
' Flags to keep track which spans are tested
Dim sLeft As Boolean
Dim sRight As Boolean
' Clear the stack
st.Clear()
' Push cursor position onto stack
st.Push(point)
' While there are items on the stack
While (st.Count() > 0)
' Pop the last position
point = CType(st.Pop(), Point)
' Retrieve the color for that position
c = MyImage.GetPixel(point)
' Copy over y position to local variable
y1 = point.Y
' Scan length of the line
While (y1 >= 0)
c = MyImage.GetPixel(New Point(point.X, y1))
If (ArgbColor.Compare(c, oldval, Tolerance) And MyImage.InSelection(New Point(point.X, y1))) Then
y1 -= 1
Else
Exit While
End If
End While
' Scan code goes 1 to far, so correct this
y1 += 1
' Get the pixel at the new position
c = MyImage.GetPixel(New Point(point.X, y1))
' Reset flags
sLeft = False
sRight = False
' Check if new position is within the image and the replacement color matches the color on this span
While (y1 < MyImage.Height And ArgbColor.Compare(c, oldval, Tolerance))
' Set the pixel
If (MyImage.InSelection(New Point(point.X, y1))) Then
MyImage.SetPixel(New Point(point.X, y1), newVal)
' Application.DoEvents()
End If
' Test left neighbour and if color needs to be replaced for this span, add it to the stack
If (MyImage.InSelection(New Point(point.X - 1, y1))) Then
If Not sLeft And point.X > 0 And ArgbColor.Compare(MyImage.GetPixel(New Point(point.X - 1, y1)), oldval, Tolerance) Then
st.Push(New Point(point.X - 1, y1))
sLeft = True
Else
If sLeft And point.X > 0 Then
If ArgbColor.Compare(MyImage.GetPixel(New Point(point.X - 1, y1)), oldval, Tolerance) Then
st.Push(New Point(point.X - 1, y1))
Else
If Not (ArgbColor.Compare(MyImage.GetPixel(New Point(point.X - 1, y1)), oldval, Tolerance)) Then
sLeft = False
End If
End If
End If
End If
End If
' Test right neighbour
If (MyImage.InSelection(New Point(point.X + 1, y1))) Then
If Not sRight And point.X < MyImage.Width - 1 And ArgbColor.Compare(MyImage.GetPixel(New Point(point.X + 1, y1)), oldval, Tolerance) Then
st.Push(New Point(point.X + 1, y1))
sRight = True
Else
If sRight And point.X < MyImage.Width - 1 Then
If ArgbColor.Compare(MyImage.GetPixel(New Point(point.X + 1, y1)), oldval, Tolerance) Then
st.Push(New Point(point.X + 1, y1))
Else
If Not ArgbColor.Compare(MyImage.GetPixel(New Point(point.X + 1, y1)), oldval, Tolerance) Then
sRight = False
End If
End If
End If
End If
End If
' Go to next line
If MyImage.InSelection(New Point(point.X, y1 + 1)) Then
y1 += 1
End If
' Retrieve new color
c = MyImage.GetPixel(New Point(point.X, y1))
If st.Count > 1500000 And firstRun = False Then
Dim thr As New Threading.Thread(AddressOf threadMessage)
thr.Start()
firstRun = True
End If
End While
End While
st.Clear()
' Reset cursor type
Cursor.Current = Cursors.Default
End If
Return True
End Function
EDIT:
As I figured out today the stack grows just too much in size.
In this part:
Code:
If sLeft And point.X > 0 Then
If ArgbColor.Compare(MyImage.GetPixel(New Point(point.X - 1, y1)), oldval, Tolerance) Then
st.Push(New Point(point.X - 1, y1))
Else
If Not (ArgbColor.Compare(MyImage.GetPixel(New Point(point.X - 1, y1)), oldval, Tolerance)) Then
sLeft = False
End If
End If
End If
I had to check not only the opposite direction (which would be sufficient if no selections were made) but also check the current direction and push those values onto the stack. i.e. If the stack was 'moving' left I had to check the values left of the current column as well, otherwise I'm left behind with gaps in the image.
If I remove this extra line however the memory leak seems to be gone (the program keeps around 31 MB). Still does someone has another workaround for those gaps? With the selections I can add or remove shapes to the current selection. Combinations of several primary shapes will make the fill tool more error-prone in words of leaving gaps.
Suggestions?
Last edited: