paint-brush
Creating a pixelation filter in Javascriptby@radarboy3000
4,276 reads
4,276 reads

Creating a pixelation filter in Javascript

by George GallyJuly 28th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Welcome to the next installment of Creative <a href="https://hackernoon.com/tagged/coding" target="_blank">Coding</a> Basics. You can see previous tutorials <a href="https://medium.com/@radarboy3000" target="_blank">here</a>.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Creating a pixelation filter in Javascript
George Gally HackerNoon profile picture

Welcome to the next installment of Creative Coding Basics. You can see previous tutorials here.

And as always the full code is available on my github: https://github.com/GeorgeGally/creative_coding

A pixelation filter is quite easy to create, and the sampling technique and formula is useful to know in creative coding, especially in computer vision, which I’ll be covering soon.

The essence of the pixelate filter is simply dividing the screen into blocks and then sampling blocks for their colours. We sample as blocks and not every pixel, for the sake of performance. The smaller the sample size the slower our artwork will run.

So let’s start just by dividing the screen into blocks… We do this by looping through all the rows and columns, which slowed down, looks like so:

The formula is a loop within a loop:


// define the sample sizevar sample_size = 20;


// loop through the rows from to to bottom of pagefor (var y= 0; y < h; y+= sample_size) {


// loop through all the columns from left to rightfor (var x = 0; x < w; x+= sample_size) {

 _// do something_

}

}

Which allows us to easily get nice effects like this:

var ctx = createCanvas("canvas1");


// define the sample sizevar sample_size = randomInt(20, 80);

function draw(){

if(chance(200)) sample_size = randomInt(10, 40);


// loop through the rows from to to bottom of pagefor (var y= 0; y < h; y+= sample_size) {

//loop through all the columns from left to right  
for (var x = 0; x < w; x+= sample\_size) {  
  ctx.fillStyle = rgb(random(205));  
  ctx.fillRect(x, y, sample\_size, sample\_size);  
}

}

}

The only new thing there is the function chance(), which is a handy little random helper function I use all the time, which enables me to fire events randomly based on a probability. The code looks like this:



function chance(value){return (random(value) > value-1);}

But I digress.

So now that we have a formula for sampling, let’s sample an image. There’s a tiny bit of theory we need…

The Javascript function for sampling a the canvas is: getImageData(start_x, start_y, sample_width, sample_height), so to sample the whole screen we can simple do:

var sample = ctx.getImageData(0,0,w,h);

This gets us back an object with a data array of rgba values. And all we need now to do is loop through the data values and extract our pixel values.

To get the position of any point on the screen we can use the magic formula:

var pos = (x + y * w);

However, remember that array the data array returned to us has four values for every pixel, so to get that pixel’s position in the array we simple multiply it by 4:

var sample_point = (x + y * w) * 4;

And then the next four values in the array would be that pixel’s rgba values…


// we want the data element of the objectvar sample = ctx.getImageData(0,0,w,h).data;

for (var y= 0; y < h; y+= sample_size) {

for (var x = 0; x < w; x+= sample_size) {

 **var pos = (x + y \* w) \* 4;  
 var red   = sample\[pos\];  
 var green = sample\[pos + 1\];  
 var blue  = sample\[pos + 2\];  
 var alpha  = sample\[pos + 3\];**  
   
_ // ...do something with those values_

}

}

We can actually ignore the alpha value as we won’t be needing it. So to put it neatly into a function, so we can play with it easily, and never think of it again, we do something like this:

function pixelate(sample_size){

var imgData=this.getImageData(0,0,w,h).data;

for (var y= 0; y < h; y+= sample_size) {

for (var x = 0; x < w; x+= sample_size) {

 var pos = (x + y \* w) \* 4;  
 var red   = sample\[pos\];  
 var green = sample\[pos + 1\];  
 var blue  = sample\[pos + 2\];

 ctx.fillStyle = rgb(red, blue, green);  
 ctx.fillRect(x, y, sample\_size, sample\_size);

}

}

And we would it like this:


// create canvasvar ctx = createCanvas("canvas1");


// define the sample sizevar sample_size = randomInt(20, 80);



//load an imagevar img = new Image();img.src = 'img/stevie.jpg';

function draw(){



if(chance(200)) sample_size = randomInt(10, 40);ctx.drawImage(img, 0, 0, w, h);pixelate(sample_size);

}

function pixelate(sample_size){

var imgData=this.getImageData(0,0,w,h).data;

for (var y= 0; y < h; y+= sample_size) {

for (var x = 0; x < w; x+= sample_size) {

 var pos = (x + y \* w) \* 4;  
 var red   = sample\[pos\];  
 var green = sample\[pos + 1\];  
 var blue  = sample\[pos + 2\];

 ctx.fillStyle = rgb(red, blue, green);  
 ctx.fillRect(x, y, sample\_size, sample\_size);

}

}

}

There’s a few tweaks I want to do to the function. Firstly let’s pass a context into it, so we can if we want, have multiple canvases on the screen and only target one of them… So we just pass in an extra argument. The line:

var context = _ctx || ctx;

…is shorthand for saying if the argument _ctx is undefined then use the default variable ctx. This allows me to not have to pass the argument in if I only have one canvas.

function pixelate(sample_size, _ctx){


var context = _ctx || ctx;var sample = context.getImageData(0,0,w,h).data;

for (var y= 0; y < h; y+= sample_size) {

for (var x = 0; x < w; x+= sample\_size) {

 var pos = (x + y \* w) \* 4;  
 var red   = sample\[pos\];  
 var green = sample\[pos + 1\];  
 var blue  = sample\[pos + 2\];  
 **context**.fillStyle = rgb(red, blue, green);  
 **context**.fillRect(x, y, sample\_size, sample\_size);  
}  

}

}

And for extra credit, there’s one more trick we can use to speed things up. The above function is perfectly fine. But as always in Javascript there are many ways to skin a cat.

We can use an unsigned 32-bit integer array, and together with using bitwise operators, we get a bit of added performance… so our final function would look like this:

function pixelate(sample_size, _ctx){


var context = _ctx || ctx;var sourceBuffer32 = new Uint32Array(context.getImageData(0,0,w,h).data.buffer);

for(var y = 0; y < h; y += sample_size){

for(var x = 0; x < w; x += sample\_size){

  **var pos = (x + y \* w);  
  var b = (sourceBuffer32\[pos\] >> 16) & 0xff;  
  var g = (sourceBuffer32\[pos\] >> 8) & 0xff;  
  var r = (sourceBuffer32\[pos\] >> 0) & 0xff;**  
  context.fillStyle = rgb(r,g,b);  
  context.centreFillRect(x, y, sample\_size, sample\_size);

}

}

}

And that’s that. Go forth and pixelate.

As usual the full code is available on my github: https://github.com/GeorgeGally/creative_coding

You can see previous all my tutorials here.

Follow me here if you so desire:

https://www.instagram.com/radarboy3000/

https://twitter.com/radarboy_japan


Radarboy_Radarboy. 145 likes. Art, design visualisation, hacks_www.facebook.com