HTML, Javascript Canvas image and make transparent with alpha mask image

Issue

I am new with canvas in html, I have two images first one is a normal jpeg image and the second is the same edited with alpha (black and white) so the thing is I want to make the first transparent applying the alpha mask from the second image.

Image https://i.stack.imgur.com/fj5mB.jpg

Alpha mask https://i.stack.imgur.com/5lDgg.png

Here the code that I found and tried to modify but doesn’t work.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var image = new Image();
image.onload = function() {
    ctx.drawImage(image, 0, 0);
};
image.src = 'image.jpg';

var maskCanvas = document.createElement('canvas');
maskCanvas.width = canvas.width;
maskCanvas.height = canvas.height;
var maskCtx = maskCanvas.getContext('2d');

var mask = new Image();
mask.onload = function() {
    maskCtx.drawImage(mask, 0, 0);
};
mask.src = 'mask.png';

var idata = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0, len = data32.length;

while(i < len) {
    data32[i] = data32[i++] << 8;
}
ctx.putImageData(idata, 0, 0);

Solution

The first part of your problem is that image loading is not a synchronous process. That means if the javascript interpreter reaches this part of your code:

ctx.putImageData(idata, 0, 0);

the two images required might not have loaded yet thus the two canvases are just empty.
You can simply verify this by inserting some console.log() statements into the onload functions and just before the last line of your code.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var image = new Image();
image.onload = function() {
  ctx.drawImage(image, 0, 0);
  console.log("image loaded");
};
image.src = 'https://i.stack.imgur.com/fj5mB.jpg';

var maskCanvas = document.createElement('canvas');
maskCanvas.width = canvas.width;
maskCanvas.height = canvas.height;
var maskCtx = maskCanvas.getContext('2d');

var mask = new Image();
mask.onload = function() {
  maskCtx.drawImage(mask, 0, 0);
  console.log("mask loaded");
};
mask.src = 'https://i.stack.imgur.com/5lDgg.png';

var idata = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0,
  len = data32.length;

while (i < len) {
  data32[i] = data32[i++] << 8;
}
console.log("drawing");
ctx.putImageData(idata, 0, 0);
<canvas id="canvas" width="400" height="400"></canvas>

This will log

drawing
image loaded
mask loaded

to the console and clearly reveal that it’s drawing before there is anything to draw.
One possible remedy is using a global counter which increments with each image loaded. As soon as it’s 2 call the drawing operation.

The second part of your problem is that your code simply is not complete. It’s just the first step in a two step process – turning the mask image into an actual alpha mask as it’s just a black & white image with fully opaque alpha channel data. The second step involves using the mask to hide parts of the original image. This can be done by changing the globalCompositeOperation of the context to destination-in and just drawing the mask onto the image’s canvas.

Here’s the complete example:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var image = new Image();
image.crossOrigin = "Anonymous";

var imagesLoaded = 0;

image.onload = function() {
  imagesLoaded++;
  ctx.drawImage(image, 0, 0);
  if (imagesLoaded == 2) {
    process();
  }
};
image.src = 'https://api.codetabs.com/v1/proxy?quest=https://i.stack.imgur.com/fj5mB.jpg';

var maskCanvas = document.createElement('canvas');
maskCanvas.width = canvas.width;
maskCanvas.height = canvas.height;
var maskCtx = maskCanvas.getContext('2d');

var mask = new Image();
mask.crossOrigin = "Anonymous";
mask.onload = function() {
  imagesLoaded++;
  maskCtx.drawImage(mask, 0, 0);
  if (imagesLoaded == 2) {
    process();
  }
};
mask.src = 'https://api.codetabs.com/v1/proxy?quest=https://i.stack.imgur.com/5lDgg.png';

function process() {
  var idata = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height);
  var data32 = new Uint32Array(idata.data.buffer);
  var i = 0,
    len = data32.length;

  while (i < len) {

    data32[i] = data32[i++] << 8;
  }
  maskCtx.putImageData(idata, 0, 0);
  ctx.globalCompositeOperation = "destination-in";
  ctx.drawImage(maskCanvas, 0, 0);
}
<canvas id="canvas" width="400" height="400"></canvas>

Answered By – obscure

Answer Checked By – Mary Flores (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.