@revizly/sharp 0.33.3-revizly5 → 0.33.4-revizly2

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/install/check.js CHANGED
@@ -10,8 +10,8 @@ try {
10
10
  log(msg);
11
11
  log('Attempting to build from source via node-gyp');
12
12
  try {
13
- require('node-addon-api');
14
- log('Found node-addon-api');
13
+ const addonApi = require('node-addon-api');
14
+ log(`Found node-addon-api ${addonApi.version || ''}`);
15
15
  } catch (err) {
16
16
  log('Please add node-addon-api to your dependencies');
17
17
  return;
package/lib/colour.js CHANGED
@@ -71,8 +71,6 @@ function grayscale (grayscale) {
71
71
  * All operations will use this colourspace before converting to the output colourspace,
72
72
  * as defined by {@link #tocolourspace|toColourspace}.
73
73
  *
74
- * This feature is experimental and has not yet been fully-tested with all operations.
75
- *
76
74
  * @since 0.29.0
77
75
  *
78
76
  * @example
package/lib/index.d.ts CHANGED
@@ -1479,6 +1479,14 @@ declare namespace sharp {
1479
1479
  tile?: boolean | undefined;
1480
1480
  /** Set to true to avoid premultipling the image below. Equivalent to the --premultiplied vips option. */
1481
1481
  premultiplied?: boolean | undefined;
1482
+ /** number representing the DPI for vector overlay image. (optional, default 72)*/
1483
+ density?: number | undefined;
1484
+ /** Set to true to read all frames/pages of an animated image. (optional, default false) */
1485
+ animated?: boolean | undefined;
1486
+ /** see sharp() constructor, (optional, default 'warning') */
1487
+ failOn?: FailOnOptions | undefined;
1488
+ /** see sharp() constructor, (optional, default 268402689) */
1489
+ limitInputPixels?: number | boolean | undefined;
1482
1490
  }
1483
1491
 
1484
1492
  interface TileOptions {
package/lib/input.js CHANGED
@@ -296,17 +296,17 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
296
296
  }
297
297
  }
298
298
  if (is.defined(inputOptions.text.width)) {
299
- if (is.number(inputOptions.text.width)) {
299
+ if (is.integer(inputOptions.text.width) && inputOptions.text.width > 0) {
300
300
  inputDescriptor.textWidth = inputOptions.text.width;
301
301
  } else {
302
- throw is.invalidParameterError('text.textWidth', 'number', inputOptions.text.width);
302
+ throw is.invalidParameterError('text.width', 'positive integer', inputOptions.text.width);
303
303
  }
304
304
  }
305
305
  if (is.defined(inputOptions.text.height)) {
306
- if (is.number(inputOptions.text.height)) {
306
+ if (is.integer(inputOptions.text.height) && inputOptions.text.height > 0) {
307
307
  inputDescriptor.textHeight = inputOptions.text.height;
308
308
  } else {
309
- throw is.invalidParameterError('text.height', 'number', inputOptions.text.height);
309
+ throw is.invalidParameterError('text.height', 'positive integer', inputOptions.text.height);
310
310
  }
311
311
  }
312
312
  if (is.defined(inputOptions.text.align)) {
@@ -324,10 +324,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
324
324
  }
325
325
  }
326
326
  if (is.defined(inputOptions.text.dpi)) {
327
- if (is.number(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 100000)) {
327
+ if (is.integer(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 1000000)) {
328
328
  inputDescriptor.textDpi = inputOptions.text.dpi;
329
329
  } else {
330
- throw is.invalidParameterError('text.dpi', 'number between 1 and 100000', inputOptions.text.dpi);
330
+ throw is.invalidParameterError('text.dpi', 'integer between 1 and 1000000', inputOptions.text.dpi);
331
331
  }
332
332
  }
333
333
  if (is.defined(inputOptions.text.rgba)) {
@@ -338,10 +338,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
338
338
  }
339
339
  }
340
340
  if (is.defined(inputOptions.text.spacing)) {
341
- if (is.number(inputOptions.text.spacing)) {
341
+ if (is.integer(inputOptions.text.spacing) && is.inRange(inputOptions.text.spacing, -1000000, 1000000)) {
342
342
  inputDescriptor.textSpacing = inputOptions.text.spacing;
343
343
  } else {
344
- throw is.invalidParameterError('text.spacing', 'number', inputOptions.text.spacing);
344
+ throw is.invalidParameterError('text.spacing', 'integer between -1000000 and 1000000', inputOptions.text.spacing);
345
345
  }
346
346
  }
347
347
  if (is.defined(inputOptions.text.wrap)) {
package/lib/libvips.js CHANGED
@@ -162,15 +162,21 @@ const pkgConfigPath = () => {
162
162
  }
163
163
  };
164
164
 
165
+ const skipSearch = (status, reason) => {
166
+ log(`Detected ${reason}, skipping search for globally-installed libvips`);
167
+ return status;
168
+ };
169
+
165
170
  const useGlobalLibvips = () => {
166
171
  if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
167
- log('Detected SHARP_IGNORE_GLOBAL_LIBVIPS, skipping search for globally-installed libvips');
168
- return false;
172
+ return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS');
173
+ }
174
+ if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
175
+ return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS');
169
176
  }
170
177
  /* istanbul ignore next */
171
178
  if (isRosetta()) {
172
- log('Detected Rosetta, skipping search for globally-installed libvips');
173
- return false;
179
+ return skipSearch(false, 'Rosetta');
174
180
  }
175
181
  const globalVipsVersion = globalLibvipsVersion();
176
182
  return !!globalVipsVersion && /* istanbul ignore next */
package/lib/resize.js CHANGED
@@ -68,6 +68,7 @@ const strategy = {
68
68
  */
69
69
  const kernel = {
70
70
  nearest: 'nearest',
71
+ linear: 'linear',
71
72
  cubic: 'cubic',
72
73
  mitchell: 'mitchell',
73
74
  lanczos2: 'lanczos2',
@@ -135,18 +136,22 @@ function isResizeExpected (options) {
135
136
  *
136
137
  * Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
137
138
  *
138
- * The experimental strategy-based approach resizes so one dimension is at its target length
139
+ * The strategy-based approach initially resizes so one dimension is at its target length
139
140
  * then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
140
141
  * - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
141
142
  * - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
142
143
  *
143
- * Possible interpolation kernels are:
144
+ * Possible downsizing kernels are:
144
145
  * - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
146
+ * - `linear`: Use a [triangle filter](https://en.wikipedia.org/wiki/Triangular_function).
145
147
  * - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
146
148
  * - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
147
149
  * - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
148
150
  * - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
149
151
  *
152
+ * When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
153
+ * Downsampling kernels without a matching upsampling interpolator map to `cubic`.
154
+ *
150
155
  * Only one resize can occur per pipeline.
151
156
  * Previous calls to `resize` in the same pipeline will be ignored.
152
157
  *
@@ -239,7 +244,7 @@ function isResizeExpected (options) {
239
244
  * @param {String} [options.fit='cover'] - How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`.
240
245
  * @param {String} [options.position='centre'] - A position, gravity or strategy to use when `fit` is `cover` or `contain`.
241
246
  * @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
242
- * @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
247
+ * @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction and the inferred interpolator to use for upsampling. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
243
248
  * @param {Boolean} [options.withoutEnlargement=false] - Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions.
244
249
  * @param {Boolean} [options.withoutReduction=false] - Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions.
245
250
  * @param {Boolean} [options.fastShrinkOnLoad=true] - Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension.
package/lib/utility.js CHANGED
@@ -153,6 +153,9 @@ function concurrency (concurrency) {
153
153
  if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
154
154
  // Reduce default concurrency to 1 when using glibc memory allocator
155
155
  sharp.concurrency(1);
156
+ } else if (detectLibc.familySync() === detectLibc.MUSL && sharp.concurrency() === 1024) {
157
+ // Reduce default concurrency when musl thread over-subscription detected
158
+ sharp.concurrency(require('node:os').availableParallelism());
156
159
  }
157
160
 
158
161
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revizly/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.33.3-revizly5",
4
+ "version": "0.33.4-revizly2",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://sharp.pixelplumbing.com",
7
7
  "contributors": [
@@ -82,7 +82,7 @@
82
82
  "Joris Dugué <zaruike10@gmail.com>",
83
83
  "Chris Banks <christopher.bradley.banks@gmail.com>",
84
84
  "Ompal Singh <ompal.hitm09@gmail.com>",
85
- "Brodan <christopher.hranj@gmail.com",
85
+ "Brodan <christopher.hranj@gmail.com>",
86
86
  "Ankur Parihar <ankur.github@gmail.com>",
87
87
  "Brahim Ait elhaj <brahima@gmail.com>",
88
88
  "Mart Jansink <m.jansink@gmail.com>",
@@ -141,30 +141,30 @@
141
141
  "semver": "^7.6.0"
142
142
  },
143
143
  "optionalDependencies": {
144
- "@revizly/sharp-libvips-linux-arm64": "1.0.3",
145
- "@revizly/sharp-libvips-linux-x64": "1.0.3",
146
- "@revizly/sharp-linux-arm64": "0.33.3-revizly1",
147
- "@revizly/sharp-linux-x64": "0.33.3-revizly1"
144
+ "@revizly/sharp-libvips-linux-arm64": "1.0.5",
145
+ "@revizly/sharp-libvips-linux-x64": "1.0.5",
146
+ "@revizly/sharp-linux-arm64": "0.33.4-revizly1",
147
+ "@revizly/sharp-linux-x64": "0.33.4-revizly1"
148
148
  },
149
149
  "devDependencies": {
150
- "@emnapi/runtime": "^1.1.0",
151
- "@revizly/sharp-libvips-dev": "1.0.3",
150
+ "@emnapi/runtime": "^1.1.1",
151
+ "@revizly/sharp-libvips-dev": "1.0.5",
152
152
  "@types/node": "*",
153
153
  "async": "^3.2.5",
154
154
  "cc": "^3.0.1",
155
- "emnapi": "^1.1.0",
155
+ "emnapi": "^1.1.1",
156
156
  "exif-reader": "^2.0.1",
157
157
  "extract-zip": "^2.0.1",
158
158
  "icc": "^3.0.0",
159
159
  "jsdoc-to-markdown": "^8.0.1",
160
160
  "license-checker": "^25.0.1",
161
- "mocha": "^10.3.0",
161
+ "mocha": "^10.4.0",
162
162
  "node-addon-api": "^8.0.0",
163
163
  "nyc": "^15.1.0",
164
- "prebuild": "^13.0.0",
164
+ "prebuild": "^13.0.1",
165
165
  "semistandard": "^17.0.0",
166
- "tar-fs": "^3.0.5",
167
- "tsd": "^0.30.7"
166
+ "tar-fs": "^3.0.6",
167
+ "tsd": "^0.31.0"
168
168
  },
169
169
  "license": "Apache-2.0",
170
170
  "engines": {
package/src/common.cc CHANGED
@@ -75,7 +75,7 @@ namespace sharp {
75
75
  Napi::Buffer<char> buffer = input.Get("buffer").As<Napi::Buffer<char>>();
76
76
  descriptor->bufferLength = buffer.Length();
77
77
  descriptor->buffer = buffer.Data();
78
- descriptor->isBuffer = TRUE;
78
+ descriptor->isBuffer = true;
79
79
  }
80
80
  descriptor->failOn = AttrAsEnum<VipsFailOn>(input, "failOn", VIPS_TYPE_FAIL_ON);
81
81
  // Density for vector-based input
@@ -384,7 +384,7 @@ namespace sharp {
384
384
  ->set("access", descriptor->access)
385
385
  ->set("fail_on", descriptor->failOn);
386
386
  if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
387
- option->set("unlimited", TRUE);
387
+ option->set("unlimited", true);
388
388
  }
389
389
  if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
390
390
  option->set("dpi", descriptor->density);
@@ -488,7 +488,7 @@ namespace sharp {
488
488
  ->set("access", descriptor->access)
489
489
  ->set("fail_on", descriptor->failOn);
490
490
  if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
491
- option->set("unlimited", TRUE);
491
+ option->set("unlimited", true);
492
492
  }
493
493
  if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
494
494
  option->set("dpi", descriptor->density);
@@ -768,7 +768,7 @@ namespace sharp {
768
768
  int *timeout = VIPS_NEW(im, int);
769
769
  *timeout = seconds;
770
770
  g_signal_connect(im, "eval", G_CALLBACK(VipsProgressCallBack), timeout);
771
- vips_image_set_progress(im, TRUE);
771
+ vips_image_set_progress(im, true);
772
772
  }
773
773
  }
774
774
  }
@@ -778,7 +778,7 @@ namespace sharp {
778
778
  */
779
779
  void VipsProgressCallBack(VipsImage *im, VipsProgress *progress, int *timeout) {
780
780
  if (*timeout > 0 && progress->run >= *timeout) {
781
- vips_image_set_kill(im, TRUE);
781
+ vips_image_set_kill(im, true);
782
782
  vips_error("timeout", "%d%% complete", progress->percent);
783
783
  *timeout = 0;
784
784
  }
@@ -1081,9 +1081,10 @@ namespace sharp {
1081
1081
  /*
1082
1082
  Ensure decoding remains sequential.
1083
1083
  */
1084
- VImage StaySequential(VImage image, VipsAccess access, bool condition) {
1085
- if (access == VIPS_ACCESS_SEQUENTIAL && condition) {
1086
- return image.copy_memory();
1084
+ VImage StaySequential(VImage image, bool condition) {
1085
+ if (vips_image_is_sequential(image.get_image()) && condition) {
1086
+ image = image.copy_memory().copy();
1087
+ image.remove(VIPS_META_SEQUENTIAL);
1087
1088
  }
1088
1089
  return image;
1089
1090
  }
package/src/common.h CHANGED
@@ -79,12 +79,12 @@ namespace sharp {
79
79
  buffer(nullptr),
80
80
  failOn(VIPS_FAIL_ON_WARNING),
81
81
  limitInputPixels(0x3FFF * 0x3FFF),
82
- unlimited(FALSE),
82
+ unlimited(false),
83
83
  access(VIPS_ACCESS_RANDOM),
84
84
  bufferLength(0),
85
- isBuffer(FALSE),
85
+ isBuffer(false),
86
86
  density(72.0),
87
- ignoreIcc(FALSE),
87
+ ignoreIcc(false),
88
88
  rawDepth(VIPS_FORMAT_UCHAR),
89
89
  rawChannels(0),
90
90
  rawWidth(0),
@@ -103,9 +103,9 @@ namespace sharp {
103
103
  textWidth(0),
104
104
  textHeight(0),
105
105
  textAlign(VIPS_ALIGN_LOW),
106
- textJustify(FALSE),
106
+ textJustify(false),
107
107
  textDpi(72),
108
- textRgba(FALSE),
108
+ textRgba(false),
109
109
  textSpacing(0),
110
110
  textWrap(VIPS_TEXT_WRAP_WORD),
111
111
  textAutofitDpi(0) {}
@@ -386,7 +386,7 @@ namespace sharp {
386
386
  /*
387
387
  Ensure decoding remains sequential.
388
388
  */
389
- VImage StaySequential(VImage image, VipsAccess access, bool condition = TRUE);
389
+ VImage StaySequential(VImage image, bool condition = true);
390
390
 
391
391
  } // namespace sharp
392
392
 
package/src/operations.cc CHANGED
@@ -155,7 +155,7 @@ namespace sharp {
155
155
  return image.conv(blur);
156
156
  } else {
157
157
  // Slower, accurate Gaussian blur
158
- return StaySequential(image, VIPS_ACCESS_SEQUENTIAL).gaussblur(sigma);
158
+ return StaySequential(image).gaussblur(sigma);
159
159
  }
160
160
  }
161
161
 
@@ -386,7 +386,7 @@ namespace sharp {
386
386
  pages.reserve(nPages);
387
387
 
388
388
  // Split the image into cropped frames
389
- image = StaySequential(image, VIPS_ACCESS_SEQUENTIAL);
389
+ image = StaySequential(image);
390
390
  for (int i = 0; i < nPages; i++) {
391
391
  pages.push_back(
392
392
  image.extract_area(left, *pageHeight * i + top, width, height));
package/src/pipeline.cc CHANGED
@@ -70,8 +70,8 @@ class PipelineWorker : public Napi::AsyncWorker {
70
70
  // Calculate angle of rotation
71
71
  VipsAngle rotation = VIPS_ANGLE_D0;
72
72
  VipsAngle autoRotation = VIPS_ANGLE_D0;
73
- bool autoFlip = FALSE;
74
- bool autoFlop = FALSE;
73
+ bool autoFlip = false;
74
+ bool autoFlop = false;
75
75
 
76
76
  if (baton->useExifOrientation) {
77
77
  // Rotate and flip image according to Exif orientation
@@ -88,7 +88,7 @@ class PipelineWorker : public Napi::AsyncWorker {
88
88
  baton->rotationAngle != 0.0);
89
89
 
90
90
  if (shouldRotateBefore) {
91
- image = sharp::StaySequential(image, access,
91
+ image = sharp::StaySequential(image,
92
92
  rotation != VIPS_ANGLE_D0 ||
93
93
  autoRotation != VIPS_ANGLE_D0 ||
94
94
  autoFlip ||
@@ -104,17 +104,17 @@ class PipelineWorker : public Napi::AsyncWorker {
104
104
  }
105
105
  if (autoFlip) {
106
106
  image = image.flip(VIPS_DIRECTION_VERTICAL);
107
- autoFlip = FALSE;
107
+ autoFlip = false;
108
108
  } else if (baton->flip) {
109
109
  image = image.flip(VIPS_DIRECTION_VERTICAL);
110
- baton->flip = FALSE;
110
+ baton->flip = false;
111
111
  }
112
112
  if (autoFlop) {
113
113
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
114
- autoFlop = FALSE;
114
+ autoFlop = false;
115
115
  } else if (baton->flop) {
116
116
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
117
- baton->flop = FALSE;
117
+ baton->flop = false;
118
118
  }
119
119
  if (rotation != VIPS_ANGLE_D0) {
120
120
  if (rotation != VIPS_ANGLE_D180) {
@@ -126,7 +126,7 @@ class PipelineWorker : public Napi::AsyncWorker {
126
126
  if (baton->rotationAngle != 0.0) {
127
127
  MultiPageUnsupported(nPages, "Rotate");
128
128
  std::vector<double> background;
129
- std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
129
+ std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, false);
130
130
  image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
131
131
  }
132
132
  }
@@ -134,7 +134,7 @@ class PipelineWorker : public Napi::AsyncWorker {
134
134
  // Trim
135
135
  if (baton->trimThreshold >= 0.0) {
136
136
  MultiPageUnsupported(nPages, "Trim");
137
- image = sharp::StaySequential(image, access);
137
+ image = sharp::StaySequential(image);
138
138
  image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
139
139
  baton->trimOffsetLeft = image.xoffset();
140
140
  baton->trimOffsetTop = image.yoffset();
@@ -337,7 +337,7 @@ class PipelineWorker : public Napi::AsyncWorker {
337
337
  // Convert to sRGB/P3 using embedded profile
338
338
  try {
339
339
  image = image.icc_transform(processingProfile, VImage::option()
340
- ->set("embedded", TRUE)
340
+ ->set("embedded", true)
341
341
  ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
342
342
  ->set("intent", VIPS_INTENT_PERCEPTUAL));
343
343
  } catch(...) {
@@ -357,11 +357,6 @@ class PipelineWorker : public Napi::AsyncWorker {
357
357
  image = sharp::Flatten(image, baton->flattenBackground);
358
358
  }
359
359
 
360
- // Negate the colours in the image
361
- if (baton->negate) {
362
- image = sharp::Negate(image, baton->negateAlpha);
363
- }
364
-
365
360
  // Gamma encoding (darken)
366
361
  if (baton->gamma >= 1 && baton->gamma <= 3) {
367
362
  image = sharp::Gamma(image, 1.0 / baton->gamma);
@@ -397,7 +392,7 @@ class PipelineWorker : public Napi::AsyncWorker {
397
392
  ->set("kernel", baton->kernel));
398
393
  }
399
394
 
400
- image = sharp::StaySequential(image, access,
395
+ image = sharp::StaySequential(image,
401
396
  autoRotation != VIPS_ANGLE_D0 ||
402
397
  baton->flip ||
403
398
  autoFlip ||
@@ -500,7 +495,7 @@ class PipelineWorker : public Napi::AsyncWorker {
500
495
 
501
496
  // Attention-based or Entropy-based crop
502
497
  MultiPageUnsupported(nPages, "Resize strategy");
503
- image = sharp::StaySequential(image, access);
498
+ image = sharp::StaySequential(image);
504
499
  image = image.smartcrop(baton->width, baton->height, VImage::option()
505
500
  ->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
506
501
  ->set("premultiplied", shouldPremultiplyAlpha)
@@ -519,7 +514,7 @@ class PipelineWorker : public Napi::AsyncWorker {
519
514
  // Rotate post-extract non-90 angle
520
515
  if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
521
516
  MultiPageUnsupported(nPages, "Rotate");
522
- image = sharp::StaySequential(image, access);
517
+ image = sharp::StaySequential(image);
523
518
  std::vector<double> background;
524
519
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
525
520
  image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
@@ -543,7 +538,7 @@ class PipelineWorker : public Napi::AsyncWorker {
543
538
  // Affine transform
544
539
  if (!baton->affineMatrix.empty()) {
545
540
  MultiPageUnsupported(nPages, "Affine");
546
- image = sharp::StaySequential(image, access);
541
+ image = sharp::StaySequential(image);
547
542
  std::vector<double> background;
548
543
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
549
544
  vips::VInterpolate interp = vips::VInterpolate::new_from_name(
@@ -566,6 +561,7 @@ class PipelineWorker : public Napi::AsyncWorker {
566
561
  std::vector<double> background;
567
562
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha);
568
563
 
564
+ image = sharp::StaySequential(image, nPages > 1);
569
565
  image = nPages > 1
570
566
  ? sharp::EmbedMultiPage(image,
571
567
  baton->extendLeft, baton->extendTop, baton->width, baton->height,
@@ -574,7 +570,7 @@ class PipelineWorker : public Napi::AsyncWorker {
574
570
  VImage::option()->set("extend", baton->extendWith)->set("background", background));
575
571
  } else {
576
572
  std::vector<double> ignoredBackground(1);
577
- image = sharp::StaySequential(image, baton->input->access);
573
+ image = sharp::StaySequential(image);
578
574
  image = nPages > 1
579
575
  ? sharp::EmbedMultiPage(image,
580
576
  baton->extendLeft, baton->extendTop, baton->width, baton->height,
@@ -670,7 +666,7 @@ class PipelineWorker : public Napi::AsyncWorker {
670
666
  if (across != 0 || down != 0) {
671
667
  int left;
672
668
  int top;
673
- compositeImage = sharp::StaySequential(compositeImage, access).replicate(across, down);
669
+ compositeImage = sharp::StaySequential(compositeImage).replicate(across, down);
674
670
  if (composite->hasOffset) {
675
671
  std::tie(left, top) = sharp::CalculateCrop(
676
672
  compositeImage.width(), compositeImage.height(), image.width(), image.height(),
@@ -728,13 +724,13 @@ class PipelineWorker : public Napi::AsyncWorker {
728
724
 
729
725
  // Apply normalisation - stretch luminance to cover full dynamic range
730
726
  if (baton->normalise) {
731
- image = sharp::StaySequential(image, access);
727
+ image = sharp::StaySequential(image);
732
728
  image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
733
729
  }
734
730
 
735
731
  // Apply contrast limiting adaptive histogram equalization (CLAHE)
736
732
  if (baton->claheWidth != 0 && baton->claheHeight != 0) {
737
- image = sharp::StaySequential(image, access);
733
+ image = sharp::StaySequential(image);
738
734
  image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
739
735
  }
740
736
 
@@ -780,7 +776,7 @@ class PipelineWorker : public Napi::AsyncWorker {
780
776
  if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK &&
781
777
  baton->withIccProfile.empty() && sharp::HasProfile(image)) {
782
778
  image = image.icc_transform(processingProfile, VImage::option()
783
- ->set("embedded", TRUE)
779
+ ->set("embedded", true)
784
780
  ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
785
781
  ->set("intent", VIPS_INTENT_PERCEPTUAL));
786
782
  }
@@ -811,7 +807,7 @@ class PipelineWorker : public Napi::AsyncWorker {
811
807
  try {
812
808
  image = image.icc_transform(const_cast<char*>(baton->withIccProfile.data()), VImage::option()
813
809
  ->set("input_profile", processingProfile)
814
- ->set("embedded", TRUE)
810
+ ->set("embedded", true)
815
811
  ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
816
812
  ->set("intent", VIPS_INTENT_PERCEPTUAL));
817
813
  } catch(...) {
@@ -820,6 +816,12 @@ class PipelineWorker : public Napi::AsyncWorker {
820
816
  } else if (baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) {
821
817
  image = sharp::SetProfile(image, inputProfile);
822
818
  }
819
+
820
+ // Negate the colours in the image
821
+ if (baton->negate) {
822
+ image = sharp::Negate(image, baton->negateAlpha);
823
+ }
824
+
823
825
  // Override EXIF Orientation tag
824
826
  if (baton->withMetadataOrientation != -1) {
825
827
  image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
@@ -1004,7 +1006,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1004
1006
  if (!sharp::HasAlpha(image)) {
1005
1007
  baton->tileBackground.pop_back();
1006
1008
  }
1007
- image = sharp::StaySequential(image, access, baton->tileAngle != 0);
1009
+ image = sharp::StaySequential(image, baton->tileAngle != 0);
1008
1010
  vips::VOption *options = BuildOptionsDZ(baton);
1009
1011
  VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
1010
1012
  baton->bufferOut = static_cast<char*>(area->data);
@@ -1206,7 +1208,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1206
1208
  if (!sharp::HasAlpha(image)) {
1207
1209
  baton->tileBackground.pop_back();
1208
1210
  }
1209
- image = sharp::StaySequential(image, access, baton->tileAngle != 0);
1211
+ image = sharp::StaySequential(image, baton->tileAngle != 0);
1210
1212
  vips::VOption *options = BuildOptionsDZ(baton);
1211
1213
  image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
1212
1214
  baton->formatOut = "dz";
@@ -1338,16 +1340,16 @@ class PipelineWorker : public Napi::AsyncWorker {
1338
1340
  std::tuple<VipsAngle, bool, bool>
1339
1341
  CalculateExifRotationAndFlip(int const exifOrientation) {
1340
1342
  VipsAngle rotate = VIPS_ANGLE_D0;
1341
- bool flip = FALSE;
1342
- bool flop = FALSE;
1343
+ bool flip = false;
1344
+ bool flop = false;
1343
1345
  switch (exifOrientation) {
1344
1346
  case 6: rotate = VIPS_ANGLE_D90; break;
1345
1347
  case 3: rotate = VIPS_ANGLE_D180; break;
1346
1348
  case 8: rotate = VIPS_ANGLE_D270; break;
1347
- case 2: flop = TRUE; break; // flop 1
1348
- case 7: flip = TRUE; rotate = VIPS_ANGLE_D90; break; // flip 6
1349
- case 4: flop = TRUE; rotate = VIPS_ANGLE_D180; break; // flop 3
1350
- case 5: flip = TRUE; rotate = VIPS_ANGLE_D270; break; // flip 8
1349
+ case 2: flop = true; break; // flop 1
1350
+ case 7: flip = true; rotate = VIPS_ANGLE_D90; break; // flip 6
1351
+ case 4: flop = true; rotate = VIPS_ANGLE_D180; break; // flop 3
1352
+ case 5: flip = true; rotate = VIPS_ANGLE_D270; break; // flip 8
1351
1353
  }
1352
1354
  return std::make_tuple(rotate, flip, flop);
1353
1355
  }
@@ -1395,7 +1397,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1395
1397
  std::string suffix;
1396
1398
  if (baton->tileFormat == "png") {
1397
1399
  std::vector<std::pair<std::string, std::string>> options {
1398
- {"interlace", baton->pngProgressive ? "TRUE" : "FALSE"},
1400
+ {"interlace", baton->pngProgressive ? "true" : "false"},
1399
1401
  {"compression", std::to_string(baton->pngCompressionLevel)},
1400
1402
  {"filter", baton->pngAdaptiveFiltering ? "all" : "none"}
1401
1403
  };
@@ -1404,25 +1406,25 @@ class PipelineWorker : public Napi::AsyncWorker {
1404
1406
  std::vector<std::pair<std::string, std::string>> options {
1405
1407
  {"Q", std::to_string(baton->webpQuality)},
1406
1408
  {"alpha_q", std::to_string(baton->webpAlphaQuality)},
1407
- {"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
1408
- {"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
1409
- {"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
1409
+ {"lossless", baton->webpLossless ? "true" : "false"},
1410
+ {"near_lossless", baton->webpNearLossless ? "true" : "false"},
1411
+ {"smart_subsample", baton->webpSmartSubsample ? "true" : "false"},
1410
1412
  {"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
1411
- {"min_size", baton->webpMinSize ? "TRUE" : "FALSE"},
1412
- {"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
1413
+ {"min_size", baton->webpMinSize ? "true" : "false"},
1414
+ {"mixed", baton->webpMixed ? "true" : "false"},
1413
1415
  {"effort", std::to_string(baton->webpEffort)}
1414
1416
  };
1415
1417
  suffix = AssembleSuffixString(".webp", options);
1416
1418
  } else {
1417
1419
  std::vector<std::pair<std::string, std::string>> options {
1418
1420
  {"Q", std::to_string(baton->jpegQuality)},
1419
- {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
1421
+ {"interlace", baton->jpegProgressive ? "true" : "false"},
1420
1422
  {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
1421
- {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
1423
+ {"trellis_quant", baton->jpegTrellisQuantisation ? "true" : "false"},
1422
1424
  {"quant_table", std::to_string(baton->jpegQuantisationTable)},
1423
- {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
1424
- {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
1425
- {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
1425
+ {"overshoot_deringing", baton->jpegOvershootDeringing ? "true": "false"},
1426
+ {"optimize_scans", baton->jpegOptimiseScans ? "true": "false"},
1427
+ {"optimize_coding", baton->jpegOptimiseCoding ? "true": "false"}
1426
1428
  };
1427
1429
  std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
1428
1430
  suffix = AssembleSuffixString(extname, options);