最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Canvas: mask an image and preserve its alpha channel? - Stack Overflow

programmeradmin1浏览0评论

Here's what I'm trying to do:

  1. Get image A, and image B. Image B is a black and white mask image.
  2. Replace image A's alpha channel with image B's red channel.
  3. Draw image C on the canvas.
  4. Draw image A on top of image C.

Everything seems ok until step 4. Image C isn't visible at all and where image A should be transparent there's white color.

cx.putImageData(imageA, 0, 0);
var resultData = cx.getImageData(0, 0, view.width, view.height);

for (var h=0; h<resultData.data.length; h+=4) {
    resultData.data[h+3] = imageB.data[h];
}

cx.putImageData(imageC, 0, 0);
cx.putImageData(resultData, 0, 0);

Here's what I'm trying to do:

  1. Get image A, and image B. Image B is a black and white mask image.
  2. Replace image A's alpha channel with image B's red channel.
  3. Draw image C on the canvas.
  4. Draw image A on top of image C.

Everything seems ok until step 4. Image C isn't visible at all and where image A should be transparent there's white color.

cx.putImageData(imageA, 0, 0);
var resultData = cx.getImageData(0, 0, view.width, view.height);

for (var h=0; h<resultData.data.length; h+=4) {
    resultData.data[h+3] = imageB.data[h];
}

cx.putImageData(imageC, 0, 0);
cx.putImageData(resultData, 0, 0);
Share Improve this question edited Jan 27, 2012 at 19:03 ellisbben 6,37128 silver badges43 bronze badges asked Jan 27, 2012 at 10:40 Nikolay DyankovNikolay Dyankov 7,22411 gold badges65 silver badges89 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 19

Simon is right: the putImageData method does not pay any attention to compositing; it merely copies pixel values. In order to get compositing, we need to use drawing operations.

We need to mess with the channels (turn red into alpha) with the pixel data, put that changed pixel data into an image, and then use a composite operation to get the desired masking.

//copy from one channel to another
var assignChannel = function(imageData, channelTo, channelFrom) {
  if(channelTo < 0 || channelTo > 3 || channelFrom < 0 || channelFrom > 3) {
    throw new Error("bad channel number");
  }
  if(channelTo == channelFrom)
    return;
  var px = imageData.data;
  for(var i = 0; i < px.length; i += 4) {
    px[i + channelTo] = px[i + channelFrom];
  }
};
/**============================================================================ 
  * this function uses 3 or 4 canvases for clarity / pedagogical reasons:
  * redCanvas has our mask image;
  * maskCanvas will be used to store the alpha channel conversion of redCanvas' image;
  * imageCanvas contains the image to be masked;
  * ctx is the context of the canvas to which the masked image will be drawn.
============================================================================**/
var drawOnTopOfRed = function(redCanvas, maskCanvas, imageCanvas, ctx) {
  var redImageData = redCanvas.getContext("2d").getImageData(0, 0, w, h);

  //assign the alpha channel
  assignChannel(redImageData, 3, 0);

  //write the mask image
  maskCanvas.getContext("2d").putImageData(redImageData, 0, 0);

  ctx.save();

  //draw the mask
  ctx.globalCompositeOperation = "copy";
  ctx.drawImage(maskCanvas, 0, 0);

  //draw the image to be masked, but only where both it
  //and the mask are opaque; see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing for details.
  ctx.globalCompositeOperation = "source-in";
  ctx.drawImage(imageCanvas, 0, 0);
  ctx.restore();
};

jsfiddle example

A doodle with the example:

Because in step 4 you are using putImageData which perfectly replaces pixels. You want to draw image A on top of image C, so you can't do this. Instead you will want to use drawImage()

So do:

cx.putImageData(imageC, 0, 0); // step 3
// create a new canvas and new context,
// call that new context ctx2 and canvas can2:
var can2 = document.createElement('canvas');
// set can2's width and height, get the context etc...
ctx2.putImageData(resultData, 0, 0);
cx.drawImage(can2, 0, 0); // step 4 using drawImage instead of putting image data
发布评论

评论列表(0)

  1. 暂无评论