Remove the closure from BitModel in the src/core/jpx.js file

This commit is contained in:
Jonas Jenwald 2023-04-21 21:16:57 +02:00
parent b0a1af306d
commit 88616f77ae

View File

@ -1738,381 +1738,380 @@ class InclusionTree {
}
// Section D. Coefficient bit modeling
const BitModel = (function BitModelClosure() {
const UNIFORM_CONTEXT = 17;
const RUNLENGTH_CONTEXT = 18;
class BitModel {
static UNIFORM_CONTEXT = 17;
static RUNLENGTH_CONTEXT = 18;
// Table D-1
// The index is binary presentation: 0dddvvhh, ddd - sum of Di (0..4),
// vv - sum of Vi (0..2), and hh - sum of Hi (0..2)
const LLAndLHContextsLabel = new Uint8Array([
static LLAndLHContextsLabel = new Uint8Array([
0, 5, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 1, 6, 8, 0, 3, 7, 8, 0, 4,
7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6,
8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8,
]);
const HLContextLabel = new Uint8Array([
static HLContextLabel = new Uint8Array([
0, 3, 4, 0, 5, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 1, 3, 4, 0, 6, 7, 7, 0, 8,
8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3,
4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8,
]);
const HHContextLabel = new Uint8Array([
static HHContextLabel = new Uint8Array([
0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5,
5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8,
8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8,
]);
// eslint-disable-next-line no-shadow
class BitModel {
constructor(width, height, subband, zeroBitPlanes, mb) {
this.width = width;
this.height = height;
constructor(width, height, subband, zeroBitPlanes, mb) {
this.width = width;
this.height = height;
let contextLabelTable;
if (subband === "HH") {
contextLabelTable = HHContextLabel;
} else if (subband === "HL") {
contextLabelTable = HLContextLabel;
} else {
contextLabelTable = LLAndLHContextsLabel;
}
this.contextLabelTable = contextLabelTable;
const coefficientCount = width * height;
// coefficients outside the encoding region treated as insignificant
// add border state cells for significanceState
this.neighborsSignificance = new Uint8Array(coefficientCount);
this.coefficentsSign = new Uint8Array(coefficientCount);
let coefficentsMagnitude;
if (mb > 14) {
coefficentsMagnitude = new Uint32Array(coefficientCount);
} else if (mb > 6) {
coefficentsMagnitude = new Uint16Array(coefficientCount);
} else {
coefficentsMagnitude = new Uint8Array(coefficientCount);
}
this.coefficentsMagnitude = coefficentsMagnitude;
this.processingFlags = new Uint8Array(coefficientCount);
const bitsDecoded = new Uint8Array(coefficientCount);
if (zeroBitPlanes !== 0) {
for (let i = 0; i < coefficientCount; i++) {
bitsDecoded[i] = zeroBitPlanes;
}
}
this.bitsDecoded = bitsDecoded;
this.reset();
let contextLabelTable;
if (subband === "HH") {
contextLabelTable = BitModel.HHContextLabel;
} else if (subband === "HL") {
contextLabelTable = BitModel.HLContextLabel;
} else {
contextLabelTable = BitModel.LLAndLHContextsLabel;
}
this.contextLabelTable = contextLabelTable;
setDecoder(decoder) {
this.decoder = decoder;
const coefficientCount = width * height;
// coefficients outside the encoding region treated as insignificant
// add border state cells for significanceState
this.neighborsSignificance = new Uint8Array(coefficientCount);
this.coefficentsSign = new Uint8Array(coefficientCount);
let coefficentsMagnitude;
if (mb > 14) {
coefficentsMagnitude = new Uint32Array(coefficientCount);
} else if (mb > 6) {
coefficentsMagnitude = new Uint16Array(coefficientCount);
} else {
coefficentsMagnitude = new Uint8Array(coefficientCount);
}
this.coefficentsMagnitude = coefficentsMagnitude;
this.processingFlags = new Uint8Array(coefficientCount);
reset() {
// We have 17 contexts that are accessed via context labels,
// plus the uniform and runlength context.
this.contexts = new Int8Array(19);
// Contexts are packed into 1 byte:
// highest 7 bits carry the index, lowest bit carries mps
this.contexts[0] = (4 << 1) | 0;
this.contexts[UNIFORM_CONTEXT] = (46 << 1) | 0;
this.contexts[RUNLENGTH_CONTEXT] = (3 << 1) | 0;
const bitsDecoded = new Uint8Array(coefficientCount);
if (zeroBitPlanes !== 0) {
for (let i = 0; i < coefficientCount; i++) {
bitsDecoded[i] = zeroBitPlanes;
}
}
this.bitsDecoded = bitsDecoded;
setNeighborsSignificance(row, column, index) {
const neighborsSignificance = this.neighborsSignificance;
const width = this.width,
height = this.height;
const left = column > 0;
const right = column + 1 < width;
let i;
this.reset();
}
if (row > 0) {
i = index - width;
if (left) {
neighborsSignificance[i - 1] += 0x10;
}
if (right) {
neighborsSignificance[i + 1] += 0x10;
}
neighborsSignificance[i] += 0x04;
}
setDecoder(decoder) {
this.decoder = decoder;
}
if (row + 1 < height) {
i = index + width;
if (left) {
neighborsSignificance[i - 1] += 0x10;
}
if (right) {
neighborsSignificance[i + 1] += 0x10;
}
neighborsSignificance[i] += 0x04;
}
reset() {
// We have 17 contexts that are accessed via context labels,
// plus the uniform and runlength context.
this.contexts = new Int8Array(19);
// Contexts are packed into 1 byte:
// highest 7 bits carry the index, lowest bit carries mps
this.contexts[0] = (4 << 1) | 0;
this.contexts[BitModel.UNIFORM_CONTEXT] = (46 << 1) | 0;
this.contexts[BitModel.RUNLENGTH_CONTEXT] = (3 << 1) | 0;
}
setNeighborsSignificance(row, column, index) {
const neighborsSignificance = this.neighborsSignificance;
const width = this.width,
height = this.height;
const left = column > 0;
const right = column + 1 < width;
let i;
if (row > 0) {
i = index - width;
if (left) {
neighborsSignificance[index - 1] += 0x01;
neighborsSignificance[i - 1] += 0x10;
}
if (right) {
neighborsSignificance[index + 1] += 0x01;
neighborsSignificance[i + 1] += 0x10;
}
neighborsSignificance[index] |= 0x80;
neighborsSignificance[i] += 0x04;
}
runSignificancePropagationPass() {
const decoder = this.decoder;
const width = this.width,
height = this.height;
const coefficentsMagnitude = this.coefficentsMagnitude;
const coefficentsSign = this.coefficentsSign;
const neighborsSignificance = this.neighborsSignificance;
const processingFlags = this.processingFlags;
const contexts = this.contexts;
const labels = this.contextLabelTable;
const bitsDecoded = this.bitsDecoded;
const processedInverseMask = ~1;
const processedMask = 1;
const firstMagnitudeBitMask = 2;
if (row + 1 < height) {
i = index + width;
if (left) {
neighborsSignificance[i - 1] += 0x10;
}
if (right) {
neighborsSignificance[i + 1] += 0x10;
}
neighborsSignificance[i] += 0x04;
}
for (let i0 = 0; i0 < height; i0 += 4) {
for (let j = 0; j < width; j++) {
let index = i0 * width + j;
for (let i1 = 0; i1 < 4; i1++, index += width) {
const i = i0 + i1;
if (i >= height) {
break;
}
// clear processed flag first
processingFlags[index] &= processedInverseMask;
if (left) {
neighborsSignificance[index - 1] += 0x01;
}
if (right) {
neighborsSignificance[index + 1] += 0x01;
}
neighborsSignificance[index] |= 0x80;
}
if (coefficentsMagnitude[index] || !neighborsSignificance[index]) {
continue;
}
runSignificancePropagationPass() {
const decoder = this.decoder;
const width = this.width,
height = this.height;
const coefficentsMagnitude = this.coefficentsMagnitude;
const coefficentsSign = this.coefficentsSign;
const neighborsSignificance = this.neighborsSignificance;
const processingFlags = this.processingFlags;
const contexts = this.contexts;
const labels = this.contextLabelTable;
const bitsDecoded = this.bitsDecoded;
const processedInverseMask = ~1;
const processedMask = 1;
const firstMagnitudeBitMask = 2;
const contextLabel = labels[neighborsSignificance[index]];
const decision = decoder.readBit(contexts, contextLabel);
if (decision) {
const sign = this.decodeSignBit(i, j, index);
coefficentsSign[index] = sign;
coefficentsMagnitude[index] = 1;
this.setNeighborsSignificance(i, j, index);
processingFlags[index] |= firstMagnitudeBitMask;
}
bitsDecoded[index]++;
processingFlags[index] |= processedMask;
for (let i0 = 0; i0 < height; i0 += 4) {
for (let j = 0; j < width; j++) {
let index = i0 * width + j;
for (let i1 = 0; i1 < 4; i1++, index += width) {
const i = i0 + i1;
if (i >= height) {
break;
}
// clear processed flag first
processingFlags[index] &= processedInverseMask;
if (coefficentsMagnitude[index] || !neighborsSignificance[index]) {
continue;
}
const contextLabel = labels[neighborsSignificance[index]];
const decision = decoder.readBit(contexts, contextLabel);
if (decision) {
const sign = this.decodeSignBit(i, j, index);
coefficentsSign[index] = sign;
coefficentsMagnitude[index] = 1;
this.setNeighborsSignificance(i, j, index);
processingFlags[index] |= firstMagnitudeBitMask;
}
bitsDecoded[index]++;
processingFlags[index] |= processedMask;
}
}
}
}
decodeSignBit(row, column, index) {
const width = this.width,
height = this.height;
const coefficentsMagnitude = this.coefficentsMagnitude;
const coefficentsSign = this.coefficentsSign;
let contribution, sign0, sign1, significance1;
let contextLabel, decoded;
decodeSignBit(row, column, index) {
const width = this.width,
height = this.height;
const coefficentsMagnitude = this.coefficentsMagnitude;
const coefficentsSign = this.coefficentsSign;
let contribution, sign0, sign1, significance1;
let contextLabel, decoded;
// calculate horizontal contribution
significance1 = column > 0 && coefficentsMagnitude[index - 1] !== 0;
if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {
sign1 = coefficentsSign[index + 1];
if (significance1) {
sign0 = coefficentsSign[index - 1];
contribution = 1 - sign1 - sign0;
} else {
contribution = 1 - sign1 - sign1;
}
} else if (significance1) {
// calculate horizontal contribution
significance1 = column > 0 && coefficentsMagnitude[index - 1] !== 0;
if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {
sign1 = coefficentsSign[index + 1];
if (significance1) {
sign0 = coefficentsSign[index - 1];
contribution = 1 - sign0 - sign0;
contribution = 1 - sign1 - sign0;
} else {
contribution = 0;
contribution = 1 - sign1 - sign1;
}
const horizontalContribution = 3 * contribution;
} else if (significance1) {
sign0 = coefficentsSign[index - 1];
contribution = 1 - sign0 - sign0;
} else {
contribution = 0;
}
const horizontalContribution = 3 * contribution;
// calculate vertical contribution and combine with the horizontal
significance1 = row > 0 && coefficentsMagnitude[index - width] !== 0;
if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {
sign1 = coefficentsSign[index + width];
if (significance1) {
sign0 = coefficentsSign[index - width];
contribution = 1 - sign1 - sign0 + horizontalContribution;
} else {
contribution = 1 - sign1 - sign1 + horizontalContribution;
}
} else if (significance1) {
// calculate vertical contribution and combine with the horizontal
significance1 = row > 0 && coefficentsMagnitude[index - width] !== 0;
if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {
sign1 = coefficentsSign[index + width];
if (significance1) {
sign0 = coefficentsSign[index - width];
contribution = 1 - sign0 - sign0 + horizontalContribution;
contribution = 1 - sign1 - sign0 + horizontalContribution;
} else {
contribution = horizontalContribution;
contribution = 1 - sign1 - sign1 + horizontalContribution;
}
if (contribution >= 0) {
contextLabel = 9 + contribution;
decoded = this.decoder.readBit(this.contexts, contextLabel);
} else {
contextLabel = 9 - contribution;
decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;
}
return decoded;
} else if (significance1) {
sign0 = coefficentsSign[index - width];
contribution = 1 - sign0 - sign0 + horizontalContribution;
} else {
contribution = horizontalContribution;
}
runMagnitudeRefinementPass() {
const decoder = this.decoder;
const width = this.width,
height = this.height;
const coefficentsMagnitude = this.coefficentsMagnitude;
const neighborsSignificance = this.neighborsSignificance;
const contexts = this.contexts;
const bitsDecoded = this.bitsDecoded;
const processingFlags = this.processingFlags;
const processedMask = 1;
const firstMagnitudeBitMask = 2;
const length = width * height;
const width4 = width * 4;
if (contribution >= 0) {
contextLabel = 9 + contribution;
decoded = this.decoder.readBit(this.contexts, contextLabel);
} else {
contextLabel = 9 - contribution;
decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;
}
return decoded;
}
for (let index0 = 0, indexNext; index0 < length; index0 = indexNext) {
indexNext = Math.min(length, index0 + width4);
for (let j = 0; j < width; j++) {
for (let index = index0 + j; index < indexNext; index += width) {
// significant but not those that have just become
if (
!coefficentsMagnitude[index] ||
(processingFlags[index] & processedMask) !== 0
) {
continue;
}
runMagnitudeRefinementPass() {
const decoder = this.decoder;
const width = this.width,
height = this.height;
const coefficentsMagnitude = this.coefficentsMagnitude;
const neighborsSignificance = this.neighborsSignificance;
const contexts = this.contexts;
const bitsDecoded = this.bitsDecoded;
const processingFlags = this.processingFlags;
const processedMask = 1;
const firstMagnitudeBitMask = 2;
const length = width * height;
const width4 = width * 4;
let contextLabel = 16;
if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {
processingFlags[index] ^= firstMagnitudeBitMask;
// first refinement
const significance = neighborsSignificance[index] & 127;
contextLabel = significance === 0 ? 15 : 14;
}
const bit = decoder.readBit(contexts, contextLabel);
coefficentsMagnitude[index] =
(coefficentsMagnitude[index] << 1) | bit;
bitsDecoded[index]++;
processingFlags[index] |= processedMask;
for (let index0 = 0, indexNext; index0 < length; index0 = indexNext) {
indexNext = Math.min(length, index0 + width4);
for (let j = 0; j < width; j++) {
for (let index = index0 + j; index < indexNext; index += width) {
// significant but not those that have just become
if (
!coefficentsMagnitude[index] ||
(processingFlags[index] & processedMask) !== 0
) {
continue;
}
let contextLabel = 16;
if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {
processingFlags[index] ^= firstMagnitudeBitMask;
// first refinement
const significance = neighborsSignificance[index] & 127;
contextLabel = significance === 0 ? 15 : 14;
}
const bit = decoder.readBit(contexts, contextLabel);
coefficentsMagnitude[index] =
(coefficentsMagnitude[index] << 1) | bit;
bitsDecoded[index]++;
processingFlags[index] |= processedMask;
}
}
}
}
runCleanupPass() {
const decoder = this.decoder;
const width = this.width,
height = this.height;
const neighborsSignificance = this.neighborsSignificance;
const coefficentsMagnitude = this.coefficentsMagnitude;
const coefficentsSign = this.coefficentsSign;
const contexts = this.contexts;
const labels = this.contextLabelTable;
const bitsDecoded = this.bitsDecoded;
const processingFlags = this.processingFlags;
const processedMask = 1;
const firstMagnitudeBitMask = 2;
const oneRowDown = width;
const twoRowsDown = width * 2;
const threeRowsDown = width * 3;
let iNext;
for (let i0 = 0; i0 < height; i0 = iNext) {
iNext = Math.min(i0 + 4, height);
const indexBase = i0 * width;
const checkAllEmpty = i0 + 3 < height;
for (let j = 0; j < width; j++) {
const index0 = indexBase + j;
// using the property: labels[neighborsSignificance[index]] === 0
// when neighborsSignificance[index] === 0
const allEmpty =
checkAllEmpty &&
processingFlags[index0] === 0 &&
processingFlags[index0 + oneRowDown] === 0 &&
processingFlags[index0 + twoRowsDown] === 0 &&
processingFlags[index0 + threeRowsDown] === 0 &&
neighborsSignificance[index0] === 0 &&
neighborsSignificance[index0 + oneRowDown] === 0 &&
neighborsSignificance[index0 + twoRowsDown] === 0 &&
neighborsSignificance[index0 + threeRowsDown] === 0;
let i1 = 0,
index = index0;
let i = i0,
sign;
if (allEmpty) {
const hasSignificantCoefficent = decoder.readBit(
contexts,
RUNLENGTH_CONTEXT
);
if (!hasSignificantCoefficent) {
bitsDecoded[index0]++;
bitsDecoded[index0 + oneRowDown]++;
bitsDecoded[index0 + twoRowsDown]++;
bitsDecoded[index0 + threeRowsDown]++;
continue; // next column
}
i1 =
(decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
decoder.readBit(contexts, UNIFORM_CONTEXT);
if (i1 !== 0) {
i = i0 + i1;
index += i1 * width;
}
runCleanupPass() {
const decoder = this.decoder;
const width = this.width,
height = this.height;
const neighborsSignificance = this.neighborsSignificance;
const coefficentsMagnitude = this.coefficentsMagnitude;
const coefficentsSign = this.coefficentsSign;
const contexts = this.contexts;
const labels = this.contextLabelTable;
const bitsDecoded = this.bitsDecoded;
const processingFlags = this.processingFlags;
const processedMask = 1;
const firstMagnitudeBitMask = 2;
const oneRowDown = width;
const twoRowsDown = width * 2;
const threeRowsDown = width * 3;
let iNext;
for (let i0 = 0; i0 < height; i0 = iNext) {
iNext = Math.min(i0 + 4, height);
const indexBase = i0 * width;
const checkAllEmpty = i0 + 3 < height;
for (let j = 0; j < width; j++) {
const index0 = indexBase + j;
// using the property: labels[neighborsSignificance[index]] === 0
// when neighborsSignificance[index] === 0
const allEmpty =
checkAllEmpty &&
processingFlags[index0] === 0 &&
processingFlags[index0 + oneRowDown] === 0 &&
processingFlags[index0 + twoRowsDown] === 0 &&
processingFlags[index0 + threeRowsDown] === 0 &&
neighborsSignificance[index0] === 0 &&
neighborsSignificance[index0 + oneRowDown] === 0 &&
neighborsSignificance[index0 + twoRowsDown] === 0 &&
neighborsSignificance[index0 + threeRowsDown] === 0;
let i1 = 0,
index = index0;
let i = i0,
sign;
if (allEmpty) {
const hasSignificantCoefficent = decoder.readBit(
contexts,
BitModel.RUNLENGTH_CONTEXT
);
if (!hasSignificantCoefficent) {
bitsDecoded[index0]++;
bitsDecoded[index0 + oneRowDown]++;
bitsDecoded[index0 + twoRowsDown]++;
bitsDecoded[index0 + threeRowsDown]++;
continue; // next column
}
i1 =
(decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 1) |
decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT);
if (i1 !== 0) {
i = i0 + i1;
index += i1 * width;
}
sign = this.decodeSignBit(i, j, index);
coefficentsSign[index] = sign;
coefficentsMagnitude[index] = 1;
this.setNeighborsSignificance(i, j, index);
processingFlags[index] |= firstMagnitudeBitMask;
index = index0;
for (let i2 = i0; i2 <= i; i2++, index += width) {
bitsDecoded[index]++;
}
i1++;
}
for (i = i0 + i1; i < iNext; i++, index += width) {
if (
coefficentsMagnitude[index] ||
(processingFlags[index] & processedMask) !== 0
) {
continue;
}
const contextLabel = labels[neighborsSignificance[index]];
const decision = decoder.readBit(contexts, contextLabel);
if (decision === 1) {
sign = this.decodeSignBit(i, j, index);
coefficentsSign[index] = sign;
coefficentsMagnitude[index] = 1;
this.setNeighborsSignificance(i, j, index);
processingFlags[index] |= firstMagnitudeBitMask;
index = index0;
for (let i2 = i0; i2 <= i; i2++, index += width) {
bitsDecoded[index]++;
}
i1++;
}
for (i = i0 + i1; i < iNext; i++, index += width) {
if (
coefficentsMagnitude[index] ||
(processingFlags[index] & processedMask) !== 0
) {
continue;
}
const contextLabel = labels[neighborsSignificance[index]];
const decision = decoder.readBit(contexts, contextLabel);
if (decision === 1) {
sign = this.decodeSignBit(i, j, index);
coefficentsSign[index] = sign;
coefficentsMagnitude[index] = 1;
this.setNeighborsSignificance(i, j, index);
processingFlags[index] |= firstMagnitudeBitMask;
}
bitsDecoded[index]++;
}
bitsDecoded[index]++;
}
}
}
checkSegmentationSymbol() {
const decoder = this.decoder;
const contexts = this.contexts;
const symbol =
(decoder.readBit(contexts, UNIFORM_CONTEXT) << 3) |
(decoder.readBit(contexts, UNIFORM_CONTEXT) << 2) |
(decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
decoder.readBit(contexts, UNIFORM_CONTEXT);
if (symbol !== 0xa) {
throw new JpxError("Invalid segmentation symbol");
}
}
}
return BitModel;
})();
checkSegmentationSymbol() {
const decoder = this.decoder;
const contexts = this.contexts;
const symbol =
(decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 3) |
(decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 2) |
(decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 1) |
decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT);
if (symbol !== 0xa) {
throw new JpxError("Invalid segmentation symbol");
}
}
}
// Section F, Discrete wavelet transformation
class Transform {