UNPKG

image-in-browser

Version:

Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)

378 lines 12.2 kB
import { LibError } from '../../error/lib-error.js'; import { HuffmanParent } from './huffman-parent.js'; import { HuffmanValue } from './huffman-value.js'; import { JpegData } from './jpeg-data.js'; import { JpegMarker } from './jpeg-marker.js'; export class JpegScan { get input() { return this._input; } get frame() { return this._frame; } get precision() { return this._precision; } get samplesPerLine() { return this._samplesPerLine; } get scanLines() { return this._scanLines; } get mcusPerLine() { return this._mcusPerLine; } get progressive() { return this._progressive; } get maxH() { return this._maxH; } get maxV() { return this._maxV; } get components() { return this._components; } get resetInterval() { return this._resetInterval; } get spectralStart() { return this._spectralStart; } get spectralEnd() { return this._spectralEnd; } get successivePrev() { return this._successivePrev; } get successive() { return this._successive; } get bitsData() { return this._bitsData; } get bitsCount() { return this._bitsCount; } get eobrun() { return this._eobrun; } get successiveACState() { return this._successiveACState; } get successiveACNextValue() { return this._successiveACNextValue; } constructor(input, frame, components, spectralStart, spectralEnd, successivePrev, successive, resetInterval) { this._bitsData = 0; this._bitsCount = 0; this._eobrun = 0; this._successiveACState = 0; this._successiveACNextValue = 0; this._input = input; this._frame = frame; this._precision = frame.precision; this._samplesPerLine = frame.samplesPerLine; this._scanLines = frame.scanLines; this._mcusPerLine = frame.mcusPerLine; this._progressive = frame.progressive; this._maxH = frame.maxHSamples; this._maxV = frame.maxVSamples; this._components = components; this._resetInterval = resetInterval; this._spectralStart = spectralStart; this._spectralEnd = spectralEnd; this._successivePrev = successivePrev; this._successive = successive; } readBit() { if (this.bitsCount > 0) { this._bitsCount--; return (this._bitsData >>> this._bitsCount) & 1; } if (this._input.isEOS) { return undefined; } this._bitsData = this._input.read(); if (this._bitsData === 0xff) { const nextByte = this.input.read(); if (nextByte !== 0) { return undefined; } } this._bitsCount = 7; return (this._bitsData >>> 7) & 1; } decodeHuffman(tree) { let node = new HuffmanParent(tree); let bit = undefined; while ((bit = this.readBit()) !== undefined) { if (node instanceof HuffmanParent) { node = node.children[bit]; } if (node instanceof HuffmanValue) { return node.value; } } return undefined; } receive(length) { let n = 0; let len = length; while (len > 0) { const bit = this.readBit(); if (bit === undefined) { return undefined; } n = (n << 1) | bit; len--; } return n; } receiveAndExtend(length) { if (length === undefined) { return 0; } if (length === 1) { return this.readBit() === 1 ? 1 : -1; } const n = this.receive(length); if (n === undefined) { return 0; } if (n >= 1 << ((length !== null && length !== void 0 ? length : 0) - 1)) { return n; } return n + (-1 << (length !== null && length !== void 0 ? length : 0)) + 1; } decodeBaseline(component, zz) { const t = this.decodeHuffman(component.huffmanTableDC); const diff = t === 0 ? 0 : this.receiveAndExtend(t); component.pred += diff; zz[0] = component.pred; let k = 1; while (k < 64) { const rs = this.decodeHuffman(component.huffmanTableAC); if (rs === undefined) { break; } let s = rs & 15; const r = rs >>> 4; if (s === 0) { if (r < 15) { break; } k += 16; continue; } k += r; s = this.receiveAndExtend(s); const z = JpegData.dctZigZag[k]; zz[z] = s; k++; } } decodeDCFirst(component, zz) { const t = this.decodeHuffman(component.huffmanTableDC); const diff = t === 0 ? 0 : this.receiveAndExtend(t) << this._successive; component.pred += diff; zz[0] = component.pred; } decodeDCSuccessive(_, zz) { zz[0] |= this.readBit() << this._successive; } decodeACFirst(component, zz) { if (this._eobrun > 0) { this._eobrun--; return; } let k = this._spectralStart; const e = this._spectralEnd; while (k <= e) { const rs = this.decodeHuffman(component.huffmanTableAC); const s = rs & 15; const r = rs >>> 4; if (s === 0) { if (r < 15) { this._eobrun = this.receive(r) + (1 << r) - 1; break; } k += 16; continue; } k += r; const z = JpegData.dctZigZag[k]; zz[z] = this.receiveAndExtend(s) * (1 << this._successive); k++; } } decodeACSuccessive(component, zz) { let k = this._spectralStart; const e = this._spectralEnd; let s = 0; let r = 0; while (k <= e) { const z = JpegData.dctZigZag[k]; switch (this._successiveACState) { case 0: { const rs = this.decodeHuffman(component.huffmanTableAC); if (rs === undefined) { throw new LibError('Invalid progressive encoding'); } s = rs & 15; r = rs >>> 4; if (s === 0) { if (r < 15) { this._eobrun = this.receive(r) + (1 << r); this._successiveACState = 4; } else { r = 16; this._successiveACState = 1; } } else { if (s !== 1) { throw new LibError('invalid ACn encoding'); } this._successiveACNextValue = this.receiveAndExtend(s); this._successiveACState = r !== 0 ? 2 : 3; } continue; } case 1: case 2: { if (zz[z] !== 0) { zz[z] += this.readBit() << this._successive; } else { r--; if (r === 0) { this._successiveACState = this._successiveACState === 2 ? 3 : 0; } } break; } case 3: { if (zz[z] !== 0) { zz[z] += this.readBit() << this._successive; } else { zz[z] = this._successiveACNextValue << this._successive; this._successiveACState = 0; } break; } case 4: { if (zz[z] !== 0) { zz[z] += this.readBit() << this._successive; } break; } } k++; } if (this._successiveACState === 4) { this._eobrun--; if (this._eobrun === 0) { this._successiveACState = 0; } } } decodeMcu(component, decodeFn, mcu, row, col) { const mcuRow = Math.floor(mcu / this._mcusPerLine); const mcuCol = mcu % this._mcusPerLine; const blockRow = mcuRow * component.vSamples + row; const blockCol = mcuCol * component.hSamples + col; if (blockRow >= component.blocks.length) { return; } const numCols = component.blocks[blockRow].length; if (blockCol >= numCols) { return; } decodeFn.call(this, component, component.blocks[blockRow][blockCol]); } decodeBlock(component, decodeFn, mcu) { const blockRow = Math.floor(mcu / component.blocksPerLine); const blockCol = mcu % component.blocksPerLine; decodeFn.call(this, component, component.blocks[blockRow][blockCol]); } decode() { const componentsLength = this._components.length; let component = undefined; let decodeFn = undefined; if (this._progressive) { if (this._spectralStart === 0) { decodeFn = this._successivePrev === 0 ? this.decodeDCFirst : this.decodeDCSuccessive; } else { decodeFn = this._successivePrev === 0 ? this.decodeACFirst : this.decodeACSuccessive; } } else { decodeFn = this.decodeBaseline; } let mcu = 0; let mcuExpected = undefined; if (componentsLength === 1) { mcuExpected = this._components[0].blocksPerLine * this._components[0].blocksPerColumn; } else { mcuExpected = this._mcusPerLine * this._frame.mcusPerColumn; } if (this._resetInterval === undefined || this._resetInterval === 0) { this._resetInterval = mcuExpected; } let h = undefined; let v = undefined; while (mcu < mcuExpected) { for (let i = 0; i < componentsLength; i++) { this._components[i].pred = 0; } this._eobrun = 0; if (componentsLength === 1) { component = this._components[0]; for (let n = 0; n < this._resetInterval; n++) { this.decodeBlock(component, decodeFn, mcu); mcu++; } } else { for (let n = 0; n < this._resetInterval; n++) { for (let i = 0; i < componentsLength; i++) { component = this.components[i]; h = component.hSamples; v = component.vSamples; for (let j = 0; j < v; j++) { for (let k = 0; k < h; k++) { this.decodeMcu(component, decodeFn, mcu, j, k); } } } mcu++; } } this._bitsCount = 0; const m1 = this._input.get(0); const m2 = this._input.get(1); if (m1 === 0xff) { if (m2 >= JpegMarker.rst0 && m2 <= JpegMarker.rst7) { this._input.skip(2); } else { break; } } } } } //# sourceMappingURL=jpeg-scan.js.map