Page 1 of 3
Now available as a paperback or ebook from Amazon.
- Getting Started With Canvas
- Drawing Paths
Extract: Basic Paths
Extract: SVG Paths**NEW!
Extract: Bezier Curves
- Stroke and Fill
Extract: Stroke Properties
Extract: Fill and Holes
Extract: Gradient & Pattern Fills
Extract: Custom Coordinates
Extract Graphics State
Extract: Text, Typography & SVG
- Clipping, Compositing and Effects
Extract: Clipping & Basic Compositing
- Generating Bitmaps
Extract: Introduction To Bitmaps
Extract : Animation
- WebWorkers & OffscreenCanvas
Extract: Web Workers
Extract: Bit Manipulation
- Typed Arrays
- Files, blobs, URLs & Fetch
Extract: Blobs & Files
Extract: Read/Writing Local Files
- Image Processing
Extract: The Filter API
- 3D WebGL
Extract: WebGL 3D
- 2D WebGL
Extract: WebGL Convolutions
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:
creates an ImageData object with width w and height h
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:
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 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:
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:
which renders the ImageData object with its top left corner at x,y.
There is another more sophisticated putImageData method:
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:
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:
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.