noVNC/core/ast2100/ast2100util.js

285 lines
9.0 KiB
JavaScript

/* (c) Copyright 2015-2017 Kevin Kelley <kelleyk@kelleyk.net>. */
function fmt_rgb_buf (n, buf) {
var s = "";
for (var y = 0; y < n; ++y) {
for (var x = 0; x < n; ++x) {
var offset = ((y*n)+x)*4;
s += fmt_u8(buf[offset+0]) + fmt_u8(buf[offset+1]) + fmt_u8(buf[offset+2]) + fmt_u8(buf[offset+3]) + ' ';
}
s += '\n';
}
return s;
}
var fmt_u8 = function (x) {
var pad = '00';
var s = x.toString(16);
return pad.substring(0, pad.length - s.length) + s;
};
var fmt_u16 = function (x) {
var pad = '0000';
var s = x.toString(16);
return pad.substring(0, pad.length - s.length) + s;
};
var fmt_u32 = function (x) { // cheesy way to ger around the sign bit
return fmt_u16(x >>> 16) + fmt_u16(x & 0xFFFF);
};
var fmt_s8 = function (x) {
if (x < 0)
x += (1 << 8);
return fmt_u8(x);
};
var fmt_s16 = function (x) {
if (x < 0)
x += (1 << 16);
return fmt_u16(x);
};
var fmt_s32 = function (x) {
if (x < 0)
x += (1 << 32);
return fmt_u32(x);
};
var fmt_array = function(f, xx) {
xx = Array.from(xx); // if xx is a typed array, the strings fmt_u8 returns will be silently coerced back to numbers
return '[' + xx.map(f).join(', ') + ']';
};
var fmt_u8a = function (xx) { return fmt_array(fmt_u8, xx); };
var fmt_u16a = function (xx) { return fmt_array(fmt_u16, xx); };
var fmt_u32a = function (xx) { return fmt_array(fmt_u32, xx); };
var fmt_s8a = function (xx) { return fmt_array(fmt_s8, xx); };
var fmt_s16a = function (xx) { return fmt_array(fmt_s16, xx); };
var fmt_s32a = function (xx) { return fmt_array(fmt_s32, xx); };
var inRangeIncl = function (x, a, b) {
return (x >= a && x <= b);
};
var inRange = function (x, a, b) {
return (x >= a && x < b);
};
var swap32 = function (val) {
return ((val & 0xFF) << 24)
| ((val & 0xFF00) << 8)
| ((val >> 8) & 0xFF00)
| ((val >> 24) & 0xFF);
};
var arrayEq = function (a, b) {
if (a.length != b.length)
return false;
for (var i = 0; i < a.length; ++i)
if (a[i] != b[i])
return false;
return true;
};
var isEmpty = function (obj) {
for(var prop in obj)
if(obj.hasOwnProperty(prop))
return false;
return true;
};
var clamp = function (x) {
x = ~~x;
if (x <= 0)
return 0;
if (x >= 255)
return 255;
return x;
};
var BitStream;
var JpegHuffmanTable;
(function () {
"use strict";
BitStream = function (defaults) {
// ATEN scan data does not treat 0xFF bytes specially.
this._interpretMarkers = false;
// True iff we've encountered "end-of-image".
this._eoi = false;
this._data = defaults.data; // uint8[]
if (this._data === undefined)
throw 'BitStream constructor requires argument "data"!';
this._nextDword = 0; // uint8
// Fill the readbuf and reservoir with the first two dwords in the data
// buffer.
this._reservoir = 0;
this._bitsInReservoir = 0;
this._refill();
this._readbuf = this._reservoir;
this._reservoir = 0;
this._bitsInReservoir = 0;
this._refill();
};
BitStream.prototype = {
getPos: function () {
return (this._nextDword * 32) - (32 + this._bitsInReservoir);
},
skip: function (bits) {
while (bits > 0) {
var n = Math.min(32, bits);
this.read(n);
bits -= n;
}
},
read: function (bits) {
// JavaScript's bitwise operators have this limitation.
if (bits >= 32)
throw 'Number of bits must be less than 32.';
// this.checkInvariants();
var that = this;
var moveFromReservoir = function (n) {
that._readbuf = (that._readbuf << n) | (that._reservoir >>> (32 - n));
that._reservoir <<= n;
that._bitsInReservoir -= n;
};
var retval = this._readbuf >>> (32 - bits); // same as this.peek(bits)
if (bits > this._bitsInReservoir) {
bits -= this._bitsInReservoir;
moveFromReservoir(this._bitsInReservoir);
this._refill();
}
moveFromReservoir(bits);
return retval;
},
_refill: function () {
// this.checkInvariants();
// N.B.: Must use >>> for unsigned shift; >> is sra.
if (this._bitsInReservoir != 0)
throw 'Oops: in _refill(), bitsInReservoir='+this._bitsInReservoir;
for (var i = 0; i < 4; ++i) {
var x = this._data[(4 * this._nextDword) + i];
if (x === undefined)
throw 'BitStream overran available data!';
// TODO: if interpretMarkers ...
this._reservoir = (this._reservoir << 8) | x;
this._bitsInReservoir += 8;
}
this._nextDword += 1;
this._reservoir = swap32(this._reservoir);
// this.checkInvariants();
},
peek: function (bits) {
if (bits >= 32) // due to the fact that the bitwise operators have this limitation
throw 'Number of bits must be less than 32.';
// this.checkInvariants();
return this._readbuf >>> (32 - bits);
},
showDebug: function () {
console.log('stream readbuf='+fmt_u32(this._readbuf)+' reservoir='+fmt_u32(this._reservoir)+' bitsLeft='+this._bitsInReservoir+'d nextDword='+fmt_u16(this._nextDword));
},
checkInvariants: function () {
var err = null;
if (this._bitsInReservoir < 0 || this._bitsInReservoir > 32)
err = 'Oops: BitStream invariant violated: bitsInReservoir='+this._bitsInReservoir;
if (err) {
console.log(err);
this.showDebug();
throw err;
}
}
};
JpegHuffmanTable = function (defaults) {
if (!defaults)
defaults = {};
if (!defaults.bits || !defaults.huffval)
throw 'Arguments bits, huffval are required.';
this._huffsize = new Uint8Array(1<<16);
this._huffval_lookup = new Uint8Array(1<<16);
// 1 to 16, plus a useless 0th element to make indexing nicer.
this._bits = new Uint8Array(17);
this._buildTables(defaults.bits, defaults.huffval);
};
JpegHuffmanTable.prototype = {
_buildTables: function (bits, huffval) {
// First, copy BITS.
for (var i = 0; i < 17; ++i) {
this._bits[i] = bits[i];
// This follows from the fact that the all-ones codeword of each
// length is reserved as a prefix for longer codewords, which
// implies that there may not be more than (2**i)-1 codewords of
// length i bits.
if ((1 << i) <= bits[i])
throw 'Oops: bad BITS.';
}
// Calculate Huffman codewords. The best explanation for this is a
// graphical one. Refer to 'Binary Tree Expansin of DHT' figure
// here:
// http://www.impulseadventure.com/photo/jpeg-huffman-coding.html.
var next_codeword = 0;
var codeword_qty = 0;
var codeword_idx = 0; // index into HUFFVAL
for (var code_len = 1; code_len < 17; ++code_len) {
for (var i = 0; i < this._bits[code_len]; ++i) {
for (var j = 0; j < (1 << (16 - code_len)); ++j) {
this._huffsize[next_codeword + j] = code_len;
this._huffval_lookup[next_codeword + j] = huffval[codeword_idx];
}
next_codeword += (1 << (16 - code_len));
codeword_idx += 1;
}
}
if (codeword_idx != huffval.length)
throw 'Oops: all codewords should be used!';
},
// Reads the next code from the stream. Consumes up to 16 bits.
// Returns the corresponding codeword (the value that the code
// represents, which is always exactly one byte).
readCode: function (stream) {
// Some number of bits, 16 < x <= 0, on the least-significant end of
// this 16b value are not relevant, depoending on how long the code
// actually is. However, we've built our lookup tables so that,
// regardless of what value the irrelevant bits have, we will get
// the same result.
var fixedlenCode = stream.peek(16); // uint16
var codeLen = this._huffsize[fixedlenCode];
stream.skip(codeLen);
var codeword = this._huffval_lookup[fixedlenCode];
return codeword;
}
};
})();