pxt-ev3/scripts/rgf2png.js

173 lines
4.2 KiB
JavaScript
Executable File

#!/usr/bin/env node
const fs = require("fs")
const zlib = require("zlib")
function compressImg(fn) {
const rgf = fs.readFileSync(fn)
const width = rgf[0]
const height = rgf[1]
const expSz = ((width + 7) >> 3) * height + 2
console.log(`w=${width} h=${height} sz=${rgf.length} exp=${expSz}`)
let crcTable
function crc32(buf) {
if (!crcTable) {
crcTable = []
for (let i = 0; i < 256; i++) {
let curr = i;
for (let j = 0; j < 8; j++) {
if (curr & 1) {
curr = 0xedb88320 ^ (curr >>> 1);
} else {
curr = curr >>> 1;
}
}
crcTable[i] = curr
}
}
let crc = -1;
for (var i = 0; i < buf.length; i++) {
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
}
return (crc ^ -1) >>> 0;
}
/*
Width 4 bytes
Height 4 bytes
Bit depth 1 byte
Colour type 1 byte
Compression method 1 byte
Filter method 1 byte
Interlace method 1 byte
*/
const chunks = []
function addChunk(mark, addsize) {
const buf = Buffer.alloc(mark.length + addsize)
for (let i = 0; i < mark.length; ++i) {
buf[i] = mark.charCodeAt(i)
}
chunks.push(buf)
return buf
}
const hd = addChunk("IHDR", 4 + 4 + 5)
hd.writeInt32BE(width, 4)
hd.writeInt32BE(height, 8)
hd[12] = 1 // bit depth
hd[13] = 0 // color type - grayscale
hd[14] = 0 // compression - deflate
hd[15] = 0 // filter method
hd[16] = 0 // interlace method - no interlace
const scanlines = []
const scanlen = (width + 7) >> 3
let srcptr = 2
let srcmask = 0x01
for (let y = 0; y < height; ++y) {
const scan = Buffer.alloc(1 + scanlen)
scanlines.push(scan)
let dstptr = 1
let dstmask = 0x80
for (let x = 0; x < width; ++x) {
if (!(rgf[srcptr] & srcmask)) {
scan[dstptr] |= dstmask
}
dstmask >>= 1;
if (dstmask == 0) {
dstmask = 0x80
dstptr++
}
srcmask <<= 1;
if (srcmask > 0x80) {
srcmask = 0x01
srcptr++
}
}
if (srcmask != 0x01) {
srcmask = 0x01
srcptr++
}
if (false) {
// seems to increase file size
scan[0] = 1 // sub
let prev = 0
for (let i = 1; i < scan.length; ++i) {
let p = scan[i]
scan[i] = (p - prev) & 0xff
prev = p
}
}
}
const dat = zlib.deflateSync(Buffer.concat(scanlines), {
level: 9
})
const idat = addChunk("IDAT", dat.length)
dat.copy(idat, 4)
addChunk("IEND", 0)
const output = [new Buffer([137, 80, 78, 71, 13, 10, 26, 10])]
function intBuf(v) {
let b = new Buffer(4)
b.writeUInt32BE(v, 0)
return b
}
for (let ch of chunks) {
output.push(intBuf(ch.length - 4))
output.push(ch)
output.push(intBuf(crc32(ch)))
}
let outp = Buffer.concat(output)
return outp
}
let sz = 0
let bf
let out = {
"*": {
namespace: "images",
dataEncoding: "base64",
mimeType: "image/png"
}
}
let ts = "namespace images {\n"
for (let i = 2; i < process.argv.length; ++i) {
let fn = process.argv[i]
let m = /([^\/]+\/[^/]+)\.rgf$/.exec(fn)
let bn = m[1]
bf = compressImg(fn)
bn = bn.replace(/\//g, " ")
.toLowerCase()
.replace(/(\d)\s+/g, (f, n) => n)
.replace(/\s+(.)/g, (f, l) => l.toUpperCase())
out[bn] = bf.toString("base64")
sz += bf.length
ts += ` //% fixedInstance jres\n`
ts += ` export const ${bn} = screen.unpackPNG(hex\`\`);\n`
}
ts += `}\n`
console.log("total " + sz)
fs.writeFileSync("out.json", JSON.stringify(out, null, 4))
fs.writeFileSync("out.ts", ts)
fs.writeFileSync("out.png", bf)
//if (require("os").platform() == "darwin")
// require('child_process').execSync("afplay out.wav")
// TODO also play on Windows