UNPKG

image-in-browser

Version:

Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)

323 lines 11.9 kB
import { Format } from '../color/format.js'; import { OutputBuffer } from '../common/output-buffer.js'; import { PaletteUint8 } from '../image/palette-uint8.js'; import { BmpCompressionMode } from './bmp/bmp-compression-mode.js'; import { BmpFileHeader } from './bmp/bmp-file-header.js'; export class BmpEncoder { constructor() { this._supportsAnimation = false; } get supportsAnimation() { return this._supportsAnimation; } encode(opt) { let image = opt.image; const out = new OutputBuffer(); const nc = image.numChannels; let palette = image.palette; const format = image.format; if (format === Format.uint1 && nc === 1 && palette === undefined) { palette = new PaletteUint8(2, 3); palette.setRgb(0, 0, 0, 0); palette.setRgb(1, 255, 255, 255); } else if (format === Format.uint1 && nc === 2) { image = image.convert({ format: Format.uint2, numChannels: 1, withPalette: true, }); palette = image.palette; } else if (format === Format.uint1 && nc === 3 && palette === undefined) { image = image.convert({ format: Format.uint4, withPalette: true, }); palette = image.palette; } else if (format === Format.uint1 && nc === 4) { image = image.convert({ format: Format.uint8, numChannels: 4, }); } else if (format === Format.uint2 && nc === 1 && palette === undefined) { image = image.convert({ format: Format.uint2, withPalette: true, }); palette = image.palette; } else if (format === Format.uint2 && nc === 2) { image = image.convert({ format: Format.uint8, withPalette: true, }); palette = image.palette; } else if (format === Format.uint2 && nc === 3 && palette === undefined) { image = image.convert({ format: Format.uint8, withPalette: true, }); palette = image.palette; } else if (format === Format.uint2 && nc === 4) { image = image.convert({ format: Format.uint8, withPalette: true, }); palette = image.palette; } else if (format === Format.uint4 && nc === 1 && palette === undefined) { image = image.convert({ format: Format.uint8, withPalette: true, }); palette = image.palette; } else if (format === Format.uint4 && nc === 2) { image = image.convert({ format: Format.uint8, numChannels: 3, }); } else if (format === Format.uint4 && nc === 3 && palette === undefined) { image = image.convert({ format: Format.uint8, numChannels: 3, }); } else if (format === Format.uint4 && nc === 4) { image = image.convert({ format: Format.uint8, numChannels: 4, }); } else if (format === Format.uint8 && nc === 1 && palette === undefined) { image = image.convert({ format: Format.uint8, withPalette: true, }); } else if (format === Format.uint8 && nc === 2) { image = image.convert({ format: Format.uint8, numChannels: 3, }); } else if (image.isHdrFormat) { image = image.convert({ format: Format.uint8, }); } else if (image.hasPalette && image.numChannels === 4) { image = image.convert({ numChannels: 4, }); } let bpp = image.bitsPerChannel * image.data.numChannels; if (bpp === 12) { bpp = 16; } const compression = bpp > 8 ? BmpCompressionMode.bitfields : BmpCompressionMode.none; const imageStride = image.rowStride; const fileStride = Math.trunc((image.width * bpp + 31) / 32) * 4; const rowPaddingSize = fileStride - imageStride; const rowPadding = rowPaddingSize > 0 ? new Uint8Array(rowPaddingSize).fill(0xff) : undefined; const implicitPaletteSize = bpp >= 1 && bpp <= 8 ? 1 << bpp : 0; const imageFileSize = fileStride * image.height; const headerInfoSize = bpp > 8 ? 124 : 40; const headerSize = headerInfoSize + 14; const paletteSize = implicitPaletteSize * 4; const origImageOffset = headerSize + paletteSize; const imageOffset = origImageOffset; const gapSize = imageOffset - origImageOffset; const fileSize = imageFileSize + headerSize + paletteSize + gapSize; const sRgb = 0x73524742; out.writeUint16(BmpFileHeader.signature); out.writeUint32(fileSize); out.writeUint32(0); out.writeUint32(imageOffset); out.writeUint32(headerInfoSize); out.writeUint32(image.width); out.writeUint32(image.height); out.writeUint16(1); out.writeUint16(bpp); out.writeUint32(compression); out.writeUint32(imageFileSize); out.writeUint32(11811); out.writeUint32(11811); out.writeUint32(bpp === 8 ? 255 : 0); out.writeUint32(bpp === 8 ? 255 : 0); if (bpp > 8) { const blueMask = bpp === 16 ? 0xf : 0xff; const greenMask = bpp === 16 ? 0xf0 : 0xff00; const redMask = bpp === 16 ? 0xf00 : 0xff0000; const alphaMask = bpp === 16 ? 0xf000 : 0xff000000; out.writeUint32(redMask); out.writeUint32(greenMask); out.writeUint32(blueMask); out.writeUint32(alphaMask); out.writeUint32(sRgb); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); out.writeUint32(2); out.writeUint32(0); out.writeUint32(0); out.writeUint32(0); } if (bpp === 1 || bpp === 2 || bpp === 4 || bpp === 8) { if (palette !== undefined) { const l = palette.numColors > implicitPaletteSize ? implicitPaletteSize : palette.numColors; let pi = 0; for (pi = 0; pi < l; ++pi) { out.writeByte(Math.trunc(palette.getBlue(pi))); out.writeByte(Math.trunc(palette.getGreen(pi))); out.writeByte(Math.trunc(palette.getRed(pi))); out.writeByte(0); } for (; pi < implicitPaletteSize; ++pi) { out.writeByte(0); out.writeByte(0); out.writeByte(0); out.writeByte(0); } } else { if (bpp === 1) { out.writeByte(0); out.writeByte(0); out.writeByte(0); out.writeByte(0); out.writeByte(255); out.writeByte(255); out.writeByte(255); out.writeByte(0); } else if (bpp === 2) { for (let pi = 0; pi < 4; ++pi) { const v = pi * 85; out.writeByte(v); out.writeByte(v); out.writeByte(v); out.writeByte(0); } } else if (bpp === 4) { for (let pi = 0; pi < 16; ++pi) { const v = pi * 17; out.writeByte(v); out.writeByte(v); out.writeByte(v); out.writeByte(0); } } else if (bpp === 8) { for (let pi = 0; pi < 256; ++pi) { out.writeByte(pi); out.writeByte(pi); out.writeByte(pi); out.writeByte(0); } } } } let gap1 = gapSize; while (gap1-- > 0) { out.writeByte(0); } if (bpp === 1 || bpp === 2 || bpp === 4 || bpp === 8) { let offset = image.byteLength - imageStride; const h = image.height; for (let y = 0; y < h; ++y) { const bytes = image.buffer !== undefined ? new Uint8Array(image.buffer, offset, imageStride) : new Uint8Array(); if (bpp === 1) { out.writeBytes(bytes); } else if (bpp === 2) { const l = bytes.length; for (let xi = 0; xi < l; ++xi) { const b = bytes[xi]; const left = b >>> 4; const right = b & 0x0f; const rb = (right << 4) | left; out.writeByte(rb); } } else if (bpp === 4) { const l = bytes.length; for (let xi = 0; xi < l; ++xi) { const b = bytes[xi]; const b1 = b >>> 4; const b2 = b & 0x0f; const rb = (b1 << 4) | b2; out.writeByte(rb); } } else { out.writeBytes(bytes); } if (rowPadding !== undefined) { out.writeBytes(rowPadding); } offset -= imageStride; } return out.getBytes(); } const hasAlpha = image.numChannels === 4; const h = image.height; const w = image.width; if (bpp === 16) { let p = undefined; for (let y = h - 1; y >= 0; --y) { p = image.getPixel(0, y, p); for (let x = 0; x < w; ++x) { out.writeByte((Math.trunc(p.g) << 4) | Math.trunc(p.b)); out.writeByte((Math.trunc(p.a) << 4) | Math.trunc(p.r)); p.next(); } if (rowPadding !== undefined) { out.writeBytes(rowPadding); } } } else { let p = undefined; for (let y = h - 1; y >= 0; --y) { p = image.getPixel(0, y, p); for (let x = 0; x < w; ++x) { out.writeByte(Math.trunc(p.b)); out.writeByte(Math.trunc(p.g)); out.writeByte(Math.trunc(p.r)); if (hasAlpha) { out.writeByte(Math.trunc(p.a)); } p.next(); } if (rowPadding !== undefined) { out.writeBytes(rowPadding); } } } return out.getBytes(); } } //# sourceMappingURL=bmp-encoder.js.map