洪水填充alghoritm导致StackOverflow

时间:2018-01-05 18:18:57

标签: c# wpf flood-fill

所以我有WPF c#app有画布要绘制(绘图正在改变矩形的填充,例如有128x128矩形)泛洪填充alghoritm,如果有大量的矩形而不是某些值,它会导致StackOverflow(例如.128x128)。我想升级此脚本以适用于任何大小的绘图区域。我读了一些像这样的问题,但我仍然不知道如何修复它,我需要帮助这个特定的脚本因为这里的绘图与普通绘图有点不同(这是Pixel Art的创建者)。所以这是我的alghoritm。

public static void FloodFIll(int x, int y, Brush color, Brush colorToReplace)
        {
            if (selectedTool == AvailableTools.FillBucket)
            {
                if (x < 1 || x > PixiManager.drawAreaSize) return;
                if (y < 1 || y > PixiManager.drawAreaSize) return;

                if (PixiManager.FieldCords(x, y).Fill != color)
                {
                    if (PixiManager.FieldCords(x, y).Fill == colorToReplace)
                    {
                        PixiManager.FieldCords(x, y).Fill = color;
                        FloodFIll(x, y - 1, color, colorToReplace);
                        FloodFIll(x + 1, y, color, colorToReplace);
                        FloodFIll(x, y + 1, color, colorToReplace);
                        FloodFIll(x - 1, y, color, colorToReplace);
                    }
                }
            }
        }

2 个答案:

答案 0 :(得分:3)

这是你算法的非递归版本。

public static void FloodFIll(int x, int y, Brush color, Brush colorToReplace)
{   
    if (selectedTool != AvailableTools.FillBucket)
    {
        return;
    }

    var stack = new Stack<Tuple<int, int>>();
    stack.Push(Tuple.Create(x, y));

    while(stack.Count > 0) 
    {
        var point = stack.Pop();
        if (point.Item1 < 1 || point.Item1 > PixiManager.drawAreaSize) continue;
        if (point.Item2 < 1 || point.Item2 > PixiManager.drawAreaSize) continue;
        if (PixiManager.FieldCords(point.Item1, point.Item2).Fill == color) continue;

        if (PixiManager.FieldCords(point.Item1, point.Item2).Fill == colorToReplace)
        {
            PixiManager.FieldCords(point.Item1, point.Item2).Fill = color;
            stack.Push(Tuple.Create(point.Item1, point.Item2 - 1));
            stack.Push(Tuple.Create(point.Item1 + 1, point.Item2));
            stack.Push(Tuple.Create(point.Item1, point.Item2 + 1));
            stack.Push(Tuple.Create(point.Item1 - 1, point.Item2));
        }
    }
}

虽然您可以创建自己的类而不是使用Tuple

答案 1 :(得分:2)

我建议不要从每个像素分支,而是在分支之前一次填充整个扫描线。使用这样的逻辑:

  1. 反复向左移动1个像素,直到找到不同颜色的像素。停止其颜色与原始像素颜色匹配的最后一个像素。
  2. 使用新颜色填充此像素。
  3. 如果上面的像素与此像素的颜色相同,并且尚未设置标志A,则在上述坐标中将步骤1中的相同工作排入队列,并设置本地标志A.
  4. 如果下面的像素与此像素的颜色相同,并且尚未设置标志B,则在下面的坐标中将相同的工作排入队列,并设置本地标志B.
  5. 向右移动。
  6. 如果此像素的颜色不同,请在此处结束此功能。
  7. 如果上面的像素颜色不同,请清除标记A.
  8. 如果下面的像素颜色不同,请清除标记B.
  9. 从第2步开始重复。