1/** 2 * @author shaozilee 3 * 4 * Bmp format decoder,support 1bit 4bit 8bit 24bit bmp 5 * 6 */ 7 8function BmpDecoder(buffer,is_with_alpha) { 9 this.pos = 0; 10 this.buffer = buffer; 11 this.is_with_alpha = !!is_with_alpha; 12 //Header should be BM 13 if (this.buffer[0] != 66 && this.buffer[1] != 77) throw new Error("Invalid BMP File"); 14 this.pos += 2; 15 this.parseHeader(); 16 this.parseBGR(); 17} 18 19BmpDecoder.prototype.parseHeader = function() { 20 var b = this.buffer; 21 this.fileSize = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 22 this.pos += 4; 23 this.reserved = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 24 this.pos += 4; 25 this.offset = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 26 this.pos += 4; 27 this.headerSize = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 28 this.pos += 4; 29 this.width = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 30 this.pos += 4; 31 this.height = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 32 this.pos += 4; 33 this.planes = (b[this.pos+1] << 8) | b[this.pos]; 34 this.pos += 2; 35 this.bitPP = (b[this.pos+1] << 8) | b[this.pos]; 36 this.pos += 2; 37 this.compress = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 38 this.pos += 4; 39 this.rawSize = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 40 this.pos += 4; 41 this.hr = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 42 this.pos += 4; 43 this.vr = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 44 this.pos += 4; 45 this.colors = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 46 this.pos += 4; 47 this.importantColors = (b[this.pos+3] << 24) | (b[this.pos+2] << 16) | (b[this.pos+1] << 8) | b[this.pos]; 48 this.pos += 4; 49 50 if(this.bitPP === 16 && this.is_with_alpha){ 51 this.bitPP = 15 52 }; 53 if (this.bitPP < 15) { 54 var len = this.colors === 0 ? 1 << this.bitPP : this.colors; 55 this.palette = new Array(len); 56 for (var i = 0; i < len; i++) { 57 var blue = this.buffer[this.pos++]; 58 var green = this.buffer[this.pos++]; 59 var red = this.buffer[this.pos++]; 60 var quad = this.buffer[this.pos++]; 61 this.palette[i] = { 62 red: red, 63 green: green, 64 blue: blue, 65 quad: quad 66 }; 67 } 68 } 69 70} 71 72BmpDecoder.prototype.parseBGR = function() { 73 this.pos = this.offset; 74 try { 75 var bitn = "bit" + this.bitPP; 76 77 var canvas = document.createElement("canvas"); 78 var ctx = canvas.getContext("2d"); 79 var imageData = ctx.createImageData(this.width, this.height); 80 this.imageData = imageData; 81 this.data = imageData.data; 82 83 this[bitn](); 84 } catch (e) { 85 console.log("bit decode error:" + e); 86 } 87 88}; 89 90BmpDecoder.prototype.bit1 = function() { 91 var xlen = Math.ceil(this.width / 8); 92 var mode = xlen%4; 93 for (var y = this.height - 1; y >= 0; y--) { 94 for (var x = 0; x < xlen; x++) { 95 var b = this.buffer[this.pos++]; 96 var location = y * this.width * 4 + x*8*4; 97 for (var i = 0; i < 8; i++) { 98 if(x*8+i<this.width){ 99 var rgb = this.palette[((b>>(7-i))&0x1)]; 100 this.data[location+i*4] = rgb.red; 101 this.data[location+i*4 + 1] = rgb.green; 102 this.data[location+i*4 + 2] = rgb.blue; 103 this.data[location+i*4 + 3] = 0xFF; 104 }else{ 105 break; 106 } 107 } 108 } 109 110 if (mode != 0){ 111 this.pos+=(4 - mode); 112 } 113 } 114}; 115 116BmpDecoder.prototype.bit4 = function() { 117 var xlen = Math.ceil(this.width/2); 118 var mode = xlen%4; 119 for (var y = this.height - 1; y >= 0; y--) { 120 for (var x = 0; x < xlen; x++) { 121 var b = this.buffer[this.pos++];//this.buffer.readUInt8(this.pos++); 122 var location = y * this.width * 4 + x*2*4; 123 124 var before = b>>4; 125 var after = b&0x0F; 126 127 var rgb = this.palette[before]; 128 this.data[location] = rgb.red; 129 this.data[location + 1] = rgb.green; 130 this.data[location + 2] = rgb.blue; 131 this.data[location + 3] = 0xFF; 132 133 if(x*2+1>=this.width)break; 134 135 rgb = this.palette[after]; 136 this.data[location+4] = rgb.red; 137 this.data[location+4 + 1] = rgb.green; 138 this.data[location+4 + 2] = rgb.blue; 139 this.data[location+4 + 3] = 0xFF; 140 } 141 142 if (mode != 0){ 143 this.pos+=(4 - mode); 144 } 145 } 146 147}; 148 149BmpDecoder.prototype.bit8 = function() { 150 var mode = this.width%4; 151 for (var y = this.height - 1; y >= 0; y--) { 152 for (var x = 0; x < this.width; x++) { 153 var b = this.buffer[this.pos++]; 154 var location = y * this.width * 4 + x*4; 155 if(b < this.palette.length) { 156 var rgb = this.palette[b]; 157 this.data[location] = rgb.red; 158 this.data[location + 1] = rgb.green; 159 this.data[location + 2] = rgb.blue; 160 this.data[location + 3] = 0xFF; 161 } else { 162 this.data[location] = 0xFF; 163 this.data[location + 1] = 0xFF; 164 this.data[location + 2] = 0xFF; 165 this.data[location + 3] = 0xFF; 166 } 167 } 168 if (mode != 0){ 169 this.pos+=(4 - mode); 170 } 171 } 172}; 173 174//Currently not used! 175BmpDecoder.prototype.bit15 = function() { 176 //FIXED BUG, padding is based on number of bytes not the width 177 var dif_w = (this.width * 2) % 4; 178 if (dif_w != 0) { 179 dif_w = 4 - dif_w; 180 } 181 var _11111 = parseInt("11111", 2),_1_5 = _11111; 182 for (var y = this.height - 1; y >= 0; y--) { 183 for (var x = 0; x < this.width; x++) { 184 185 var B = (this.buffer[this.pos+1] << 8) | this.buffer[this.pos]; 186 this.pos+=2; 187 var blue = (B & _1_5) / _1_5 * 255 | 0; 188 var green = (B >> 5 & _1_5 ) / _1_5 * 255 | 0; 189 var red = (B >> 10 & _1_5) / _1_5 * 255 | 0; 190 var alpha = (B>>15)?0xFF:0x00; 191 192 var location = y * this.width * 4 + x * 4; 193 this.data[location] = red; 194 this.data[location + 1] = green; 195 this.data[location + 2] = blue; 196 this.data[location + 3] = alpha; 197 } 198 //skip extra bytes 199 this.pos += dif_w; 200 } 201}; 202 203//TODO support other RGB masks, e.g., RGB565 204BmpDecoder.prototype.bit16 = function() { 205 //FIXED BUG, padding is based on number of bytes not the width 206 var dif_w = (this.width * 2) % 4; 207 if (dif_w != 0) { 208 dif_w = 4 - dif_w; 209 } 210 var _11111 = parseInt("11111", 2),_1_5 = _11111; 211 var _111111 = parseInt("111111", 2),_1_6 = _111111; 212 for (var y = this.height - 1; y >= 0; y--) { 213 for (var x = 0; x < this.width; x++) { 214 215 var B = (this.buffer[this.pos+1] << 8) | this.buffer[this.pos]; 216 this.pos+=2; 217 var alpha = 0xFF; 218 var blue = (B & _1_5) / _1_5 * 255 | 0; 219 var green = (B >> 5 & _1_5) / _1_5 * 255 | 0; 220 var red = (B >> 10 & _1_5) / _1_5 * 255 | 0; 221 222 var location = y * this.width * 4 + x * 4; 223 this.data[location] = red; 224 this.data[location + 1] = green; 225 this.data[location + 2] = blue; 226 this.data[location + 3] = alpha; 227 } 228 //skip extra bytes 229 this.pos += dif_w; 230 } 231}; 232 233BmpDecoder.prototype.bit24 = function() { 234 //when height > 0 235 //FIXED BUG, padding is based on number of bytes not the width 236 var dif_w = ((this.width * 3) % 4); 237 if (dif_w != 0) { 238 dif_w = 4 - dif_w; 239 } 240 for (var y = this.height - 1; y >= 0; y--) { 241 for (var x = 0; x < this.width; x++) { 242 var blue = this.buffer[this.pos++]; 243 var green = this.buffer[this.pos++]; 244 var red = this.buffer[this.pos++]; 245 var location = y * this.width * 4 + x * 4; 246 this.data[location] = red; 247 this.data[location + 1] = green; 248 this.data[location + 2] = blue; 249 this.data[location + 3] = 0xFF; 250 } 251 //skip extra bytes 252 this.pos += dif_w; 253 } 254 255}; 256 257/** 258 * add 32bit decode func 259 * @author soubok 260 */ 261BmpDecoder.prototype.bit32 = function() { 262 //when height > 0 263 for (var y = this.height - 1; y >= 0; y--) { 264 for (var x = 0; x < this.width; x++) { 265 var blue = this.buffer[this.pos++]; 266 var green = this.buffer[this.pos++]; 267 var red = this.buffer[this.pos++]; 268 var alpha = this.buffer[this.pos++]; 269 var location = y * this.width * 4 + x * 4; 270 //FIXED BUG alpha is the last byte in image data 271 this.data[location] = red; 272 this.data[location + 1] = green; 273 this.data[location + 2] = blue; 274 this.data[location + 3] = alpha; 275 } 276 //FIXED BUG no padding is needed for 32 bit images "the length of the rows IS a multiple of four bytes" 277 } 278 279}; 280 281BmpDecoder.prototype.getData = function() { 282 return this.data; 283};