function decodeBitmap()

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;
}