【问题标题】:Canvas: mask an image and preserve its alpha channel?画布:屏蔽图像并保留其 Alpha 通道?
【发布时间】:2012-02-20 08:51:03
【问题描述】:

这是我想要做的:

  1. 获取图像 A 和图像 B。图像 B 是黑白蒙版图像。
  2. 将图像 A 的 alpha 通道替换为图像 B 的红色通道。
  3. 在画布上绘制图像 C。
  4. 在图像 C 上绘制图像 A。

在第 4 步之前一切正常。图像 C 根本不可见,图像 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);

【问题讨论】:

    标签: javascript html canvas html5-canvas


    【解决方案1】:

    Simon 是对的:putImageData 方法不关注合成;它只是复制像素值。为了进行合成,我们需要使用绘图操作。

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

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

    带有示例的涂鸦:

    【讨论】:

      【解决方案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
      

      【讨论】:

      • 哇,我不知道您可以将画布对象作为参数传递给 drawImage()。这可行,但由于某种原因 drawImage() 比 putImageData() 慢得多,而且由于我需要每 20-40 毫秒绘制一次图像,所以这不是一个完美的解决方案。我做了一些实验,我用两张画布解决了这个问题。 PutImageData() 完全替换了像素值,但是当我将另一个画布放在第一个画布下方时,我可以看到它是透明的。无论如何,感谢您的宝贵时间,感激不尽!
      • 这很奇怪。使用imageData 几乎总是比使用drawImage 慢得多。这里可能还有其他东西在起作用 - 我可以看看你的完整代码吗?
      猜你喜欢
      • 2015-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-11
      相关资源
      最近更新 更多