Image to G-code in Javascript (part 1)
Years ago I wrote a little script that converts images into G-code to CNC mill heightmaps. Some weeks ago I created a new app based on the old script and added other features. This app is called https://gcode.studio and is currently used by over 170 users from all over the world. Some of those users are students that ask me questions about the inner workings. This post will explain some of the basics in Javascript.
Loading image data
The first step is to load image data in Javascript. For this you need an Image and canvas object. First let's create the Image object: ```let image = new Image();``` When we set an url as source for the image it will start to load immediately. After loading, the Image will fire a `load` event. We can use this event to check if the image is loaded. So let's add an event listener to the image: ``` let image = new Image(); image.addEventListener('load', function() { // here will be our script that reads the image data }); ``` And now we can set the source of the Image so it starts loading: ``` let image = new Image(); image.addEventListener('load', function() { // here will be our script that reads the image data }); image.src = 'https://www.example.com/myimage.jpg'; ```
When the image is loaded we want to read the image data. This can only be done by first copying the pixels of the Image object to a canvas object. Let's create a canvas object inside the load event handler and make it the same size as the image: ``` image.addEventListener('load', function() { let canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; }); ``` We now can get the 2d drawing context from the canvas and draw the image on the canvas: ``` ... canvas.height = image.height; let context = canvas.getContext('2d'); context.drawImage( image, // source image 0, // source x position 0, // source y position image.width, // source with image.height, // source height 0, // destination x position 0, // destination y position canvas.width, // destination width canvas.height // destination height ); ```
The 2d drawing context of a canvas can be used for drawing, but you can also get the underlying pixel data. The method for this is `getImageData`. This method will return an ImageData object containing the raw pixels: ``` let imageData = context.getImageData( 0, // x position 0, // y position canvas.width, // width canvas.height // height ); ``` The variable imageData contains 4 read-only properties. The `width`, `height`, `colorSpace` and the `data`. The `data` is what we are most interested in. This property contains the raw image data.
Understanding the raw pixel data
`imageData.data` contains the raw pixels of the canvas. This is in fact an `Uint8ClampedArray`. In other words: a list with only positive real numbers in the range of 0 to 255. Each pixel takes up 4 items in the list, for `R`ed, `G`reen, `B`lue and `A`lpha. If the canvas only contained one pixel the array would look something like: `[255, 255, 255, 255]`. A two pixel canvas would result in an array of 8 items, and so on.
To loop through the pixels we could use the following code: ``` for(let i = 0; i < imageData.data.length; i += 4) { let r = imageData.data[i]; let g = imageData.data[i + 1]; let b = imageData.data[i + 2]; let a = imageData.data[i + 3]; } ```
We now can read the raw pixel data from an image. In my next post I will give some examples of how we can prepare the image data to generate G-code: https://speakr.blog/thuijzer/posts/3