我有2D画布,其中绘制了一些东西,我想知道区域中的所有像素(rect - x,y,w,h)是否都是空的/完全透明的?我知道这可以用getImageData完成,但有更快的方法吗?我正在编写一个简单的java脚本图像打包器,我希望从最终工作表中排除空图像。
答案 0 :(得分:6)
读取像素的唯一方法是使用getImageData()
,但您可以使用与默认Uint8ClampedArray
不同的视图加快此类检查,例如允许Uint32Array
你每次迭代读取一个像素:
function isEmpty(ctx, x, y, w, h) {
var idata = ctx.getImageData(x, y, w, h), // needed as usual ...
u32 = new Uint32Array(idata.data.buffer), // reads 1x uint32 instead of 4x uint8
i = 0, len = u32.length;
while(i < len) if (u32[i++]) return false; // if !== 0 return false, not empty
return true // all empty, all OK
}
但是,这不能用于检查透明度。即使像素是完全透明的,也可能存在其他通道中的颜色数据。例如,这会产生一个不可见的像素:rgba(255,128,0,0)
和isEmpty()
会报告该区域非空,即使该像素不可见。
要检查这些情况,您只需要检查Alpha通道,并且您可以简单地修改上述内容以使用AND掩码过滤掉颜色数据,或者将Alpha通道位移过来,推动另一个位出 - 在任何一种情况下,我们都在非0值之后。
由于这是little-endian(LSB)格式(如今在大多数主流计算机上),组件的顺序为ABGR(0xAABBGGRR
),因此我们可以这样做:
u32[i] & 0xff000000
或使用shift(在这种情况下,符号无关紧要,但我个人更喜欢使用无符号移位(&gt;&gt;&gt;而不是&gt;&gt;)当我处理无符号数时):
u32[i]>>>24
性能方面没有什么区别,我猜想如果有的话,ANDing会稍快一点:
<强> AND运算强>
function isTransparent(ctx, x, y, w, h) {
var idata = ctx.getImageData(x, y, w, h), // needed as usual ...
u32 = new Uint32Array(idata.data.buffer), // reads 1x uint32 instead of 4x bytes
i = 0, len = u32.length;
while(i < len) if (u32[i++] & 0xff000000) return false; // not transparent?
return true // all transparent, all OK
}
<强>位移强>
function isTransparent(ctx, x, y, w, h) {
var idata = ctx.getImageData(x, y, w, h), // needed as usual ...
u32 = new Uint32Array(idata.data.buffer), // reads 1x uint32 instead of 4x bytes
i = 0, len = u32.length;
while(i < len) if (u32[i++]>>>24) return false; // not transparent?
return true // all transparent, all OK
}
<强>更新强>
如果你知道你检查的数据至少有一些大小,比如2x2像素,你也可以跳过每隔一个像素,甚至每隔一行来提高速度:
while(i < len) if (u32[(i += 2)]>>>24) return false; // skips every 2. pixel
对于行,您需要两个迭代器:
while(i < len) {
var endLine = i + width, p = i; // p in case you deal with odd widths
while(p < endLine) if (u32[(p += 2)]>>>24) return false; // skip every 2. pixel
i += width * 2; // skip a line
}