Kurt
Kurt

Reputation: 1897

Canvas paint bucket/flood fill tool gets stuck in loop

I'm using the code from this tutorial. I'm also letting the user select the color using <input type="color"> (and I convert that to RGB since it returns a hex value). The fill seems to work just fine if the color is left at the default (set to black on initialization). If the color is changed using the input though, sometimes it ends up freezing. From what I can tell it gets stuck in this loop:

while (pixelStack.length) {...}

There is a section in the loop where it pushes new values to the pixelStack array:

while (y++ < height - 1 && matchStartColor(pixelPos)) {
   colorPixel(pixelPos);
   if (x > 0) {
      if (matchStartColor(pixelPos - 4)) {
         if (!reachLeft) {
            pixelStack.push([x - 1, y]);
            reachLeft = true;
         }
      } else if (reachLeft) {
            reachLeft = false;
         }
   }

   if (x < width - 1) {
      if (matchStartColor(pixelPos + 4)) {
         if (!reachRight) {
            pixelStack.push([x + 1, y]);
            reachRight = true;
         }
      } else if (reachRight) {
            reachRight = false;
        }
      }
      pixelPos += width * 4;
   }

As I understand this, the only reason it would constantly push to the pixelStack array is if the matchStartColor function never came back false.

function matchStartColor(pixelPos) {
   var r = imageData.data[pixelPos];
   var g = imageData.data[pixelPos + 1];
   var b = imageData.data[pixelPos + 2];

   return (r && g && b);
}

But I don't see why this sometimes works and other times not.

Basically the pixelStack array ends up growing continually so it can never exit the loop. I made a jsFiddle with a working example (though it will lock up eventually if you change the color and try to fill). It doesn't seem to fail 100% of the time when the color is changed, but change the color enough and fill and it eventually does (and some colors seem to fail more than others. The pinkish/red in the top left of the color picker window is a bad one for example).

Upvotes: 0

Views: 3796

Answers (1)

James Montagne
James Montagne

Reputation: 78660

It seems your issue is in your implementation of matchStartColor.

With the following implementation:

function matchStartColor(pixelPos) {
    var r = imageData.data[pixelPos];
    var g = imageData.data[pixelPos + 1];
    var b = imageData.data[pixelPos + 2];

    return (r && g && b);
}

This will only ever return false if either r, g or b are 0. When you use black (or any color with a 0 component), this works. If you pick a color where none of the components are 0, this function never returns false.

I think what you actually want to do here is compare the color of the image to the color that you've chosen.

Based on how you use this function, I think this should work:

function matchStartColor(pixelPos) {
    var r = imageData.data[pixelPos];
    var g = imageData.data[pixelPos + 1];
    var b = imageData.data[pixelPos + 2];

    return (r !== curColor.r || g !== curColor.g || b !== curColor.b);
} 

https://jsfiddle.net/6Uy3U/4/

Upvotes: 3

Related Questions