/** * @author shaozilee * * Bmp format decoder,support 1bit 4bit 8bit 24bit bmp * */ function BmpDecoder(buffer,is_with_alpha) { this.pos = 0; this.buffer = buffer; this.is_with_alpha = !!is_with_alpha; this.bottom_up = true; this.flag = this.buffer.toString("utf-8", 0, this.pos += 2); if (this.flag != "BM") throw new Error("Invalid BMP File"); this.parseHeader(); this.parseRGBA(); } BmpDecoder.prototype.parseHeader = function() { this.fileSize = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.reserved = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.offset = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.headerSize = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.width = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.height = this.buffer.readInt32LE(this.pos); this.pos += 4; this.planes = this.buffer.readUInt16LE(this.pos); this.pos += 2; this.bitPP = this.buffer.readUInt16LE(this.pos); this.pos += 2; this.compress = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.rawSize = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.hr = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.vr = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.colors = this.buffer.readUInt32LE(this.pos); this.pos += 4; this.importantColors = this.buffer.readUInt32LE(this.pos); this.pos += 4; if(this.bitPP === 16 && this.is_with_alpha){ this.bitPP = 15 } if (this.bitPP < 15) { var len = this.colors === 0 ? 1 << this.bitPP : this.colors; this.palette = new Array(len); for (var i = 0; i < len; i++) { var blue = this.buffer.readUInt8(this.pos++); var green = this.buffer.readUInt8(this.pos++); var red = this.buffer.readUInt8(this.pos++); var quad = this.buffer.readUInt8(this.pos++); this.palette[i] = { red: red, green: green, blue: blue, quad: quad }; } } if(this.height < 0) { this.height *= -1; this.bottom_up = false; } } BmpDecoder.prototype.parseRGBA = function() { var bitn = "bit" + this.bitPP; var len = this.width * this.height * 4; this.data = new Buffer(len); this[bitn](); }; BmpDecoder.prototype.bit1 = function() { var xlen = Math.ceil(this.width / 8); var mode = xlen%4; var y = this.height >= 0 ? this.height - 1 : -this.height for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y for (var x = 0; x < xlen; x++) { var b = this.buffer.readUInt8(this.pos++); var location = line * this.width * 4 + x*8*4; for (var i = 0; i < 8; i++) { if(x*8+i>(7-i))&0x1)]; this.data[location+i*4] = 0; this.data[location+i*4 + 1] = rgb.blue; this.data[location+i*4 + 2] = rgb.green; this.data[location+i*4 + 3] = rgb.red; }else{ break; } } } if (mode != 0){ this.pos+=(4 - mode); } } }; BmpDecoder.prototype.bit4 = function() { //RLE-4 if(this.compress == 2){ this.data.fill(0xff); var location = 0; var lines = this.bottom_up?this.height-1:0; var low_nibble = false;//for all count of pixel while(location>4); } if ((i & 1) && (i+1 < b)){ c = this.buffer.readUInt8(this.pos++); } low_nibble = !low_nibble; } if ((((b+1) >> 1) & 1 ) == 1){ this.pos++ } } }else{//encoded mode for (var i = 0; i < a; i++) { if (low_nibble) { setPixelData.call(this, (b & 0x0f)); } else { setPixelData.call(this, (b & 0xf0)>>4); } low_nibble = !low_nibble; } } } function setPixelData(rgbIndex){ var rgb = this.palette[rgbIndex]; this.data[location] = 0; this.data[location + 1] = rgb.blue; this.data[location + 2] = rgb.green; this.data[location + 3] = rgb.red; location+=4; } }else{ var xlen = Math.ceil(this.width/2); var mode = xlen%4; for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y for (var x = 0; x < xlen; x++) { var b = this.buffer.readUInt8(this.pos++); var location = line * this.width * 4 + x*2*4; var before = b>>4; var after = b&0x0F; var rgb = this.palette[before]; 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)break; rgb = this.palette[after]; 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; } if (mode != 0){ this.pos+=(4 - mode); } } } }; BmpDecoder.prototype.bit8 = function() { //RLE-8 if(this.compress == 1){ this.data.fill(0xff); var location = 0; var lines = this.bottom_up?this.height-1:0; while(location= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y for (var x = 0; x < this.width; x++) { var b = this.buffer.readUInt8(this.pos++); var location = line * this.width * 4 + x * 4; if (b < this.palette.length) { var 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; } } if (mode != 0) { this.pos += (4 - mode); } } } }; BmpDecoder.prototype.bit15 = function() { var dif_w =this.width % 3; var _11111 = parseInt("11111", 2),_1_5 = _11111; for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y for (var x = 0; x < this.width; x++) { var B = this.buffer.readUInt16LE(this.pos); this.pos+=2; var blue = (B & _1_5) / _1_5 * 255 | 0; var green = (B >> 5 & _1_5 ) / _1_5 * 255 | 0; var red = (B >> 10 & _1_5) / _1_5 * 255 | 0; var alpha = (B>>15)?0xFF:0x00; var location = line * this.width * 4 + x * 4; this.data[location] = alpha; this.data[location + 1] = blue; this.data[location + 2] = green; this.data[location + 3] = red; } //skip extra bytes this.pos += dif_w; } }; BmpDecoder.prototype.bit16 = function() { var dif_w =(this.width % 2)*2; //default xrgb555 this.maskRed = 0x7C00; this.maskGreen = 0x3E0; this.maskBlue =0x1F; this.mask0 = 0; if(this.compress == 3){ this.maskRed = this.buffer.readUInt32LE(this.pos); this.pos+=4; this.maskGreen = this.buffer.readUInt32LE(this.pos); this.pos+=4; this.maskBlue = this.buffer.readUInt32LE(this.pos); this.pos+=4; this.mask0 = this.buffer.readUInt32LE(this.pos); this.pos+=4; } var ns=[0,0,0]; for (var i=0;i<16;i++){ if ((this.maskRed>>i)&0x01) ns[0]++; if ((this.maskGreen>>i)&0x01) ns[1]++; if ((this.maskBlue>>i)&0x01) ns[2]++; } ns[1]+=ns[0]; ns[2]+=ns[1]; ns[0]=8-ns[0]; ns[1]-=8; ns[2]-=8; for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y; for (var x = 0; x < this.width; x++) { var B = this.buffer.readUInt16LE(this.pos); this.pos+=2; var blue = (B&this.maskBlue)<>ns[1]; var red = (B&this.maskRed)>>ns[2]; var location = line * this.width * 4 + x * 4; this.data[location] = 0; this.data[location + 1] = blue; this.data[location + 2] = green; this.data[location + 3] = red; } //skip extra bytes this.pos += dif_w; } }; BmpDecoder.prototype.bit24 = function() { for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y for (var x = 0; x < this.width; x++) { //Little Endian rgb var blue = this.buffer.readUInt8(this.pos++); var green = this.buffer.readUInt8(this.pos++); var red = this.buffer.readUInt8(this.pos++); var location = line * this.width * 4 + x * 4; this.data[location] = 0; this.data[location + 1] = blue; this.data[location + 2] = green; this.data[location + 3] = red; } //skip extra bytes this.pos += (this.width % 4); } }; /** * add 32bit decode func * @author soubok */ BmpDecoder.prototype.bit32 = function() { //BI_BITFIELDS if(this.compress == 3){ this.maskRed = this.buffer.readUInt32LE(this.pos); this.pos+=4; this.maskGreen = this.buffer.readUInt32LE(this.pos); this.pos+=4; this.maskBlue = this.buffer.readUInt32LE(this.pos); this.pos+=4; this.mask0 = this.buffer.readUInt32LE(this.pos); this.pos+=4; for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y; for (var x = 0; x < this.width; x++) { //Little Endian rgba var alpha = this.buffer.readUInt8(this.pos++); var blue = this.buffer.readUInt8(this.pos++); var green = this.buffer.readUInt8(this.pos++); var red = this.buffer.readUInt8(this.pos++); var location = line * this.width * 4 + x * 4; this.data[location] = alpha; this.data[location + 1] = blue; this.data[location + 2] = green; this.data[location + 3] = red; } } }else{ for (var y = this.height - 1; y >= 0; y--) { var line = this.bottom_up ? y : this.height - 1 - y; for (var x = 0; x < this.width; x++) { //Little Endian argb var blue = this.buffer.readUInt8(this.pos++); var green = this.buffer.readUInt8(this.pos++); var red = this.buffer.readUInt8(this.pos++); var alpha = this.buffer.readUInt8(this.pos++); var location = line * this.width * 4 + x * 4; this.data[location] = alpha; this.data[location + 1] = blue; this.data[location + 2] = green; this.data[location + 3] = red; } } } }; BmpDecoder.prototype.getData = function() { return this.data; }; module.exports = function(bmpData) { var decoder = new BmpDecoder(bmpData); return decoder; };