I'm creating a collaborative image drawing application using socket.io and canvas. By necessity, the canvas has to refresh fairly often, currently around every 50 milliseconds. I wanted to add a fill tool to the application, so I used my limited knowledge of flood-fill to create one. Because all of this data has to be transferred, I store every fill command as a simple object
{
tool: 'fill',
coordinate: {
x: 5,
y: 5
}
fillcolor: '#000'
}
Then each client's canvas runs the algorithm and fills using "getImageData" and "putImageData" for each individual pixel. Here's an (abbreviated) version of my implementation.
function floodfill (start,target_color,fill_color)
{
var pixelStack = [start]; //the stack of pixels to check
while (pixelStack.length > 0)
{
var current = pixelStack[pixelStack.length-1]; //check the last pixel of the pixelstack
pixelStack.pop(); //delete current from stack
if (isSameColor(current)) //matches our target color
{
var mydat = ctx.createImageData(1,1);
mydat.data = new Array();
mydat.data[0] = hexToRGB(fill_color).r; //red
mydat.data[1] = hexToRGB(fill_color).g; //green
mydat.data[2] = hexToRGB(fill_color).b; //blue
mydat.data[3] = 255;
ctx.putImageData(mydat,current.x,current.y);
pixelStack.push(bit(current.x+1,current.y)); //right
pixelStack.push(bit(current.x-1,current.y)); //left
pixelStack.push(bit(current.x,current.y+1)); //up
pixelStack.push(bit(current.x,current.y-1)); //down
}
}
function isSameColor (pixel)
{
var imgdat = ctx.getImageData(pixel.x,pixel.y,1,1).data;
if (imgdat[0]== hexToRGB(target_color).r && imgdat[1]== hexToRGB(target_color).g, imgdat[2]== hexToRGB(target_color).b)
{
return true;
}
else
{
return false;
}
}
function hexToRGB (hex)
{
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
rgb: parseInt(result[1], 16) + ", " + parseInt(result[2], 16) + ", " + parseInt(result[3], 16)
} : null;
}
}
Unfortunately, once the algorithm has been run canvas drawing is absurdly slow. Since I have mouse coordinate details for all the painting, I considering trying to use vectors to fill it, but my mathematical background isn't really strong enough to do it without help.
What's the slow part of my application? How can I fix it?
EDIT: As I mentioned in the comments, I've tried both using just one large putImageData (very slow), and using createImageData instead of getImageData (marginally faster).
EDIT2: Every paint brush stroke is stored as a series of x-y coordinates that are recorded when a user clicks and drags. However, they are not closed paths. Instead they are drawn as a series of lines, and when the user lifts the mouse, move-to's.
Code updated to reflect my current implementation.