123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /**
- * @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<this.width){
- var rgb = this.palette[((b>>(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<this.data.length){
- var a = this.buffer.readUInt8(this.pos++);
- var b = this.buffer.readUInt8(this.pos++);
- //absolute mode
- if(a == 0){
- if(b == 0){//line end
- if(this.bottom_up){
- lines--;
- }else{
- lines++;
- }
- location = lines*this.width*4;
- low_nibble = false;
- continue;
- }else if(b == 1){//image end
- break;
- }else if(b ==2){
- //offset x,y
- var x = this.buffer.readUInt8(this.pos++);
- var y = this.buffer.readUInt8(this.pos++);
- if(this.bottom_up){
- lines-=y;
- }else{
- lines+=y;
- }
- location +=(y*this.width*4+x*4);
- }else{
- var c = this.buffer.readUInt8(this.pos++);
- for(var i=0;i<b;i++){
- if (low_nibble) {
- setPixelData.call(this, (c & 0x0f));
- } else {
- setPixelData.call(this, (c & 0xf0)>>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<this.data.length){
- var a = this.buffer.readUInt8(this.pos++);
- var b = this.buffer.readUInt8(this.pos++);
- //absolute mode
- if(a == 0){
- if(b == 0){//line end
- if(this.bottom_up){
- lines--;
- }else{
- lines++;
- }
- location = lines*this.width*4;
- continue;
- }else if(b == 1){//image end
- break;
- }else if(b ==2){
- //offset x,y
- var x = this.buffer.readUInt8(this.pos++);
- var y = this.buffer.readUInt8(this.pos++);
- if(this.bottom_up){
- lines-=y;
- }else{
- lines+=y;
- }
- location +=(y*this.width*4+x*4);
- }else{
- for(var i=0;i<b;i++){
- var c = this.buffer.readUInt8(this.pos++);
- setPixelData.call(this, c);
- }
- if(b&1 == 1){
- this.pos++;
- }
- }
- }else{//encoded mode
- for (var i = 0; i < a; i++) {
- setPixelData.call(this, b);
- }
- }
- }
- 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 mode = this.width % 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++) {
- 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[0];
- var green = (B&this.maskGreen)>>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;
- };
|