LianaEnt Posted December 22, 2005 Posted December 22, 2005 Hey all you GDI+ gurus, I'm 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, it's 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 don't think RegionData works for this. Any help (in VB.Net) would be greatly appreciated! Larry Quote
LianaEnt Posted December 22, 2005 Author Posted December 22, 2005 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. It's actually fairly simple, and here's 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 Quote
LianaEnt Posted December 22, 2005 Author Posted December 22, 2005 Here's a fuller version of the 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 I'm 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). I'd like to fill each enclosed section of the path and create a region based on it. I'm 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 it's 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, they're 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 there's unnecessary data points, is because I don't think you need all the interior datapoints to define a region, or we'd be seeing stack overflows all over the place. Since I can't 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 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.