bmpDecoder.js 8.4 KB

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