markerLoop: while()

in src/core/jpg.js [898:1160]


    markerLoop: while (fileMarker !== /* EOI (End of Image) = */ 0xffd9) {
      let i, j, l;
      switch (fileMarker) {
        case 0xffe0: // APP0 (Application Specific)
        case 0xffe1: // APP1
        case 0xffe2: // APP2
        case 0xffe3: // APP3
        case 0xffe4: // APP4
        case 0xffe5: // APP5
        case 0xffe6: // APP6
        case 0xffe7: // APP7
        case 0xffe8: // APP8
        case 0xffe9: // APP9
        case 0xffea: // APP10
        case 0xffeb: // APP11
        case 0xffec: // APP12
        case 0xffed: // APP13
        case 0xffee: // APP14
        case 0xffef: // APP15
        case 0xfffe: // COM (Comment)
          const { appData, newOffset } = readDataBlock(data, offset);
          offset = newOffset;

          if (fileMarker === 0xffe0) {
            // 'JFIF\x00'
            if (
              appData[0] === 0x4a &&
              appData[1] === 0x46 &&
              appData[2] === 0x49 &&
              appData[3] === 0x46 &&
              appData[4] === 0
            ) {
              jfif = {
                version: { major: appData[5], minor: appData[6] },
                densityUnits: appData[7],
                xDensity: (appData[8] << 8) | appData[9],
                yDensity: (appData[10] << 8) | appData[11],
                thumbWidth: appData[12],
                thumbHeight: appData[13],
                thumbData: appData.subarray(
                  14,
                  14 + 3 * appData[12] * appData[13]
                ),
              };
            }
          }
          // TODO APP1 - Exif
          if (fileMarker === 0xffee) {
            // 'Adobe'
            if (
              appData[0] === 0x41 &&
              appData[1] === 0x64 &&
              appData[2] === 0x6f &&
              appData[3] === 0x62 &&
              appData[4] === 0x65
            ) {
              adobe = {
                version: (appData[5] << 8) | appData[6],
                flags0: (appData[7] << 8) | appData[8],
                flags1: (appData[9] << 8) | appData[10],
                transformCode: appData[11],
              };
            }
          }
          break;

        case 0xffdb: // DQT (Define Quantization Tables)
          const quantizationTablesLength = readUint16(data, offset);
          offset += 2;
          const quantizationTablesEnd = quantizationTablesLength + offset - 2;
          let z;
          while (offset < quantizationTablesEnd) {
            const quantizationTableSpec = data[offset++];
            const tableData = new Uint16Array(64);
            if (quantizationTableSpec >> 4 === 0) {
              // 8 bit values
              for (j = 0; j < 64; j++) {
                z = dctZigZag[j];
                tableData[z] = data[offset++];
              }
            } else if (quantizationTableSpec >> 4 === 1) {
              // 16 bit values
              for (j = 0; j < 64; j++) {
                z = dctZigZag[j];
                tableData[z] = readUint16(data, offset);
                offset += 2;
              }
            } else {
              throw new JpegError("DQT - invalid table spec");
            }
            quantizationTables[quantizationTableSpec & 15] = tableData;
          }
          break;

        case 0xffc0: // SOF0 (Start of Frame, Baseline DCT)
        case 0xffc1: // SOF1 (Start of Frame, Extended DCT)
        case 0xffc2: // SOF2 (Start of Frame, Progressive DCT)
          if (frame) {
            throw new JpegError("Only single frame JPEGs supported");
          }
          offset += 2; // Skip marker length.

          frame = {};
          frame.extended = fileMarker === 0xffc1;
          frame.progressive = fileMarker === 0xffc2;
          frame.precision = data[offset++];
          const sofScanLines = readUint16(data, offset);
          offset += 2;
          frame.scanLines = dnlScanLines || sofScanLines;
          frame.samplesPerLine = readUint16(data, offset);
          offset += 2;
          frame.components = [];
          frame.componentIds = {};
          const componentsCount = data[offset++];
          let maxH = 0,
            maxV = 0;
          for (i = 0; i < componentsCount; i++) {
            const componentId = data[offset];
            const h = data[offset + 1] >> 4;
            const v = data[offset + 1] & 15;
            if (maxH < h) {
              maxH = h;
            }
            if (maxV < v) {
              maxV = v;
            }
            const qId = data[offset + 2];
            l = frame.components.push({
              h,
              v,
              quantizationId: qId,
              quantizationTable: null, // See comment below.
            });
            frame.componentIds[componentId] = l - 1;
            offset += 3;
          }
          frame.maxH = maxH;
          frame.maxV = maxV;
          prepareComponents(frame);
          break;

        case 0xffc4: // DHT (Define Huffman Tables)
          const huffmanLength = readUint16(data, offset);
          offset += 2;
          for (i = 2; i < huffmanLength; ) {
            const huffmanTableSpec = data[offset++];
            const codeLengths = new Uint8Array(16);
            let codeLengthSum = 0;
            for (j = 0; j < 16; j++, offset++) {
              codeLengthSum += codeLengths[j] = data[offset];
            }
            const huffmanValues = new Uint8Array(codeLengthSum);
            for (j = 0; j < codeLengthSum; j++, offset++) {
              huffmanValues[j] = data[offset];
            }
            i += 17 + codeLengthSum;

            (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[
              huffmanTableSpec & 15
            ] = buildHuffmanTable(codeLengths, huffmanValues);
          }
          break;

        case 0xffdd: // DRI (Define Restart Interval)
          offset += 2; // Skip marker length.

          resetInterval = readUint16(data, offset);
          offset += 2;
          break;

        case 0xffda: // SOS (Start of Scan)
          // A DNL marker (0xFFDC), if it exists, is only allowed at the end
          // of the first scan segment and may only occur once in an image.
          // Furthermore, to prevent an infinite loop, do *not* attempt to
          // parse DNL markers during re-parsing of the JPEG scan data.
          const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines;

          offset += 2; // Skip marker length.

          const selectorsCount = data[offset++],
            components = [];
          for (i = 0; i < selectorsCount; i++) {
            const index = data[offset++];
            const componentIndex = frame.componentIds[index];
            const component = frame.components[componentIndex];
            component.index = index;
            const tableSpec = data[offset++];
            component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
            component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
            components.push(component);
          }
          const spectralStart = data[offset++],
            spectralEnd = data[offset++],
            successiveApproximation = data[offset++];
          try {
            const processed = decodeScan(
              data,
              offset,
              frame,
              components,
              resetInterval,
              spectralStart,
              spectralEnd,
              successiveApproximation >> 4,
              successiveApproximation & 15,
              parseDNLMarker
            );
            offset += processed;
          } catch (ex) {
            if (ex instanceof DNLMarkerError) {
              warn(`${ex.message} -- attempting to re-parse the JPEG image.`);
              return this.parse(data, { dnlScanLines: ex.scanLines });
            } else if (ex instanceof EOIMarkerError) {
              warn(`${ex.message} -- ignoring the rest of the image data.`);
              break markerLoop;
            }
            throw ex;
          }
          break;

        case 0xffdc: // DNL (Define Number of Lines)
          // Ignore the marker, since it's being handled in `decodeScan`.
          offset += 4;
          break;

        case 0xffff: // Fill bytes
          if (data[offset] !== 0xff) {
            // Avoid skipping a valid marker.
            offset--;
          }
          break;

        default:
          // Could be incorrect encoding -- the last 0xFF byte of the previous
          // block could have been eaten by the encoder, hence we fallback to
          // `startPos = offset - 3` when looking for the next valid marker.
          const nextFileMarker = findNextFileMarker(
            data,
            /* currentPos = */ offset - 2,
            /* startPos = */ offset - 3
          );
          if (nextFileMarker?.invalid) {
            warn(
              "JpegImage.parse - unexpected data, current marker is: " +
                nextFileMarker.invalid
            );
            offset = nextFileMarker.offset;
            break;
          }
          if (!nextFileMarker || offset >= data.length - 1) {
            warn(
              "JpegImage.parse - reached the end of the image data " +
                "without finding an EOI marker (0xFFD9)."
            );
            break markerLoop;
          }
          throw new JpegError(
            "JpegImage.parse - unknown marker: " + fileMarker.toString(16)
          );
      }
      fileMarker = readUint16(data, offset);
      offset += 2;
    }