@depup/sharp 0.34.5-depup.0 → 0.35.4-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +16 -109
  2. package/changes.json +5 -0
  3. package/{lib/channel.js → dist/channel.cjs} +1 -1
  4. package/dist/channel.mjs +177 -0
  5. package/{lib/colour.js → dist/colour.cjs} +11 -7
  6. package/dist/colour.mjs +199 -0
  7. package/{lib/composite.js → dist/composite.cjs} +8 -7
  8. package/dist/composite.mjs +213 -0
  9. package/{lib/constructor.js → dist/constructor.cjs} +42 -30
  10. package/dist/constructor.mjs +511 -0
  11. package/dist/index.cjs +25 -0
  12. package/dist/index.d.cts +1999 -0
  13. package/dist/index.d.mts +2046 -0
  14. package/dist/index.mjs +25 -0
  15. package/{lib/input.js → dist/input.cjs} +47 -37
  16. package/dist/input.mjs +819 -0
  17. package/{lib/is.js → dist/is.cjs} +1 -1
  18. package/dist/is.mjs +143 -0
  19. package/{lib/libvips.js → dist/libvips.cjs} +35 -30
  20. package/dist/libvips.mjs +212 -0
  21. package/{lib/operation.js → dist/operation.cjs} +37 -51
  22. package/dist/operation.mjs +1002 -0
  23. package/{lib/output.js → dist/output.cjs} +166 -47
  24. package/dist/output.mjs +1785 -0
  25. package/{lib/resize.js → dist/resize.cjs} +54 -32
  26. package/dist/resize.mjs +617 -0
  27. package/dist/sharp.cjs +174 -0
  28. package/dist/sharp.mjs +174 -0
  29. package/{lib/utility.js → dist/utility.cjs} +18 -8
  30. package/dist/utility.mjs +301 -0
  31. package/install/build.js +3 -3
  32. package/lib/index.d.ts +111 -83
  33. package/package.json +95 -54
  34. package/src/binding.gyp +19 -14
  35. package/src/common.cc +77 -21
  36. package/src/common.h +23 -4
  37. package/src/metadata.cc +66 -8
  38. package/src/metadata.h +6 -1
  39. package/src/operations.cc +25 -8
  40. package/src/operations.h +1 -1
  41. package/src/pipeline.cc +203 -69
  42. package/src/pipeline.h +15 -1
  43. package/src/stats.cc +6 -6
  44. package/src/utilities.cc +7 -6
  45. package/install/check.js +0 -14
  46. package/lib/index.js +0 -16
  47. package/lib/sharp.js +0 -121
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  const path = require('node:path');
7
- const is = require('./is');
8
- const sharp = require('./sharp');
7
+ const is = require('./is.cjs');
8
+ const sharp = require('./sharp.cjs');
9
9
 
10
10
  const formats = new Map([
11
11
  ['heic', 'heif'],
@@ -33,7 +33,7 @@ const jp2Regex = /\.(jp[2x]|j2[kc])$/i;
33
33
 
34
34
  const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG');
35
35
 
36
- const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
36
+ const bitdepthFromColourCount = (colours) => 1 << 32 - Math.clz32(Math.ceil(Math.log2(colours)) - 1);
37
37
 
38
38
  /**
39
39
  * Write output image data to a file.
@@ -76,7 +76,7 @@ function toFile (fileOut, callback) {
76
76
  err = new Error('Missing output file path');
77
77
  } else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
78
78
  err = new Error('Cannot use same file for input and output');
79
- } else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
79
+ } else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2.output.file) {
80
80
  err = errJp2Save();
81
81
  }
82
82
  if (err) {
@@ -113,24 +113,20 @@ function toFile (fileOut, callback) {
113
113
  * Animated output will also contain `pageHeight` and `pages`.
114
114
  * May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
115
115
  *
116
- * A `Promise` is returned when `callback` is not provided.
116
+ * The underlying `ArrayBuffer` may be marked as non-transferable by some JavaScript runtimes.
117
+ * Use {@link #touint8array toUint8Array} for a guaranteed transferable `ArrayBuffer`.
117
118
  *
118
- * @example
119
- * sharp(input)
120
- * .toBuffer((err, data, info) => { ... });
119
+ * A `Promise` is returned when `callback` is not provided.
121
120
  *
122
121
  * @example
123
- * sharp(input)
124
- * .toBuffer()
125
- * .then(data => { ... })
126
- * .catch(err => { ... });
122
+ * const data = await sharp(input)
123
+ * .png()
124
+ * .toBuffer();
127
125
  *
128
126
  * @example
129
- * sharp(input)
127
+ * const { data, info } = await sharp(input)
130
128
  * .png()
131
- * .toBuffer({ resolveWithObject: true })
132
- * .then(({ data, info }) => { ... })
133
- * .catch(err => { ... });
129
+ * .toBuffer({ resolveWithObject: true });
134
130
  *
135
131
  * @example
136
132
  * const { data, info } = await sharp('my-image.jpg')
@@ -164,6 +160,64 @@ function toBuffer (options, callback) {
164
160
  return this._pipeline(is.fn(options) ? options : callback, stack);
165
161
  }
166
162
 
163
+ /**
164
+ * Write output to a `Uint8Array` backed by a transferable `ArrayBuffer`.
165
+ * JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
166
+ *
167
+ * Use {@link #toformat toFormat} or one of the format-specific functions such as {@link #jpeg jpeg}, {@link #png png} etc. to set the output format.
168
+ *
169
+ * If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
170
+ *
171
+ * By default all metadata will be removed, which includes EXIF-based orientation.
172
+ * See {@link #keepexif keepExif} and similar methods for control over this.
173
+ *
174
+ * Resolves with an `Object` containing:
175
+ * - `data` is the output image as a `Uint8Array` backed by a transferable `ArrayBuffer`.
176
+ * - `info` contains properties relating to the output image such as `width` and `height`.
177
+ *
178
+ * @since v0.35.0
179
+ *
180
+ * @example
181
+ * const { data, info } = await sharp(input).toUint8Array();
182
+ *
183
+ * @example
184
+ * const { data } = await sharp(input)
185
+ * .avif()
186
+ * .toUint8Array();
187
+ * const base64String = data.toBase64();
188
+ *
189
+ * @returns {Promise<{ data: Uint8Array, info: Object }>}
190
+ */
191
+ function toUint8Array () {
192
+ this.options.resolveWithObject = true;
193
+ this.options.typedArrayOut = true;
194
+ const stack = Error();
195
+ return this._pipeline(null, stack);
196
+ }
197
+
198
+ /**
199
+ * Set output density (DPI) in EXIF metadata.
200
+ *
201
+ * @since 0.35.0
202
+ *
203
+ * @example
204
+ * const data = await sharp(input)
205
+ * .withDensity(96)
206
+ * .toBuffer();
207
+ *
208
+ * @param {number} density Number of pixels per inch (DPI).
209
+ * @returns {Sharp}
210
+ * @throws {Error} Invalid parameters
211
+ */
212
+ function withDensity (density) {
213
+ if (is.number(density) && density > 0) {
214
+ this.options.withMetadataDensity = density;
215
+ } else {
216
+ throw is.invalidParameterError('density', 'positive number', density);
217
+ }
218
+ return this.keepExif();
219
+ }
220
+
167
221
  /**
168
222
  * Keep all EXIF metadata from the input image in the output image.
169
223
  *
@@ -319,6 +373,60 @@ function withIccProfile (icc, options) {
319
373
  return this;
320
374
  }
321
375
 
376
+ /**
377
+ * If the input contains gain map metadata, attempt to process the image and gain map separately,
378
+ * recombining them into a single output image.
379
+ *
380
+ * This approach is faster and should produce better results than {@link #withgainmap withGainMap},
381
+ * however not all operations are supported.
382
+ *
383
+ * Only JPEG input and output are supported.
384
+ * JPEG output options other than `quality` are ignored.
385
+ *
386
+ * This feature is experimental and the API may change.
387
+ *
388
+ * @since 0.35.0
389
+ *
390
+ * @example
391
+ * const outputWithResizedGainMap = await sharp(inputWithGainMap)
392
+ * .keepGainMap()
393
+ * .resize({ width: 64 })
394
+ * .toBuffer();
395
+ *
396
+ * @returns {Sharp}
397
+ */
398
+ function keepGainMap() {
399
+ this.options.keepGainMap = true;
400
+ this.options.withGainMap = false;
401
+ this.options.keepMetadata |= 0b100000;
402
+ return this;
403
+ }
404
+
405
+ /**
406
+ * If the input contains gain map metadata, use it to convert the main image to HDR (High Dynamic Range) before further processing.
407
+ * The input gain map is discarded.
408
+ *
409
+ * If the output is JPEG, generate and attach a new ISO 21496-1 gain map.
410
+ * JPEG output options other than `quality` are ignored.
411
+ *
412
+ * This feature is experimental and the API may change.
413
+ *
414
+ * @since 0.35.0
415
+ *
416
+ * @example
417
+ * const outputWithRegeneratedGainMap = await sharp(inputWithGainMap)
418
+ * .withGainMap()
419
+ * .toBuffer();
420
+ *
421
+ * @returns {Sharp}
422
+ */
423
+ function withGainMap() {
424
+ this.options.withGainMap = true;
425
+ this.options.keepGainMap = false;
426
+ this.options.colourspace = 'scrgb';
427
+ return this;
428
+ }
429
+
322
430
  /**
323
431
  * Keep XMP metadata from the input image in the output image.
324
432
  *
@@ -388,7 +496,7 @@ function withXmp (xmp) {
388
496
  * @returns {Sharp}
389
497
  */
390
498
  function keepMetadata () {
391
- this.options.keepMetadata = 0b11111;
499
+ this.options.keepMetadata |= 0b11111;
392
500
  return this;
393
501
  }
394
502
 
@@ -690,6 +798,7 @@ function png (options) {
690
798
  * @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
691
799
  * @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
692
800
  * @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
801
+ * @param {boolean} [options.exact=false] - preserve the colour data in transparent pixels
693
802
  * @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
694
803
  * @returns {Sharp}
695
804
  * @throws {Error} Invalid options
@@ -742,6 +851,9 @@ function webp (options) {
742
851
  if (is.defined(options.mixed)) {
743
852
  this._setBooleanOption('webpMixed', options.mixed);
744
853
  }
854
+ if (is.defined(options.exact)) {
855
+ this._setBooleanOption('webpExact', options.exact);
856
+ }
745
857
  }
746
858
  trySetAnimationOptions(options, this.options);
747
859
  return this._updateFormatOut('webp', options);
@@ -813,7 +925,7 @@ function gif (options) {
813
925
  }
814
926
  }
815
927
  if (is.defined(options.effort)) {
816
- if (is.number(options.effort) && is.inRange(options.effort, 1, 10)) {
928
+ if (is.integer(options.effort) && is.inRange(options.effort, 1, 10)) {
817
929
  this.options.gifEffort = options.effort;
818
930
  } else {
819
931
  throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
@@ -887,7 +999,7 @@ function gif (options) {
887
999
  */
888
1000
  function jp2 (options) {
889
1001
  /* node:coverage ignore next 41 */
890
- if (!this.constructor.format.jp2k.output.buffer) {
1002
+ if (!this.constructor.format.jp2.output.buffer) {
891
1003
  throw errJp2Save();
892
1004
  }
893
1005
  if (is.object(options)) {
@@ -987,12 +1099,12 @@ function trySetAnimationOptions (source, target) {
987
1099
  * @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
988
1100
  * @param {boolean} [options.pyramid=false] - write an image pyramid
989
1101
  * @param {boolean} [options.tile=false] - write a tiled tiff
990
- * @param {number} [options.tileWidth=256] - horizontal tile size
991
- * @param {number} [options.tileHeight=256] - vertical tile size
992
- * @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
993
- * @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
1102
+ * @param {number} [options.tileWidth=256] - horizontal tile size, valid values are integers in the range 1-32768
1103
+ * @param {number} [options.tileHeight=256] - vertical tile size, valid values are integers in the range 1-32768
1104
+ * @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm, valid values are numbers in the range 0.001-1000000
1105
+ * @param {number} [options.yres=1.0] - vertical resolution in pixels/mm, valid values are numbers in the range 0.001-1000000
994
1106
  * @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
995
- * @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
1107
+ * @param {number} [options.bitdepth=0] - reduce bitdepth to 1, 2 or 4 bit
996
1108
  * @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
997
1109
  * @returns {Sharp}
998
1110
  * @throws {Error} Invalid options
@@ -1007,10 +1119,10 @@ function tiff (options) {
1007
1119
  }
1008
1120
  }
1009
1121
  if (is.defined(options.bitdepth)) {
1010
- if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4, 8])) {
1122
+ if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4])) {
1011
1123
  this.options.tiffBitdepth = options.bitdepth;
1012
1124
  } else {
1013
- throw is.invalidParameterError('bitdepth', '1, 2, 4 or 8', options.bitdepth);
1125
+ throw is.invalidParameterError('bitdepth', '1, 2 or 4', options.bitdepth);
1014
1126
  }
1015
1127
  }
1016
1128
  // tiling
@@ -1018,17 +1130,17 @@ function tiff (options) {
1018
1130
  this._setBooleanOption('tiffTile', options.tile);
1019
1131
  }
1020
1132
  if (is.defined(options.tileWidth)) {
1021
- if (is.integer(options.tileWidth) && options.tileWidth > 0) {
1133
+ if (is.integer(options.tileWidth) && is.inRange(options.tileWidth, 1, 32768)) {
1022
1134
  this.options.tiffTileWidth = options.tileWidth;
1023
1135
  } else {
1024
- throw is.invalidParameterError('tileWidth', 'integer greater than zero', options.tileWidth);
1136
+ throw is.invalidParameterError('tileWidth', 'integer between 1 and 32768', options.tileWidth);
1025
1137
  }
1026
1138
  }
1027
1139
  if (is.defined(options.tileHeight)) {
1028
- if (is.integer(options.tileHeight) && options.tileHeight > 0) {
1140
+ if (is.integer(options.tileHeight) && is.inRange(options.tileHeight, 1, 32768)) {
1029
1141
  this.options.tiffTileHeight = options.tileHeight;
1030
1142
  } else {
1031
- throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
1143
+ throw is.invalidParameterError('tileHeight', 'integer between 1 and 32768', options.tileHeight);
1032
1144
  }
1033
1145
  }
1034
1146
  // miniswhite
@@ -1041,17 +1153,17 @@ function tiff (options) {
1041
1153
  }
1042
1154
  // resolution
1043
1155
  if (is.defined(options.xres)) {
1044
- if (is.number(options.xres) && options.xres > 0) {
1156
+ if (is.number(options.xres) && is.inRange(options.xres, 0.001, 1000000)) {
1045
1157
  this.options.tiffXres = options.xres;
1046
1158
  } else {
1047
- throw is.invalidParameterError('xres', 'number greater than zero', options.xres);
1159
+ throw is.invalidParameterError('xres', 'number between 0.001 and 1000000', options.xres);
1048
1160
  }
1049
1161
  }
1050
1162
  if (is.defined(options.yres)) {
1051
- if (is.number(options.yres) && options.yres > 0) {
1163
+ if (is.number(options.yres) && is.inRange(options.yres, 0.001, 1000000)) {
1052
1164
  this.options.tiffYres = options.yres;
1053
1165
  } else {
1054
- throw is.invalidParameterError('yres', 'number greater than zero', options.yres);
1166
+ throw is.invalidParameterError('yres', 'number between 0.001 and 1000000', options.yres);
1055
1167
  }
1056
1168
  }
1057
1169
  // compression
@@ -1090,10 +1202,6 @@ function tiff (options) {
1090
1202
  * Use these AVIF options for output image.
1091
1203
  *
1092
1204
  * AVIF image sequences are not supported.
1093
- * Prebuilt binaries support a bitdepth of 8 only.
1094
- *
1095
- * This feature is experimental on the Windows ARM64 platform
1096
- * and requires a CPU with ARM64v8.4 or later.
1097
1205
  *
1098
1206
  * @example
1099
1207
  * const data = await sharp(input)
@@ -1113,6 +1221,7 @@ function tiff (options) {
1113
1221
  * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
1114
1222
  * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
1115
1223
  * @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
1224
+ * @param {string} [options.tune='auto'] - tune output for a quality metric, one of 'auto' (default), 'iq', 'psnr' or 'ssim'
1116
1225
  * @returns {Sharp}
1117
1226
  * @throws {Error} Invalid options
1118
1227
  */
@@ -1140,6 +1249,7 @@ function avif (options) {
1140
1249
  * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
1141
1250
  * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
1142
1251
  * @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
1252
+ * @param {string} [options.tune='auto'] - tune output for a quality metric, one of 'auto' (default), 'iq', 'psnr' or 'ssim'
1143
1253
  * @returns {Sharp}
1144
1254
  * @throws {Error} Invalid options
1145
1255
  */
@@ -1180,14 +1290,22 @@ function heif (options) {
1180
1290
  }
1181
1291
  if (is.defined(options.bitdepth)) {
1182
1292
  if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [8, 10, 12])) {
1183
- if (options.bitdepth !== 8 && this.constructor.versions.heif) {
1184
- throw is.invalidParameterError('bitdepth when using prebuilt binaries', 8, options.bitdepth);
1185
- }
1186
1293
  this.options.heifBitdepth = options.bitdepth;
1187
1294
  } else {
1188
1295
  throw is.invalidParameterError('bitdepth', '8, 10 or 12', options.bitdepth);
1189
1296
  }
1190
1297
  }
1298
+ if (is.defined(options.tune)) {
1299
+ if (is.string(options.tune) && is.inArray(options.tune, ['auto', 'iq', 'psnr', 'ssim'])) {
1300
+ if (this.options.heifLossless && options.tune === 'iq') {
1301
+ this.options.heifTune = 'ssim';
1302
+ } else {
1303
+ this.options.heifTune = options.tune;
1304
+ }
1305
+ } else {
1306
+ throw is.invalidParameterError('tune', 'one of: auto, iq, psnr, ssim', options.tune);
1307
+ }
1308
+ }
1191
1309
  } else {
1192
1310
  throw is.invalidParameterError('options', 'Object', options);
1193
1311
  }
@@ -1531,7 +1649,7 @@ function _pipeline (callback, stack) {
1531
1649
  // output=file/buffer
1532
1650
  if (this._isStreamInput()) {
1533
1651
  // output=file/buffer, input=stream
1534
- this.on('finish', () => {
1652
+ this._whenStreamInFinished(() => {
1535
1653
  this._flattenBufferIn();
1536
1654
  sharp.pipeline(this.options, (err, data, info) => {
1537
1655
  if (err) {
@@ -1556,7 +1674,7 @@ function _pipeline (callback, stack) {
1556
1674
  // output=stream
1557
1675
  if (this._isStreamInput()) {
1558
1676
  // output=stream, input=stream
1559
- this.once('finish', () => {
1677
+ this._whenStreamInFinished(() => {
1560
1678
  this._flattenBufferIn();
1561
1679
  sharp.pipeline(this.options, (err, data, info) => {
1562
1680
  if (err) {
@@ -1569,9 +1687,6 @@ function _pipeline (callback, stack) {
1569
1687
  this.on('end', () => this.emit('close'));
1570
1688
  });
1571
1689
  });
1572
- if (this.streamInFinished) {
1573
- this.emit('finish');
1574
- }
1575
1690
  } else {
1576
1691
  // output=stream, input=file/buffer
1577
1692
  sharp.pipeline(this.options, (err, data, info) => {
@@ -1591,7 +1706,7 @@ function _pipeline (callback, stack) {
1591
1706
  if (this._isStreamInput()) {
1592
1707
  // output=promise, input=stream
1593
1708
  return new Promise((resolve, reject) => {
1594
- this.once('finish', () => {
1709
+ this._whenStreamInFinished(() => {
1595
1710
  this._flattenBufferIn();
1596
1711
  sharp.pipeline(this.options, (err, data, info) => {
1597
1712
  if (err) {
@@ -1635,11 +1750,15 @@ module.exports = (Sharp) => {
1635
1750
  // Public
1636
1751
  toFile,
1637
1752
  toBuffer,
1753
+ toUint8Array,
1754
+ withDensity,
1638
1755
  keepExif,
1639
1756
  withExif,
1640
1757
  withExifMerge,
1641
1758
  keepIccProfile,
1642
1759
  withIccProfile,
1760
+ keepGainMap,
1761
+ withGainMap,
1643
1762
  keepXmp,
1644
1763
  withXmp,
1645
1764
  keepMetadata,