在Javascript中检查一个区域中的所有像素是否全部为空?
Check if all pixels in a region are all empty in Javascript?
我有 2D canvas 上面画了东西,我想知道一个区域 (rect - x,y,w,h) 中的所有像素是否都是 empty/fully 透明的?我知道这可以用 getImageData 来完成,但是有更快的方法吗?我正在编写一个简单的 java 脚本图像打包器,我希望从最终 sheet.
中排除空图像
读取像素的唯一方法是使用 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 通道位移过来,推另一个bits out - 在任何一种情况下,我们都在寻找非 0 值。
由于这是小端 (LSB) 格式(与当今大多数主流计算机一样),组件的顺序为 ABGR (0xAABBGGRR
),因此我们可以执行以下任一操作:
u32[i] & 0xff000000
或使用 shift(在这种情况下符号并不重要,但我个人更喜欢在处理无符号数字时使用无符号 shift(>>> 而不是 >>)):
u32[i]>>>24
在性能方面差别很小,我想 ANDing 稍微快一点:
ANDing
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
}
我有 2D canvas 上面画了东西,我想知道一个区域 (rect - x,y,w,h) 中的所有像素是否都是 empty/fully 透明的?我知道这可以用 getImageData 来完成,但是有更快的方法吗?我正在编写一个简单的 java 脚本图像打包器,我希望从最终 sheet.
中排除空图像读取像素的唯一方法是使用 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 通道位移过来,推另一个bits out - 在任何一种情况下,我们都在寻找非 0 值。
由于这是小端 (LSB) 格式(与当今大多数主流计算机一样),组件的顺序为 ABGR (0xAABBGGRR
),因此我们可以执行以下任一操作:
u32[i] & 0xff000000
或使用 shift(在这种情况下符号并不重要,但我个人更喜欢在处理无符号数字时使用无符号 shift(>>> 而不是 >>)):
u32[i]>>>24
在性能方面差别很小,我想 ANDing 稍微快一点:
ANDing
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
}