bitpacker.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. "use strict";
  2. let constants = require("./constants");
  3. module.exports = function (dataIn, width, height, options) {
  4. let outHasAlpha =
  5. [constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA].indexOf(
  6. options.colorType
  7. ) !== -1;
  8. if (options.colorType === options.inputColorType) {
  9. let bigEndian = (function () {
  10. let buffer = new ArrayBuffer(2);
  11. new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
  12. // Int16Array uses the platform's endianness.
  13. return new Int16Array(buffer)[0] !== 256;
  14. })();
  15. // If no need to convert to grayscale and alpha is present/absent in both, take a fast route
  16. if (options.bitDepth === 8 || (options.bitDepth === 16 && bigEndian)) {
  17. return dataIn;
  18. }
  19. }
  20. // map to a UInt16 array if data is 16bit, fix endianness below
  21. let data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer);
  22. let maxValue = 255;
  23. let inBpp = constants.COLORTYPE_TO_BPP_MAP[options.inputColorType];
  24. if (inBpp === 4 && !options.inputHasAlpha) {
  25. inBpp = 3;
  26. }
  27. let outBpp = constants.COLORTYPE_TO_BPP_MAP[options.colorType];
  28. if (options.bitDepth === 16) {
  29. maxValue = 65535;
  30. outBpp *= 2;
  31. }
  32. let outData = Buffer.alloc(width * height * outBpp);
  33. let inIndex = 0;
  34. let outIndex = 0;
  35. let bgColor = options.bgColor || {};
  36. if (bgColor.red === undefined) {
  37. bgColor.red = maxValue;
  38. }
  39. if (bgColor.green === undefined) {
  40. bgColor.green = maxValue;
  41. }
  42. if (bgColor.blue === undefined) {
  43. bgColor.blue = maxValue;
  44. }
  45. function getRGBA() {
  46. let red;
  47. let green;
  48. let blue;
  49. let alpha = maxValue;
  50. switch (options.inputColorType) {
  51. case constants.COLORTYPE_COLOR_ALPHA:
  52. alpha = data[inIndex + 3];
  53. red = data[inIndex];
  54. green = data[inIndex + 1];
  55. blue = data[inIndex + 2];
  56. break;
  57. case constants.COLORTYPE_COLOR:
  58. red = data[inIndex];
  59. green = data[inIndex + 1];
  60. blue = data[inIndex + 2];
  61. break;
  62. case constants.COLORTYPE_ALPHA:
  63. alpha = data[inIndex + 1];
  64. red = data[inIndex];
  65. green = red;
  66. blue = red;
  67. break;
  68. case constants.COLORTYPE_GRAYSCALE:
  69. red = data[inIndex];
  70. green = red;
  71. blue = red;
  72. break;
  73. default:
  74. throw new Error(
  75. "input color type:" +
  76. options.inputColorType +
  77. " is not supported at present"
  78. );
  79. }
  80. if (options.inputHasAlpha) {
  81. if (!outHasAlpha) {
  82. alpha /= maxValue;
  83. red = Math.min(
  84. Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0),
  85. maxValue
  86. );
  87. green = Math.min(
  88. Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0),
  89. maxValue
  90. );
  91. blue = Math.min(
  92. Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0),
  93. maxValue
  94. );
  95. }
  96. }
  97. return { red: red, green: green, blue: blue, alpha: alpha };
  98. }
  99. for (let y = 0; y < height; y++) {
  100. for (let x = 0; x < width; x++) {
  101. let rgba = getRGBA(data, inIndex);
  102. switch (options.colorType) {
  103. case constants.COLORTYPE_COLOR_ALPHA:
  104. case constants.COLORTYPE_COLOR:
  105. if (options.bitDepth === 8) {
  106. outData[outIndex] = rgba.red;
  107. outData[outIndex + 1] = rgba.green;
  108. outData[outIndex + 2] = rgba.blue;
  109. if (outHasAlpha) {
  110. outData[outIndex + 3] = rgba.alpha;
  111. }
  112. } else {
  113. outData.writeUInt16BE(rgba.red, outIndex);
  114. outData.writeUInt16BE(rgba.green, outIndex + 2);
  115. outData.writeUInt16BE(rgba.blue, outIndex + 4);
  116. if (outHasAlpha) {
  117. outData.writeUInt16BE(rgba.alpha, outIndex + 6);
  118. }
  119. }
  120. break;
  121. case constants.COLORTYPE_ALPHA:
  122. case constants.COLORTYPE_GRAYSCALE: {
  123. // Convert to grayscale and alpha
  124. let grayscale = (rgba.red + rgba.green + rgba.blue) / 3;
  125. if (options.bitDepth === 8) {
  126. outData[outIndex] = grayscale;
  127. if (outHasAlpha) {
  128. outData[outIndex + 1] = rgba.alpha;
  129. }
  130. } else {
  131. outData.writeUInt16BE(grayscale, outIndex);
  132. if (outHasAlpha) {
  133. outData.writeUInt16BE(rgba.alpha, outIndex + 2);
  134. }
  135. }
  136. break;
  137. }
  138. default:
  139. throw new Error("unrecognised color Type " + options.colorType);
  140. }
  141. inIndex += inBpp;
  142. outIndex += outBpp;
  143. }
  144. }
  145. return outData;
  146. };