Skip to content

Instantly share code, notes, and snippets.

@vukicevic
Last active October 31, 2023 08:50
Show Gist options
  • Save vukicevic/8112515 to your computer and use it in GitHub Desktop.
Save vukicevic/8112515 to your computer and use it in GitHub Desktop.
Generate a monochrome bitmap image in JavaScript using a byte-array. The image is generated such that every bit in the array is shown as either a black (1) or white (0) pixel.
/**
* depth: 1 - monochrome
* 4 - 4-bit grayscale
* 8 - 8-bit grayscale
* 16 - 16-bit colour
* 32 - 32-bit colour
**/
function drawArray(arr, depth) {
var offset, height, data, image;
function conv(size) {
return String.fromCharCode(size&0xff, (size>>8)&0xff, (size>>16)&0xff, (size>>24)&0xff);
}
offset = depth <= 8 ? 54 + Math.pow(2, depth)*4 : 54;
height = Math.ceil(Math.sqrt(arr.length * 8/depth));
//BMP Header
data = 'BM'; // ID field
data += conv(offset + arr.length); // BMP size
data += conv(0); // unused
data += conv(offset); // pixel data offset
//DIB Header
data += conv(40); // DIB header length
data += conv(height); // image height
data += conv(height); // image width
data += String.fromCharCode(1, 0); // colour panes
data += String.fromCharCode(depth, 0); // bits per pixel
data += conv(0); // compression method
data += conv(arr.length); // size of the raw data
data += conv(2835); // horizontal print resolution
data += conv(2835); // vertical print resolution
data += conv(0); // colour palette, 0 == 2^n
data += conv(0); // important colours
//Grayscale tables for bit depths <= 8
if (depth <= 8) {
data += conv(0);
for (var s = Math.floor(255/(Math.pow(2, depth)-1)), i = s; i < 256; i += s) {
data += conv(i + i*256 + i*65536);
}
}
//Pixel data
data += String.fromCharCode.apply(String, arr);
//Image element
image = document.createElement('img');
image.src = 'data:image/bmp;base64,' + btoa(data);
return image;
}
/*Usage example, visualize random numbers generated by Math.random */
for (var a= [], i = 0; i < 8192; i++) {
a[i] = Math.floor(Math.random()*256);
}
document.body.appendChild(drawArray(a, 1));
document.body.appendChild(drawArray(a, 4));
document.body.appendChild(drawArray(a, 8));
document.body.appendChild(drawArray(a, 16));
document.body.appendChild(drawArray(a, 32));
@kalzso
Copy link

kalzso commented Oct 28, 2014

Helo Vukicevic!

This is awesome, can you make it even better? For example to make it colored instead of monochrome?

Pozdrav ;)

@vukicevic
Copy link
Author

Hi!

I've updated this gist to add some colour support, but it's a tricky problem. You can create 1,4,8 bit grayscale or 16 or 32 bit colour as output.

There are padding issues at different bit lengths, which would make the code much longer. 2-bit grayscale doesn't work for some reason, I don't have time to debug, you can fix it if you wish.

@ApoorvaJ
Copy link

ApoorvaJ commented Jan 5, 2016

Small correction: Comments on line 26 and 27 are incorrect. Width should be specified before height in DIB header.

@Lunasole
Copy link

Nice one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment