Tricked.dev

Converting Images to monochrome BMP's using the canvas

Recently i had to create monochrome bmp’s from a image to be able to print them on a thermal printer, i couldn’t find out how to do this online so i made a little code snippet with the help of chatgpt that does.

export async function canvasToMonochromeBMP() {
let canvas = _("canvas");
const ctx = canvas.getContext("2d", { willReadFrequently: true });
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imgData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg;
}
let rows = [];
for (let y = 0; y < canvas.height; y++) {
let row = "";
for (let x = 0; x < canvas.width; x++) {
const i = (y * canvas.width + x) * 4;
row += data[i + 3] == 0 ? "1" : "0";
}
while (row.length % 32 !== 0) {
row += "0";
}
rows.push(row);
}
rows = rows.reverse();
let bits = rows.join("");
const bytes = [];
for (let i = 0; i < bits.length; i += 8) {
bytes.push(parseInt(bits.substr(i, 8), 2));
}
const fileSize = 14 + 40 + 8 + bytes.length;
const dataOffset = 14 + 40 + 8;
const fileHeader = [
0x42,
0x4d,
fileSize & 0xff,
(fileSize >> 8) & 0xff,
(fileSize >> 16) & 0xff,
(fileSize >> 24) & 0xff,
0x00,
0x00,
0x00,
0x00,
dataOffset & 0xff,
(dataOffset >> 8) & 0xff,
(dataOffset >> 16) & 0xff,
(dataOffset >> 24) & 0xff,
];
const infoHeader = [
40,
0,
0,
0,
canvas.width & 0xff,
(canvas.width >> 8) & 0xff,
(canvas.width >> 16) & 0xff,
(canvas.width >> 24) & 0xff,
canvas.height & 0xff,
(canvas.height >> 8) & 0xff,
(canvas.height >> 16) & 0xff,
(canvas.height >> 24) & 0xff,
1,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
0,
];
const palette = [0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00];
const bmpData = new Uint8Array([
...fileHeader,
...infoHeader,
...palette,
...bytes,
]);
return bmpData;
}

Thats it!

Fun fact BMP’s have the data order reversed thats why the rows = rows.reverse(); without that it still works but you just have a bmp with the data in the wrong order lol.

The canvas can create BMP’s and they work fine as BMP’s but they wont be 1 bit which this image will be.

I also have this tiny helper function that turns the canvas black white

function toMonochrome(imgData, threshold = 128) {
const newColorRgb = [0, 0, 0];
for (let i = 0; i < imgData.data.length; i += 4) {
if (imgData.data[i + 3] < 255) {
imgData.data[i] = 255;
imgData.data[i + 1] = 255;
imgData.data[i + 2] = 255;
imgData.data[i + 3] = 0;
continue;
}
const gray =
0.299 * imgData.data[i] +
0.587 * imgData.data[i + 1] +
0.114 * imgData.data[i + 2];
const binary = gray >= threshold ? 255 : 0;
imgData.data[i] = binary === 0 ? newColorRgb[0] : 255;
imgData.data[i + 1] = binary === 0 ? newColorRgb[1] : 255;
imgData.data[i + 2] = binary === 0 ? newColorRgb[2] : 255;
imgData.data[i + 3] = binary === 0 ? 255 : 0;
}
}

I made a little demo down below if you just need to convert a single image and don’t need this in your website

View on Github