108 lines
2.9 KiB
JavaScript

'use strict';
//const debug = require('debug')('compress');
function count_same(arr, offset) {
let same = 1;
let val = arr[offset];
for (let i = offset + 1; i < arr.length; i++) {
if (arr[i] !== val) break;
same++;
}
return same;
}
//
// Compress pixels with RLE-like algorythm (modified I3BN)
//
// 1. Require minimal repeat count (1) to enter I3BN mode
// 2. Increased 1-bit-replaced repeat limit (2 => 10)
// 3. Length of direct repetition counter reduced (8 => 6 bits).
//
// pixels - flat array of pixels (one per entry)
// options.bpp - bits per pixels
//
module.exports = function compress(bitStream, pixels, options) {
const opts = Object.assign({}, { repeat: 1 }, options);
// Minimal repetitions count to enable RLE mode.
const RLE_SKIP_COUNT = 1;
// Number of repeats, when `1` used to replace data
// If more - write as number
const RLE_BIT_COLLAPSED_COUNT = 10;
const RLE_COUNTER_BITS = 6; // (2^bits - 1) - max value
const RLE_COUNTER_MAX = (1 << RLE_COUNTER_BITS) - 1;
// Force flush if counter dencity exceeded.
const RLE_MAX_REPEATS = RLE_COUNTER_MAX + RLE_BIT_COLLAPSED_COUNT + 1;
//let bits_start_offset = bitStream.index;
let offset = 0;
while (offset < pixels.length) {
const p = pixels[offset];
let same = count_same(pixels, offset);
// Clamp value because RLE counter density is limited
if (same > RLE_MAX_REPEATS + RLE_SKIP_COUNT) {
same = RLE_MAX_REPEATS + RLE_SKIP_COUNT;
}
//debug(`offset: ${offset}, count: ${same}, pixel: ${p}`);
offset += same;
// If not enough for RLE - write as is.
if (same <= RLE_SKIP_COUNT) {
for (let i = 0; i < same; i++) {
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits`);
}
continue;
}
// First, write "skipped" head as is.
for (let i = 0; i < RLE_SKIP_COUNT; i++) {
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits`);
}
same -= RLE_SKIP_COUNT;
// Not reached state to use counter => dump bit-extended
if (same <= RLE_BIT_COLLAPSED_COUNT) {
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits (val)`);
for (let i = 0; i < same; i++) {
/*eslint-disable max-depth*/
if (i < same - 1) {
bitStream.writeBits(1, 1);
//debug('==> 1 bit (rle repeat)');
} else {
bitStream.writeBits(0, 1);
//debug('==> 1 bit (rle repeat last)');
}
}
continue;
}
same -= RLE_BIT_COLLAPSED_COUNT + 1;
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits (val)`);
for (let i = 0; i < RLE_BIT_COLLAPSED_COUNT + 1; i++) {
bitStream.writeBits(1, 1);
//debug('==> 1 bit (rle repeat)');
}
bitStream.writeBits(same, RLE_COUNTER_BITS);
//debug(`==> 4 bits (rle repeat count ${same})`);
}
//debug(`output bits: ${bitStream.index - bits_start_offset}`);
};