Skip to content

Instantly share code, notes, and snippets.

@hamrammi
Created November 16, 2019 11:01
Show Gist options
  • Save hamrammi/018d8d546a5aa8c63a93425af144106c to your computer and use it in GitHub Desktop.
Save hamrammi/018d8d546a5aa8c63a93425af144106c to your computer and use it in GitHub Desktop.
Transform BMP images: rotate180, mirror vertically, mirror horizontally
const fs = require('fs')
const fileName = 'RAY.BMP'
const readBmp = fileName => new Promise((resolve, reject) => {
fs.readFile(fileName, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
const copyBuffer = buffer => Uint8Array.prototype.slice.call(buffer)
const parse = bufferSlice =>
parseInt(copyBuffer(bufferSlice).reverse().toString('hex'), 16)
const readHeader = bmp => ({
'signature': parse(bmp.slice(0x00, 0x02)),
'fileSize': parse(bmp.slice(0x02, 0x02 + 4))
})
const readInfoHeader = bmp => ({
'size': parse(bmp.slice(0x0e, 0x0e + 4)),
'width': parse(bmp.slice(0x12, 0x12 + 4)),
'height': parse(bmp.slice(0x16, 0x16 + 4)),
'bitsPerPixel': parse(bmp.slice(0x1c, 0x1c + 2)),
'imageSize': parse(bmp.slice(0x22, 0x22 + 4)),
})
const readColorTable = (bitsPerPixel, bmp) => {
if (bitsPerPixel === 24) {
return {
'bytesLength': 0,
'data': null,
}
}
const bytesLength = Math.pow(2, bitsPerPixel)
return {
'bytesLength': bytesLength,
'data': bmp.slice(0x36, 0x36 + bytesLength),
}
}
const readPixelData = (colorTableBytesLength, bmp) => {
const offset = 0x36 + colorTableBytesLength
return copyBuffer(bmp.slice(offset))
}
const transform = (infoHeader, buf, transform = 'rotate180') => {
if (infoHeader['bitsPerPixel'] === 24) {
const totalPixels = infoHeader['width'] * infoHeader['height']
const bytesPerPixel = infoHeader['bitsPerPixel'] / 8
let pixelCounter = 0
let pixels = []
while (pixelCounter < totalPixels) {
const pixel = buf.slice(
pixelCounter * bytesPerPixel,
pixelCounter * bytesPerPixel + bytesPerPixel
)
pixels.push([pixel[0], pixel[1], pixel[2]])
pixelCounter += 1
}
if (transform === 'rotate180') {
return Buffer.from(pixels.reverse().flat())
}
if (transform === 'mirrorHorizontally') {
const rows = []
for (let i = 0, l = pixels.length, chunk = infoHeader['width']; i < l; i += chunk) {
rows.push(pixels.slice(i, i + chunk))
}
return Buffer.from(rows.reverse().flat(Infinity))
}
if (transform === 'mirrorVertically') {
const rows = []
for (let i = 0, l = pixels.length, chunk = infoHeader['width']; i < l; i += chunk) {
rows.push(pixels.slice(i, i + chunk).reverse())
}
return Buffer.from(rows.flat(Infinity))
}
}
return buf
}
readBmp(fileName).then(bmp => {
const header = readHeader(bmp)
const infoHeader = readInfoHeader(bmp)
const bitsPerPixel = infoHeader['bitsPerPixel']
const colorTable = readColorTable(bitsPerPixel, bmp)
const pixelData = readPixelData(colorTable['bytesLength'], bmp)
;['rotate180', 'mirrorVertically', 'mirrorHorizontally'].forEach(direction => {
const newPixelData = transform(infoHeader, pixelData, direction)
const newFile = Buffer.concat([bmp.slice(0, 0x36 + colorTable['bytesLength']), newPixelData], bmp.length)
fs.writeFile(`${direction}.bmp`, newFile, (err) => {
if (err) throw err
console.log(`File ${direction}.bmp saved`);
})
})
console.log(header, infoHeader);
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment