JavaScript Canvas - Image Processing
Monday, 07 October 2019
Article Index
JavaScript Canvas - Image Processing
Augment ImageData
The Complete Program

You can use Canvas to draw new graphics or you can load existing images and process them at the pixel level. In this extract from a chapter in my new book on JavaScript Graphics we look at how it works.

Now available as a paperback or ebook from Amazon.

JavaScript Bitmap Graphics
With Canvas

largecover360

 

Contents

  1. JavaScript Graphics
  2. Getting Started With Canvas
  3. Drawing Paths
  4. Stroke and Fill
  5. Transformations
  6. Text
  7. Clipping, Compositing and Effects
  8. Generating Bitmaps
  9. WebWorkers & OffscreenCanvas
      Extract 1: OffscreenCanvas
  10. Bit Manipulation In JavaScript
  11. Typed Arrays
  12. Files, blobs, URLs & Fetch
  13. Image Processing
      Extract 1:ImageData **NEW!
  14. 3D WebGL
  15. 2D WebGL

Related Articles

A Programmer's Guide to Canvas

Canvas bitmap operations - bitblt in JavaScript

Reading A BMP File In JavaScript

Getting Started With SVG

SVG, JavaScript and the DOM

Getting started with WebGL

Getting Started with Box2D in JavaScript

<ASIN:1871962625>

<ASIN:1871962579>

<ASIN:1871962560>

<ASIN:1871962501>

<ASIN:1871962528>

In the early part of this book the emphasis was on creating graphics by drawing on the canvas. By contrast the latter part of the book is about loading graphics files into the canvas. Why would you want to do this as opposed to simply loading files into an Image object? If you only want to display the bitmaps then you should use an image object. The advantage of a canvas object is that you can manipulate the pixel values to change what is displayed. This is image processing and it is the subject of this chapter.

Getting at the Pixels

The drawImage method allows you to make the connection between a bitmap and the canvas object, but what about getting at the pixels of a bitmap?

You can do this quite easily with the help of the ImageData object.

There are two methods for creating an ImageData object:

 

  • ctx.createImageData(w,h)  
    creates an ImageData object with width w and height h
  • ctx.createImageData(IData)
    creates an ImageData object the same
    size as the ImageData object specified by IData.

 

In both cases all pixels are set to transparent black, i.e. R=0, G=0, B=0 and A=0.

These two methods correspond to constructors which can be used in a Worker where no canvas is available:

 

  • new ImageData(w,h)
  • new ImageDate(IData)

 

A third method creates an ImageData object from the pixels in a specified area of a canvas object:

 

  • ctx.getImageData(x,y,w,h) creates an ImageData object from the pixels in the rectangle with top left corner at x,y and width w and height h.

 

To manipulate the pixels in the ImageData object you make use of its data property which is a Uint8ClampedArray (see Chapter11) of pixel values in the order RGBA for each pixel.

The first element of the array i.e. data[0] is the R value for the pixel in the top left corner. The pixels are stored in the array in row order with four elements to each pixel.

A few moments thought should convince you that the R value for the pixel at x,y in the rectangle of pixels is stored at:

data[(x+y*w)*4]

We can use this to write a method that allows direct access to the color information for the pixel at x,y. We can then use these methods to manipulate the pixel data and then write the result to the canvas using:

ctx.putImageData(ImageData,x,y);

which renders the ImageData object with its top left corner at x,y.

There is another more sophisticated putImageData method:

ctx.putImageData(ImageData,x,y,sx,sy,sw,sh)

which only transfers data from the ImageData object within the rectangle with top left corner at sx,sy and width sw and height sh.

This is more or less all we need to create and modify graphics working at the pixel level. Of course, it would be a good idea to implement some slightly higher-level methods and while this is easy it does raise the question of how best to package them to make them easy to use. One approach is simply to add them as ad-hoc methods to the ImageData object that you create. For example:

var ImDat=ctx.createImageData(100,100);
ImDat.getPixel=function(x,y){
  var i=(x+y*this.width)*4;
  return {R:this.data[i],
          G:this.data[i+1],
          B:this.data[i+2],
          A:this.data[i+3]
         }
 }

adds the getPixel method to the ImDat object to return an object with the properties R, G, B and A for the pixel at x,y.

You can add a similar setPixel method:

ImDat.setPixel=function(x,y,c){
  var i=(x+y*this.width)*4;
  this.data[i]=c.R;
  this.data[i+1]=c.G;
  this.data[i+2]=c.B;
  this.data[i+3]=c.A;
}

to set the pixel at x,y to the color specified by the RGBA properties of the c object. Of course, in a production system you would need to add checks that the parameters were of the correct type and that the values were all in the range 0 to 255. You could also write other methods to work with color defined in other ways - CSS colors, color in the range 0 to 1 and so on.



Last Updated ( Monday, 07 October 2019 )