Initial commit
This commit is contained in:
commit
e49726988c
5445 files changed
+1089914
No files matched your search
+50
@@ -0,0 +1,50 @@
|
||||
/// <reference types="node" />
|
||||
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;
|
||||
}
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const header_types_js_1 = __importDefault(require("./header-types.js"));
|
||||
const mask_color_js_1 = __importDefault(require("./mask-color.js"));
|
||||
const types_js_1 = require("./types.js");
|
||||
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 header_types_js_1.default)) {
|
||||
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 > header_types_js_1.default.BITMAP_INFO_HEADER ||
|
||||
this.compression === types_js_1.BmpCompression.BI_BIT_FIELDS ||
|
||||
this.compression === types_js_1.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 > header_types_js_1.default.BITMAP_V2_INFO_HEADER ||
|
||||
this.compression === types_js_1.BmpCompression.BI_ALPHA_BIT_FIELDS) {
|
||||
this.maskAlpha = this.readUInt32LE();
|
||||
}
|
||||
// End of BITMAP_V3_INFO_HEADER
|
||||
if (this.headerSize > header_types_js_1.default.BITMAP_V3_INFO_HEADER) {
|
||||
this.pos +=
|
||||
header_types_js_1.default.BITMAP_V4_HEADER - header_types_js_1.default.BITMAP_V3_INFO_HEADER;
|
||||
}
|
||||
// End of BITMAP_V4_HEADER
|
||||
if (this.headerSize > header_types_js_1.default.BITMAP_V4_HEADER) {
|
||||
this.pos += header_types_js_1.default.BITMAP_V5_HEADER - header_types_js_1.default.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 = (0, mask_color_js_1.default)(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 === types_js_1.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 === types_js_1.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;
|
||||
}
|
||||
}
|
||||
exports.default = BmpDecoder;
|
||||
//# sourceMappingURL=decoder.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"decoder.js","sourceRoot":"","sources":["../../src/decoder.ts"],"names":[],"mappings":"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 truncated
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/// <reference types="node" />
|
||||
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;
|
||||
}
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const header_types_js_1 = __importDefault(require("./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);
|
||||
}
|
||||
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 = header_types_js_1.default.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;
|
||||
}
|
||||
}
|
||||
exports.default = BmpEncoder;
|
||||
//# sourceMappingURL=encoder.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/encoder.ts"],"names":[],"mappings":"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 truncated
|
||||
+8
@@ -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;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
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 = {}));
|
||||
exports.default = HeaderTypes;
|
||||
//# sourceMappingURL=header-types.js.map
|
||||
+1
@@ -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,kBAAe,WAAW,CAAC"}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/// <reference types="node" />
|
||||
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';
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.encode = exports.decode = void 0;
|
||||
const decoder_js_1 = __importDefault(require("./decoder.js"));
|
||||
const encoder_js_1 = __importDefault(require("./encoder.js"));
|
||||
function decode(bmpData, options) {
|
||||
return new decoder_js_1.default(bmpData, options);
|
||||
}
|
||||
exports.decode = decode;
|
||||
function encode(imgData) {
|
||||
return new encoder_js_1.default(imgData);
|
||||
}
|
||||
exports.encode = encode;
|
||||
__exportStar(require("./types.js"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,8DAAsC;AACtC,8DAAsC;AAGtC,SAAgB,MAAM,CAAC,OAAe,EAAE,OAA2B;IACjE,OAAO,IAAI,oBAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAFD,wBAEC;AACD,SAAgB,MAAM,CAAC,OAAiB;IACtC,OAAO,IAAI,oBAAU,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAFD,wBAEC;AAED,6CAA2B"}
|
||||
+6
@@ -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;
|
||||
};
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
// 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.
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
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,
|
||||
};
|
||||
}
|
||||
exports.default = maskColor;
|
||||
//# sourceMappingURL=mask-color.js.map
|
||||
+1
@@ -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,SAAwB,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;AA7BD,4BA6BC"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/// <reference types="node" />
|
||||
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[];
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BmpCompression = void 0;
|
||||
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 || (exports.BmpCompression = BmpCompression = {}));
|
||||
//# sourceMappingURL=types.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";;;AAAA,IAAY,cAMX;AAND,WAAY,cAAc;IACxB,mDAAQ,CAAA;IACR,yDAAW,CAAA;IACX,yDAAW,CAAA;IACX,qEAAiB,CAAA;IACjB,iFAAuB,CAAA;AACzB,CAAC,EANW,cAAc,8BAAd,cAAc,QAMzB"}
|
||||
Reference in new issue
Block a user