【问题标题】:Merge canvas image and canvas alpha mask into dataurl generated png将画布图像和画布 alpha 蒙版合并到 dataurl 生成的 png 中
【发布时间】:2014-07-14 16:07:40
【问题描述】:

给定两个具有相同像素大小的画布,其中 canvas1 包含任意图像(jpg、png 等),而 canvas2 包含黑色和非黑色像素。

我想要实现的目标:使用第三个 canvas3 我想克隆 canvas1 并让每个黑色 canvas2 像素(可能包括黑度阈值)在 canvas3 中都是透明的

我已经有了这样一个可行的解决方案:

canvas3context.drawImage(canvas1,0,0);
var c3img = canvas3context.getImageData(0,0,canvas3.width,canvas3.height);
var c2img = canvas2context.getImageData(0,0,canvas2.width,canvas2.height);
loop(){
    if(c2img i-th,i+1-th,i+2-th pixel is lower than threshold)
      set c3img.data[i]=c3img.data[i+1]=c3img.data[i+2]=c3img.data[i+3]=0
}

上面(伪)代码的问题是,它很慢 所以我的问题是:任何人都可以分享一个如何显着加快速度的想法? 我考虑过 webgl,但我从未使用过它——所以我不知道着色器或为此所需的工具或术语。另一个想法是,也许我可以以某种方式非常快速地将 canvas2 转换为黑白(不仅仅是像上面那样修改循环中的每个像素)并使用混合模式来生成透明像素

非常感谢任何帮助

【问题讨论】:

  • 迭代像素的另一个问题是读取canvas图像数据可能会遇到跨域问题。

标签: javascript transparency


【解决方案1】:

回答我自己的问题,我提供了一种将任意图像与黑白图像合并的解决方案。我仍然缺少的是如何为画布的一种颜色设置 Alpha 通道。

我将问题分成几部分并逐个回答。

问题一:如何在不迭代每个像素的情况下将画布转为灰度?

答案:使用混合模式“亮度”将图像绘制到白色画布上

function convertCanvasToGrayscale(canvas){
    var tmp = document.createElement('canvas');
    tmp.width = canvas.width;
    tmp.height = canvas.height;
    var tmpctx = tmp.getContext('2d');

    // conversion
    tmpctx.globalCompositeOperation="source-over";  // default composite value
    tmpctx.fillStyle="#FFFFFF";
    tmpctx.fillRect(0,0,canvas.width,canvas.height);
    tmpctx.globalCompositeOperation="luminosity";
    tmpctx.drawImage(canvas,0,0);

    // write converted back to canvas
    ctx = canvas.getContext('2d');
    ctx.globalCompositeOperation="source-over";
    ctx.drawImage(tmp, 0, 0);
}

问题2:如何在不迭代每个像素的情况下将灰度画布转换为黑白?

答案:使用颜色#FEFEFE 进行两次颜色减淡混合模式即可完成工作

function convertGrayscaleCanvasToBlackNWhite(canvas){
    var ctx = canvas.getContext('2d');

    // in case the grayscale conversion is to bulky for ya
    // darken the canvas bevore further black'nwhite conversion
    //for(var i=0;i<3;i++){
    //    ctx.globalCompositeOperation = 'multiply';
    //    ctx.drawImage(canvas, 0, 0);
    //}

    ctx.globalCompositeOperation = 'color-dodge';
    ctx.fillStyle = "rgba(253, 253, 253, 1)";
    ctx.beginPath();
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fill();
    ctx.globalCompositeOperation = 'color-dodge';
    ctx.fillStyle = "rgba(253, 253, 253, 1)";
    ctx.beginPath();
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fill();
}

注意:此函数假定您希望黑色区域保持黑色,并且每个非黑色像素都变为白色!因此没有黑色像素的灰度图像将变为全白 我选择这个操作的原因是它在我的情况下效果更好,并且只使用两个混合操作意味着它非常快 - 如果你想让更多的暗像素保持黑色而更多的白色像素变成白色,你可以使用注释的 for 循环变暗事先的图像。因此暗像素会变黑,亮像素会变暗。随着您增加黑色像素的数量,使用颜色减淡将再次完成剩下的工作

问题 3:如何在不迭代每个像素的情况下将黑白画布与另一个画布合并?

答案:使用“乘法”混合模式

function getBlendedImageWithBlackNWhite(canvasimage, canvasbw){
    var tmp = document.createElement('canvas');
    tmp.width = canvasimage.width;
    tmp.height = canvasimage.height;

    var tmpctx = tmp.getContext('2d');

    tmpctx.globalCompositeOperation = 'source-over';
    tmpctx.drawImage(canvasimage, 0, 0);

    // multiply means, that every white pixel gets replaced by canvasimage pixel
    // and every black pixel will be left black
    tmpctx.globalCompositeOperation = 'multiply';
    tmpctx.drawImage(canvasbw, 0, 0);

    return tmp;
}

问题 4:如何在不迭代每个像素的情况下反转黑白画布?

答案:使用“差异”混合模式

function invertCanvas(canvas){
    var ctx = canvas.getContext("2d");

    ctx.globalCompositeOperation = 'difference';
    ctx.fillStyle = "rgba(255, 255, 255, 1)";
    ctx.beginPath();
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fill();
}

现在将canvasimage与canvasmask“合并”

convertCanvasToGrayscale(canvasmask);
convertGrayscaleCanvasToBlackNWhite(canvasmask);
result = getBlendedImageWithBlackNWhite(canvasimage, canvasmask);

关于性能:显然,这些混合模式比修改每个像素要快得多,并且为了更快一点,可以根据需要将所有功能打包到一个功能中并仅回收一个 tmpcanvas - 但这留给读者^^

作为旁注:当您将上面的 getBlendedImageWithBlackNWhite 结果与相同的图像进行比较时,我测试了生成的 png 的大小有何不同,但黑色区域通过迭代每个像素并设置 alpha 通道变得透明 大小上的差异几乎没有,因此如果您真的不需要 alpha 掩码,那么每个黑色像素都应该是透明的信息可能足以进行进一步处理

注意:可以使用 invertCanvas() 函数反转黑白的含义

如果您想了解更多关于我为什么使用这些混合模式或混合模式如何真正起作用的信息 你应该检查它们背后的数学 - 恕我直言,如果你不知道它们是如何工作的,你就无法开发这样的功能: math behind blend modes canvas composition standard including a bit math

需要一个例子 - 找出不同之处:http://jsfiddle.net/C3fp4/1/

【讨论】:

  • 好东西。请注意,其中一些 globalCompositeOperation 过滤器还不能在 safari 中使用。
猜你喜欢
  • 2016-04-03
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
  • 2015-02-12
  • 2017-07-05
  • 2021-02-20
  • 2021-08-24
相关资源
最近更新 更多