177 lines
4.2 KiB
TypeScript
177 lines
4.2 KiB
TypeScript
|
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
|
||
|
}
|