image-bitmap.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. "use strict";
  2. var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
  3. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  4. Object.defineProperty(exports, "__esModule", {
  5. value: true
  6. });
  7. exports.parseBitmap = parseBitmap;
  8. exports.getBuffer = getBuffer;
  9. exports.getBufferAsync = getBufferAsync;
  10. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  11. var _fileType = _interopRequireDefault(require("file-type"));
  12. var _exifParser = _interopRequireDefault(require("exif-parser"));
  13. var _utils = require("@jimp/utils");
  14. var constants = _interopRequireWildcard(require("../constants"));
  15. var MIME = _interopRequireWildcard(require("./mime"));
  16. var _promisify = _interopRequireDefault(require("./promisify"));
  17. function getMIMEFromBuffer(buffer, path) {
  18. var fileTypeFromBuffer = (0, _fileType["default"])(buffer);
  19. if (fileTypeFromBuffer) {
  20. // If fileType returns something for buffer, then return the mime given
  21. return fileTypeFromBuffer.mime;
  22. }
  23. if (path) {
  24. // If a path is supplied, and fileType yields no results, then retry with MIME
  25. // Path can be either a file path or a url
  26. return MIME.getType(path);
  27. }
  28. return null;
  29. }
  30. /*
  31. * Obtains image orientation from EXIF metadata.
  32. *
  33. * @param img {Jimp} a Jimp image object
  34. * @returns {number} a number 1-8 representing EXIF orientation,
  35. * in particular 1 if orientation tag is missing
  36. */
  37. function getExifOrientation(img) {
  38. return img._exif && img._exif.tags && img._exif.tags.Orientation || 1;
  39. }
  40. /**
  41. * Returns a function which translates EXIF-rotated coordinates into
  42. * non-rotated ones.
  43. *
  44. * Transformation reference: http://sylvana.net/jpegcrop/exif_orientation.html.
  45. *
  46. * @param img {Jimp} a Jimp image object
  47. * @returns {function} transformation function for transformBitmap().
  48. */
  49. function getExifOrientationTransformation(img) {
  50. var w = img.getWidth();
  51. var h = img.getHeight();
  52. switch (getExifOrientation(img)) {
  53. case 1:
  54. // Horizontal (normal)
  55. // does not need to be supported here
  56. return null;
  57. case 2:
  58. // Mirror horizontal
  59. return function (x, y) {
  60. return [w - x - 1, y];
  61. };
  62. case 3:
  63. // Rotate 180
  64. return function (x, y) {
  65. return [w - x - 1, h - y - 1];
  66. };
  67. case 4:
  68. // Mirror vertical
  69. return function (x, y) {
  70. return [x, h - y - 1];
  71. };
  72. case 5:
  73. // Mirror horizontal and rotate 270 CW
  74. return function (x, y) {
  75. return [y, x];
  76. };
  77. case 6:
  78. // Rotate 90 CW
  79. return function (x, y) {
  80. return [y, h - x - 1];
  81. };
  82. case 7:
  83. // Mirror horizontal and rotate 90 CW
  84. return function (x, y) {
  85. return [w - y - 1, h - x - 1];
  86. };
  87. case 8:
  88. // Rotate 270 CW
  89. return function (x, y) {
  90. return [w - y - 1, x];
  91. };
  92. default:
  93. return null;
  94. }
  95. }
  96. /*
  97. * Transforms bitmap in place (moves pixels around) according to given
  98. * transformation function.
  99. *
  100. * @param img {Jimp} a Jimp image object, which bitmap is supposed to
  101. * be transformed
  102. * @param width {number} bitmap width after the transformation
  103. * @param height {number} bitmap height after the transformation
  104. * @param transformation {function} transformation function which defines pixel
  105. * mapping between new and source bitmap. It takes a pair of coordinates
  106. * in the target, and returns a respective pair of coordinates in
  107. * the source bitmap, i.e. has following form:
  108. * `function(new_x, new_y) { return [src_x, src_y] }`.
  109. */
  110. function transformBitmap(img, width, height, transformation) {
  111. // Underscore-prefixed values are related to the source bitmap
  112. // Their counterparts with no prefix are related to the target bitmap
  113. var _data = img.bitmap.data;
  114. var _width = img.bitmap.width;
  115. var data = Buffer.alloc(_data.length);
  116. for (var x = 0; x < width; x++) {
  117. for (var y = 0; y < height; y++) {
  118. var _transformation = transformation(x, y),
  119. _transformation2 = (0, _slicedToArray2["default"])(_transformation, 2),
  120. _x = _transformation2[0],
  121. _y = _transformation2[1];
  122. var idx = width * y + x << 2;
  123. var _idx = _width * _y + _x << 2;
  124. var pixel = _data.readUInt32BE(_idx);
  125. data.writeUInt32BE(pixel, idx);
  126. }
  127. }
  128. img.bitmap.data = data;
  129. img.bitmap.width = width;
  130. img.bitmap.height = height;
  131. }
  132. /*
  133. * Automagically rotates an image based on its EXIF data (if present).
  134. * @param img {Jimp} a Jimp image object
  135. */
  136. function exifRotate(img) {
  137. if (getExifOrientation(img) < 2) return;
  138. var transformation = getExifOrientationTransformation(img);
  139. var swapDimensions = getExifOrientation(img) > 4;
  140. var newWidth = swapDimensions ? img.bitmap.height : img.bitmap.width;
  141. var newHeight = swapDimensions ? img.bitmap.width : img.bitmap.height;
  142. transformBitmap(img, newWidth, newHeight, transformation);
  143. } // parses a bitmap from the constructor to the JIMP bitmap property
  144. function parseBitmap(data, path, cb) {
  145. var mime = getMIMEFromBuffer(data, path);
  146. if (typeof mime !== 'string') {
  147. return cb(new Error('Could not find MIME for Buffer <' + path + '>'));
  148. }
  149. this._originalMime = mime.toLowerCase();
  150. try {
  151. var _mime = this.getMIME();
  152. if (this.constructor.decoders[_mime]) {
  153. this.bitmap = this.constructor.decoders[_mime](data);
  154. } else {
  155. return _utils.throwError.call(this, 'Unsupported MIME type: ' + _mime, cb);
  156. }
  157. } catch (error) {
  158. return cb.call(this, error, this);
  159. }
  160. try {
  161. this._exif = _exifParser["default"].create(data).parse();
  162. exifRotate(this); // EXIF data
  163. } catch (error) {
  164. /* meh */
  165. }
  166. cb.call(this, null, this);
  167. return this;
  168. }
  169. function compositeBitmapOverBackground(Jimp, image) {
  170. return new Jimp(image.bitmap.width, image.bitmap.height, image._background).composite(image, 0, 0).bitmap;
  171. }
  172. /**
  173. * Converts the image to a buffer
  174. * @param {string} mime the mime type of the image buffer to be created
  175. * @param {function(Error, Jimp)} cb a Node-style function to call with the buffer as the second argument
  176. * @returns {Jimp} this for chaining of methods
  177. */
  178. function getBuffer(mime, cb) {
  179. if (mime === constants.AUTO) {
  180. // allow auto MIME detection
  181. mime = this.getMIME();
  182. }
  183. if (typeof mime !== 'string') {
  184. return _utils.throwError.call(this, 'mime must be a string', cb);
  185. }
  186. if (typeof cb !== 'function') {
  187. return _utils.throwError.call(this, 'cb must be a function', cb);
  188. }
  189. mime = mime.toLowerCase();
  190. if (this._rgba && this.constructor.hasAlpha[mime]) {
  191. this.bitmap.data = Buffer.from(this.bitmap.data);
  192. } else {
  193. // when format doesn't support alpha
  194. // composite onto a new image so that the background shows through alpha channels
  195. this.bitmap.data = compositeBitmapOverBackground(this.constructor, this).data;
  196. }
  197. if (this.constructor.encoders[mime]) {
  198. var buffer = this.constructor.encoders[mime](this);
  199. cb.call(this, null, buffer);
  200. } else {
  201. cb.call(this, 'Unsupported MIME type: ' + mime);
  202. }
  203. return this;
  204. }
  205. function getBufferAsync(mime) {
  206. return (0, _promisify["default"])(getBuffer, this, mime);
  207. }
  208. //# sourceMappingURL=image-bitmap.js.map