Files
cut-abstractions/tests/dev1/dataHandle/common/bmp.ts
2025-07-22 18:22:31 +08:00

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
}