谋智社区

火狐浏览器在中国

« PreviousNext »

颠覆网络35天 ─ 使用Canvas操作像素

3 July 2009

原文地址:http://hacks.mozilla.org/2009/06/pushing-pixels-with-canvas/

系列地址:颠覆网络35天

====================================

这篇文章由Paul Rouget创作,Paul是Mozilla Evangelism团队成员,生活在法国巴黎,以在开放互联网视频方面的卓越工作而被大家熟知。

Canvas,最简单的来描述他就是一种最直接简便的方法可以允许您在HTML页面上绘画位图。他提供了方法可以直接绘画矩形、弧线、曲线等其他简单图形元素。不过有时候,为了某些特殊效果您需要直接能够访问到像素信息。

在Firefox 3.5中,我们为Canvas元素添加了一个新的方法──createImageDatacreateImageData是一个非常便利的方式用来生成全空的像素集合用来控制从而画回到Canvas上。

既然我们开始讨论这个调用,那么我们想最好能够从头到尾为您描述这一调用的全过程,包括读取、控制和更新在Canvas上的像素,从而了解如何更好的使用createImageData

获取像素数据

您不能直接在Canvas中控制像素。为了改变在Canvas的数据,您需要先把数据拷贝出来,然后改变数据,最后在把修改好的数据重新画到目标Canvas上。

方法getImageData可以让您从Canvas中拷贝某一矩形区域的像素数据。具体的调用代码如下:


var canvas = document.getElementById(‘myCanvasElt’);
var ctx = canvas.getContext(‘2d’);
var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);

canvasData对象中包含了需要的像素数据。他具有下面这些成员变量:


canvasData {
    width: unsigned long, // the width of the canvas
    height: unsigned long, // the height of the canvas
    data: CanvasPixelArray // the values of the pixels
}

其中成员变量data是一个展开的数组,按照一定顺序对应着每个像素中的颜色组成部分,依据从左到右、从上到下的顺序描述像素,而每个像素还包括四个值对应着RGBA。

例如,在一个2×2的Canvas中,应该有如下16个值来描述4个像素点:

RGBA RGBA RGBA RGBA

所以您可以使用下面的公式来计算数组的长度:width * height * 4

在一个大一些的Canvas中,如果您想知道在x=10、y=20这个坐标上像素的蓝色值是多少,可以使用下面示例的代码:


var x = 10;
var y = 10;
var blue = canvasData.data[(y * width + x) * 4 + 2];

注意,每个RGB像素值取值在0到255之间,Alpha也取值于0到255之间──0表示完全的透明255表示完全的不透明。

生成新的像素集合

如果您希望完全从新生成像素矩阵,可以使用createImageData函数,并需要提供两个参数:矩阵的宽度和高度。

需要注意的是,createImageData调用并不会从现有的Canvas中拷贝像素出来,他生成的是完全空白的像素矩阵,他的初始值是全透明的黑色,即 (255,255,255,0) 。

下面的示例展示如何生成像素集合可以适合Canvas的大小:


var canvas = document.getElementById(‘myCanvasElt’);
var ctx = canvas.getContext(‘2d’);
var canvasData = ctx.createImageData(canvas.width, canvas.height);

需要注意的是,如果您希望新建像素数据,就应该调用这个方法。以前版本的Firefox直接使用JavaScript对象来构建canvasData对象,之后用来更新Canvas的数据。这个调用被添加进来从而保持和WebKit的兼容,但是在底层使用的是特定的对象而非简单的纯JavaScript对象了。

更新像素数据

当您拥有canvasData对象之后,您就可以通过其中的数据数组来更新特定的像素值了。下面的示例就展示了如何遍历数据来读取和更新这些数值。


for (var x = 0; x < canvasData.width; x++)  {
    for (var y = 0; y < canvasData.height; y++)  {
        // Index of the pixel in the array
        var idx = (x + y * width) * 4;
        // If you want to know the values of the pixel
        var r = canvasData.data[idx + 0];
        var g = canvasData.data[idx + 1];
        var b = canvasData.data[idx + 2];
        var a = canvasData.data[idx + 3];
        //[…] do what you want with these values
        // If you want to update the values of the pixel
        canvasData.data[idx + 0] =; // Red channel
        canvasData.data[idx + 1] =; // Green channel
        canvasData.data[idx + 2] =; // Blue channel
        canvasData.data[idx + 3] =; // Alpha channel
    }
}

更新Canvas

现在您已经更新了像素集合,可以简单的调用putImageData来更新Canvas。这个调用需要canvasData和x,y位置参数来控制更新Canvas的矩形数据信息:


var canvas = document.getElementById(‘myCanvasElt’);
var ctx = canvas.getContext(‘2d’);
var canvasData = ctx.putImageData(canvasData, 0, 0);

getImageData的完整示例

下面这段代码把一个彩色图片转换为灰度图片。您也可以在Paul的网站上看到演示的效果。


var canvas = document.getElementById(‘myCanvasElt’);
var ctx = canvas.getContext(‘2d’);
var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var x = 0; x < canvasData.width; x++) {
    for (var y = 0; y < canvasData.height; y++) {
        // Index of the pixel in the array
        var idx = (x + y * canvas.width) * 4;
        // The RGB values
        var r = canvasData.data[idx + 0];
        var g = canvasData.data[idx + 1];
        var b = canvasData.data[idx + 2];
        // Update the values of the pixel;
        var gray = (r + g + b) / 3;
        canvasData.data[idx + 0] = gray;
        canvasData.data[idx + 1] = gray;
        canvasData.data[idx + 2] = gray;
    }
}
ctx.putImageData(canvasData, 0, 0);

createImageData的完整示例

下面的代码会在Canvas上画出一副分形图案。同样,您可以在Paul的网站上查看演示效果。


var canvas = document.getElementById(‘myCanvasElt’);
var ctx = canvas.getContext(‘2d’);
var canvasData = ctx.createImageData(canvas.width, canvas.height);// Mandelbrot
function computeColor(x, y) {
    x = 2.5 * (x/canvas.width - 0.5);
    y = 2 * (y/canvas.height - 0.5);
    var x0 = x;
    var y0 = y;
     var iteration = 0;
     var max_iteration = 100;
     while (x * x + y * y <= 4 && iteration < max_iteration ) {
          var xtemp = x*x - y*y + x0;
          y = 2*x*y + y0;
          x = xtemp;
          iteration++;
     }
     return Math.round(255 * iteration / max_iteration);
}
for (var x = 0; x < canvasData.width; x++) {
     for (var y = 0; y < canvasData.height; y++) {
          var color = computeColor(x, y);
          // Index of the pixel in the array
          var idx = (x + y * canvas.width) * 4;
          // Update the values of the pixel;
          canvasData.data[idx + 0] = color;
          canvasData.data[idx + 1] = color;
          canvasData.data[idx + 2] = color;
          canvasData.data[idx + 3] = 255;
     }
}
ctx.putImageData(canvasData, 0, 0);

更多文档

如果想了解更多关于Canvas的内容,我们强烈建议您浏览MDC(Mozilla开发者中心)上有关Canvas的文档:

我们希望这些能够帮助所有人。

Posted in 颠覆网络35天 | Trackback | del.icio.us | Top Of Page

No comments yet

Leave a Reply