背景:有一天接到一个小游戏,里面有一个部分是 一起来找茬,一开始准备用设计给的坐标来写,但是发现好像不太符合程序员爱钻研的精神,于是就想着做一个能自动识别的,几经周折,后来决定用 canvas的像素来处理这个问题。
熟悉API
在处理图片找茬前,先啰嗦一下,canvas像素处理里面最重要的两个API ctx.getImageData
和ctx.putImageData
,前者负责获取canvas像素信息,后者负责把像素信息绘制到canvas画布上。
处理像素前,首先得在画布上 画写东西,我们这里就以画两个图片为例,如下:
1.绘制图片
ctx1.drawImage(img1, 0, 0, img1.width, img1.height, 0, 0, cavsW, cavsH);
ctx2.drawImage(img2, 0, 0, img2.width, img2.height, 0, 0, cavsW, cavsH);
复制代码
2.获取像素的API ctx.getImageData
MDN上的解释是:
CanvasRenderingContext2D.getImageData()
返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。;
sx
: 将要被提取的图像数据矩形区域的左上角 x 坐标。
sy
: 将要被提取的图像数据矩形区域的左上角 y 坐标。
sw
: 将要被提取的图像数据矩形区域的宽度。
sh
: 将要被提取的图像数据矩形区域的高度。
返回值
一个ImageData
对象,包含canvas给定的矩形图像数据。其中,
ImageData.data
: Uint8ClampedArray 描述了一个一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示。
ImageData.height
: 无符号长整型(unsigned long),使用像素描述 ImageData 的实际高度。
ImageData.width
: 无符号长整型(unsigned long),使用像素描述 ImageData 的实际宽度。
下面,以一个宽高分别为750
和400
的canvas画布为例:
ctx.getImageData(x,y, caves.width, canvas.height);
// 获取的是一个包含像素信息的对象,如下
ImageData = {
data: Uint8ClampedArray(1200000), // 4 * 750 * 400
width: 750,
height: 400
}
复制代码
由于ImageData.data是一维数组,所以我们需要把canvas的像素平铺到一行,对应如下下面为canvas中坐标对应的的下标值的对应示意图
若点A坐标为 (x,y),canvas画布的宽度为width,则A的四个rgba信息是为第[n, n + 3]个
// 把二维坐标坐标转成一纬的序号
n = y * width + x;
A.R = 4n
A.G = 4n + 1
A.B = 4n + 2
A.A = 4n + 3
复制代码
3. 绘制像素信息到 canvas画布的API,ctx.putImageData
。
对于ctx.putImageData
, MDN上的解释是:
CanvasRenderingContext2D.putImageData()
是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。 如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。
void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
复制代码
参数:
ImageData
: 包含像素值的数组对象。
dx
: 源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)。
dy
: 源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)。
dirtyX
: (可选) 在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。
dirtyY
: (可选) 在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标ÿ