我正在尝试根据我看到的codepen对我的画布应用噪音效果,这又看起来与SO answer非常相似。
我想产生一个随机透明像素的“屏幕”,但我得到一个完全不透明的红色字段。我希望更熟悉画布或类型化数组的人可以告诉我我做错了什么,也许可以帮助我理解一些在起作用的技术。
我显着重构了 codepen 代码,因为(现在)我不关心动画噪声:
/**
* apply a "noise filter" to a rectangular region of the canvas
* @param {Canvas2DContext} ctx - the context to draw on
* @param {Number} x - the x-coordinate of the top-left corner of the region to noisify
* @param {Number} y - the y-coordinate of the top-left corner of the region to noisify
* @param {Number} width - how wide, in canvas units, the noisy region should be
* @param {Number} height - how tall, in canvas units, the noisy region should be
* @effect draws directly to the canvas
*/
function drawNoise( ctx, x, y, width, height ) {
let imageData = ctx.createImageData(width, height)
let buffer32 = new Uint32Array(imageData.data.buffer)
for (let i = 0, len = buffer32.length; i < len; i++) {
buffer32[i] = Math.random() < 0.5
? 0x00000088 // "noise" pixel
: 0x00000000 // non-noise pixel
}
ctx.putImageData(imageData, x, y)
}
据我所知,正在发生的事情的核心是我们将ImageData
的原始数据表示(一系列 8 位元素,它们依次反映每个像素的红色、绿色、蓝色和 alpha 值)在一个32 位数组,它允许我们将每个像素作为一个联合元组进行操作。我们得到一个每个像素一个元素而不是每个像素四个元素的数组。
然后,我们遍历该数组中的元素,根据我们的噪声逻辑将 RGBA 值写入每个元素(即每个像素)。这里的噪声逻辑非常简单:每个像素都有约 50% 的机会成为“噪声”像素。
噪声像素被分配了 32 位值0x00000088
,这(感谢阵列提供的 32 位分块)相当于rgba(0, 0, 0, 0.5)
50% 的不透明度,即黑色。
非噪声像素被分配32位值0x00000000
,即黑色0%不透明度,即完全透明。
有趣的是,我们不会将 写到buffer32
画布上。相反,我们编写了imageData
用于构造 的Uint32Array
,这让我相信我们正在通过某种传递引用来改变 imageData 对象;我不清楚这是为什么。我知道值和引用传递通常在 JS 中是如何工作的(标量通过值传递,对象通过引用传递),但是在非类型化数组世界中,传递给数组构造函数的值只决定了数组的长度。这显然不是这里发生的事情。
如前所述,我得到的不是一个 50% 或 100% 透明的黑色像素场,而是一个全实心像素场,全是红色。我不仅不希望看到红色,而且随机颜色分配的证据为零:每个像素都是纯红色。
通过使用两个十六进制值,我发现这会在黑色上产生红色的散射,具有正确的分布:
buffer32[i] = Math.random() < 0.5
? 0xff0000ff // <-- I'd assume this is solid red
: 0xff000000 // <-- I'd assume this is invisible red
但它仍然是纯红色,纯黑色。没有任何底层画布数据通过应该不可见的像素显示。
令人困惑的是,除了红色或黑色之外,我找不到任何颜色。除了 100% 不透明,我也无法获得任何透明度。为了说明断开连接,我删除了随机元素并尝试将这九个值中的每一个写入每个像素,看看会发生什么:
buffer32[i] = 0xRrGgBbAa
// EXPECTED // ACTUAL
buffer32[i] = 0xff0000ff // red 100% // red 100%
buffer32[i] = 0x00ff00ff // green 100% // red 100%
buffer32[i] = 0x0000ffff // blue 100% // red 100%
buffer32[i] = 0xff000088 // red 50% // blood red; could be red on black at 50%
buffer32[i] = 0x00ff0088 // green 50% // red 100%
buffer32[i] = 0x0000ff88 // blue 50% // red 100%
buffer32[i] = 0xff000000 // red 0% // black 100%
buffer32[i] = 0x00ff0000 // green 0% // red 100%
buffer32[i] = 0x0000ff00 // blue 0% // red 100%
这是怎么回事?
Uint32Array
编辑:根据MDN 关于以下内容的文章,ImageData.data
在消除了和诡异的突变后的类似(坏)结果:
/**
* fails in exactly the same way
*/
function drawNoise( ctx, x, y, width, height ) {
let imageData = ctx.createImageData(width, height)
for (let i = 0, len = imageData.data.length; i < len; i += 4) {
imageData.data[i + 0] = 0
imageData.data[i + 1] = 0
imageData.data[i + 2] = 0
imageData.data[i + 3] = Math.random() < 0.5 ? 255 : 0
}
ctx.putImageData(imageData, x, y)
}