0

I'm trying to write my own floodfill function in ActionScript3, because the one provided by Adobe isn't flexible enough (you can only give one target color, I would like to give a range of target colours.)

So, the terrible code I came up with so far is this:

private function beginAlgortime(xx:int,yy:int):void
{
    if (bmp.bitmapData.getPixel(xx, yy) != 0x333333) {

        bmp.bitmapData.setPixel(xx, yy, 0x333333);

        beginAlgortime(xx + 1, yy);
        beginAlgortime(xx + 1, yy+1);
        beginAlgortime(xx + 1, yy - 1);

        beginAlgortime(xx , yy+1);
        beginAlgortime(xx , yy - 1);

        beginAlgortime(xx - 1, yy);
        beginAlgortime(xx - 1, yy+1);
        beginAlgortime(xx - 1, yy-1);
    }
}

a pretty basic recursive function.. But obviously using this method, flash player craps out on me :) Anyone got a solution for this ? cheers!

Allan
  • 3,339
  • 2
  • 21
  • 24
Jaaq
  • 611
  • 1
  • 5
  • 6
  • talk about timing, I was trying the exact same thing today. Unfortunatly I couldn't shake the stackoverflow errors from too many recursive calls. – Allan Feb 23 '10 at 10:49

3 Answers3

2

there's no need to recurse to diagonals directly, because the vertical and horizontal recursion will hit those pixels anyway.

what exactly do you mean by floodfill? and what do you mean by "range of colors"?

the routine you provided will fill the whole bitmap with 0x333333. and it won't terminate, since you never check whether you leave the bitmaps bounds.

Taryn
  • 242,637
  • 56
  • 362
  • 405
back2dos
  • 15,588
  • 34
  • 50
  • 1
    yes. @Jaaq Have a look over the algorithms here http://www.cs.nps.navy.mil/people/faculty/capps/iap/class1/fill/fill.html Boundary fill will result in a stackoverflow (got about 3/4 of filling an image before it happened) – Allan Feb 23 '10 at 11:05
  • I found a nice solution for floodFilling: http://www.multimediacollege.be/2010/04/optimizing-the-floodfill-method/ – Jaaq Apr 29 '10 at 15:05
0

You could try converting it from a recursive function to an iterative function that uses an array instead of the program stack.

Here's an iterative implementation of FloodFill as described in the article Allan mentioned:

function floodFill(bmpData:BitmapData, startX:int, startY:int, fill:uint, old:uint):void
{
    var queue:Array = new Array();

    queue.push(new Point(startX, startY));

    var iterations:uint = 0;
    var bmpWidth:int = bmpData.width;
    var bmpHeight:int = bmpData.height;

    var searchBmp:BitmapData = new BitmapData(bmpData.width, bmpData.height);
    var currPoint:Point, newPoint:Point;
    while (queue.length > 0)
    {
        currPoint = queue.shift();
        ++iterations;

        if (currPoint.x < 0 || currPoint.x >= bmpWidth) continue;
        if (currPoint.y < 0 || currPoint.y >= bmpHeight) continue;

        searchBmp.setPixel(currPoint.x, currPoint.y, 0x00);

        if (bmpData.getPixel(currPoint.x, currPoint.y) == old)
        {
            bmpData.setPixel(currPoint.x, currPoint.y, fill);

            if (searchBmp.getPixel(currPoint.x + 1, currPoint.y) == 0xFFFFFF)
            {
                queue.push(new Point(currPoint.x + 1, currPoint.y));
            }
            if (searchBmp.getPixel(currPoint.x, currPoint.y + 1) == 0xFFFFFF)
            {
                queue.push(new Point(currPoint.x, currPoint.y + 1));
            }
            if (searchBmp.getPixel(currPoint.x - 1, currPoint.y) == 0xFFFFFF)
            {
                queue.push(new Point(currPoint.x - 1, currPoint.y));
            }
            if (searchBmp.getPixel(currPoint.x, currPoint.y - 1) == 0xFFFFFF)
            {
                queue.push(new Point(currPoint.x, currPoint.y - 1));
            }
        }

    }       
    trace("iterations: " + iterations);
}

Edit: After trying a couple of ways to keep track of which pixels have already been searched, I realized that using a second BitmapData instance works really well. I've updated the code accordingly.

This code fills a 2000x1400 bitmap in ~14 seconds on my computer. For a better user experience, you should break up the fill processing over multiple frames, so the user doesn't have to stare at a waiting icon.

Oh, and this still only affects one color, but it should be easy to modify it to take multiple colors, or a color threshold, or similar.

Edit to add: The Flood fill article on Wikipedia has several algorithm suggestions.

Selene
  • 1,917
  • 2
  • 13
  • 17
0

This is what I've used:


private function floodFill(bitmapData: BitmapData, x: int, y: int, backColor: uint, fillColor: uint): void
{
    var data: Vector. = bitmapData.getVector(bitmapData.rect);

    var queueX:Vector. = new Vector.();
    var queueY:Vector. = new Vector.();

    queueX.push(x);
    queueY.push(y);

    while (queueX.length > 0) {
        x = queueX.shift();
        y = queueY.shift();

        if (x = width || y = height)
            return;

        if (data[y*width + x] == backColor) {
            data[y*width + x] = fillColor;

            queueX.push(x+1);
            queueY.push(y);

            queueX.push(x-1);
            queueY.push(y);

            queueX.push(x);
            queueY.push(y+1);

            queueX.push(x);
            queueY.push(y-1);
        }
    }

    bitmapData.setVector(bitmapData.rect, data);
}

Pavel Alexeev
  • 6,026
  • 4
  • 43
  • 51