Next page | Contents page |

Processing image pixels

The JavaScript constructor Image () creates an object that represents an HTML <img> element and loads into memory the data from the location specified by the src property which we attach to the object. It may take a while to load the data and this happens in the background while our program continues. So we may need to be able to specify things to happen when loading is complete. Just as for the body of our HTML we can use an onload event and the script may look something like this:


  var im = new Image ();
  im.loaded = false;
  im.onload = function () { im.loaded = true; /* ...etc */ };
  im.onerror = function () { alert ('Failed to load myfile.jpg'); }
  im.src = 'im/myfile.jpg'; // Starts the loading

Note the use of anonymous functions here. Alternatively just give the name of a function to be found elsewhere, with no parameters (and no brackets after the function name here).

Suppose we wish to alter the pixels of the image in some way, such as to change contrast. The object of type Image does not allow access to the pixel values. It is necessary first to draw the image into a canvas. The graphics context of that canvas has a method called getImageData () for retrieving the pixel data.

We first create a canvas element: document.createElement ('canvas');. Note that this will not appear on the HTML page unless we append it to an already visible element (such as the body); so we can keep it hidden, just somewhere in memory. The following code shows how to get the pixel data as an array.


  if (im.loaded) // from previous code snippet
  {
    var canvas = document.createElement ('canvas');
    canvas.width = im.width;   // After loading these will
    canvas.height = im.height; // have been set from the file
    var context = canvas.getContext ('2d');
    context.drawImage (im, 0, 0);
    var imData = context.getImageData (0, 0, im.width, im.height); // 0, 0 is top left
  }

The object called imData is then of type ImageData and it has 3 properties: width, height and data. The last of these is an array containing the pixel values, reading from top left across in rows and then down. Each pixel of the image is represented by four 8-bit values in the array. In order these are the red, green, blue and alpha values for the pixel (rgba). Alpha conventionally means opacity, ranging from 0 (transparent) to 255 (opaque), which determines how the pixel will be merged with any already existing at its position in the canvas.

Having obtained the pixel data we might then process them and rewrite the result into the canvas. This example inverts the contrast (makes a negative image).


  for (var i = 0; i < imData.data.length; i += 4)
  {
    for (var j = i; j < i + 3; j++) imData.data [j] = 255 - imData.data [j]; 
  }
  context.putImageData (imData, 0, 0); // Put the changed data back at top left

In this example we inverted red, green and blue but skipped over alpha.

Getting the result as an Image

It is all very well having the result of pixel processing as an ImageData object but putImageData () is far less flexible than drawImage () if further transformations are required. The latter can very conveniently scale an image by using two more optional parameters:


  context.drawImage (im, x, y, w, h);

where x and y are the coordinates within the canvas for placing the top left corner of the image and w and h are the required (scaled) width and height. This is very useful if the same image is to be placed several times in the canvas at different positions and different sizes. (I make heavy use of this in drawing the scenes in my program The Forest.)

So how do we get an ImageData back into an Image? Awkwardly, it seems, though I am sure the designers of HTML5 had good reason for this (probably hampered by earlier developments). We make the ImageData object into a data source for constructing a new Image, like this:


  var newIm = new Image ();
  newIm.loaded = false;
  newIm.onload = function () { newIm.loaded = true; /* ...etc */ };
  newIm.src = canvas.toDataURL ('image/png'); // MIME type shows what file format to use
Next page | Contents page |