Created
November 16, 2019 11:01
-
-
Save hamrammi/018d8d546a5aa8c63a93425af144106c to your computer and use it in GitHub Desktop.
Transform BMP images: rotate180, mirror vertically, mirror horizontally
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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