1

I have 2 identical canvasses on which I draw with my mouse and a 3rd one (still same width and height), on which I want to have the 'combination' of the other 2 by only showing overlapping pixels, meaning the ones with the same coordinates from the initial 2 canvasses.

Something like:

const left = document.querySelector('#canvasLeft');
const right = document.querySelector('#canvasRight');
const combined = document.querySelector('#canvasCombined');
        
const draw = () => {
   // do drawing on each canvas...
    
   // get image data from the canvasses...
   const imageDataRight = right.ctx.getImageData(0, 0, right.canvas.width, right.canvas.height);
   const imageDataLeft = left.ctx.getImageData(0, 0, left.canvas.width, left.canvas.height);
    
   // do something with the image data picking only the matching pixels from each canvas --> PLEASE HELP!       
   combined.ctx.putImageData(imageDataCombined, 0, 0);
        
}

UPDATE: See the image below as a better explanation of what I'm trying to achieve

enter image description here

Mauro74
  • 4,686
  • 15
  • 58
  • 80
  • Do you mean something like this: [Combining two or more Canvas elements with some sort of blending](https://stackoverflow.com/q/6787899)? – 001 Jan 29 '21 at 16:02
  • @JohnnyMopp not really. I can already draw on both canvasses and show both drawing in the third. What I want to show on the 3rd one is the combination of the 2 but only where the drawn pixels are in the same place. See the updated question with image – Mauro74 Jan 29 '21 at 17:07

1 Answers1

1

I believe what you want is the opposite of XOR composite mode. You should get the desired result by drawing the 2 canvases into a 3rd in "normal" composite mode. Then draw again in XOR mode.

let canvas1 = document.getElementById("canvas1");
let canvas2 = document.getElementById("canvas2");
let canvas3 = document.getElementById("canvas3");

let c1 = canvas1.getContext("2d");
let c2 = canvas2.getContext("2d");
let c3 = canvas3.getContext("2d");

// Draw into canvas1
c1.fillStyle = "#000000";
c1.beginPath();
c1.fillRect(0, 0, 60, 60);
c1.stroke();

// Draw into canvas2
c2.fillStyle = "#000000";
c2.beginPath();
c2.fillRect(40, 40, 60, 60);
c2.stroke();

// Reset composite mode to default
c3.globalCompositeOperation = 'source-over';
// Draw 1 and 2 into 3
c3.drawImage(canvas1, 0, 0);
c3.drawImage(canvas2, 0, 0);
// Turn on xor
c3.globalCompositeOperation = 'xor';
// Draw 1 and 2 into 3 again
c3.drawImage(canvas1, 0, 0);
c3.drawImage(canvas2, 0, 0);
canvas {
  border: 1px solid red;
}
<canvas id="canvas1" width="100" height="100"></canvas>
<canvas id="canvas2" width="100" height="100"></canvas>
<canvas id="canvas3" width="100" height="100"></canvas>

I tried this with a circle and it had issues with anti-aliasing on rounded lines. So here's another way using image data.

let canvas1 = document.getElementById("canvas1");
let canvas2 = document.getElementById("canvas2");
let canvas3 = document.getElementById("canvas3");

let c1 = canvas1.getContext("2d");
let c2 = canvas2.getContext("2d");
let c3 = canvas3.getContext("2d");

// Draw into canvas1
c1.beginPath();
c1.fillRect(0, 0, 60, 60);
c1.stroke();

// Draw into canvas2
c2.beginPath();
c2.arc(60, 60, 20, 0, 2 * Math.PI, false);
c2.fill();
c2.lineWidth = 1;
c2.stroke();

var data1 = c1.getImageData(0, 0, 100, 100);
var data2 = c2.getImageData(0, 0, 100, 100);
var data3 = c3.getImageData(0, 0, 100, 100);

for (let pixel = 0; pixel < data3.data.length; pixel += 4) {
    if (data1.data[pixel+3] > 0 && data2.data[pixel+3] > 0) {
    data3.data[pixel]   = 0;
    data3.data[pixel+1] = 0;
    data3.data[pixel+2] = 0;
    data3.data[pixel+3] = 255;
  }
}
c3.putImageData(data3, 0, 0);
canvas {
  border: 1px solid red;
}
<canvas id="canvas1" width="100" height="100"></canvas>
<canvas id="canvas2" width="100" height="100"></canvas>
<canvas id="canvas3" width="100" height="100"></canvas>
001
  • 13,291
  • 5
  • 35
  • 66