@revizly/sharp 0.33.3-revizly4 → 0.33.4-revizly1

Sign up to get free protection for your applications and to get access to all the features.
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-revizly4",
4
+ "version": "0.33.4-revizly1",
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.2-revizly8",
147
- "@revizly/sharp-linux-x64": "0.33.2-revizly8"
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.3-revizly1",
147
+ "@revizly/sharp-linux-x64": "0.33.3-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);