const decoder = new TextDecoder() export class BmpDecoder { pos: number buffer: ArrayBuffer is_with_alpha: boolean bottom_up: boolean flag: string constructor(buffer: ArrayBuffer, is_with_alpha: boolean) { this.pos = 0 this.buffer = buffer this.is_with_alpha = !!is_with_alpha this.bottom_up = true this.flag = decoder.decode(this.buffer.slice(0, (this.pos += 2))) if (this.flag != 'BM') throw new Error('Invalid BMP File') // this.parseHeader(); // this.parseRGBA(); } parseHeader() {} parseRGBA() {} } export function createBmpFile( imgData: ImageData, paramDic: { [key: string]: string } = {}, ) { let dpi = 300 let bitPP = paramDic.bpp ? Number(paramDic.bpp) : 24 let printResolution = Math.round(dpi * 39.3701) if ([1, 24, 32].includes(bitPP) == false) { throw new Error(`不支持的${bitPP}bpp BMP`) } let data = imgData.data let width = imgData.width let height = imgData.height let extraBytes = width % 4 let rgbSize = data.length switch (bitPP) { case 1: { let rowSize = width / 8 + (width % 8 > 0 ? 1 : 0) extraBytes = rowSize % 4 rgbSize = height * (rowSize + extraBytes) } break case 24: { let rowSize = 3 * width extraBytes = rowSize % 4 rgbSize = height * (rowSize + extraBytes) } break } let headerInfoSize = 40 /** ****************header */ let flag = 'BM' let reserved = 0 let offset = 54 let fileSize = rgbSize + offset let planes = 1 let compress = 0 let hr = printResolution let vr = printResolution let colors = 0 let importantColors = 0 let colorPaletteSize = 0 if (bitPP == 1) { colorPaletteSize = 2 * 4 } let buffer = new ArrayBuffer(offset + colorPaletteSize + rgbSize) let tempBuffer = new DataView(buffer) let pos = 0 // BMP Header // ID tempBuffer.setUint8(pos, flag.charCodeAt(0)) pos += 1 tempBuffer.setUint8(pos, flag.charCodeAt(1)) pos += 1 // size tempBuffer.setUint32(pos, fileSize, true) pos += 4 // Application specific tempBuffer.setUint32(pos, reserved, true) pos += 4 // Offset tempBuffer.setUint32(pos, offset, true) pos += 4 // DIB Header // Number of bytes in the DIB header (from this point) tempBuffer.setUint32(pos, headerInfoSize, true) pos += 4 tempBuffer.setUint32(pos, width, true) pos += 4 tempBuffer.setUint32(pos, height, true) pos += 4 tempBuffer.setUint16(pos, planes, true) pos += 2 tempBuffer.setUint16(pos, bitPP, true) pos += 2 tempBuffer.setUint16(pos, compress, true) pos += 4 tempBuffer.setUint16(pos, rgbSize, true) pos += 4 tempBuffer.setUint16(pos, hr, true) pos += 4 tempBuffer.setUint16(pos, vr, true) pos += 4 tempBuffer.setUint16(pos, colors, true) pos += 4 tempBuffer.setUint16(pos, importantColors, true) pos += 4 if (bitPP == 1) { tempBuffer.setUint8(pos++, 0) // b tempBuffer.setUint8(pos++, 0) // g tempBuffer.setUint8(pos++, 0) // r tempBuffer.setUint8(pos++, 0) // a tempBuffer.setUint8(pos++, 255) // b tempBuffer.setUint8(pos++, 255) // g tempBuffer.setUint8(pos++, 255) // r tempBuffer.setUint8(pos++, 0) // a } let pixData = '' let writeBinData = () => { tempBuffer.setUint8(pos++, Number.parseInt(pixData.padEnd(8, '0'), 2)) pixData = '' } for (let y = height - 1; y >= 0; y--) { let rowIndex = y * width * 4 for (let x = 0; x < width; x++) { let p = rowIndex + x * 4 if (bitPP == 1) { if (pixData.length == 8) { writeBinData() } let rgb = [data[p + 0], data[p + 1], data[p + 2]] pixData += rgb.filter((v, i) => v > 170).length == 3 ? '1' : '0' // 170 以下小板缩略图条会断 } else { tempBuffer.setUint8(pos++, data[p + 2]) // b tempBuffer.setUint8(pos++, data[p + 1]) // g tempBuffer.setUint8(pos++, data[p + 0]) // r if (bitPP == 32) { tempBuffer.setUint8(pos++, data[p + 3]) // a } } } if (bitPP == 1 && pixData.length > 0) { writeBinData() } for (let index = 0; index < extraBytes; index++) { tempBuffer.setUint8(pos++, 0) } } return tempBuffer.buffer }