decoder.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /**
  2. * @author shaozilee
  3. *
  4. * Bmp format decoder,support 1bit 4bit 8bit 24bit bmp
  5. *
  6. */
  7. function BmpDecoder(buffer,is_with_alpha) {
  8. this.pos = 0;
  9. this.buffer = buffer;
  10. this.is_with_alpha = !!is_with_alpha;
  11. this.bottom_up = true;
  12. this.flag = this.buffer.toString("utf-8", 0, this.pos += 2);
  13. if (this.flag != "BM") throw new Error("Invalid BMP File");
  14. this.parseHeader();
  15. this.parseRGBA();
  16. }
  17. BmpDecoder.prototype.parseHeader = function() {
  18. this.fileSize = this.buffer.readUInt32LE(this.pos);
  19. this.pos += 4;
  20. this.reserved = this.buffer.readUInt32LE(this.pos);
  21. this.pos += 4;
  22. this.offset = this.buffer.readUInt32LE(this.pos);
  23. this.pos += 4;
  24. this.headerSize = this.buffer.readUInt32LE(this.pos);
  25. this.pos += 4;
  26. this.width = this.buffer.readUInt32LE(this.pos);
  27. this.pos += 4;
  28. this.height = this.buffer.readInt32LE(this.pos);
  29. this.pos += 4;
  30. this.planes = this.buffer.readUInt16LE(this.pos);
  31. this.pos += 2;
  32. this.bitPP = this.buffer.readUInt16LE(this.pos);
  33. this.pos += 2;
  34. this.compress = this.buffer.readUInt32LE(this.pos);
  35. this.pos += 4;
  36. this.rawSize = this.buffer.readUInt32LE(this.pos);
  37. this.pos += 4;
  38. this.hr = this.buffer.readUInt32LE(this.pos);
  39. this.pos += 4;
  40. this.vr = this.buffer.readUInt32LE(this.pos);
  41. this.pos += 4;
  42. this.colors = this.buffer.readUInt32LE(this.pos);
  43. this.pos += 4;
  44. this.importantColors = this.buffer.readUInt32LE(this.pos);
  45. this.pos += 4;
  46. if(this.bitPP === 16 && this.is_with_alpha){
  47. this.bitPP = 15
  48. }
  49. if (this.bitPP < 15) {
  50. var len = this.colors === 0 ? 1 << this.bitPP : this.colors;
  51. this.palette = new Array(len);
  52. for (var i = 0; i < len; i++) {
  53. var blue = this.buffer.readUInt8(this.pos++);
  54. var green = this.buffer.readUInt8(this.pos++);
  55. var red = this.buffer.readUInt8(this.pos++);
  56. var quad = this.buffer.readUInt8(this.pos++);
  57. this.palette[i] = {
  58. red: red,
  59. green: green,
  60. blue: blue,
  61. quad: quad
  62. };
  63. }
  64. }
  65. if(this.height < 0) {
  66. this.height *= -1;
  67. this.bottom_up = false;
  68. }
  69. }
  70. BmpDecoder.prototype.parseRGBA = function() {
  71. var bitn = "bit" + this.bitPP;
  72. var len = this.width * this.height * 4;
  73. this.data = new Buffer(len);
  74. this[bitn]();
  75. };
  76. BmpDecoder.prototype.bit1 = function() {
  77. var xlen = Math.ceil(this.width / 8);
  78. var mode = xlen%4;
  79. var y = this.height >= 0 ? this.height - 1 : -this.height
  80. for (var y = this.height - 1; y >= 0; y--) {
  81. var line = this.bottom_up ? y : this.height - 1 - y
  82. for (var x = 0; x < xlen; x++) {
  83. var b = this.buffer.readUInt8(this.pos++);
  84. var location = line * this.width * 4 + x*8*4;
  85. for (var i = 0; i < 8; i++) {
  86. if(x*8+i<this.width){
  87. var rgb = this.palette[((b>>(7-i))&0x1)];
  88. this.data[location+i*4] = 0;
  89. this.data[location+i*4 + 1] = rgb.blue;
  90. this.data[location+i*4 + 2] = rgb.green;
  91. this.data[location+i*4 + 3] = rgb.red;
  92. }else{
  93. break;
  94. }
  95. }
  96. }
  97. if (mode != 0){
  98. this.pos+=(4 - mode);
  99. }
  100. }
  101. };
  102. BmpDecoder.prototype.bit4 = function() {
  103. //RLE-4
  104. if(this.compress == 2){
  105. this.data.fill(0xff);
  106. var location = 0;
  107. var lines = this.bottom_up?this.height-1:0;
  108. var low_nibble = false;//for all count of pixel
  109. while(location<this.data.length){
  110. var a = this.buffer.readUInt8(this.pos++);
  111. var b = this.buffer.readUInt8(this.pos++);
  112. //absolute mode
  113. if(a == 0){
  114. if(b == 0){//line end
  115. if(this.bottom_up){
  116. lines--;
  117. }else{
  118. lines++;
  119. }
  120. location = lines*this.width*4;
  121. low_nibble = false;
  122. continue;
  123. }else if(b == 1){//image end
  124. break;
  125. }else if(b ==2){
  126. //offset x,y
  127. var x = this.buffer.readUInt8(this.pos++);
  128. var y = this.buffer.readUInt8(this.pos++);
  129. if(this.bottom_up){
  130. lines-=y;
  131. }else{
  132. lines+=y;
  133. }
  134. location +=(y*this.width*4+x*4);
  135. }else{
  136. var c = this.buffer.readUInt8(this.pos++);
  137. for(var i=0;i<b;i++){
  138. if (low_nibble) {
  139. setPixelData.call(this, (c & 0x0f));
  140. } else {
  141. setPixelData.call(this, (c & 0xf0)>>4);
  142. }
  143. if ((i & 1) && (i+1 < b)){
  144. c = this.buffer.readUInt8(this.pos++);
  145. }
  146. low_nibble = !low_nibble;
  147. }
  148. if ((((b+1) >> 1) & 1 ) == 1){
  149. this.pos++
  150. }
  151. }
  152. }else{//encoded mode
  153. for (var i = 0; i < a; i++) {
  154. if (low_nibble) {
  155. setPixelData.call(this, (b & 0x0f));
  156. } else {
  157. setPixelData.call(this, (b & 0xf0)>>4);
  158. }
  159. low_nibble = !low_nibble;
  160. }
  161. }
  162. }
  163. function setPixelData(rgbIndex){
  164. var rgb = this.palette[rgbIndex];
  165. this.data[location] = 0;
  166. this.data[location + 1] = rgb.blue;
  167. this.data[location + 2] = rgb.green;
  168. this.data[location + 3] = rgb.red;
  169. location+=4;
  170. }
  171. }else{
  172. var xlen = Math.ceil(this.width/2);
  173. var mode = xlen%4;
  174. for (var y = this.height - 1; y >= 0; y--) {
  175. var line = this.bottom_up ? y : this.height - 1 - y
  176. for (var x = 0; x < xlen; x++) {
  177. var b = this.buffer.readUInt8(this.pos++);
  178. var location = line * this.width * 4 + x*2*4;
  179. var before = b>>4;
  180. var after = b&0x0F;
  181. var rgb = this.palette[before];
  182. this.data[location] = 0;
  183. this.data[location + 1] = rgb.blue;
  184. this.data[location + 2] = rgb.green;
  185. this.data[location + 3] = rgb.red;
  186. if(x*2+1>=this.width)break;
  187. rgb = this.palette[after];
  188. this.data[location+4] = 0;
  189. this.data[location+4 + 1] = rgb.blue;
  190. this.data[location+4 + 2] = rgb.green;
  191. this.data[location+4 + 3] = rgb.red;
  192. }
  193. if (mode != 0){
  194. this.pos+=(4 - mode);
  195. }
  196. }
  197. }
  198. };
  199. BmpDecoder.prototype.bit8 = function() {
  200. //RLE-8
  201. if(this.compress == 1){
  202. this.data.fill(0xff);
  203. var location = 0;
  204. var lines = this.bottom_up?this.height-1:0;
  205. while(location<this.data.length){
  206. var a = this.buffer.readUInt8(this.pos++);
  207. var b = this.buffer.readUInt8(this.pos++);
  208. //absolute mode
  209. if(a == 0){
  210. if(b == 0){//line end
  211. if(this.bottom_up){
  212. lines--;
  213. }else{
  214. lines++;
  215. }
  216. location = lines*this.width*4;
  217. continue;
  218. }else if(b == 1){//image end
  219. break;
  220. }else if(b ==2){
  221. //offset x,y
  222. var x = this.buffer.readUInt8(this.pos++);
  223. var y = this.buffer.readUInt8(this.pos++);
  224. if(this.bottom_up){
  225. lines-=y;
  226. }else{
  227. lines+=y;
  228. }
  229. location +=(y*this.width*4+x*4);
  230. }else{
  231. for(var i=0;i<b;i++){
  232. var c = this.buffer.readUInt8(this.pos++);
  233. setPixelData.call(this, c);
  234. }
  235. if(b&1 == 1){
  236. this.pos++;
  237. }
  238. }
  239. }else{//encoded mode
  240. for (var i = 0; i < a; i++) {
  241. setPixelData.call(this, b);
  242. }
  243. }
  244. }
  245. function setPixelData(rgbIndex){
  246. var rgb = this.palette[rgbIndex];
  247. this.data[location] = 0;
  248. this.data[location + 1] = rgb.blue;
  249. this.data[location + 2] = rgb.green;
  250. this.data[location + 3] = rgb.red;
  251. location+=4;
  252. }
  253. }else {
  254. var mode = this.width % 4;
  255. for (var y = this.height - 1; y >= 0; y--) {
  256. var line = this.bottom_up ? y : this.height - 1 - y
  257. for (var x = 0; x < this.width; x++) {
  258. var b = this.buffer.readUInt8(this.pos++);
  259. var location = line * this.width * 4 + x * 4;
  260. if (b < this.palette.length) {
  261. var rgb = this.palette[b];
  262. this.data[location] = 0;
  263. this.data[location + 1] = rgb.blue;
  264. this.data[location + 2] = rgb.green;
  265. this.data[location + 3] = rgb.red;
  266. } else {
  267. this.data[location] = 0;
  268. this.data[location + 1] = 0xFF;
  269. this.data[location + 2] = 0xFF;
  270. this.data[location + 3] = 0xFF;
  271. }
  272. }
  273. if (mode != 0) {
  274. this.pos += (4 - mode);
  275. }
  276. }
  277. }
  278. };
  279. BmpDecoder.prototype.bit15 = function() {
  280. var dif_w =this.width % 3;
  281. var _11111 = parseInt("11111", 2),_1_5 = _11111;
  282. for (var y = this.height - 1; y >= 0; y--) {
  283. var line = this.bottom_up ? y : this.height - 1 - y
  284. for (var x = 0; x < this.width; x++) {
  285. var B = this.buffer.readUInt16LE(this.pos);
  286. this.pos+=2;
  287. var blue = (B & _1_5) / _1_5 * 255 | 0;
  288. var green = (B >> 5 & _1_5 ) / _1_5 * 255 | 0;
  289. var red = (B >> 10 & _1_5) / _1_5 * 255 | 0;
  290. var alpha = (B>>15)?0xFF:0x00;
  291. var location = line * this.width * 4 + x * 4;
  292. this.data[location] = alpha;
  293. this.data[location + 1] = blue;
  294. this.data[location + 2] = green;
  295. this.data[location + 3] = red;
  296. }
  297. //skip extra bytes
  298. this.pos += dif_w;
  299. }
  300. };
  301. BmpDecoder.prototype.bit16 = function() {
  302. var dif_w =(this.width % 2)*2;
  303. //default xrgb555
  304. this.maskRed = 0x7C00;
  305. this.maskGreen = 0x3E0;
  306. this.maskBlue =0x1F;
  307. this.mask0 = 0;
  308. if(this.compress == 3){
  309. this.maskRed = this.buffer.readUInt32LE(this.pos);
  310. this.pos+=4;
  311. this.maskGreen = this.buffer.readUInt32LE(this.pos);
  312. this.pos+=4;
  313. this.maskBlue = this.buffer.readUInt32LE(this.pos);
  314. this.pos+=4;
  315. this.mask0 = this.buffer.readUInt32LE(this.pos);
  316. this.pos+=4;
  317. }
  318. var ns=[0,0,0];
  319. for (var i=0;i<16;i++){
  320. if ((this.maskRed>>i)&0x01) ns[0]++;
  321. if ((this.maskGreen>>i)&0x01) ns[1]++;
  322. if ((this.maskBlue>>i)&0x01) ns[2]++;
  323. }
  324. ns[1]+=ns[0]; ns[2]+=ns[1]; ns[0]=8-ns[0]; ns[1]-=8; ns[2]-=8;
  325. for (var y = this.height - 1; y >= 0; y--) {
  326. var line = this.bottom_up ? y : this.height - 1 - y;
  327. for (var x = 0; x < this.width; x++) {
  328. var B = this.buffer.readUInt16LE(this.pos);
  329. this.pos+=2;
  330. var blue = (B&this.maskBlue)<<ns[0];
  331. var green = (B&this.maskGreen)>>ns[1];
  332. var red = (B&this.maskRed)>>ns[2];
  333. var location = line * this.width * 4 + x * 4;
  334. this.data[location] = 0;
  335. this.data[location + 1] = blue;
  336. this.data[location + 2] = green;
  337. this.data[location + 3] = red;
  338. }
  339. //skip extra bytes
  340. this.pos += dif_w;
  341. }
  342. };
  343. BmpDecoder.prototype.bit24 = function() {
  344. for (var y = this.height - 1; y >= 0; y--) {
  345. var line = this.bottom_up ? y : this.height - 1 - y
  346. for (var x = 0; x < this.width; x++) {
  347. //Little Endian rgb
  348. var blue = this.buffer.readUInt8(this.pos++);
  349. var green = this.buffer.readUInt8(this.pos++);
  350. var red = this.buffer.readUInt8(this.pos++);
  351. var location = line * this.width * 4 + x * 4;
  352. this.data[location] = 0;
  353. this.data[location + 1] = blue;
  354. this.data[location + 2] = green;
  355. this.data[location + 3] = red;
  356. }
  357. //skip extra bytes
  358. this.pos += (this.width % 4);
  359. }
  360. };
  361. /**
  362. * add 32bit decode func
  363. * @author soubok
  364. */
  365. BmpDecoder.prototype.bit32 = function() {
  366. //BI_BITFIELDS
  367. if(this.compress == 3){
  368. this.maskRed = this.buffer.readUInt32LE(this.pos);
  369. this.pos+=4;
  370. this.maskGreen = this.buffer.readUInt32LE(this.pos);
  371. this.pos+=4;
  372. this.maskBlue = this.buffer.readUInt32LE(this.pos);
  373. this.pos+=4;
  374. this.mask0 = this.buffer.readUInt32LE(this.pos);
  375. this.pos+=4;
  376. for (var y = this.height - 1; y >= 0; y--) {
  377. var line = this.bottom_up ? y : this.height - 1 - y;
  378. for (var x = 0; x < this.width; x++) {
  379. //Little Endian rgba
  380. var alpha = this.buffer.readUInt8(this.pos++);
  381. var blue = this.buffer.readUInt8(this.pos++);
  382. var green = this.buffer.readUInt8(this.pos++);
  383. var red = this.buffer.readUInt8(this.pos++);
  384. var location = line * this.width * 4 + x * 4;
  385. this.data[location] = alpha;
  386. this.data[location + 1] = blue;
  387. this.data[location + 2] = green;
  388. this.data[location + 3] = red;
  389. }
  390. }
  391. }else{
  392. for (var y = this.height - 1; y >= 0; y--) {
  393. var line = this.bottom_up ? y : this.height - 1 - y;
  394. for (var x = 0; x < this.width; x++) {
  395. //Little Endian argb
  396. var blue = this.buffer.readUInt8(this.pos++);
  397. var green = this.buffer.readUInt8(this.pos++);
  398. var red = this.buffer.readUInt8(this.pos++);
  399. var alpha = this.buffer.readUInt8(this.pos++);
  400. var location = line * this.width * 4 + x * 4;
  401. this.data[location] = alpha;
  402. this.data[location + 1] = blue;
  403. this.data[location + 2] = green;
  404. this.data[location + 3] = red;
  405. }
  406. }
  407. }
  408. };
  409. BmpDecoder.prototype.getData = function() {
  410. return this.data;
  411. };
  412. module.exports = function(bmpData) {
  413. var decoder = new BmpDecoder(bmpData);
  414. return decoder;
  415. };