sharp 0.32.1 → 0.32.3

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.
package/binding.gyp CHANGED
@@ -70,7 +70,9 @@
70
70
  }, {
71
71
  'target_name': 'sharp-<(platform_and_arch)',
72
72
  'defines': [
73
- 'NAPI_VERSION=7'
73
+ 'NAPI_VERSION=7',
74
+ 'NODE_ADDON_API_DISABLE_DEPRECATED',
75
+ 'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS'
74
76
  ],
75
77
  'dependencies': [
76
78
  '<!(node -p "require(\'node-addon-api\').gyp")',
package/lib/composite.js CHANGED
@@ -46,7 +46,7 @@ const blend = {
46
46
  * The images to composite must be the same size or smaller than the processed image.
47
47
  * If both `top` and `left` options are provided, they take precedence over `gravity`.
48
48
  *
49
- * Any resize or rotate operations in the same processing pipeline
49
+ * Any resize, rotate or extract operations in the same processing pipeline
50
50
  * will always be applied to the input image before composition.
51
51
  *
52
52
  * The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
@@ -108,14 +108,14 @@ const blend = {
108
108
  * @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
109
109
  * @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
110
110
  * @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
111
- * @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
111
+ * @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`.
112
112
  * @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
113
113
  * @param {String} [images[].blend='over'] - how to blend this image with the image below.
114
114
  * @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
115
115
  * @param {Number} [images[].top] - the pixel offset from the top edge.
116
116
  * @param {Number} [images[].left] - the pixel offset from the left edge.
117
117
  * @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
118
- * @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option.
118
+ * @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultiplying the image below. Equivalent to the `--premultiplied` vips option.
119
119
  * @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
120
120
  * @param {Object} [images[].raw] - describes overlay when using raw pixel data.
121
121
  * @param {Number} [images[].raw.width]
@@ -49,7 +49,7 @@ const debuglog = util.debuglog('sharp');
49
49
  * readableStream.pipe(transformer).pipe(writableStream);
50
50
  *
51
51
  * @example
52
- * // Create a blank 300x200 PNG image of semi-transluent red pixels
52
+ * // Create a blank 300x200 PNG image of semi-translucent red pixels
53
53
  * sharp({
54
54
  * create: {
55
55
  * width: 300,
@@ -122,7 +122,7 @@ const debuglog = util.debuglog('sharp');
122
122
  * a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
123
123
  * JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
124
124
  * @param {Object} [options] - if present, is an Object with optional attributes.
125
- * @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort.
125
+ * @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), higher levels imply lower levels, invalid metadata will always abort.
126
126
  * @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
127
127
  * (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
128
128
  * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
@@ -291,6 +291,7 @@ const Sharp = function (input, options) {
291
291
  webpLossless: false,
292
292
  webpNearLossless: false,
293
293
  webpSmartSubsample: false,
294
+ webpPreset: 'default',
294
295
  webpEffort: 4,
295
296
  webpMinSize: false,
296
297
  webpMixed: false,
package/lib/index.d.ts CHANGED
@@ -1350,9 +1350,9 @@ declare namespace sharp {
1350
1350
  grayscale?: boolean | undefined;
1351
1351
  }
1352
1352
 
1353
- interface OverlayOptions {
1353
+ interface OverlayOptions extends SharpOptions {
1354
1354
  /** Buffer containing image data, String containing the path to an image file, or Create object */
1355
- input?: string | Buffer | { create: Create } | { text: CreateText } | undefined;
1355
+ input?: string | Buffer | { create: Create } | { text: CreateText } | { raw: CreateRaw } | undefined;
1356
1356
  /** how to blend this image with the image below. (optional, default `'over'`) */
1357
1357
  blend?: Blend | undefined;
1358
1358
  /** gravity at which to place the overlay. (optional, default 'centre') */
@@ -1363,25 +1363,8 @@ declare namespace sharp {
1363
1363
  left?: number | undefined;
1364
1364
  /** set to true to repeat the overlay image across the entire image with the given gravity. (optional, default false) */
1365
1365
  tile?: boolean | undefined;
1366
- /** number representing the DPI for vector overlay image. (optional, default 72) */
1367
- density?: number | undefined;
1368
- /** describes overlay when using raw pixel data. */
1369
- raw?: Raw | undefined;
1370
1366
  /** Set to true to avoid premultipling the image below. Equivalent to the --premultiplied vips option. */
1371
1367
  premultiplied?: boolean | undefined;
1372
- /** Set to true to read all frames/pages of an animated image. (optional, default false). */
1373
- animated?: boolean | undefined;
1374
- /**
1375
- * When to abort processing of invalid pixel data, one of (in order of sensitivity):
1376
- * 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
1377
- */
1378
- failOn?: FailOnOptions | undefined;
1379
- /**
1380
- * Do not process input images where the number of pixels (width x height) exceeds this limit.
1381
- * Assumes image dimensions contained in the input metadata can be trusted.
1382
- * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default 268402689)
1383
- */
1384
- limitInputPixels?: number | boolean | undefined;
1385
1368
  }
1386
1369
 
1387
1370
  interface TileOptions {
package/lib/operation.js CHANGED
@@ -11,7 +11,7 @@ const is = require('./is');
11
11
  * or auto-orient based on the EXIF `Orientation` tag.
12
12
  *
13
13
  * If an angle is provided, it is converted to a valid positive degree rotation.
14
- * For example, `-450` will produce a 270deg rotation.
14
+ * For example, `-450` will produce a 270 degree rotation.
15
15
  *
16
16
  * When rotating by an angle other than a multiple of 90,
17
17
  * the background colour can be provided with the `background` option.
@@ -80,9 +80,13 @@ function rotate (angle, options) {
80
80
  }
81
81
 
82
82
  /**
83
- * Flip the image about the vertical Y axis. This always occurs before rotation, if any.
83
+ * Mirror the image vertically (up-down) about the x-axis.
84
+ * This always occurs before rotation, if any.
85
+ *
84
86
  * The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
85
87
  *
88
+ * This operation does not work correctly with multi-page images.
89
+ *
86
90
  * @example
87
91
  * const output = await sharp(input).flip().toBuffer();
88
92
  *
@@ -95,7 +99,9 @@ function flip (flip) {
95
99
  }
96
100
 
97
101
  /**
98
- * Flop the image about the horizontal X axis. This always occurs before rotation, if any.
102
+ * Mirror the image horizontally (left-right) about the y-axis.
103
+ * This always occurs before rotation, if any.
104
+ *
99
105
  * The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
100
106
  *
101
107
  * @example
@@ -766,7 +772,7 @@ function linear (a, b) {
766
772
  }
767
773
 
768
774
  /**
769
- * Recomb the image with the specified matrix.
775
+ * Recombine the image with the specified matrix.
770
776
  *
771
777
  * @since 0.21.1
772
778
  *
@@ -779,7 +785,7 @@ function linear (a, b) {
779
785
  * ])
780
786
  * .raw()
781
787
  * .toBuffer(function(err, data, info) {
782
- * // data contains the raw pixel data after applying the recomb
788
+ * // data contains the raw pixel data after applying the matrix
783
789
  * // With this example input, a sepia filter has been applied
784
790
  * });
785
791
  *
@@ -836,7 +842,7 @@ function recomb (inputMatrix) {
836
842
  * .toBuffer();
837
843
  *
838
844
  * @example
839
- * // decreate brightness and saturation while also hue-rotating by 90 degrees
845
+ * // decrease brightness and saturation while also hue-rotating by 90 degrees
840
846
  * const output = await sharp(input)
841
847
  * .modulate({
842
848
  * brightness: 0.5,
package/lib/output.js CHANGED
@@ -29,7 +29,7 @@ const formats = new Map([
29
29
  ['jxl', 'jxl']
30
30
  ]);
31
31
 
32
- const jp2Regex = /\.jp[2x]|j2[kc]$/i;
32
+ 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
 
@@ -75,7 +75,7 @@ function toFile (fileOut, callback) {
75
75
  err = new Error('Missing output file path');
76
76
  } else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
77
77
  err = new Error('Cannot use same file for input and output');
78
- } else if (jp2Regex.test(fileOut) && !this.constructor.format.jp2k.output.file) {
78
+ } else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
79
79
  err = errJp2Save();
80
80
  }
81
81
  if (err) {
@@ -483,6 +483,7 @@ function png (options) {
483
483
  * @param {boolean} [options.lossless=false] - use lossless compression mode
484
484
  * @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
485
485
  * @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
486
+ * @param {string} [options.preset='default'] - named preset for preprocessing/filtering, one of: default, photo, picture, drawing, icon, text
486
487
  * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
487
488
  * @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
488
489
  * @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
@@ -517,6 +518,13 @@ function webp (options) {
517
518
  if (is.defined(options.smartSubsample)) {
518
519
  this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
519
520
  }
521
+ if (is.defined(options.preset)) {
522
+ if (is.string(options.preset) && is.inArray(options.preset, ['default', 'photo', 'picture', 'drawing', 'icon', 'text'])) {
523
+ this.options.webpPreset = options.preset;
524
+ } else {
525
+ throw is.invalidParameterError('preset', 'one of: default, photo, picture, drawing, icon, text', options.preset);
526
+ }
527
+ }
520
528
  if (is.defined(options.effort)) {
521
529
  if (is.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
522
530
  this.options.webpEffort = options.effort;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sharp",
3
3
  "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
4
- "version": "0.32.1",
4
+ "version": "0.32.3",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -136,9 +136,9 @@
136
136
  "detect-libc": "^2.0.1",
137
137
  "node-addon-api": "^6.1.0",
138
138
  "prebuild-install": "^7.1.1",
139
- "semver": "^7.5.0",
139
+ "semver": "^7.5.4",
140
140
  "simple-get": "^4.0.1",
141
- "tar-fs": "^2.1.1",
141
+ "tar-fs": "^3.0.4",
142
142
  "tunnel-agent": "^0.6.0"
143
143
  },
144
144
  "devDependencies": {
@@ -153,7 +153,7 @@
153
153
  "mocha": "^10.2.0",
154
154
  "mock-fs": "^5.2.0",
155
155
  "nyc": "^15.1.0",
156
- "prebuild": "^11.0.4",
156
+ "prebuild": "lovell/prebuild#add-nodejs-20-drop-nodejs-10-and-12",
157
157
  "semistandard": "^16.0.1",
158
158
  "tsd": "^0.28.1"
159
159
  },
package/src/common.cc CHANGED
@@ -669,6 +669,10 @@ namespace sharp {
669
669
  if (image.width() > 65535 || height > 65535) {
670
670
  throw vips::VError("Processed image is too large for the GIF format");
671
671
  }
672
+ } else if (imageType == ImageType::HEIF) {
673
+ if (image.width() > 16384 || height > 16384) {
674
+ throw vips::VError("Processed image is too large for the HEIF format");
675
+ }
672
676
  }
673
677
  }
674
678
 
@@ -1031,4 +1035,13 @@ namespace sharp {
1031
1035
  return std::make_pair(hshrink, vshrink);
1032
1036
  }
1033
1037
 
1038
+ /*
1039
+ Ensure decoding remains sequential.
1040
+ */
1041
+ VImage StaySequential(VImage image, VipsAccess access, bool condition) {
1042
+ if (access == VIPS_ACCESS_SEQUENTIAL && condition) {
1043
+ return image.copy_memory();
1044
+ }
1045
+ return image;
1046
+ }
1034
1047
  } // namespace sharp
package/src/common.h CHANGED
@@ -370,6 +370,11 @@ namespace sharp {
370
370
  std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
371
371
  Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction);
372
372
 
373
+ /*
374
+ Ensure decoding remains sequential.
375
+ */
376
+ VImage StaySequential(VImage image, VipsAccess access, bool condition = TRUE);
377
+
373
378
  } // namespace sharp
374
379
 
375
380
  #endif // SRC_COMMON_H_
package/src/pipeline.cc CHANGED
@@ -56,6 +56,7 @@ class PipelineWorker : public Napi::AsyncWorker {
56
56
  vips::VImage image;
57
57
  sharp::ImageType inputImageType;
58
58
  std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
59
+ VipsAccess access = baton->input->access;
59
60
  image = sharp::EnsureColourspace(image, baton->colourspaceInput);
60
61
 
61
62
  int nPages = baton->input->pages;
@@ -90,6 +91,13 @@ class PipelineWorker : public Napi::AsyncWorker {
90
91
  baton->rotationAngle != 0.0);
91
92
 
92
93
  if (shouldRotateBefore) {
94
+ image = sharp::StaySequential(image, access,
95
+ rotation != VIPS_ANGLE_D0 ||
96
+ autoRotation != VIPS_ANGLE_D0 ||
97
+ autoFlip ||
98
+ baton->flip ||
99
+ baton->rotationAngle != 0.0);
100
+
93
101
  if (autoRotation != VIPS_ANGLE_D0) {
94
102
  image = image.rot(autoRotation);
95
103
  autoRotation = VIPS_ANGLE_D0;
@@ -116,13 +124,14 @@ class PipelineWorker : public Napi::AsyncWorker {
116
124
  MultiPageUnsupported(nPages, "Rotate");
117
125
  std::vector<double> background;
118
126
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
119
- image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
127
+ image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
120
128
  }
121
129
  }
122
130
 
123
131
  // Trim
124
132
  if (baton->trimThreshold > 0.0) {
125
133
  MultiPageUnsupported(nPages, "Trim");
134
+ image = sharp::StaySequential(image, access);
126
135
  image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
127
136
  baton->trimOffsetLeft = image.xoffset();
128
137
  baton->trimOffsetTop = image.yoffset();
@@ -209,7 +218,7 @@ class PipelineWorker : public Napi::AsyncWorker {
209
218
  // pdfload* and svgload*
210
219
  if (jpegShrinkOnLoad > 1) {
211
220
  vips::VOption *option = VImage::option()
212
- ->set("access", baton->input->access)
221
+ ->set("access", access)
213
222
  ->set("shrink", jpegShrinkOnLoad)
214
223
  ->set("unlimited", baton->input->unlimited)
215
224
  ->set("fail_on", baton->input->failOn);
@@ -224,7 +233,7 @@ class PipelineWorker : public Napi::AsyncWorker {
224
233
  }
225
234
  } else if (scale != 1.0) {
226
235
  vips::VOption *option = VImage::option()
227
- ->set("access", baton->input->access)
236
+ ->set("access", access)
228
237
  ->set("scale", scale)
229
238
  ->set("fail_on", baton->input->failOn);
230
239
  if (inputImageType == sharp::ImageType::WEBP) {
@@ -376,15 +385,20 @@ class PipelineWorker : public Napi::AsyncWorker {
376
385
  ->set("kernel", baton->kernel));
377
386
  }
378
387
 
388
+ image = sharp::StaySequential(image, access,
389
+ autoRotation != VIPS_ANGLE_D0 ||
390
+ baton->flip ||
391
+ autoFlip ||
392
+ rotation != VIPS_ANGLE_D0);
379
393
  // Auto-rotate post-extract
380
394
  if (autoRotation != VIPS_ANGLE_D0) {
381
395
  image = image.rot(autoRotation);
382
396
  }
383
- // Flip (mirror about Y axis)
397
+ // Mirror vertically (up-down) about the x-axis
384
398
  if (baton->flip || autoFlip) {
385
399
  image = image.flip(VIPS_DIRECTION_VERTICAL);
386
400
  }
387
- // Flop (mirror about X axis)
401
+ // Mirror horizontally (left-right) about the y-axis
388
402
  if (baton->flop || autoFlop) {
389
403
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
390
404
  }
@@ -399,6 +413,7 @@ class PipelineWorker : public Napi::AsyncWorker {
399
413
  sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
400
414
 
401
415
  for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
416
+ baton->joinChannelIn[i]->access = access;
402
417
  std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
403
418
  joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
404
419
  image = image.bandjoin(joinImage);
@@ -467,13 +482,12 @@ class PipelineWorker : public Napi::AsyncWorker {
467
482
 
468
483
  // Attention-based or Entropy-based crop
469
484
  MultiPageUnsupported(nPages, "Resize strategy");
470
- image = image.tilecache(VImage::option()
471
- ->set("access", VIPS_ACCESS_RANDOM)
472
- ->set("threaded", TRUE));
473
-
485
+ image = sharp::StaySequential(image, access);
474
486
  image = image.smartcrop(baton->width, baton->height, VImage::option()
475
487
  ->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
488
+ #if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
476
489
  ->set("premultiplied", shouldPremultiplyAlpha)
490
+ #endif
477
491
  ->set("attention_x", &attention_x)
478
492
  ->set("attention_y", &attention_y));
479
493
  baton->hasCropOffset = true;
@@ -489,6 +503,7 @@ class PipelineWorker : public Napi::AsyncWorker {
489
503
  // Rotate post-extract non-90 angle
490
504
  if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
491
505
  MultiPageUnsupported(nPages, "Rotate");
506
+ image = sharp::StaySequential(image, access);
492
507
  std::vector<double> background;
493
508
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
494
509
  image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
@@ -512,6 +527,7 @@ class PipelineWorker : public Napi::AsyncWorker {
512
527
  // Affine transform
513
528
  if (!baton->affineMatrix.empty()) {
514
529
  MultiPageUnsupported(nPages, "Affine");
530
+ image = sharp::StaySequential(image, access);
515
531
  std::vector<double> background;
516
532
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
517
533
  vips::VInterpolate interp = vips::VInterpolate::new_from_name(
@@ -608,6 +624,7 @@ class PipelineWorker : public Napi::AsyncWorker {
608
624
  for (Composite *composite : baton->composite) {
609
625
  VImage compositeImage;
610
626
  sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
627
+ composite->input->access = access;
611
628
  std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
612
629
  compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
613
630
  // Verify within current dimensions
@@ -694,11 +711,13 @@ class PipelineWorker : public Napi::AsyncWorker {
694
711
 
695
712
  // Apply normalisation - stretch luminance to cover full dynamic range
696
713
  if (baton->normalise) {
697
- image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
714
+ image = sharp::StaySequential(image, access);
715
+ image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
698
716
  }
699
717
 
700
718
  // Apply contrast limiting adaptive histogram equalization (CLAHE)
701
719
  if (baton->claheWidth != 0 && baton->claheHeight != 0) {
720
+ image = sharp::StaySequential(image, access);
702
721
  image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
703
722
  }
704
723
 
@@ -706,6 +725,7 @@ class PipelineWorker : public Napi::AsyncWorker {
706
725
  if (baton->boolean != nullptr) {
707
726
  VImage booleanImage;
708
727
  sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
728
+ baton->boolean->access = access;
709
729
  std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
710
730
  booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
711
731
  image = sharp::Boolean(image, booleanImage, baton->booleanOp);
@@ -874,6 +894,7 @@ class PipelineWorker : public Napi::AsyncWorker {
874
894
  ->set("lossless", baton->webpLossless)
875
895
  ->set("near_lossless", baton->webpNearLossless)
876
896
  ->set("smart_subsample", baton->webpSmartSubsample)
897
+ ->set("preset", baton->webpPreset)
877
898
  ->set("effort", baton->webpEffort)
878
899
  ->set("min_size", baton->webpMinSize)
879
900
  ->set("mixed", baton->webpMixed)
@@ -933,6 +954,7 @@ class PipelineWorker : public Napi::AsyncWorker {
933
954
  } else if (baton->formatOut == "heif" ||
934
955
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
935
956
  // Write HEIF to buffer
957
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
936
958
  image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
937
959
  VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
938
960
  ->set("strip", !baton->withMetadata)
@@ -954,6 +976,7 @@ class PipelineWorker : public Napi::AsyncWorker {
954
976
  if (!sharp::HasAlpha(image)) {
955
977
  baton->tileBackground.pop_back();
956
978
  }
979
+ image = sharp::StaySequential(image, access, baton->tileAngle != 0);
957
980
  vips::VOption *options = BuildOptionsDZ(baton);
958
981
  VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
959
982
  baton->bufferOut = static_cast<char*>(area->data);
@@ -1077,6 +1100,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1077
1100
  ->set("lossless", baton->webpLossless)
1078
1101
  ->set("near_lossless", baton->webpNearLossless)
1079
1102
  ->set("smart_subsample", baton->webpSmartSubsample)
1103
+ ->set("preset", baton->webpPreset)
1080
1104
  ->set("effort", baton->webpEffort)
1081
1105
  ->set("min_size", baton->webpMinSize)
1082
1106
  ->set("mixed", baton->webpMixed)
@@ -1122,6 +1146,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1122
1146
  } else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
1123
1147
  (willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
1124
1148
  // Write HEIF to file
1149
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
1125
1150
  image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
1126
1151
  image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1127
1152
  ->set("strip", !baton->withMetadata)
@@ -1152,6 +1177,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1152
1177
  if (!sharp::HasAlpha(image)) {
1153
1178
  baton->tileBackground.pop_back();
1154
1179
  }
1180
+ image = sharp::StaySequential(image, access, baton->tileAngle != 0);
1155
1181
  vips::VOption *options = BuildOptionsDZ(baton);
1156
1182
  image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
1157
1183
  baton->formatOut = "dz";
@@ -1353,6 +1379,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1353
1379
  {"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
1354
1380
  {"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
1355
1381
  {"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
1382
+ {"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
1356
1383
  {"min_size", baton->webpMinSize ? "TRUE" : "FALSE"},
1357
1384
  {"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
1358
1385
  {"effort", std::to_string(baton->webpEffort)}
@@ -1605,6 +1632,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1605
1632
  baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
1606
1633
  baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
1607
1634
  baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
1635
+ baton->webpPreset = sharp::AttrAsEnum<VipsForeignWebpPreset>(options, "webpPreset", VIPS_TYPE_FOREIGN_WEBP_PRESET);
1608
1636
  baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
1609
1637
  baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
1610
1638
  baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
@@ -1664,24 +1692,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1664
1692
  baton->tileId = sharp::AttrAsStr(options, "tileId");
1665
1693
  baton->tileBasename = sharp::AttrAsStr(options, "tileBasename");
1666
1694
 
1667
- // Force random access for certain operations
1668
- if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
1669
- if (
1670
- baton->trimThreshold > 0.0 ||
1671
- baton->normalise ||
1672
- baton->position == 16 || baton->position == 17 ||
1673
- baton->angle != 0 ||
1674
- baton->rotationAngle != 0.0 ||
1675
- baton->tileAngle != 0 ||
1676
- baton->useExifOrientation ||
1677
- baton->flip ||
1678
- baton->claheWidth != 0 ||
1679
- !baton->affineMatrix.empty()
1680
- ) {
1681
- baton->input->access = VIPS_ACCESS_RANDOM;
1682
- }
1683
- }
1684
-
1685
1695
  // Function to notify of libvips warnings
1686
1696
  Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
1687
1697
 
package/src/pipeline.h CHANGED
@@ -153,6 +153,7 @@ struct PipelineBaton {
153
153
  bool webpNearLossless;
154
154
  bool webpLossless;
155
155
  bool webpSmartSubsample;
156
+ VipsForeignWebpPreset webpPreset;
156
157
  int webpEffort;
157
158
  bool webpMinSize;
158
159
  bool webpMixed;
@@ -318,6 +319,7 @@ struct PipelineBaton {
318
319
  webpNearLossless(false),
319
320
  webpLossless(false),
320
321
  webpSmartSubsample(false),
322
+ webpPreset(VIPS_FOREIGN_WEBP_PRESET_DEFAULT),
321
323
  webpEffort(4),
322
324
  webpMinSize(false),
323
325
  webpMixed(false),