Ready to watch this entire course?
Become a member and get unlimited access to the entire skills library of over 4,971 courses, including more Developer and personalized recommendations.Start Your Free Trial Now
- View Offline
- Understanding the differences between Canvas and SVG Graphics
- Drawing shapes
- Drawing arcs and paths
- Rendering text
- Using clipping paths
- Drawing images and video
- Transforming objects with the translate tag
- Manipulating raw pixels
- Applying a custom transformation
- Creating an animation or slideshow control with Canvas
Skill Level Intermediate
Probably one of the coolest and most powerful features of the HTML5 canvas is that your script can now get access to raw pixel data on the canvas and manipulate the pixels directly. You can access the individual pixel data as an array of bytes on the canvas. Then this data can then be manipulated and put back into the canvas. Every image in a canvas, and in fact this is true of all canvas content, is composed of four-byte-wide pixels.
So let's imagine we had a picture like this on the canvas and we looked at an individual row. So that individual row would be made up of pixels, starting at the left-hand side over here and continuing all the way over to here. Imagine each pixel is a little box going across the row. Well, each one of these pixels is made up of a four-byte combination. There's a red, green, and blue component of that pixel along with the alpha setting. So by getting access directly to these bytes, we can manipulate how the image looks in the canvas.
So if we were to calculate the size of that image data array, it would be the height of the image, right, so this height here, times the width, times four-- because remember each one of these pixels is four bytes wide. So to get the number of bytes, you'd multiply the height, times the width, times four to account for the four bytes that make up each pixel. Now one of the things I should point out here is that this is an obvious security issue. If any willy-nilly script could just get access to the image data on a canvas, it could surreptitiously send it back to another web site or another web server.
So, for security reasons scripts have to come from the same origin. If a script wants to try to access the content of a canvas on the web page, then the script had to have come from the same origin as the web page itself; otherwise, it is denied access and the browser raises a security exception. So what are the functions for accessing image data? Well, we have a couple things we can do. We can get the width and height of the canvas pixel data.
There is also a data property, which is a single-dimension array of all the raw pixel data. This is the big array that has all the bytes of the image. We can create new image data from scratch. So this is createImageData. There are two versions. One takes a width and a height; the other one takes an existing image data structure and creates a new one from it. We can also get image data, and we can get image data within the given bounds. So starting at the source canvas, we can start at source x and source y point and given a width and a height, we can get a portion of the image data.
We can then do whatever we want with the image data and put it back using putImageData. Now putImageData puts the modified image data specified by image data back into the image at point dx and dy. These arguments here--dirtyDx, dirtyDy, dirtyW, and dirtyH-- these are all optional and they specify what's called a dirty rectangle. Now if a dirty rectangle is supplied, then only the bits inside that rectangle are updated. So let's take a look at a real-life example of using the image data access.
So here's my code, and here in my snippets I've got my rawdata example, and here's my rawdata file. Now, one of the things I want to point out is that I'm running this from a web server that happens to be located on my machine. So here you can see in the browser, I've got localhost accessing this file. Again, that's so that the browser has a domain to go off of when it's running my script code and trying to access the image data. So if you want to see this example work, all you need to do is run it from a real web site. It doesn't matter if it's localhost or whatever web site you usually work on; you can just simply run this by loading up the local file in the browser.
So let's go back to the code. So let's go to the snippet, and what I'm going to do is copy this first example here. So, let's scroll down a little bit. Here is the source image that I'm going to be manipulating, and it is this image right here. So what I'm going to do is go back to the source. So what we are doing is getting the element out of the document, and then we're going to simply draw the image onto the canvas.
So let's make sure that that works first. So let me refresh. Okay, so that's working. So we're taking the source image, drawing it onto the canvas, and everything is fine. That's no big deal; we've already seen that before. So let's go back to the code. So, let's do something interesting to this image. Let's get the image data and do some manipulation to it. Paste. So let's take a look at what the code does that I just copied in here. What we're going to do is get the image data and every 15th row of the image we're going to convert into a five- row-high strip of inverted pixels.
So we're going to take whatever the pixel value is and invert it. I've got a couple of variables here. I've got a variable that's tracking the current row, and I'll start it for row 0. And then I have the maximum row, which is the same as the canvas height. Then I get the image data by calling the ctx.getImageData function, and to get the image data, I want all of the image data. I want from the upper-left corner down to the bottom right. So this will get me all of the bytes in the image. And then from the image data that comes back, I want the data property, because this is the array of bytes, and I'm going to store that in a local variable named pixels.
So now, I'm going to loop over every row in the image. So I'm starting off at row 0. While the current row is less than the maximum row, I'm going to have a five-row-high strip. So I have this local counter named i, which is going to count to our five rows. Now remember, to get access to each one of the rows' bytes, which is what this variable right here is going to keep track of, I need to take the current row, add the local row counter. So we're going to start off at 0 and then go 0, 1, 2, 3, 4, 5 then go to the next row, and so on.
So, to get to the current RowBytes, I have the current row times the canvas width, times four, because there's four bytes in every pixel. So this will move me to the beginning of each row in the image, in the byte array. Once I've done that, I then need to loop across all the bytes in that particular row. So we start off at 0, and then we go to the canvas width times four, so that's all the bytes. And we're going to increment j by four each time, because we need to skip over each pixel's four bytes. So to skip over a pixel, let's just skip over four bytes.
So each time through this loop, we're going to say that the pixel's content at thisRowBytes plus my index is equal to the number 255, because that's the maximum value of any given byte, minus whatever's currently in there. We're going to that for the red, the green, and the blue components of each one of the bytes. We're not going to do anything with the alpha channel, although we could choose to, but we're not going to right now. So we're basically going to invert all of the pixel values.
Then after we've done that, we increment the current row by 15, so we skip over to the next 15 rows, and we let this remove complete. So by the time this loop completes, every 15th row will have a four-row-high strip of inverted pixels. And when that's done, we simply call putImageData to put the image data back into the canvas. So let's save. Let's refresh. You can see that what happened here is you've got five rows where the pixels are all being inverted from their original colors.
So here is the source image, here's the canvas and our loop has gone through and inverted every 15th row of a five-row-high pixel strip. We could stop there, but let's go for the extra point. Let's try using the optional dirty rectangle, and remember this only affects the bytes that are inside this rectangle. So I'm just going to say 50, 50, and let's make the rectangle 400 pixels wide.
Let's make it 200 pixels high. So we'll save, and we will refresh. See that now only the pixels inside of that rectangle are the ones that got affected. So in this example, we've seen how to get access to the raw image data of a canvas, manipulate the pixels, put them back in, and optionally only affect a certain range of pixels by a rectangle.