Making and Modifying Images with Canvas

by Patrick Horgan

(Back to canvas tutorials)

The Basics

I'm going to be fast and loose about how to use a canvas. If you don't know anything about using a canvas from javascript, you might want to look at HTML5 Canvas Basics.

The data url

In August of 1998, a "data" URL scheme was published as RFC 2397. (If you don't know what an RFC is, see RFCs and a Script to get them) It allows you to embed data in your web page instead of making separate requests across the web. As an example, the image of me you see here is represented in this web page by 166 lines of data that starts like:

<img src= "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAE8AAABkCAMAAADwgZ1BAAAABGdBTUEAALGPC/xhBQAACh5pQ0NQ SUNDIHByb2ZpbGUAAEiJtVZ5PJRrG37e9519sc2Q3di3RpYwyL6TyE6bMTMYy2DMoNImqXAiSbYS . . .

I made it by loading the original picture into gimp, setting the graphics mode to indexed with an optimized pallete (to reduce the file size), exporting as a png file (a nice portable open format), and then using the base64 application on linux to encode the result into a text file. Then from my editor (vi), I read it in between the comma and the last quote in:

<img class='floatleft' src= "data:image/png;base64,">

A Canvas Can Give You a data url

Another way to get a data url, is to draw anything you want on a canvas, and then ask the canvas to give you back what you've drawn as a data url by calling its toDataURL() method. At that point you can assign the returned value as the src value of anything that you'd normally use image data for. For example you can do this:

<img id='bigdot'></img> <script> var img=document.getElementById('bigdot'); var cvs=document.createElement('canvas'); cvs.width=cvs.height=100; var ctx=cvs.getContext('2d'); ctx.fillStyle='rgb(240,120,18)'; ctx.arc(50,50,50,0,Math.PI*2,false); ctx.fill(); var dataurl=cvs.toDataURL(); img.src=dataurl; </script>

Which is just how I got this orange dot to appear. Given the image tag in the document with the id 'bigdot', I can create a canvas, draw the dot on it, ask it for the data url and then set the src field for the img tag to it.

So why would you really do this?

Recently I made a page for minecraft designers that wanted to make circles out of blocks . I had two needs that canvas could meet. First, if the images I needed for grass and stone wouldn't load, I wanted to make blank squares of the appropriate color to substitute, and second, no matter how I got the images, every time someone changed the number of blocks across, I needed to scale the stone and grass images to a particular size to fill up the canvas.

Make a rectangle of a particular color

var makerectimage=function(xsize,ysize,color,callback,brdrsz,brdrclr)

This is a method that uses a canvas to make a rectangle of a particular size. The arguments are:

xsize
The width of the rectangle
ysize
The height of the rectangle
color
The color of the rectangle
callback
A method to call with the javascript Image object
brdrsz
An optional border width, defaults to 0px
brdrclr
An optional border color defaults to rgb(0,0,0)

Let's step through and see how it does it

var makerectimage=function(xsize,ysize,color,callback,brdrsz,brdrclr) { if (typeof brdrsz == "undefined" ){ brdrsz = 0; }else { if (typeof brdrclr == "undefined" ){ brdrclr = 'rgb(0,0,0)'; } } xsize=Math.floor(xsize); ysize=Math.floor(ysize);

The first thing we do is deal with our two optional arguments. First, if the border width is not set, we default to 0 pixels, i.e. no border, and then ignore the border color. If border width is specified, then we check to see if the border color is specified, if not we default it to black. Then we make sure our x/ysize arguments are integers.

var tmpcvs=document.createElement('canvas'); // and set it to be the image size tmpcvs.height=ysize; tmpcvs.width=xsize;

Create a canvas and set it to the size we were asked to make the image.

// grab its context so we can draw var tmpctx=tmpcvs.getContext('2d'); // and draw the image scaled to that size tmpctx.fillStyle=color; tmpctx.fillRect(0,0,xsize,ysize);

Grab the context so we can use it to draw, and then fill the canvas with the requested color.

if(brdrsz>0){ tmpctx.strokeStyle=brdrclr; tmpctx.lineWidth=brdrsz; tmpctx.strokeRect(0,0,xsize,ysize); }

Now, if they wanted a border, we set the strokeStyle to the passed in border color, and the lineWidth to the passed in border width, and then stroke the edge of the image.

// now we get the data from the canvas var dataurl=tmpcvs.toDataURL(); // create a new image, var retimage=new Image();

We're almost done. We've gotten the image data from the canvas by calling the canvas' method to DataURL(). We've created a new Image to return to the caller. The only thing left to do is to set the src attribute of the Image to the dataurl. Before we can do that we have to set the onload method of the image. See,

retimage.onload=function(){ callback(retimage); } retimage.src=dataurl; }

setting the src attribute of an Image object is always asynchronous. That means that if you set it, return the Image and the caller uses it right away, it will probably be blank. That's why we needed to have the caller of our routine pass a callback method. We set the onload of the image to a method that invokes the callback to return the image. Then we set the src attribute of the image.

Using makerectimage()

makerectimage() might be used like this:

<img id='doublebox'> <script> var db=document.getElementById('doublebox'); var setdb=function(img){ console.log("setdb"+img); db.src=img.src; } makerectimage(120,140,'lime',setdb,60,'yellow'); </script>

We get a reference to an img tag in the html document, set up the callback routine, setdb(), to set the src of the img tag to the src of the returned Image, and then call makerectimage, passing it the arguments that tell it how to draw the image and to callback to our setdb() method when done.

Scale an image to another size.

var scale_image=function(img,xsize,ysize,callback,error_cb)

This is a method that takes an image and scales it to a new image. The arguments are:

img
The image we want to scale
xsize
The desired width of the final scaled image
ysize
The desired height of the final scaled image
callback
A method to call with the scaled Image object
error_cb
A method to call when something failed.

Let's step through and see how it does it

var scale_image=function(img,xsize,ysize,callback,error_cb){ xsize=Math.floor(xsize); ysize=Math.floor(xsize); var tmpcvs=document.createElement('canvas'); // and set it to be the image size tmpcvs.height=ysize; tmpcvs.width=xsize; // grab its context so we can draw

The first thing we do is to make sure our x/y sizes are integers, make a temporary canvas to draw into, and set its width and height to xsize and ysize.

var tmpctx=tmpcvs.getContext('2d'); // and draw the image scaled to that size tmpctx.drawImage(img,0,0,xsize,ysize);

We draw the image into the canvas at the new size, and voila, we're done scaling the image. Next we need to get it back as an image to return.

var dataurl=tmpcvs.toDataURL(); // create a new image, var sclimage=new Image(); // and set it to the data from the canvas. Once that's done, // we will call the callback passing it the contex, and the scaled // image. sclimage.onload=function(){ callback(sclimage); } sclimage.onerror=function(){ error_cb(); } sclimage.src=dataurl;

The next thing to do is to get the image data from the canvas by calling the canvas' toDataURL() method. Then we only need to create a new Image() set up its onlyad and onerror methods and then assign the dataurl from the canvas to its src attribute. When the image gets done loading it calls the callback passing the scaled image. If there's an error it calls the error callback but returns nothing.

}
(Back to canvas tutorials)