2012-01-27 73 views
11

这里就是我想要做的事:画布:掩盖图像并保留其Alpha通道?

  1. 获取图像A,和B图像的图像B是黑色和白色口罩的形象。
  2. 将图像A的alpha通道替换为图像B的红色通道。
  3. 在画布上绘制图像C.
  4. 图像的顶部C.

一切似乎确定,直到第4步图像C绘制图像A是不是在所有地方和图像A应该是透明的有白色可见。

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); 

回答

16

西蒙是正确的:putImageData方法不关注任何合成;它只是复制像素值。为了获得合成,我们需要使用绘图操作。

我们需要使用像素数据来处理通道(将红色变为alpha),将更改过的像素数据放入图像中,然后使用复合操作获得所需的遮罩。

Kinda lame

//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

与例如涂鸦:

Kinda lame about the same

+0

谢谢,阅读我对Simon的回答的评论:)问题解决了... – 2012-01-27 19:16:56

2

因为在步骤4中,您使用的是putImageData,它完全取代了像素。你想在图像C的顶部绘制图像A,所以你不能这样做。相反,你将要使用drawImage()

所以做:

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

哇,我不知道,你可以传递一个画布对象作为参数drawImage()。这是有效的,但由于某些原因,drawImage()比putImageData()慢得多,并且因为我需要每隔20-40ms绘制图像,所以它不是一个完美的解决方案。我做了一些实验,并用两个画布解决了它。 PutImageData()完全替换了像素值,但是当我将另一个画布放在第一个画布的下面时,我可以看到它是透明的。无论如何,谢谢你的时间,欣赏它! – 2012-01-27 19:16:27

+2

这是奇怪的。使用'imageData'几乎总是比使用'drawImage'慢得多。在这里可能还有别的东西 - 我可以看看你的完整代码吗? – 2012-01-27 21:33:57

+0

我给你发了一封邮件。 – 2012-01-28 10:20:53