packer.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. "use strict";
  2. let constants = require("./constants");
  3. let CrcStream = require("./crc");
  4. let bitPacker = require("./bitpacker");
  5. let filter = require("./filter-pack");
  6. let zlib = require("zlib");
  7. let Packer = (module.exports = function (options) {
  8. this._options = options;
  9. options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
  10. options.deflateLevel =
  11. options.deflateLevel != null ? options.deflateLevel : 9;
  12. options.deflateStrategy =
  13. options.deflateStrategy != null ? options.deflateStrategy : 3;
  14. options.inputHasAlpha =
  15. options.inputHasAlpha != null ? options.inputHasAlpha : true;
  16. options.deflateFactory = options.deflateFactory || zlib.createDeflate;
  17. options.bitDepth = options.bitDepth || 8;
  18. // This is outputColorType
  19. options.colorType =
  20. typeof options.colorType === "number"
  21. ? options.colorType
  22. : constants.COLORTYPE_COLOR_ALPHA;
  23. options.inputColorType =
  24. typeof options.inputColorType === "number"
  25. ? options.inputColorType
  26. : constants.COLORTYPE_COLOR_ALPHA;
  27. if (
  28. [
  29. constants.COLORTYPE_GRAYSCALE,
  30. constants.COLORTYPE_COLOR,
  31. constants.COLORTYPE_COLOR_ALPHA,
  32. constants.COLORTYPE_ALPHA,
  33. ].indexOf(options.colorType) === -1
  34. ) {
  35. throw new Error(
  36. "option color type:" + options.colorType + " is not supported at present"
  37. );
  38. }
  39. if (
  40. [
  41. constants.COLORTYPE_GRAYSCALE,
  42. constants.COLORTYPE_COLOR,
  43. constants.COLORTYPE_COLOR_ALPHA,
  44. constants.COLORTYPE_ALPHA,
  45. ].indexOf(options.inputColorType) === -1
  46. ) {
  47. throw new Error(
  48. "option input color type:" +
  49. options.inputColorType +
  50. " is not supported at present"
  51. );
  52. }
  53. if (options.bitDepth !== 8 && options.bitDepth !== 16) {
  54. throw new Error(
  55. "option bit depth:" + options.bitDepth + " is not supported at present"
  56. );
  57. }
  58. });
  59. Packer.prototype.getDeflateOptions = function () {
  60. return {
  61. chunkSize: this._options.deflateChunkSize,
  62. level: this._options.deflateLevel,
  63. strategy: this._options.deflateStrategy,
  64. };
  65. };
  66. Packer.prototype.createDeflate = function () {
  67. return this._options.deflateFactory(this.getDeflateOptions());
  68. };
  69. Packer.prototype.filterData = function (data, width, height) {
  70. // convert to correct format for filtering (e.g. right bpp and bit depth)
  71. let packedData = bitPacker(data, width, height, this._options);
  72. // filter pixel data
  73. let bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
  74. let filteredData = filter(packedData, width, height, this._options, bpp);
  75. return filteredData;
  76. };
  77. Packer.prototype._packChunk = function (type, data) {
  78. let len = data ? data.length : 0;
  79. let buf = Buffer.alloc(len + 12);
  80. buf.writeUInt32BE(len, 0);
  81. buf.writeUInt32BE(type, 4);
  82. if (data) {
  83. data.copy(buf, 8);
  84. }
  85. buf.writeInt32BE(
  86. CrcStream.crc32(buf.slice(4, buf.length - 4)),
  87. buf.length - 4
  88. );
  89. return buf;
  90. };
  91. Packer.prototype.packGAMA = function (gamma) {
  92. let buf = Buffer.alloc(4);
  93. buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0);
  94. return this._packChunk(constants.TYPE_gAMA, buf);
  95. };
  96. Packer.prototype.packIHDR = function (width, height) {
  97. let buf = Buffer.alloc(13);
  98. buf.writeUInt32BE(width, 0);
  99. buf.writeUInt32BE(height, 4);
  100. buf[8] = this._options.bitDepth; // Bit depth
  101. buf[9] = this._options.colorType; // colorType
  102. buf[10] = 0; // compression
  103. buf[11] = 0; // filter
  104. buf[12] = 0; // interlace
  105. return this._packChunk(constants.TYPE_IHDR, buf);
  106. };
  107. Packer.prototype.packIDAT = function (data) {
  108. return this._packChunk(constants.TYPE_IDAT, data);
  109. };
  110. Packer.prototype.packIEND = function () {
  111. return this._packChunk(constants.TYPE_IEND, null);
  112. };