_encodeBMP()

in src/core/image_resizer.js [392:558]


  _encodeBMP() {
    const { width, height, kind } = this._imgData;
    let data = this._imgData.data;
    let bitPerPixel;
    let colorTable = new Uint8Array(0);
    let maskTable = colorTable;
    let compression = 0;

    // Each row of the image must be padded in order to have a final size which
    // is a multiple of 4.

    switch (kind) {
      case ImageKind.GRAYSCALE_1BPP: {
        bitPerPixel = 1;
        colorTable = new Uint8Array(
          this._isMask
            ? [255, 255, 255, 255, 0, 0, 0, 0]
            : [0, 0, 0, 0, 255, 255, 255, 255]
        );
        const rowLen = (width + 7) >> 3;
        const rowSize = (rowLen + 3) & -4;
        if (rowLen !== rowSize) {
          const newData = new Uint8Array(rowSize * height);
          let k = 0;
          for (
            let i = 0, ii = height * rowLen;
            i < ii;
            i += rowLen, k += rowSize
          ) {
            newData.set(data.subarray(i, i + rowLen), k);
          }
          data = newData;
        }
        break;
      }
      case ImageKind.RGB_24BPP: {
        bitPerPixel = 24;
        if (width & 3) {
          const rowLen = 3 * width;
          const rowSize = (rowLen + 3) & -4;
          const extraLen = rowSize - rowLen;
          const newData = new Uint8Array(rowSize * height);
          let k = 0;
          for (let i = 0, ii = height * rowLen; i < ii; i += rowLen) {
            const row = data.subarray(i, i + rowLen);
            for (let j = 0; j < rowLen; j += 3) {
              newData[k++] = row[j + 2];
              newData[k++] = row[j + 1];
              newData[k++] = row[j];
            }
            k += extraLen;
          }
          data = newData;
        } else {
          for (let i = 0, ii = data.length; i < ii; i += 3) {
            // Just swap R and B.
            const tmp = data[i];
            data[i] = data[i + 2];
            data[i + 2] = tmp;
          }
        }
        break;
      }
      case ImageKind.RGBA_32BPP:
        bitPerPixel = 32;
        compression = 3;
        maskTable = new Uint8Array(
          4 /* R mask */ +
            4 /* G mask */ +
            4 /* B mask */ +
            4 /* A mask */ +
            52 /* Windows color space stuff */
        );
        const view = new DataView(maskTable.buffer);
        if (FeatureTest.isLittleEndian) {
          view.setUint32(0, 0x000000ff, true);
          view.setUint32(4, 0x0000ff00, true);
          view.setUint32(8, 0x00ff0000, true);
          view.setUint32(12, 0xff000000, true);
        } else {
          view.setUint32(0, 0xff000000, true);
          view.setUint32(4, 0x00ff0000, true);
          view.setUint32(8, 0x0000ff00, true);
          view.setUint32(12, 0x000000ff, true);
        }
        break;
      default:
        throw new Error("invalid format");
    }

    let i = 0;
    const headerLength = 40 + maskTable.length;
    const fileLength = 14 + headerLength + colorTable.length + data.length;
    const bmpData = new Uint8Array(fileLength);
    const view = new DataView(bmpData.buffer);

    // Signature.
    view.setUint16(i, 0x4d42, true);
    i += 2;

    // File size.
    view.setUint32(i, fileLength, true);
    i += 4;

    // Reserved.
    view.setUint32(i, 0, true);
    i += 4;

    // Data offset.
    view.setUint32(i, 14 + headerLength + colorTable.length, true);
    i += 4;

    // Header size.
    view.setUint32(i, headerLength, true);
    i += 4;

    // Width.
    view.setInt32(i, width, true);
    i += 4;

    // Height.
    // Negative height indicates that the image is stored from top to bottom.
    view.setInt32(i, -height, true);
    i += 4;

    // Number of planes (must be 1).
    view.setUint16(i, 1, true);
    i += 2;

    // Number of bit per pixel.
    view.setUint16(i, bitPerPixel, true);
    i += 2;

    // Compression method.
    view.setUint32(i, compression, true);
    i += 4;

    // The image size.
    view.setUint32(i, 0, true);
    i += 4;

    // Horizontal resolution.
    view.setInt32(i, 0, true);
    i += 4;

    // Vertical resolution.
    view.setInt32(i, 0, true);
    i += 4;

    // Number of colors in the palette (0 to default).
    view.setUint32(i, colorTable.length / 4, true);
    i += 4;

    // Number of important colors used (0 to default).
    view.setUint32(i, 0, true);
    i += 4;

    bmpData.set(maskTable, i);
    i += maskTable.length;

    bmpData.set(colorTable, i);
    i += colorTable.length;

    bmpData.set(data, i);

    return bmpData;
  }