132 lines
3.2 KiB
JavaScript

'use strict';
function set_byte_depth(depth) {
return function (byte) {
// calculate significant bits, e.g. for depth=2 it's 0, 1, 2 or 3
let value = ~~(byte / (256 >> depth));
// spread those bits around 0..255 range, e.g. for depth=2 it's 0, 85, 170 or 255
let scale = (2 << (depth - 1)) - 1;
return (value * 0xFFFF / scale) >> 8;
};
}
module.exports.set_depth = function set_depth(glyph, depth) {
let pixels = [];
let fn = set_byte_depth(depth);
for (let y = 0; y < glyph.bbox.height; y++) {
pixels.push(glyph.pixels[y].map(fn));
}
return Object.assign({}, glyph, { pixels });
};
function count_bits(val) {
let count = 0;
val = ~~val;
while (val) {
count++;
val >>= 1;
}
return count;
}
// Minimal number of bits to store unsigned value
module.exports.unsigned_bits = count_bits;
// Minimal number of bits to store signed value
module.exports.signed_bits = function signed_bits(val) {
if (val >= 0) return count_bits(val) + 1;
return count_bits(Math.abs(val) - 1) + 1;
};
// Align value to 4x - useful to create word-aligned arrays
function align4(size) {
if (size % 4 === 0) return size;
return size + 4 - (size % 4);
}
module.exports.align4 = align4;
// Align buffer length to 4x (returns copy with zero-filled tail)
module.exports.balign4 = function balign4(buf) {
let buf_aligned = Buffer.alloc(align4(buf.length));
buf.copy(buf_aligned);
return buf_aligned;
};
// Pre-filter image to improve compression ratio
// In this case - XOR lines, because it's very effective
// in decompressor and does not depend on bpp.
module.exports.prefilter = function prefilter(pixels) {
return pixels.map((line, l_idx, arr) => {
if (l_idx === 0) return line.slice();
return line.map((p, idx) => p ^ arr[l_idx - 1][idx]);
});
};
// Convert array with uint16 data to buffer
module.exports.bFromA16 = function bFromA16(arr) {
const buf = Buffer.alloc(arr.length * 2);
for (let i = 0; i < arr.length; i++) buf.writeUInt16LE(arr[i], i * 2);
return buf;
};
// Convert array with uint32 data to buffer
module.exports.bFromA32 = function bFromA32(arr) {
const buf = Buffer.alloc(arr.length * 4);
for (let i = 0; i < arr.length; i++) buf.writeUInt32LE(arr[i], i * 4);
return buf;
};
function chunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
// Dump long array to multiline format with X columns and Y indent
module.exports.long_dump = function long_dump(arr, options = {}) {
const defaults = {
col: 8,
indent: 4,
hex: false
};
let opts = Object.assign({}, defaults, options);
let indent = ' '.repeat(opts.indent);
return chunk(Array.from(arr), opts.col)
.map(l => l.map(v => (opts.hex ? `0x${v.toString(16)}` : v.toString())))
.map(l => `${indent}${l.join(', ')}`)
.join(',\n');
};
// stable sort by pick() result
module.exports.sort_by = function sort_by(arr, pick) {
return arr
.map((el, idx) => ({ el, idx }))
.sort((a, b) => (pick(a.el) - pick(b.el)) || (a.idx - b.idx))
.map(({ el }) => el);
};
module.exports.sum = function sum(arr) {
return arr.reduce((a, v) => a + v, 0);
};