UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

351 lines (316 loc) • 10.4 kB
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; }