bitmapper.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. "use strict";
  2. let interlaceUtils = require("./interlace");
  3. let pixelBppMapper = [
  4. // 0 - dummy entry
  5. function () {},
  6. // 1 - L
  7. // 0: 0, 1: 0, 2: 0, 3: 0xff
  8. function (pxData, data, pxPos, rawPos) {
  9. if (rawPos === data.length) {
  10. throw new Error("Ran out of data");
  11. }
  12. let pixel = data[rawPos];
  13. pxData[pxPos] = pixel;
  14. pxData[pxPos + 1] = pixel;
  15. pxData[pxPos + 2] = pixel;
  16. pxData[pxPos + 3] = 0xff;
  17. },
  18. // 2 - LA
  19. // 0: 0, 1: 0, 2: 0, 3: 1
  20. function (pxData, data, pxPos, rawPos) {
  21. if (rawPos + 1 >= data.length) {
  22. throw new Error("Ran out of data");
  23. }
  24. let pixel = data[rawPos];
  25. pxData[pxPos] = pixel;
  26. pxData[pxPos + 1] = pixel;
  27. pxData[pxPos + 2] = pixel;
  28. pxData[pxPos + 3] = data[rawPos + 1];
  29. },
  30. // 3 - RGB
  31. // 0: 0, 1: 1, 2: 2, 3: 0xff
  32. function (pxData, data, pxPos, rawPos) {
  33. if (rawPos + 2 >= data.length) {
  34. throw new Error("Ran out of data");
  35. }
  36. pxData[pxPos] = data[rawPos];
  37. pxData[pxPos + 1] = data[rawPos + 1];
  38. pxData[pxPos + 2] = data[rawPos + 2];
  39. pxData[pxPos + 3] = 0xff;
  40. },
  41. // 4 - RGBA
  42. // 0: 0, 1: 1, 2: 2, 3: 3
  43. function (pxData, data, pxPos, rawPos) {
  44. if (rawPos + 3 >= data.length) {
  45. throw new Error("Ran out of data");
  46. }
  47. pxData[pxPos] = data[rawPos];
  48. pxData[pxPos + 1] = data[rawPos + 1];
  49. pxData[pxPos + 2] = data[rawPos + 2];
  50. pxData[pxPos + 3] = data[rawPos + 3];
  51. },
  52. ];
  53. let pixelBppCustomMapper = [
  54. // 0 - dummy entry
  55. function () {},
  56. // 1 - L
  57. // 0: 0, 1: 0, 2: 0, 3: 0xff
  58. function (pxData, pixelData, pxPos, maxBit) {
  59. let pixel = pixelData[0];
  60. pxData[pxPos] = pixel;
  61. pxData[pxPos + 1] = pixel;
  62. pxData[pxPos + 2] = pixel;
  63. pxData[pxPos + 3] = maxBit;
  64. },
  65. // 2 - LA
  66. // 0: 0, 1: 0, 2: 0, 3: 1
  67. function (pxData, pixelData, pxPos) {
  68. let pixel = pixelData[0];
  69. pxData[pxPos] = pixel;
  70. pxData[pxPos + 1] = pixel;
  71. pxData[pxPos + 2] = pixel;
  72. pxData[pxPos + 3] = pixelData[1];
  73. },
  74. // 3 - RGB
  75. // 0: 0, 1: 1, 2: 2, 3: 0xff
  76. function (pxData, pixelData, pxPos, maxBit) {
  77. pxData[pxPos] = pixelData[0];
  78. pxData[pxPos + 1] = pixelData[1];
  79. pxData[pxPos + 2] = pixelData[2];
  80. pxData[pxPos + 3] = maxBit;
  81. },
  82. // 4 - RGBA
  83. // 0: 0, 1: 1, 2: 2, 3: 3
  84. function (pxData, pixelData, pxPos) {
  85. pxData[pxPos] = pixelData[0];
  86. pxData[pxPos + 1] = pixelData[1];
  87. pxData[pxPos + 2] = pixelData[2];
  88. pxData[pxPos + 3] = pixelData[3];
  89. },
  90. ];
  91. function bitRetriever(data, depth) {
  92. let leftOver = [];
  93. let i = 0;
  94. function split() {
  95. if (i === data.length) {
  96. throw new Error("Ran out of data");
  97. }
  98. let byte = data[i];
  99. i++;
  100. let byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1;
  101. switch (depth) {
  102. default:
  103. throw new Error("unrecognised depth");
  104. case 16:
  105. byte2 = data[i];
  106. i++;
  107. leftOver.push((byte << 8) + byte2);
  108. break;
  109. case 4:
  110. byte2 = byte & 0x0f;
  111. byte1 = byte >> 4;
  112. leftOver.push(byte1, byte2);
  113. break;
  114. case 2:
  115. byte4 = byte & 3;
  116. byte3 = (byte >> 2) & 3;
  117. byte2 = (byte >> 4) & 3;
  118. byte1 = (byte >> 6) & 3;
  119. leftOver.push(byte1, byte2, byte3, byte4);
  120. break;
  121. case 1:
  122. byte8 = byte & 1;
  123. byte7 = (byte >> 1) & 1;
  124. byte6 = (byte >> 2) & 1;
  125. byte5 = (byte >> 3) & 1;
  126. byte4 = (byte >> 4) & 1;
  127. byte3 = (byte >> 5) & 1;
  128. byte2 = (byte >> 6) & 1;
  129. byte1 = (byte >> 7) & 1;
  130. leftOver.push(byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8);
  131. break;
  132. }
  133. }
  134. return {
  135. get: function (count) {
  136. while (leftOver.length < count) {
  137. split();
  138. }
  139. let returner = leftOver.slice(0, count);
  140. leftOver = leftOver.slice(count);
  141. return returner;
  142. },
  143. resetAfterLine: function () {
  144. leftOver.length = 0;
  145. },
  146. end: function () {
  147. if (i !== data.length) {
  148. throw new Error("extra data found");
  149. }
  150. },
  151. };
  152. }
  153. function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) {
  154. // eslint-disable-line max-params
  155. let imageWidth = image.width;
  156. let imageHeight = image.height;
  157. let imagePass = image.index;
  158. for (let y = 0; y < imageHeight; y++) {
  159. for (let x = 0; x < imageWidth; x++) {
  160. let pxPos = getPxPos(x, y, imagePass);
  161. pixelBppMapper[bpp](pxData, data, pxPos, rawPos);
  162. rawPos += bpp; //eslint-disable-line no-param-reassign
  163. }
  164. }
  165. return rawPos;
  166. }
  167. function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) {
  168. // eslint-disable-line max-params
  169. let imageWidth = image.width;
  170. let imageHeight = image.height;
  171. let imagePass = image.index;
  172. for (let y = 0; y < imageHeight; y++) {
  173. for (let x = 0; x < imageWidth; x++) {
  174. let pixelData = bits.get(bpp);
  175. let pxPos = getPxPos(x, y, imagePass);
  176. pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit);
  177. }
  178. bits.resetAfterLine();
  179. }
  180. }
  181. exports.dataToBitMap = function (data, bitmapInfo) {
  182. let width = bitmapInfo.width;
  183. let height = bitmapInfo.height;
  184. let depth = bitmapInfo.depth;
  185. let bpp = bitmapInfo.bpp;
  186. let interlace = bitmapInfo.interlace;
  187. let bits;
  188. if (depth !== 8) {
  189. bits = bitRetriever(data, depth);
  190. }
  191. let pxData;
  192. if (depth <= 8) {
  193. pxData = Buffer.alloc(width * height * 4);
  194. } else {
  195. pxData = new Uint16Array(width * height * 4);
  196. }
  197. let maxBit = Math.pow(2, depth) - 1;
  198. let rawPos = 0;
  199. let images;
  200. let getPxPos;
  201. if (interlace) {
  202. images = interlaceUtils.getImagePasses(width, height);
  203. getPxPos = interlaceUtils.getInterlaceIterator(width, height);
  204. } else {
  205. let nonInterlacedPxPos = 0;
  206. getPxPos = function () {
  207. let returner = nonInterlacedPxPos;
  208. nonInterlacedPxPos += 4;
  209. return returner;
  210. };
  211. images = [{ width: width, height: height }];
  212. }
  213. for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
  214. if (depth === 8) {
  215. rawPos = mapImage8Bit(
  216. images[imageIndex],
  217. pxData,
  218. getPxPos,
  219. bpp,
  220. data,
  221. rawPos
  222. );
  223. } else {
  224. mapImageCustomBit(
  225. images[imageIndex],
  226. pxData,
  227. getPxPos,
  228. bpp,
  229. bits,
  230. maxBit
  231. );
  232. }
  233. }
  234. if (depth === 8) {
  235. if (rawPos !== data.length) {
  236. throw new Error("extra data found");
  237. }
  238. } else {
  239. bits.end();
  240. }
  241. return pxData;
  242. };