给出两个具有相同像素大小的画布,其中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转换为黑色和白色(不仅仅是像上面那样修改循环中的每个像素)并使用混合模式来生成透明像素
任何帮助都非常感谢
答案 0 :(得分:7)
回答我自己的问题,我提供了将任意图像与黑白图像合并的解决方案。我还缺少的是如何为一种颜色的画布设置alpha通道。
我将问题分成几部分并逐一回答。
问题1:如何在不迭代每个像素的情况下将画布转换为灰度?
答案:将图像绘制到具有混合模式'亮度'
的白色画布上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 :如何在不迭代每个像素的情况下将Black&amp; White画布与另一个画布合并?
答案:使用&#39;乘以&#39;混合模式
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:如何在不迭代每个像素的情况下反转黑白画布?
答案:使用&#39;差异&#39;混合模式
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();
}
现在要&#39;合并&#39;带有canvasmask的canvasimage可以做
convertCanvasToGrayscale(canvasmask);
convertGrayscaleCanvasToBlackNWhite(canvasmask);
result = getBlendedImageWithBlackNWhite(canvasimage, canvasmask);
关于性能:显然这些混合模式比修改每个像素快得多,并且要快一点,可以根据需要将所有功能组合到一个功能中,并且只回收一个tmpcanvas - 但这留给了读者^^
作为旁注:当我将上面的getBlendedImageWithBlackNWhite结果与相同的图像进行比较时,我测试了生成的png的大小是如何不同的,但是通过迭代每个像素并设置alpha通道使黑色区域变得透明 尺寸的差异几乎没有,因此,如果你真的不需要alpha-mask,那么每个黑色像素都是透明的信息可能足以进一步处理
注意:可以使用invertCanvas()函数
反转黑白的含义如果您想了解更多为什么我使用这些混合模式或混合模式如何真正起作用 你应该检查一下他们背后的数学 - 如果你不知道它们是如何工作的话,你就无法开发这些功能: math behind blend modes canvas composition standard including a bit math
需要一个例子 - 发现差异:http://jsfiddle.net/C3fp4/1/