Evangelism/Firefox3.5/35Days/Articles/createImageData

From MozillaWiki
Jump to: navigation, search

Work in progress

Canvas: Welcome into the matrix

Canvas, at its most simple level, is an easy way to draw bitmap data into an HTML page. It has methods that allow you to draw rectangles, arcs, curves and other simple primitives. However, for some types of effects and drawing you need direct access to the pixels.

In Firefox 3.5 we've added a new method to the canvas element - createImageData(). The createImageData() call is a convenience method to create a blank set of pixels for manipulation that can eventually be copied back onto a canvas.

Since we're talking about a single call we thought that it might be worth it to go through all of the calls that let you read, manipulate and update the pixels directly in a canvas.

Manipulating Canvas Pixels

Retrieving Pixel Data

You can't directly manipulate pixels in a canvas. In order to make changes to the data in a canvas you first need to copy the data out, make changes, and copy the changed data back to the target canvas.

The getImageData() call lets you copy a rectangle of pixels out of a canvas. A call to get all of the pixel data out of a canvas looks like this:

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

The canvasData object contains the pixel data. It has the following members:

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

The data is a flat array of values that has one value for each component in a pixel, organized left to right, top to bottom, with each pixel represented as four values in RGBA order.

For example, in a 2x2 canvas, there would be 4 pixels represented with 16 values that look like this:

0,0  0,1  1,0  1,1
RGBA RGBA RGBA RGBA

So you can calculate the length of that array with the following formula: width * height * 4.

In a larger canvas if you wanted to know the value of a blue in a pixel at x = 10, y = 20 you would use the following code:

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

Note that each RGB pixel has a value of 0..255 with the alpha bit being 0..255 with 0 being completely transparent and 255 fully opaque.

Create a new set of pixels

If you want to create a new matrix from scratch, just use the createImageData call which needs two arguments: the height and the width of the matrix.

Note that the createImageData call does not copy pixels out of the existing canvas, it produces a blank matrix of pixels with the values set to transparent black (255,255,255,0).

Here's an example you want to create a set of pixels that fits the canvas size:

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

Update the pixels

Once you have the canvasData object you can update the pixel values through the array. Here's one example of how to walk through the array reading and updating the values.

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
  }
}

Update the canvas

Now that you've got a set of pixels updated you can use the simple putImageData() call. This call takes the canvasData object and the x,y location where you would like to draw the rectangle of pixel data into the canvas:

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

Examples

getImageData

Here is a code that transforms your image to a gray scale:

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);

See in action http://people.mozilla.com/~prouget/demos/35days/gray.xhtml

createImageData

Here is a code to draw a fractal:

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);

See in action: http://people.mozilla.com/~prouget/demos/35days/fractal.xhtml.

Documentation

If you want to know more about Canvas and pixels manipulation, I strongly encourage you to browse the MDC documentation: