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
JavaScript
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