写在前面
上篇文章我们了解了canvas用来处理图像的两个简单的方式:拉伸和裁切。这次我们来挑战像素级的图像处理,这话看起来挺唬人的,不过不用担心,它并没有那么可怕。
像素处理API
你可以通过ImageData对象在字节级的水平来处理图像数据,先看看与此相关的一些API属性及方法:
imagedata = context . createImageData(sw, sh)
根据给定的尺寸返回一个ImageData对象,该尺寸为CSS像素值(CSS Pixel),返回的对象中的像素点都是黑色透明的,即rgba(0,0,0,0)。
imagedata = context . createImageData(imagedata)
返回一个跟参数所指对象大小一致的ImageData对象,返回的对象中的像素点都是黑色透明的。
imagedata = context . getImageData(sx, sy, sw, sh)
返回包含canvas指定区域图像数据的ImageData对象
imagedata . width
imagedata . height
返回ImageData对象数据的实际尺寸,该值为设备像素值(Device Pixel)。
imagedata . data
返回一个一维数组,该数组包含按照RGBA顺序排列的范围从0到255的数据
context . putImageData(imagedata, dx, dy [, dirtyX, dirtyY, dirtyWidth, dirtyHeight ])
将指定的ImageData对象的数据绘制到canvas上。
备注:globalAlpha,globalCompositeOperation,还有shadow属性在调用该方法时会被忽略。
createImageData()方法会根据所给的参数创建一个以sw为宽,sh为高的空白ImageData对象,当该方法只有imagedata 参数时,它将返回一个与参数所指的ImageData对象一样大小的ImageData对象,并且返回的ImageData对象是被填充为透明黑色的(transparent black)。
getImageData(sx, sy, sw, sh)方法返回包含在canvas坐标系中由(sx, sy), (sx+sw, sy),(sx+sw, sy+sh), (sx, sy+sh)四个坐标在canvas上所围成区域的基本像素数据的ImageData对象,如果该区域超出了canvas范围,超出的部分被填充为透明黑色的(transparent black)。
如果createImageData()和getImageData()的参数值是无穷的或NaN,或者createImageData()只有一个参数值为null,方法会抛出NOT_SUPPORTED_ERR异常,如果值为0则抛出INDEX_SIZE_ERR异常。
ImageData对象必须被初始化,有宽度和高度,data属性值被初始化为一个CanvasPixelArray对象用来存放图像数据,至少要返回一个有效的一像素图像数据。
CanvasPixelArray对象包含了图像数据每个像素点有序的可索引的色值数据。这些数据从上到下,从左到右(即从左上角开始)描述了各个像素点的红,绿,蓝还有alpha值,它们的取值范围从0到255,用8个比特的数据描绘一个值。因此,该CanvasPixelArray对象包含了w×h× 4字节的数据,一个CanvasPixelArray对象的length属性必须返回这个数字。因为该对象可索引的范围就是[0,w×h× 4-1]。
注意:这里所说的宽(w)和高(h)有别于sw和sh,比如当canvas支持高分辨率的位图或sw与sh为负数时。
putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight)方法把ImageData结构的数据描绘在canvas上,如果任一参数值是无穷或NaN的,它将抛出NOT_SUPPORTED_ERR异常;如果第一个参数不是ImageData对象或为null它将抛出TYPE_MISMATCH_ERR异常;如果最后四个参数被省略,那它们将分别被赋值为0,0,imagedata的width和imagedata的height值。
像素处理实例
看了上面那么大一段的描述,估计你也头晕了,我也是,我想来看几个例子比较有助于理解。先来看第一个Demo。查看Demo
?
View Code HTML1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Canvas javascript api demo</title>
<style type="text/css">
canvas {
border:1px #000 solid;
}
</style>
</head>
<body>
<h1>this is a canvas api test page!</h1>
<canvas>this browser does not support canvas...</canvas>
<a href="">返回文章</a>
<script type="text/javascript">
var $=function(id){
return document.getElementById(id);
}
window.onload=function(){
var ctx=$('canvas').getContext('2d');
var img=new Image();
img.src='firefox.jpg';
img.onload=function(){
ctx.drawImage(img,0,0,240,240,30,10,120,120);
//for security reason,you should turn on UniversalBrowserRead in Firefox.
//netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
imgData=ctx.getImageData(30,10,120,120);
ctx.putImageData(imgData,30,160);
}
}
</script>
</body>
</html>
这段代码很简单,在上篇文章的Demo中加入了getImageData和putImageData两个函数实现了图像的复制。如果你将代码保存到本地测试了并且没有效果那就对了,在Firefox的错误控制台中你会看到:
错误: uncaught exception: [Exception... "Security error" code: "1000" nsresult: "0x805303e8 (NS_ERROR_DOM_SECURITY_ERR)" location: "file:///C:/Documents and Settings/Administrator/桌面/html5_canvas.html Line: 30"]
在Chrome的JavaScript控制台你会看到:
Uncaught Error: SECURITY_ERR: DOM Exception 18
而在Safari中,该代码是可以正常运行的,原因在于:Firefox与Chrome对本地文件JavaScript访问权限控制比较严格。Demo中的注释去掉后,可以在Firefox中看到我们实际想得到的结果,但其实这是没必要的,因为在服务器环境下代码是可以正常运行的。
通过下面的例子,我们来看如何操作图像数据。查看Demo
?
View Code HTML1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Canvas javascript api demo</title>
<style type="text/css">
canvas {
border:1px #000 solid;
}
</style>
</head>
<body>
<h1>this is a canvas api test page!</h1>
<canvas>this browser does not support canvas...</canvas>
<a href="">返回文章</a>
<script type="text/javascript">
var $=function(id){
return document.getElementById(id);
}
window.onload=function(){
var ctx=$('canvas').getContext('2d');
var img=new Image();
img.src='firefox.jpg';
img.onload=function(){
ctx.drawImage(img,0,0,240,240,30,10,120,120);
imgData1=ctx.getImageData(30,10,120,120);
imgData2=ctx.createImageData(50,50);
for(var i=0;i<imgData2.data.length/4;i++){
//set the rgba to 255,0,0,255
imgData2.data[i*4]=0xfff;
imgData2.data[i*4+1]=0;
imgData2.data[i*4+2]=0;
imgData2.data[i*4+3]=255;
}
ctx.putImageData(imgData1,30,160);
ctx.putImageData(imgData2,50,200);
}
}
</script>
</body>
</html>