@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
351 lines (316 loc) • 10.4 kB
JavaScript
export const dctZigZag = new Int32Array([
0,
1, 8,
16, 9, 2,
3, 10, 17, 24,
32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40,
48, 41, 34, 27, 20, 13, 6,
7, 14, 21, 28, 35, 42, 49, 56,
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58,
59, 52, 45, 38, 31,
39, 46, 53, 60,
61, 54, 47,
55, 62,
63
]);
/**
*
* @param {Uint8Array} data
* @param {number} offset
* @param {JpegFrame} frame
* @param {JpegFrameComponent[]} components
* @param {number} resetInterval
* @param {number} spectralStart
* @param {number} spectralEnd
* @param {number} successivePrev
* @param {number} successive
* @param {boolean} [tolerantDecoding]
* @return {number}
*/
export function decodeScan(
data,
offset,
frame,
components,
resetInterval,
spectralStart,
spectralEnd,
successivePrev,
successive,
tolerantDecoding = false
) {
const mcusPerLine = frame.mcusPerLine;
const progressive = frame.progressive;
const startOffset = offset;
let bitsData = 0, bitsCount = 0;
/**
*
* @return {number}
*/
function readBit() {
if (bitsCount > 0) {
bitsCount--;
return (bitsData >> bitsCount) & 1;
}
bitsData = data[offset++];
if (bitsData === 0xFF) {
const nextByte = data[offset++];
if (nextByte) {
throw new Error("unexpected marker: " + ((bitsData << 8) | nextByte).toString(16));
}
// unstuff 0
}
bitsCount = 7;
return bitsData >>> 7;
}
function decodeHuffman(tree) {
let node = tree, bit;
while ((bit = readBit()) !== null) {
node = node[bit];
if (typeof node === 'number')
return node;
if (typeof node !== 'object')
throw new Error("invalid huffman sequence");
}
return null;
}
function receive(length) {
let n = 0;
while (length > 0) {
const bit = readBit();
if (bit === null) return;
n = (n << 1) | bit;
length--;
}
return n;
}
function receiveAndExtend(length) {
const n = receive(length);
if (n >= 1 << (length - 1))
return n;
return n + (-1 << length) + 1;
}
/**
*
* @param {JpegFrameComponent} component
* @param {number[]} zz
*/
function decodeBaseline(component, zz) {
const t = decodeHuffman(component.huffmanTableDC);
const diff = t === 0 ? 0 : receiveAndExtend(t);
zz[0] = (component.pred += diff);
let k = 1;
while (k < 64) {
const rs = decodeHuffman(component.huffmanTableAC);
const s = rs & 15, r = rs >> 4;
if (s === 0) {
if (r < 15)
break;
k += 16;
continue;
}
k += r;
const z = dctZigZag[k];
zz[z] = receiveAndExtend(s);
k++;
}
}
function decodeDCFirst(component, zz) {
const t = decodeHuffman(component.huffmanTableDC);
const diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
zz[0] = (component.pred += diff);
}
function decodeDCSuccessive(component, zz) {
zz[0] |= readBit() << successive;
}
let eobrun = 0;
function decodeACFirst(component, zz) {
if (eobrun > 0) {
eobrun--;
return;
}
let k = spectralStart;
while (k <= spectralEnd) {
const rs = decodeHuffman(component.huffmanTableAC);
const s = rs & 15, r = rs >> 4;
if (s === 0) {
if (r < 15) {
eobrun = receive(r) + (1 << r) - 1;
break;
}
k += 16;
continue;
}
k += r;
const z = dctZigZag[k];
zz[z] = receiveAndExtend(s) * (1 << successive);
k++;
}
}
let successiveACState = 0, successiveACNextValue;
function decodeACSuccessive(component, zz) {
let k = spectralStart, r = 0;
while (k <= spectralEnd) {
const z = dctZigZag[k];
const direction = zz[z] < 0 ? -1 : 1;
switch (successiveACState) {
case 0: // initial state
const rs = decodeHuffman(component.huffmanTableAC);
let s = rs & 15;
r = rs >> 4;
if (s === 0) {
if (r < 15) {
eobrun = receive(r) + (1 << r);
successiveACState = 4;
} else {
r = 16;
successiveACState = 1;
}
} else {
if (s !== 1)
throw new Error("invalid ACn encoding");
successiveACNextValue = receiveAndExtend(s);
successiveACState = r ? 2 : 3;
}
continue;
case 1: // skipping r zero items
case 2:
if (zz[z])
zz[z] += (readBit() << successive) * direction;
else {
r--;
if (r === 0)
successiveACState = successiveACState === 2 ? 3 : 0;
}
break;
case 3: // set value for a zero item
if (zz[z])
zz[z] += (readBit() << successive) * direction;
else {
zz[z] = successiveACNextValue << successive;
successiveACState = 0;
}
break;
case 4: // eob
if (zz[z])
zz[z] += (readBit() << successive) * direction;
break;
}
k++;
}
if (successiveACState === 4) {
eobrun--;
if (eobrun === 0)
successiveACState = 0;
}
}
/**
*
* @param {JpegFrameComponent} component
* @param {function} decode
* @param {number} mcu
* @param {number} row
* @param {number} col
*/
function decodeMcu(component, decode, mcu, row, col) {
const mcuRow = (mcu / mcusPerLine) | 0;
const mcuCol = mcu % mcusPerLine;
const blockRow = mcuRow * component.v + row;
const blockCol = mcuCol * component.h + col;
if (component.blocks[blockRow] === undefined && tolerantDecoding) {
// If the block is missing and we're in tolerant mode, just skip it.
return;
}
decode(component, component.blocks[blockRow][blockCol]);
}
/**
*
* @param {JpegFrameComponent} component
* @param {function} decode
* @param {number} mcu
*/
function decodeBlock(component, decode, mcu) {
const blockRow = (mcu / component.blocksPerLine) | 0;
const blockCol = mcu % component.blocksPerLine;
if (component.blocks[blockRow] === undefined && tolerantDecoding) {
// If the block is missing and we're in tolerant mode, just skip it.
return;
}
decode(component, component.blocks[blockRow][blockCol]);
}
const componentsLength = components.length;
let component, i, j, k, n;
let decodeFn;
if (progressive) {
if (spectralStart === 0)
decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
else
decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
} else {
decodeFn = decodeBaseline;
}
let mcu = 0, marker;
let mcuExpected;
if (componentsLength === 1) {
mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
} else {
mcuExpected = mcusPerLine * frame.mcusPerColumn;
}
if (!resetInterval) {
resetInterval = mcuExpected;
}
let h, v;
while (mcu < mcuExpected) {
// reset interval stuff
for (i = 0; i < componentsLength; i++)
components[i].pred = 0;
eobrun = 0;
if (componentsLength === 1) {
component = components[0];
for (n = 0; n < resetInterval; n++) {
decodeBlock(component, decodeFn, mcu);
mcu++;
}
} else {
for (n = 0; n < resetInterval; n++) {
for (i = 0; i < componentsLength; i++) {
component = components[i];
h = component.h;
v = component.v;
for (j = 0; j < v; j++) {
for (k = 0; k < h; k++) {
decodeMcu(component, decodeFn, mcu, j, k);
}
}
}
mcu++;
// If we've reached our expected MCU's, stop decoding
if (mcu === mcuExpected) break;
}
}
if (mcu === mcuExpected) {
// Skip trailing bytes at the end of the scan - until we reach the next marker
do {
if (data[offset] === 0xFF) {
if (data[offset + 1] !== 0x00) {
break;
}
}
offset += 1;
} while (offset < data.length - 2);
}
// find marker
bitsCount = 0;
marker = (data[offset] << 8) | data[offset + 1];
if (marker < 0xFF00) {
throw new Error("marker was not found");
}
if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
offset += 2;
} else
break;
}
return offset - startOffset;
}