Friday, June 17, 2016

Pushing data to canvas

Canvas object are part of HTML5 and provides a way to direcly manipulate pixel data in an HTML page. See w3schools, MDN and html5canvastutorials for an introduction.

In this short post I'll illustrate how to programmatically create a canvas and how to fill it with pixel data coming from another source (e.g. a decompressed image coming from a decoder).
canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.style.backgroundColor = "#0D0E1B"; 
 
This will create a canvas object, which can be later added to the page dom.
In this case we set the basic properties of the canvas, width, height and the background color (question: what happens if we change it later?).

Now suppose you want to push data in the canvas. First of all we need to make sure that the data is in the RGBA format, that is we have an array of UINT8 integers (we don't assume padding at the end of each line, so we assume that each line is long exactly WIDTH*4 bytes).

Then we need to access the data through the ImageData object in the canvas object (see here for details).

It works more or less like this:

var ctx = canvas.getContext('2d');
var imageData = ctx.getImageData(0, 0, width, height);
imageData.data.set(argbData);
ctx.putImageData(imageData, 0, 0); 
 
 
Note the additional parameters in ctx.getImageData() and in ctx.putImageData(), since the general syntax is:

ctx.getImageData(left, top, width, height);

that is it conveniently allows to extract or fill only a rectangular fill of the canvas.


YUV to RGB conversion in Javascript

There are many ways to convert YUV (assuming 420P) to RGB in Javascript.

function clamp(n, low, high) {
    if (n < low)  { return(low);  }
    if (n > high) { return(high); }
    return n;
}
 
function yuv2rgb(y, u, v) {
    r = clamp(Math.floor(y+1.4075*(v-128)), 0, 255);
    g = clamp(Math.floor(y-0.3455*(u-128)-(0.7169*(v-128))), 0, 255);
    b = clamp(Math.floor(y+1.7790*(u-128)), 0, 255);
    return({r:r,g:g,b:b});
} 

The clamp function is required since the output result may not be comprised in the [0-255] range.
This is assuming that YUV values are in studio level range (16-235 for Y, 16-240 for U and V).

Note that it is assuming float conversion since Javascript perform all operation with floats
(since it has no notion of integer - unless using asm.js).

More on the meaning of the coefficients and about how to compute them (hopefully) in a later
post.