Reputation: 5129
I want to do a selector tool in HTML5. As the user click and moves the mouse, a rectangle should be drawn from the starting point to the mouse pointer. This is usually done choosing a logical operator for the color of the stroke (so the lines are drawn as the inverse of the background), but it looks like I can only do this in HTML5 manipulating the image pixel by pixel. Wouldn't it be too slow? Is there a direct way to do that?
Upvotes: 0
Views: 1107
Reputation:
You can do this two ways:
For option 1 you would need to iterate the area each time using JavaScript which will be a relative slow operation compared to option 2.
Example of option 1:
// inside the mouse move code (please see live demo below)
ctx.drawImage(img, 0, 0); // update image (clear prev inv)
//invert the region
if (w !== 0 && h !== 0) {
var idata = ctx.getImageData(x1, y1, w, h), // get buffer
buffer = idata.data,
len = buffer.length,
i = 0;
for(; i < len; i += 4) { // inverse it
buffer[i] = 255 - buffer[i];
buffer[i+1] = 255 - buffer[i+1];
buffer[i+2] = 255 - buffer[i+2];
}
ctx.putImageData(idata, x1, y1);
}
As you can see the performance is not bad at all using just JavaScript and the CPU. The performance threshold is dependent on size of region though.
In option 2 you pre-invert the image in the second canvas and draw a clipped version of it to main canvas. This means from that point on you will get the benefit from using the GPU (if available) and performance will be much better.
Example of option 2:
// have a second canvas setup
var ocanvas = document.createElement('canvas'),
octx = ocanvas.getContext('2d'),
//invert the off-screen canvas
var idata = octx.getImageData(0, 0, ocanvas.width, ocanvas.height),
buffer = idata.data,
len = buffer.length,
i = 0;
for(; i < len; i += 4) {
buffer[i] = 255 - buffer[i];
buffer[i+1] = 255 - buffer[i+1];
buffer[i+2] = 255 - buffer[i+2];
}
octx.putImageData(idata, 0, 0);
Now it's a matter of getting the region of that area and update with a clipped version:
if (w !== 0 && h !== 0)
ctx.drawImage(ocanvas, x1, y1, w, h, x1, y1, w, h);
Upvotes: 1
Reputation: 105015
You can optimize your "inverted stroke" to be fast enough for "live" strokes.
Layer 2 canvases, one on top of another.
Draw the inverted image on the bottom canvas.
Draw the normal image on the top canvas.
Draw your strokes on the top canvas with context.globalCompositeOperation="destination-out".
"destination-out" compositing causes the top canvas pixels to be made transparent where the stroking occurs.
This "reveals" the inverted image below only where the strokes are.
This reveal is very fast since the GPU assists in compositing.
Upvotes: 1