My javascript code paints the wall with multiply blending. If I use fillRect it's faster but not smooth and putImageData is opposite of it and takes approximately 10 seconds to paint the image. For example left one is made with fillRect:
Code used in the left image;
context_paintLayer.fillStyle = 'rgba(' + pixel.data[0] + ',' + pixel.data[1] + ',' + pixel.data[2] + ',' + pixel.data[3] + ')';
context_paintLayer.fillRect(x, y, 1, 1);
Code used in the right image;
context_paintLayer.putImageData(pixel, x, y);
If you want, I can post the full code here. Thanks.
function applyPaint(selectedColor)
{
var partitionImageData = context_partitionLayer.getImageData(0, 0, width, height);
var imageData = context_imageLayer.getImageData(0, 0, width, height);
var selectedColorRGB = selectedColor.split("-");
for(var y = targetStartY; y < targetEndY; y++)
{
for(var x = targetStartX; x < targetEndX; x++)
{
var index = ((width * y) + x) * 4;
var r = partitionImageData.data[index];
var g = partitionImageData.data[index + 1];
var b = partitionImageData.data[index + 2];
var a = partitionImageData.data[index + 3];
pixel.data[0] = (selectedColorRGB[0] * (imageData.data[index]+50)) / 255;
pixel.data[1] = (selectedColorRGB[1] * (imageData.data[index + 1]+50)) / 255;
pixel.data[2] = (selectedColorRGB[2] * (imageData.data[index + 2]+50)) / 255;
pixel.data[3] = (a * (imageData.data[index + 3])) / 255;
if(isInPart(r, g, b) == 1)
{
paint(x, y);
}
}
}
}
function paint(x, y)
{
context_paintLayer.fillStyle = 'rgba(' + pixel.data[0] + ',' + pixel.data[1] + ',' + pixel.data[2] + ',' + pixel.data[3] + ')';
context_paintLayer.fillRect(x, y, 1, 1);
//context_paintLayer.putImageData(pixel, x, y);
}
function isInPart(r, g, b, targetPart) {
if ((r >= targetRMin && r <= targetRMax) && (g >= targetGMin && g <= targetGMax) && (b >= targetBMin && b <= targetBMax)) {
return 1;
}
else {
return 0;
}
}
I suggest you 'paint' directly into an imageData during your loop, then do one single puImageData at the end.
And Since your canvas sizes are not changing, you might cache the arrays to avoid the costs of creation / disposal.
The code below should be waay faster.
I don't know enough of your requirements to know, for instance, if the image is changing and thus need to be getImageDataed (:-)) on each call, but you'll get the idea :
function applyPaint() {
var partitionImageData = context_partitionLayer.getImageData(0, 0, width, height);
var partitionData = partitionImageData.data;
var sourceImageData = context_imageLayer.getImageData(0, 0, width, height);
var sourceData = sourceImageData.data;
var destImageData = context_paintLayer.getImageData(0, 0, width, height);
var destData = destImageData.data;
applyPaint = function (selectedColor) {
var selectedColorRGB = selectedColor.split("-");
var index = 0;
var selectedR = selectedColorRGB[0];
var selectedG = selectedColorRGB[1];
var selectedB = selectedColorRGB[2];
// cache arrays for speed up
var _partitionData = partitionData;
var _sourceData = sourceData ;
var _destData = destData;
for (var y = targetStartY; y < targetEndY; y++) {
for (var x = targetStartX; x < targetEndX; x++) {
index = ((width * y) + x) << 2;
var r = _partitionData[index];
var g = _partitionData[index + 1];
var b = _partitionData[index + 2];
if (isInPart(r, g, b) == 1) {
var a = _partitionData[index + 3];
_destData[index] = (selectedR * (_sourceData[index] + 50)) >> 8;
_destData[index + 1] = (selectedG * (_sourceData[index + 1] + 50)) >> 8;
_destData[index + 2] = (selectedB * (_sourceData[index + 2] + 50)) >> 8;
_destData[index + 3] = (a * (_sourceData[index + 3])) >> 8;
}
}
}
context_paintLayer.putImageData(destImageData, 0, 0);
}
return applyPaint.apply(this, arguments);
}
Edit : if you plan on updating the whole image each time, you can simplify the two for loops to :
var pixelCount = width*height;
var index = 0;
while (pixelCount--) {
// process index
// ...
index+=4;
}
Edit 2 :
Here is a way to get, for a given image, real-time recoloring (meaning image rate is > 60 fps). The idea is to have the gpu compute for you by using composite modes / blending modes.
http://dev.w3.org/fxtf/compositing-1/
Step 1) Re-process your partition image to get a partition mask canvas, that will contains full white for pixels matching your criteria, and full black for pixels not matching.
It would be built like
• build new canvas to store the partition mask.
• for each pixel of the partition image :
if (condition on the partition data r,g,b )
partitionMaskImageData[ ...r and g and b and a ] = 255
else
partitionMaskImageData[ ...r and g and b and a ] = 0
Now, build a static temporary canvas for your operations, and each time you want to change color :
1) clear the temp canvas.
2) draw the partition mask on the temp canvas.
3) draw the image on the temp canvas using mode 'destination-in'.
4) add 50 to all rgb : draw a rect of color 'rgb(50,50,50) using mode 'lighter'.
5) fill a rect with your selected color using mode 'multiply'
--> now the temp canvas is ready.
6) draw the original image on destination canvas
7) draw the temp canvas on the destination canvas.
Little trouble here is that not all Browser support 'multiply', but quite some do as you can see here http://caniuse.com/#feat=canvas-blending.
Still i bet that the update will take far less than 1ms (on desktop).