Initial commit

This commit is contained in:
robert committed 2025-05-30 18:13:30 +08:00
commit e49726988c
5445 files changed
+1089914

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
/// <reference types="node" resolution-mode="require"/>
import { BmpCompression, BmpColor, BmpDecoderOptions, BmpImage } from './types.js';
export default class BmpDecoder implements BmpImage {
flag: string;
fileSize: number;
reserved1: number;
reserved2: number;
offset: number;
headerSize: number;
width: number;
height: number;
planes: number;
bitPP: number;
compression?: BmpCompression;
rawSize: number;
hr: number;
vr: number;
colors: number;
importantColors: number;
palette: BmpColor[];
data: Buffer;
private maskRed;
private maskGreen;
private maskBlue;
private maskAlpha;
private readonly toRGBA;
private pos;
private bottomUp;
private readonly buffer;
private readonly locRed;
private readonly locGreen;
private readonly locBlue;
private readonly locAlpha;
private shiftRed;
private shiftGreen;
private shiftBlue;
private shiftAlpha;
constructor(buffer: Buffer, { toRGBA }?: BmpDecoderOptions);
private parseHeader;
private parseRGBA;
private bit1;
private bit4;
private bit8;
private bit16;
private bit24;
private bit32;
private scanImage;
private readUInt32LE;
private setPixelData;
}
+402
View File
@@ -0,0 +1,402 @@
import HeaderTypes from './header-types.js';
import maskColor from './mask-color.js';
import { BmpCompression } from './types.js';
export default class BmpDecoder {
// Header
flag;
fileSize;
reserved1;
reserved2;
offset;
headerSize;
width;
height;
planes;
bitPP;
compression;
rawSize;
hr;
vr;
colors;
importantColors;
palette;
data;
maskRed;
maskGreen;
maskBlue;
maskAlpha;
toRGBA;
pos;
bottomUp;
buffer;
locRed;
locGreen;
locBlue;
locAlpha;
shiftRed;
shiftGreen;
shiftBlue;
shiftAlpha;
constructor(buffer, { toRGBA } = { toRGBA: false }) {
this.buffer = buffer;
this.toRGBA = !!toRGBA;
this.pos = 0;
this.bottomUp = true;
this.flag = this.buffer.toString('utf-8', 0, (this.pos += 2));
if (this.flag !== 'BM') {
throw new Error('Invalid BMP File');
}
this.locRed = this.toRGBA ? 0 : 3;
this.locGreen = this.toRGBA ? 1 : 2;
this.locBlue = this.toRGBA ? 2 : 1;
this.locAlpha = this.toRGBA ? 3 : 0;
this.parseHeader();
this.parseRGBA();
}
parseHeader() {
this.fileSize = this.readUInt32LE();
this.reserved1 = this.buffer.readUInt16LE(this.pos);
this.pos += 2;
this.reserved2 = this.buffer.readUInt16LE(this.pos);
this.pos += 2;
this.offset = this.readUInt32LE();
// End of BITMAP_FILE_HEADER
this.headerSize = this.readUInt32LE();
if (!(this.headerSize in HeaderTypes)) {
throw new Error(`Unsupported BMP header size ${this.headerSize}`);
}
this.width = this.readUInt32LE();
this.height = this.readUInt32LE();
// negative value are possible here => implies bottom down
this.height =
this.height > 0x7fffffff ? this.height - 0x100000000 : this.height;
this.planes = this.buffer.readUInt16LE(this.pos);
this.pos += 2;
this.bitPP = this.buffer.readUInt16LE(this.pos);
this.pos += 2;
this.compression = this.readUInt32LE();
this.rawSize = this.readUInt32LE();
this.hr = this.readUInt32LE();
this.vr = this.readUInt32LE();
this.colors = this.readUInt32LE();
this.importantColors = this.readUInt32LE();
// De facto defaults
if (this.bitPP === 32) {
this.maskAlpha = 0;
this.maskRed = 0x00ff0000;
this.maskGreen = 0x0000ff00;
this.maskBlue = 0x000000ff;
}
else if (this.bitPP === 16) {
this.maskAlpha = 0;
this.maskRed = 0x7c00;
this.maskGreen = 0x03e0;
this.maskBlue = 0x001f;
}
// End of BITMAP_INFO_HEADER
if (this.headerSize > HeaderTypes.BITMAP_INFO_HEADER ||
this.compression === BmpCompression.BI_BIT_FIELDS ||
this.compression === BmpCompression.BI_ALPHA_BIT_FIELDS) {
this.maskRed = this.readUInt32LE();
this.maskGreen = this.readUInt32LE();
this.maskBlue = this.readUInt32LE();
}
// End of BITMAP_V2_INFO_HEADER
if (this.headerSize > HeaderTypes.BITMAP_V2_INFO_HEADER ||
this.compression === BmpCompression.BI_ALPHA_BIT_FIELDS) {
this.maskAlpha = this.readUInt32LE();
}
// End of BITMAP_V3_INFO_HEADER
if (this.headerSize > HeaderTypes.BITMAP_V3_INFO_HEADER) {
this.pos +=
HeaderTypes.BITMAP_V4_HEADER - HeaderTypes.BITMAP_V3_INFO_HEADER;
}
// End of BITMAP_V4_HEADER
if (this.headerSize > HeaderTypes.BITMAP_V4_HEADER) {
this.pos += HeaderTypes.BITMAP_V5_HEADER - HeaderTypes.BITMAP_V4_HEADER;
}
// End of BITMAP_V5_HEADER
if (this.bitPP <= 8 || this.colors > 0) {
const len = this.colors === 0 ? 1 << this.bitPP : this.colors;
this.palette = new Array(len);
for (let i = 0; i < len; i++) {
const blue = this.buffer.readUInt8(this.pos++);
const green = this.buffer.readUInt8(this.pos++);
const red = this.buffer.readUInt8(this.pos++);
const quad = this.buffer.readUInt8(this.pos++);
this.palette[i] = {
red,
green,
blue,
quad,
};
}
}
// End of color table
// Can the height ever be negative?
if (this.height < 0) {
this.height *= -1;
this.bottomUp = false;
}
const coloShift = maskColor(this.maskRed, this.maskGreen, this.maskBlue, this.maskAlpha);
this.shiftRed = coloShift.shiftRed;
this.shiftGreen = coloShift.shiftGreen;
this.shiftBlue = coloShift.shiftBlue;
this.shiftAlpha = coloShift.shiftAlpha;
}
parseRGBA() {
this.data = Buffer.alloc(this.width * this.height * 4);
switch (this.bitPP) {
case 1:
this.bit1();
break;
case 4:
this.bit4();
break;
case 8:
this.bit8();
break;
case 16:
this.bit16();
break;
case 24:
this.bit24();
break;
default:
this.bit32();
}
}
bit1() {
const xLen = Math.ceil(this.width / 8);
const mode = xLen % 4;
const padding = mode !== 0 ? 4 - mode : 0;
let lastLine;
this.scanImage(padding, xLen, (x, line) => {
if (line !== lastLine) {
lastLine = line;
}
const b = this.buffer.readUInt8(this.pos++);
const location = line * this.width * 4 + x * 8 * 4;
for (let i = 0; i < 8; i++) {
if (x * 8 + i < this.width) {
const rgb = this.palette[(b >> (7 - i)) & 0x1];
this.data[location + i * this.locAlpha] = 0;
this.data[location + i * 4 + this.locBlue] = rgb.blue;
this.data[location + i * 4 + this.locGreen] = rgb.green;
this.data[location + i * 4 + this.locRed] = rgb.red;
}
else {
break;
}
}
});
}
bit4() {
if (this.compression === BmpCompression.BI_RLE4) {
this.data.fill(0);
let lowNibble = false; //for all count of pixel
let lines = this.bottomUp ? this.height - 1 : 0;
let location = 0;
while (location < this.data.length) {
const a = this.buffer.readUInt8(this.pos++);
const b = this.buffer.readUInt8(this.pos++);
//absolute mode
if (a === 0) {
if (b === 0) {
//line end
lines += this.bottomUp ? -1 : 1;
location = lines * this.width * 4;
lowNibble = false;
continue;
}
if (b === 1) {
// image end
break;
}
if (b === 2) {
// offset x, y
const x = this.buffer.readUInt8(this.pos++);
const y = this.buffer.readUInt8(this.pos++);
lines += this.bottomUp ? -y : y;
location += y * this.width * 4 + x * 4;
}
else {
let c = this.buffer.readUInt8(this.pos++);
for (let i = 0; i < b; i++) {
location = this.setPixelData(location, lowNibble ? c & 0x0f : (c & 0xf0) >> 4);
if (i & 1 && i + 1 < b) {
c = this.buffer.readUInt8(this.pos++);
}
lowNibble = !lowNibble;
}
if ((((b + 1) >> 1) & 1) === 1) {
this.pos++;
}
}
}
else {
//encoded mode
for (let i = 0; i < a; i++) {
location = this.setPixelData(location, lowNibble ? b & 0x0f : (b & 0xf0) >> 4);
lowNibble = !lowNibble;
}
}
}
}
else {
const xLen = Math.ceil(this.width / 2);
const mode = xLen % 4;
const padding = mode !== 0 ? 4 - mode : 0;
this.scanImage(padding, xLen, (x, line) => {
const b = this.buffer.readUInt8(this.pos++);
const location = line * this.width * 4 + x * 2 * 4;
const first4 = b >> 4;
let rgb = this.palette[first4];
this.data[location] = 0;
this.data[location + 1] = rgb.blue;
this.data[location + 2] = rgb.green;
this.data[location + 3] = rgb.red;
if (x * 2 + 1 >= this.width) {
// throw new Error('Something');
return false;
}
const last4 = b & 0x0f;
rgb = this.palette[last4];
this.data[location + 4] = 0;
this.data[location + 4 + 1] = rgb.blue;
this.data[location + 4 + 2] = rgb.green;
this.data[location + 4 + 3] = rgb.red;
});
}
}
bit8() {
if (this.compression === BmpCompression.BI_RLE8) {
this.data.fill(0);
let lines = this.bottomUp ? this.height - 1 : 0;
let location = 0;
while (location < this.data.length) {
const a = this.buffer.readUInt8(this.pos++);
const b = this.buffer.readUInt8(this.pos++);
//absolute mode
if (a === 0) {
if (b === 0) {
//line end
lines += this.bottomUp ? -1 : 1;
location = lines * this.width * 4;
continue;
}
if (b === 1) {
//image end
break;
}
if (b === 2) {
//offset x,y
const x = this.buffer.readUInt8(this.pos++);
const y = this.buffer.readUInt8(this.pos++);
lines += this.bottomUp ? -y : y;
location += y * this.width * 4 + x * 4;
}
else {
for (let i = 0; i < b; i++) {
const c = this.buffer.readUInt8(this.pos++);
location = this.setPixelData(location, c);
}
// @ts-ignore
const shouldIncrement = b & (1 === 1);
if (shouldIncrement) {
this.pos++;
}
}
}
else {
//encoded mode
for (let i = 0; i < a; i++) {
location = this.setPixelData(location, b);
}
}
}
}
else {
const mode = this.width % 4;
const padding = mode !== 0 ? 4 - mode : 0;
this.scanImage(padding, this.width, (x, line) => {
const b = this.buffer.readUInt8(this.pos++);
const location = line * this.width * 4 + x * 4;
if (b < this.palette.length) {
const rgb = this.palette[b];
this.data[location] = 0;
this.data[location + 1] = rgb.blue;
this.data[location + 2] = rgb.green;
this.data[location + 3] = rgb.red;
}
else {
this.data[location] = 0;
this.data[location + 1] = 0xff;
this.data[location + 2] = 0xff;
this.data[location + 3] = 0xff;
}
});
}
}
bit16() {
const padding = (this.width % 2) * 2;
this.scanImage(padding, this.width, (x, line) => {
const loc = line * this.width * 4 + x * 4;
const px = this.buffer.readUInt16LE(this.pos);
this.pos += 2;
this.data[loc + this.locRed] = this.shiftRed(px);
this.data[loc + this.locGreen] = this.shiftGreen(px);
this.data[loc + this.locBlue] = this.shiftBlue(px);
this.data[loc + this.locAlpha] = this.shiftAlpha(px);
});
}
bit24() {
const padding = this.width % 4;
this.scanImage(padding, this.width, (x, line) => {
const loc = line * this.width * 4 + x * 4;
const blue = this.buffer.readUInt8(this.pos++);
const green = this.buffer.readUInt8(this.pos++);
const red = this.buffer.readUInt8(this.pos++);
this.data[loc + this.locRed] = red;
this.data[loc + this.locGreen] = green;
this.data[loc + this.locBlue] = blue;
this.data[loc + this.locAlpha] = 0;
});
}
bit32() {
this.scanImage(0, this.width, (x, line) => {
const loc = line * this.width * 4 + x * 4;
const px = this.readUInt32LE();
this.data[loc + this.locRed] = this.shiftRed(px);
this.data[loc + this.locGreen] = this.shiftGreen(px);
this.data[loc + this.locBlue] = this.shiftBlue(px);
this.data[loc + this.locAlpha] = this.shiftAlpha(px);
});
}
scanImage(padding = 0, width = this.width, processPixel) {
for (let y = this.height - 1; y >= 0; y--) {
const line = this.bottomUp ? y : this.height - 1 - y;
for (let x = 0; x < width; x++) {
const result = processPixel.call(this, x, line);
if (result === false) {
return;
}
}
this.pos += padding;
}
}
readUInt32LE() {
const value = this.buffer.readUInt32LE(this.pos);
this.pos += 4;
return value;
}
setPixelData(location, rgbIndex) {
const { blue, green, red } = this.palette[rgbIndex];
this.data[location + this.locAlpha] = 0;
this.data[location + 1 + this.locBlue] = blue;
this.data[location + 2 + this.locGreen] = green;
this.data[location + 3 + this.locRed] = red;
return location + 4;
}
}
//# sourceMappingURL=decoder.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decoder.js","sourceRoot":"","sources":["../../src/decoder.ts"],"names":[],"mappings":"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 truncated
+38
View File
@@ -0,0 +1,38 @@
/// <reference types="node" resolution-mode="require"/>
import { BmpColor, BmpImage } from './types.js';
export default class BmpEncoder implements BmpImage {
readonly fileSize: number;
readonly reserved1: number;
readonly reserved2: number;
readonly offset: number;
readonly width: number;
readonly flag: string;
readonly height: number;
readonly planes: number;
readonly bitPP: number;
readonly compress: number;
readonly hr: number;
readonly vr: number;
readonly colors: number;
readonly importantColors: number;
readonly rawSize: number;
readonly headerSize: number;
readonly data: Buffer;
readonly palette: BmpColor[];
private readonly extraBytes;
private readonly buffer;
private readonly bytesInColor;
private pos;
constructor(imgData: BmpImage);
encode(): void;
private writeHeader;
private bit1;
private bit4;
private bit8;
private bit16;
private bit24;
private bit32;
private writeImage;
private initColors;
private writeUInt32LE;
}
+263
View File
@@ -0,0 +1,263 @@
import HeaderTypes from './header-types.js';
function createInteger(numbers) {
return numbers.reduce((final, n) => (final << 1) | n, 0);
}
function createColor(color) {
return ((color.quad << 24) | (color.red << 16) | (color.green << 8) | color.blue);
}
export default class BmpEncoder {
fileSize;
reserved1;
reserved2;
offset;
width;
flag;
height;
planes;
bitPP;
compress;
hr;
vr;
colors;
importantColors;
rawSize;
headerSize;
data;
palette;
extraBytes;
buffer;
bytesInColor;
pos;
constructor(imgData) {
this.buffer = imgData.data;
this.width = imgData.width;
this.height = imgData.height;
this.headerSize = HeaderTypes.BITMAP_INFO_HEADER;
// Header
this.flag = 'BM';
this.bitPP = imgData.bitPP || 24;
this.offset = 54;
this.reserved1 = imgData.reserved1 || 0;
this.reserved2 = imgData.reserved2 || 0;
this.planes = 1;
this.compress = 0;
this.hr = imgData.hr || 0;
this.vr = imgData.vr || 0;
this.importantColors = imgData.importantColors || 0;
this.colors = Math.min(2 ** (this.bitPP - 1 || 1), imgData.colors || Infinity);
this.palette = imgData.palette || [];
if (this.colors && this.bitPP < 16) {
this.offset += this.colors * 4;
}
else {
this.colors = 0;
}
switch (this.bitPP) {
case 32:
this.bytesInColor = 4;
break;
case 16:
this.bytesInColor = 2;
break;
case 8:
this.bytesInColor = 1;
break;
case 4:
this.bytesInColor = 1 / 2;
break;
case 1:
this.bytesInColor = 1 / 8;
break;
default:
this.bytesInColor = 3;
this.bitPP = 24;
}
const rowWidth = (this.width * this.bitPP) / 32;
const rowBytes = Math.ceil(rowWidth);
this.extraBytes = (rowBytes - rowWidth) * 4;
// Why 2?
this.rawSize = this.height * rowBytes * 4 + 2;
this.fileSize = this.rawSize + this.offset;
this.data = Buffer.alloc(this.fileSize, 0x1);
this.pos = 0;
this.encode();
}
encode() {
this.pos = 0;
this.writeHeader();
switch (this.bitPP) {
case 32:
this.bit32();
break;
case 16:
this.bit16();
break;
case 8:
this.bit8();
break;
case 4:
this.bit4();
break;
case 1:
this.bit1();
break;
default:
this.bit24();
}
}
writeHeader() {
this.data.write(this.flag, this.pos, 2);
this.pos += 2;
this.writeUInt32LE(this.fileSize);
// Writing 2 UInt16LE resulted in a weird bug
this.writeUInt32LE((this.reserved1 << 16) | this.reserved2);
this.writeUInt32LE(this.offset);
this.writeUInt32LE(this.headerSize);
this.writeUInt32LE(this.width);
this.writeUInt32LE(this.height);
this.data.writeUInt16LE(this.planes, this.pos);
this.pos += 2;
this.data.writeUInt16LE(this.bitPP, this.pos);
this.pos += 2;
this.writeUInt32LE(this.compress);
this.writeUInt32LE(this.rawSize);
this.writeUInt32LE(this.hr);
this.writeUInt32LE(this.vr);
this.writeUInt32LE(this.colors);
this.writeUInt32LE(this.importantColors);
}
bit1() {
if (this.palette.length && this.colors === 2) {
this.initColors(1);
}
else {
this.writeUInt32LE(0x00ffffff); // Black
this.writeUInt32LE(0x00000000); // White
}
this.pos += 1; // ?
let lineArr = [];
this.writeImage((p, index, x) => {
let i = index;
i++;
const b = this.buffer[i++];
const g = this.buffer[i++];
const r = this.buffer[i++];
const brightness = r * 0.2126 + g * 0.7152 + b * 0.0722;
lineArr.push(brightness > 127 ? 0 : 1);
if ((x + 1) % 8 === 0) {
this.data[p - 1] = createInteger(lineArr);
lineArr = [];
}
else if (x === this.width - 1 && lineArr.length > 0) {
this.data[p - 1] = createInteger(lineArr) << 4;
lineArr = [];
}
return i;
});
}
bit4() {
const colors = this.initColors(4);
let integerPair = [];
this.writeImage((p, index, x) => {
let i = index;
const colorInt = createColor({
quad: this.buffer[i++],
blue: this.buffer[i++],
green: this.buffer[i++],
red: this.buffer[i++],
});
const colorExists = colors.findIndex((c) => c === colorInt);
if (colorExists !== -1) {
integerPair.push(colorExists);
}
else {
integerPair.push(0);
}
if ((x + 1) % 2 === 0) {
this.data[p] = (integerPair[0] << 4) | integerPair[1];
integerPair = [];
}
return i;
});
}
bit8() {
const colors = this.initColors(8);
this.writeImage((p, index) => {
let i = index;
const colorInt = createColor({
quad: this.buffer[i++],
blue: this.buffer[i++],
green: this.buffer[i++],
red: this.buffer[i++],
});
const colorExists = colors.findIndex((c) => c === colorInt);
if (colorExists !== -1) {
this.data[p] = colorExists;
}
else {
this.data[p] = 0;
}
return i;
});
}
bit16() {
this.writeImage((p, index) => {
let i = index + 1;
const b = this.buffer[i++] / 8; // b
const g = this.buffer[i++] / 8; // g
const r = this.buffer[i++] / 8; // r
const color = (r << 10) | (g << 5) | b;
this.data[p] = color & 0x00ff;
this.data[p + 1] = (color & 0xff00) >> 8;
return i;
});
}
bit24() {
this.writeImage((p, index) => {
let i = index + 1;
this.data[p] = this.buffer[i++]; //b
this.data[p + 1] = this.buffer[i++]; //g
this.data[p + 2] = this.buffer[i++]; //r
return i;
});
}
bit32() {
this.writeImage((p, index) => {
let i = index;
this.data[p + 3] = this.buffer[i++]; // a
this.data[p] = this.buffer[i++]; // b
this.data[p + 1] = this.buffer[i++]; // g
this.data[p + 2] = this.buffer[i++]; // r
return i;
});
}
writeImage(writePixel) {
const rowBytes = this.extraBytes + this.width * this.bytesInColor;
let i = 0;
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
const p = Math.floor(this.pos + (this.height - 1 - y) * rowBytes + x * this.bytesInColor);
i = writePixel.call(this, p, i, x, y);
}
}
}
initColors(bit) {
const colors = [];
if (this.palette.length) {
for (let i = 0; i < this.colors; i++) {
const rootColor = createColor(this.palette[i]);
this.writeUInt32LE(rootColor);
colors.push(rootColor);
}
}
else {
throw new Error(`To encode ${bit}-bit BMPs a pallette is needed. Please choose up to ${this.colors} colors. Colors must be 32-bit integers.`);
}
return colors;
}
writeUInt32LE(value) {
this.data.writeUInt32LE(value, this.pos);
this.pos += 4;
}
}
//# sourceMappingURL=encoder.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/encoder.ts"],"names":[],"mappings":"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 truncated
+8
View File
@@ -0,0 +1,8 @@
declare enum HeaderTypes {
BITMAP_INFO_HEADER = 40,
BITMAP_V2_INFO_HEADER = 52,
BITMAP_V3_INFO_HEADER = 56,
BITMAP_V4_HEADER = 108,
BITMAP_V5_HEADER = 124
}
export default HeaderTypes;
+10
View File
@@ -0,0 +1,10 @@
var HeaderTypes;
(function (HeaderTypes) {
HeaderTypes[HeaderTypes["BITMAP_INFO_HEADER"] = 40] = "BITMAP_INFO_HEADER";
HeaderTypes[HeaderTypes["BITMAP_V2_INFO_HEADER"] = 52] = "BITMAP_V2_INFO_HEADER";
HeaderTypes[HeaderTypes["BITMAP_V3_INFO_HEADER"] = 56] = "BITMAP_V3_INFO_HEADER";
HeaderTypes[HeaderTypes["BITMAP_V4_HEADER"] = 108] = "BITMAP_V4_HEADER";
HeaderTypes[HeaderTypes["BITMAP_V5_HEADER"] = 124] = "BITMAP_V5_HEADER";
})(HeaderTypes || (HeaderTypes = {}));
export default HeaderTypes;
//# sourceMappingURL=header-types.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"header-types.js","sourceRoot":"","sources":["../../src/header-types.ts"],"names":[],"mappings":"AAAA,IAAK,WAMJ;AAND,WAAK,WAAW;IACd,0EAAuB,CAAA;IACvB,gFAA0B,CAAA;IAC1B,gFAA0B,CAAA;IAC1B,uEAAsB,CAAA;IACtB,uEAAsB,CAAA;AACxB,CAAC,EANI,WAAW,KAAX,WAAW,QAMf;AAED,eAAe,WAAW,CAAC"}
+7
View File
@@ -0,0 +1,7 @@
/// <reference types="node" resolution-mode="require"/>
import BmpDecoder from './decoder.js';
import BmpEncoder from './encoder.js';
import { BmpDecoderOptions, BmpImage } from './types.js';
export declare function decode(bmpData: Buffer, options?: BmpDecoderOptions): BmpDecoder;
export declare function encode(imgData: BmpImage): BmpEncoder;
export * from './types.js';
+10
View File
@@ -0,0 +1,10 @@
import BmpDecoder from './decoder.js';
import BmpEncoder from './encoder.js';
export function decode(bmpData, options) {
return new BmpDecoder(bmpData, options);
}
export function encode(imgData) {
return new BmpEncoder(imgData);
}
export * from './types.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,UAAU,MAAM,cAAc,CAAC;AAGtC,MAAM,UAAU,MAAM,CAAC,OAAe,EAAE,OAA2B;IACjE,OAAO,IAAI,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AACD,MAAM,UAAU,MAAM,CAAC,OAAiB;IACtC,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,cAAc,YAAY,CAAC"}
+6
View File
@@ -0,0 +1,6 @@
export default function maskColor(maskRed: number, maskGreen: number, maskBlue: number, maskAlpha: number): {
shiftRed: (x: number) => number;
shiftGreen: (x: number) => number;
shiftBlue: (x: number) => number;
shiftAlpha: (x: number) => number;
};
+58
View File
@@ -0,0 +1,58 @@
// We have these:
//
// const sample = 0101 0101 0101 0101
// const mask = 0111 1100 0000 0000
// 256 === 0000 0001 0000 0000
//
// We want to take the sample and turn it into an 8-bit value.
//
// 1. We extract the last bit of the mask:
//
// 0000 0100 0000 0000
// ^
//
// Like so:
//
// const a = ~mask = 1000 0011 1111 1111
// const b = a + 1 = 1000 0100 0000 0000
// const c = b & mask = 0000 0100 0000 0000
//
// 2. We shift it to the right and extract the bit before the first:
//
// 0000 0000 0010 0000
// ^
//
// Like so:
//
// const d = mask / c = 0000 0000 0001 1111
// const e = mask + 1 = 0000 0000 0010 0000
//
// 3. We apply the mask and the two values above to a sample:
//
// const f = sample & mask = 0101 0100 0000 0000
// const g = f / c = 0000 0000 0001 0101
// const h = 256 / e = 0000 0000 0000 0100
// const i = g * h = 0000 0000 1010 1000
// ^^^^ ^
//
// Voila, we have extracted a sample and "stretched" it to 8 bits. For samples
// which are already 8-bit, h === 1 and g === i.
export default function maskColor(maskRed, maskGreen, maskBlue, maskAlpha) {
const maskRedR = (~maskRed + 1) & maskRed;
const maskGreenR = (~maskGreen + 1) & maskGreen;
const maskBlueR = (~maskBlue + 1) & maskBlue;
const maskAlphaR = (~maskAlpha + 1) & maskAlpha;
const shiftedMaskRedL = maskRed / maskRedR + 1;
const shiftedMaskGreenL = maskGreen / maskGreenR + 1;
const shiftedMaskBlueL = maskBlue / maskBlueR + 1;
const shiftedMaskAlphaL = maskAlpha / maskAlphaR + 1;
return {
shiftRed: (x) => (((x & maskRed) / maskRedR) * 0x100) / shiftedMaskRedL,
shiftGreen: (x) => (((x & maskGreen) / maskGreenR) * 0x100) / shiftedMaskGreenL,
shiftBlue: (x) => (((x & maskBlue) / maskBlueR) * 0x100) / shiftedMaskBlueL,
shiftAlpha: maskAlpha !== 0
? (x) => (((x & maskAlpha) / maskAlphaR) * 0x100) / shiftedMaskAlphaL
: () => 255,
};
}
//# sourceMappingURL=mask-color.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"mask-color.js","sourceRoot":"","sources":["../../src/mask-color.ts"],"names":[],"mappings":"AAAA,iBAAiB;AACjB,EAAE;AACF,qCAAqC;AACrC,qCAAqC;AACrC,qCAAqC;AACrC,EAAE;AACF,8DAA8D;AAC9D,EAAE;AACF,0CAA0C;AAC1C,EAAE;AACF,sBAAsB;AACtB,UAAU;AACV,EAAE;AACF,WAAW;AACX,EAAE;AACF,2CAA2C;AAC3C,2CAA2C;AAC3C,2CAA2C;AAC3C,EAAE;AACF,oEAAoE;AACpE,EAAE;AACF,sBAAsB;AACtB,gBAAgB;AAChB,EAAE;AACF,WAAW;AACX,EAAE;AACF,2CAA2C;AAC3C,2CAA2C;AAC3C,EAAE;AACF,6DAA6D;AAC7D,EAAE;AACF,gDAAgD;AAChD,gDAAgD;AAChD,gDAAgD;AAChD,gDAAgD;AAChD,6CAA6C;AAC7C,EAAE;AACF,8EAA8E;AAC9E,gDAAgD;AAEhD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,OAAe,EACf,SAAiB,EACjB,QAAgB,EAChB,SAAiB;IAEjB,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC;IAC1C,MAAM,UAAU,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;IAChD,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IAC7C,MAAM,UAAU,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;IAEhD,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAC;IAC/C,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC;IAClD,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC;IAErD,OAAO;QACL,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE,CACtB,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,KAAK,CAAC,GAAG,eAAe;QACxD,UAAU,EAAE,CAAC,CAAS,EAAE,EAAE,CACxB,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,GAAG,iBAAiB;QAC9D,SAAS,EAAE,CAAC,CAAS,EAAE,EAAE,CACvB,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,gBAAgB;QAC3D,UAAU,EACR,SAAS,KAAK,CAAC;YACb,CAAC,CAAC,CAAC,CAAS,EAAE,EAAE,CACZ,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,GAAG,iBAAiB;YAChE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG;KAChB,CAAC;AACJ,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
+67
View File
@@ -0,0 +1,67 @@
/// <reference types="node" resolution-mode="require"/>
export declare enum BmpCompression {
NONE = 0,
BI_RLE8 = 1,
BI_RLE4 = 2,
BI_BIT_FIELDS = 3,
BI_ALPHA_BIT_FIELDS = 6
}
export interface BmpColor {
red: number;
green: number;
blue: number;
quad: number;
}
export interface BmpDecoderOptions {
toRGBA?: boolean;
}
export interface BmpImage {
width: number;
height: number;
data: Buffer;
flag?: string;
fileSize?: number;
/**
* Used to store extra data or information in the image file structure that isn't typically used or necessary for interpreting the basic image data. These reserved fields are often set to 0 when not used. They are each two bytes in size. If the programmer or software uses these for specific purposes, it should be documented accordingly.
*/
reserved1?: number;
/**
* Used to store extra data or information in the image file structure that isn't typically used or necessary for interpreting the basic image data. These reserved fields are often set to 0 when not used. They are each two bytes in size. If the programmer or software uses these for specific purposes, it should be documented accordingly.
*/
reserved2?: number;
offset?: number;
headerSize?: number;
planes?: number;
bitPP?: number;
compression?: BmpCompression;
rawSize?: number;
/**
* The hr value is used to store information about the intended pixel density of the image when printed or displayed. It doesn't directly influence how the image is displayed on the screen, as screens don't usually have the ability to adjust their pixel density
*/
hr?: number;
/**
* Signifies the vertical resolution of the image in pixels per meter. This value is meant to store information about the targeted pixel density of the image when it is displayed or printed.
*/
vr?: number;
/**
* represents the number of color indexes in the color table actually used by the bitmap. This value is stored as a 4-byte field.
* For a 24-bit bitmap, the colors field will be 0, as each pixel is defined by explicit Red, Green, and Blue values, making a color table unnecessary.
* However, for less than 24-bit bitmaps (like 1-bit, 4-bit, 8-bit), the value of the field specifies how many colors are used. For example, an 8-bit bitmap can display up to 256 different colors. If all possible colors are utilized, this field will be 256.
* When the colors field is 0 in these situations, it means that the maximum number of colors for the given bits-per-pixel (bitPP) value are used. For instance, if an 8-bit bitmap has colors set to 0, it means it uses 256 colors.
* In cases where the bitmap contains a color palette but doesn't use all the potential colors, the colors field would contain the actual number of colors used in the palette.
*/
colors?: number;
/**
* specifies the number of color indexes in the color table that are considered important for displaying the bitmap. This value is stored as a 4-byte field.
* If this value is 0, then all colors are considered important. This is often the case for most bitmaps.
* This field has significance for devices with limited color display capabilities. If the device cannot display the full range of colors in the bitmap, it will refer to the importantColors field to decide which colors to prioritize. However, in modern systems with sophisticated color capabilities, this field is often disregarded.
*/
importantColors?: number;
/**
* A palette, in the context of the Bitmap (BMP) image file format, is a table that defines the colors that are used in the image. This feature is typically used in images that have less than 24 bits per pixel (bitPP).
* The palette follows the BITMAPINFOHEADER or BITMAPV4HEADER (or similar) structure and precedes the actual pixel data in the file. Each entry in the palette corresponds to a single color, using either 3 bytes (R, G, B) or 4 bytes (R, G, B, 0) in the RGB color space.
* For 1-bit images, the palette has 2 colors, for 4-bit images, the palette has 16 colors, and for an 8-bit image, the palette can contain up to 256 colors. The actual number of colors used in the palette is identified by the colors (biClrUsed) field in the header.
* For 24-bit and 32-bit BMP, where each pixel is described by distinct R, G, and B (and potentially alpha) values, a palette isn't used or needed.
*/
palette?: BmpColor[];
}
+9
View File
@@ -0,0 +1,9 @@
export var BmpCompression;
(function (BmpCompression) {
BmpCompression[BmpCompression["NONE"] = 0] = "NONE";
BmpCompression[BmpCompression["BI_RLE8"] = 1] = "BI_RLE8";
BmpCompression[BmpCompression["BI_RLE4"] = 2] = "BI_RLE4";
BmpCompression[BmpCompression["BI_BIT_FIELDS"] = 3] = "BI_BIT_FIELDS";
BmpCompression[BmpCompression["BI_ALPHA_BIT_FIELDS"] = 6] = "BI_ALPHA_BIT_FIELDS";
})(BmpCompression || (BmpCompression = {}));
//# sourceMappingURL=types.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,cAMX;AAND,WAAY,cAAc;IACxB,mDAAQ,CAAA;IACR,yDAAW,CAAA;IACX,yDAAW,CAAA;IACX,qEAAiB,CAAA;IACjB,iFAAuB,CAAA;AACzB,CAAC,EANW,cAAc,KAAd,cAAc,QAMzB"}