像素操作 1030+

到目前为止,我们尚未深入了解 canvas 画布真实像素的原理,事实上,你可以直接通过 ImageData 对象操纵像素数据,直接读取或将数据数组写入该对象中。

ImageData 对象

在快应用中 ImageData 对象是一个普通对象,其中存储着 canvas 对象真实的像素数据,它包含以下几个属性

  • width 使用像素描述 ImageData 的实际宽度
  • height 使用像素描述 ImageData 的实际高度
  • data Uint8ClampedArray 类型,描述了一个一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示data 属性返回一个 Uint8ClampedArray,它可以被使用作为查看初始像素数据。每个像素用 4 个 1 bytes 值(按照红,绿,蓝和透明值的顺序; 这就是 "RGBA" 格式) 来代表。每个颜色值部份用 0 至 255 来代表。每个部份被分配到一个在数组内连续的索引,左上角像素的红色部份在数组的索引 0 位置。像素从左到右被处理,然后往下,遍历整个数组。

Uint8ClampedArray 包含 高度 × 宽度 × 4 bytes 数据,索引值从 0 到(高度 × 宽度 × 4) - 1

例如,要读取图片中位于第 50 行,第 200 列的像素的蓝色部份,你会写以下代码:

  1. const blueComponent = imageData.data[50 * (imageData.width * 4) + 200 * 4 + 2]

你可能用会使用 Uint8ClampedArray.length 属性来读取像素数组的大小(以 bytes 为单位):

  1. const numBytes = imageData.data.length

创建一个 ImageData 对象

去创建一个新的,空白的 ImageData 对象,你应该会使用 createImageData() 方法。有 2 个版本的 createImageData() 方法

  1. const myImageData = ctx.createImageData(width, height)

上面代码创建了一个新的具体特定尺寸的 ImageData 对象。所有像素被预设为透明黑。

你也可以创建一个被 anotherImageData 对象指定的相同像素的 ImageData 对象。这个新的对象像素全部被预设为透明黑。这个并非复制了图片数据。

  1. const myImageData = ctx.createImageData(anotherImageData)

得到场景像素数据

为了获得一个包含画布场景像素数据的 ImageData 对像,你可以用 getImageData() 方法:

  1. const myImageData = ctx.getImageData(left, top, width, height)

这个方法会返回一个 ImageData 对象,它代表了画布区域的对象数据,此画布的四个角落分别表示为(left, top),(left + width, top),(left, top + height),以及(left + width, top + height)四个点。这些坐标点被设定为画布坐标空间元素。

在场景中写入像素数据

你可以用 putImageData() 方法去对场景进行像素数据的写入。

  1. ctx.putImageData(myImageData, dx, dy)

dx 和 dy 参数表示你希望在场景内左上角绘制的像素数据所得到的设备坐标。

例如,为了在场景内左上角绘制 myImageData 代表的图片,你可以写如下的代码:

  1. ctx.putImageData(myImageData, 0, 0)

举例

在这个例子里,我们接着对刚才的快应用 logo 进行置灰色,我们使用 getImageData 获取 ImageData 对象,遍历所有像素以改变他们的数值。然后我们将被修改的像素数组通过 putImageData() 放回到画布中去。 grayscale 函数仅仅是用以计算红绿和蓝的平均值。你也可以用加权平均,例如 x = 0.299r + 0.587g + 0.114b 这个公式

  1. setGray() {
  2. const canvas = this.$element('new-canvas')
  3. const ctx = canvas.getContext('2d')
  4. const canvasW = 380
  5. const canvasH = 380
  6. // 得到场景像素数据
  7. const imageData = ctx.getImageData(0, 0, 380, 380)
  8. const data = imageData.data
  9. for (let i = 0; i < data.length; i += 4) {
  10. const avg = (data[i] + data[i + 1] + data[i + 2]) / 3
  11. data[i] = avg; // red
  12. data[i + 1] = avg; // green
  13. data[i + 2] = avg; // blue
  14. }
  15. // 在场景中写入像素数据
  16. ctx.putImageData(imageData, 0, 0)
  17. }

运行效果如下

操作像素