in src/core/jbig2.js [334:492]
function decodeBitmap(
mmr,
width,
height,
templateIndex,
prediction,
skip,
at,
decodingContext
) {
if (mmr) {
const input = new Reader(
decodingContext.data,
decodingContext.start,
decodingContext.end
);
return decodeMMRBitmap(input, width, height, false);
}
// Use optimized version for the most common case
if (
templateIndex === 0 &&
!skip &&
!prediction &&
at.length === 4 &&
at[0].x === 3 &&
at[0].y === -1 &&
at[1].x === -3 &&
at[1].y === -1 &&
at[2].x === 2 &&
at[2].y === -2 &&
at[3].x === -2 &&
at[3].y === -2
) {
return decodeBitmapTemplate0(width, height, decodingContext);
}
const useskip = !!skip;
const template = CodingTemplates[templateIndex].concat(at);
// Sorting is non-standard, and it is not required. But sorting increases
// the number of template bits that can be reused from the previous
// contextLabel in the main loop.
template.sort((a, b) => a.y - b.y || a.x - b.x);
const templateLength = template.length;
const templateX = new Int8Array(templateLength);
const templateY = new Int8Array(templateLength);
const changingTemplateEntries = [];
let reuseMask = 0,
minX = 0,
maxX = 0,
minY = 0;
let c, k;
for (k = 0; k < templateLength; k++) {
templateX[k] = template[k].x;
templateY[k] = template[k].y;
minX = Math.min(minX, template[k].x);
maxX = Math.max(maxX, template[k].x);
minY = Math.min(minY, template[k].y);
// Check if the template pixel appears in two consecutive context labels,
// so it can be reused. Otherwise, we add it to the list of changing
// template entries.
if (
k < templateLength - 1 &&
template[k].y === template[k + 1].y &&
template[k].x === template[k + 1].x - 1
) {
reuseMask |= 1 << (templateLength - 1 - k);
} else {
changingTemplateEntries.push(k);
}
}
const changingEntriesLength = changingTemplateEntries.length;
const changingTemplateX = new Int8Array(changingEntriesLength);
const changingTemplateY = new Int8Array(changingEntriesLength);
const changingTemplateBit = new Uint16Array(changingEntriesLength);
for (c = 0; c < changingEntriesLength; c++) {
k = changingTemplateEntries[c];
changingTemplateX[c] = template[k].x;
changingTemplateY[c] = template[k].y;
changingTemplateBit[c] = 1 << (templateLength - 1 - k);
}
// Get the safe bounding box edges from the width, height, minX, maxX, minY
const sbb_left = -minX;
const sbb_top = -minY;
const sbb_right = width - maxX;
const pseudoPixelContext = ReusedContexts[templateIndex];
let row = new Uint8Array(width);
const bitmap = [];
const decoder = decodingContext.decoder;
const contexts = decodingContext.contextCache.getContexts("GB");
let ltp = 0,
j,
i0,
j0,
contextLabel = 0,
bit,
shift;
for (let i = 0; i < height; i++) {
if (prediction) {
const sltp = decoder.readBit(contexts, pseudoPixelContext);
ltp ^= sltp;
if (ltp) {
bitmap.push(row); // duplicate previous row
continue;
}
}
row = new Uint8Array(row);
bitmap.push(row);
for (j = 0; j < width; j++) {
if (useskip && skip[i][j]) {
row[j] = 0;
continue;
}
// Are we in the middle of a scanline, so we can reuse contextLabel
// bits?
if (j >= sbb_left && j < sbb_right && i >= sbb_top) {
// If yes, we can just shift the bits that are reusable and only
// fetch the remaining ones.
contextLabel = (contextLabel << 1) & reuseMask;
for (k = 0; k < changingEntriesLength; k++) {
i0 = i + changingTemplateY[k];
j0 = j + changingTemplateX[k];
bit = bitmap[i0][j0];
if (bit) {
bit = changingTemplateBit[k];
contextLabel |= bit;
}
}
} else {
// compute the contextLabel from scratch
contextLabel = 0;
shift = templateLength - 1;
for (k = 0; k < templateLength; k++, shift--) {
j0 = j + templateX[k];
if (j0 >= 0 && j0 < width) {
i0 = i + templateY[k];
if (i0 >= 0) {
bit = bitmap[i0][j0];
if (bit) {
contextLabel |= bit << shift;
}
}
}
}
}
const pixel = decoder.readBit(contexts, contextLabel);
row[j] = pixel;
}
}
return bitmap;
}