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