sharp 0.33.4-rc.0 → 0.33.5-rc.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.
package/install/check.js CHANGED
@@ -18,7 +18,7 @@ try {
18
18
  }
19
19
  try {
20
20
  const gyp = require('node-gyp');
21
- log(`Found node-gyp version ${gyp().version}`);
21
+ log(`Found node-gyp ${gyp().version}`);
22
22
  } catch (err) {
23
23
  log('Please add node-gyp to your dependencies');
24
24
  return;
@@ -30,7 +30,7 @@ try {
30
30
  }
31
31
  };
32
32
 
33
- if (useGlobalLibvips()) {
33
+ if (useGlobalLibvips(log)) {
34
34
  buildFromSource(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
35
35
  } else if (process.env.npm_config_build_from_source) {
36
36
  buildFromSource('Detected --build-from-source flag');
@@ -226,6 +226,8 @@ const Sharp = function (input, options) {
226
226
  negateAlpha: true,
227
227
  medianSize: 0,
228
228
  blurSigma: 0,
229
+ precision: 'integer',
230
+ minAmpl: 0.2,
229
231
  sharpenSigma: 0,
230
232
  sharpenM1: 1,
231
233
  sharpenM2: 2,
package/lib/index.d.ts CHANGED
@@ -464,7 +464,7 @@ declare namespace sharp {
464
464
  * @throws {Error} Invalid parameters
465
465
  * @returns A sharp instance that can be used to chain operations
466
466
  */
467
- blur(sigma?: number | boolean): Sharp;
467
+ blur(sigma?: number | boolean | BlurOptions): Sharp;
468
468
 
469
469
  /**
470
470
  * Merge alpha transparency channel, if any, with background.
@@ -571,11 +571,11 @@ declare namespace sharp {
571
571
 
572
572
  /**
573
573
  * Recomb the image with the specified matrix.
574
- * @param inputMatrix 3x3 Recombination matrix
574
+ * @param inputMatrix 3x3 Recombination matrix or 4x4 Recombination matrix
575
575
  * @throws {Error} Invalid parameters
576
576
  * @returns A sharp instance that can be used to chain operations
577
577
  */
578
- recomb(inputMatrix: Matrix3x3): Sharp;
578
+ recomb(inputMatrix: Matrix3x3 | Matrix4x4): Sharp;
579
579
 
580
580
  /**
581
581
  * Transforms the image using brightness, saturation, hue rotation and lightness.
@@ -1108,6 +1108,8 @@ declare namespace sharp {
1108
1108
  resolutionUnit?: 'inch' | 'cm' | undefined;
1109
1109
  /** String containing format for images loaded via *magick */
1110
1110
  formatMagick?: string | undefined;
1111
+ /** Array of keyword/text pairs representing PNG text blocks, if present. */
1112
+ comments?: CommentsMetadata[] | undefined;
1111
1113
  }
1112
1114
 
1113
1115
  interface LevelMetadata {
@@ -1115,6 +1117,11 @@ declare namespace sharp {
1115
1117
  height: number;
1116
1118
  }
1117
1119
 
1120
+ interface CommentsMetadata {
1121
+ keyword: string;
1122
+ text: string;
1123
+ }
1124
+
1118
1125
  interface Stats {
1119
1126
  /** Array of channel statistics for each channel in the image. */
1120
1127
  channels: ChannelStats[];
@@ -1335,6 +1342,17 @@ declare namespace sharp {
1335
1342
  background?: Color | undefined;
1336
1343
  }
1337
1344
 
1345
+ type Precision = 'integer' | 'float' | 'approximate';
1346
+
1347
+ interface BlurOptions {
1348
+ /** A value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2` */
1349
+ sigma: number;
1350
+ /** A value between 0.001 and 1. A smaller value will generate a larger, more accurate mask. */
1351
+ minAmplitude?: number;
1352
+ /** How accurate the operation should be, one of: integer, float, approximate. (optional, default "integer") */
1353
+ precision?: Precision | undefined;
1354
+ }
1355
+
1338
1356
  interface FlattenOptions {
1339
1357
  /** background colour, parsed by the color module, defaults to black. (optional, default {r:0,g:0,b:0}) */
1340
1358
  background?: Color | undefined;
@@ -1730,6 +1748,7 @@ declare namespace sharp {
1730
1748
 
1731
1749
  type Matrix2x2 = [[number, number], [number, number]];
1732
1750
  type Matrix3x3 = [[number, number, number], [number, number, number], [number, number, number]];
1751
+ type Matrix4x4 = [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]];
1733
1752
  }
1734
1753
 
1735
1754
  export = sharp;
package/lib/input.js CHANGED
@@ -450,6 +450,7 @@ function _isStreamInput () {
450
450
  * - `xmp`: Buffer containing raw XMP data, if present
451
451
  * - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
452
452
  * - `formatMagick`: String containing format for images loaded via *magick
453
+ * - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
453
454
  *
454
455
  * @example
455
456
  * const metadata = await sharp(input).metadata();
package/lib/libvips.js CHANGED
@@ -10,10 +10,10 @@ const semverGreaterThanOrEqualTo = require('semver/functions/gte');
10
10
  const semverSatisfies = require('semver/functions/satisfies');
11
11
  const detectLibc = require('detect-libc');
12
12
 
13
- const { engines, optionalDependencies } = require('../package.json');
13
+ const { config, engines, optionalDependencies } = require('../package.json');
14
14
 
15
15
  const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
16
- engines.libvips;
16
+ config.libvips;
17
17
  const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
18
18
 
19
19
  const prebuiltPlatforms = [
@@ -162,21 +162,23 @@ const pkgConfigPath = () => {
162
162
  }
163
163
  };
164
164
 
165
- const skipSearch = (status, reason) => {
166
- log(`Detected ${reason}, skipping search for globally-installed libvips`);
165
+ const skipSearch = (status, reason, logger) => {
166
+ if (logger) {
167
+ logger(`Detected ${reason}, skipping search for globally-installed libvips`);
168
+ }
167
169
  return status;
168
170
  };
169
171
 
170
- const useGlobalLibvips = () => {
172
+ const useGlobalLibvips = (logger) => {
171
173
  if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
172
- return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS');
174
+ return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS', logger);
173
175
  }
174
176
  if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
175
- return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS');
177
+ return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
176
178
  }
177
179
  /* istanbul ignore next */
178
180
  if (isRosetta()) {
179
- return skipSearch(false, 'Rosetta');
181
+ return skipSearch(false, 'Rosetta', logger);
180
182
  }
181
183
  const globalVipsVersion = globalLibvipsVersion();
182
184
  return !!globalVipsVersion && /* istanbul ignore next */
package/lib/operation.js CHANGED
@@ -6,6 +6,17 @@
6
6
  const color = require('color');
7
7
  const is = require('./is');
8
8
 
9
+ /**
10
+ * How accurate an operation should be.
11
+ * @member
12
+ * @private
13
+ */
14
+ const vipsPrecision = {
15
+ integer: 'integer',
16
+ float: 'float',
17
+ approximate: 'approximate'
18
+ };
19
+
9
20
  /**
10
21
  * Rotate the output image by either an explicit angle
11
22
  * or auto-orient based on the EXIF `Orientation` tag.
@@ -367,23 +378,51 @@ function median (size) {
367
378
  * .blur(5)
368
379
  * .toBuffer();
369
380
  *
370
- * @param {number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
381
+ * @param {Object|number|Boolean} [options]
382
+ * @param {number} [options.sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
383
+ * @param {string} [options.precision='integer'] How accurate the operation should be, one of: integer, float, approximate.
384
+ * @param {number} [options.minAmplitude=0.2] A value between 0.001 and 1. A smaller value will generate a larger, more accurate mask.
371
385
  * @returns {Sharp}
372
386
  * @throws {Error} Invalid parameters
373
387
  */
374
- function blur (sigma) {
375
- if (!is.defined(sigma)) {
388
+ function blur (options) {
389
+ let sigma;
390
+ if (is.number(options)) {
391
+ sigma = options;
392
+ } else if (is.plainObject(options)) {
393
+ if (!is.number(options.sigma)) {
394
+ throw is.invalidParameterError('options.sigma', 'number between 0.3 and 1000', sigma);
395
+ }
396
+ sigma = options.sigma;
397
+ if ('precision' in options) {
398
+ if (is.string(vipsPrecision[options.precision])) {
399
+ this.options.precision = vipsPrecision[options.precision];
400
+ } else {
401
+ throw is.invalidParameterError('precision', 'one of: integer, float, approximate', options.precision);
402
+ }
403
+ }
404
+ if ('minAmplitude' in options) {
405
+ if (is.number(options.minAmplitude) && is.inRange(options.minAmplitude, 0.001, 1)) {
406
+ this.options.minAmpl = options.minAmplitude;
407
+ } else {
408
+ throw is.invalidParameterError('minAmplitude', 'number between 0.001 and 1', options.minAmplitude);
409
+ }
410
+ }
411
+ }
412
+
413
+ if (!is.defined(options)) {
376
414
  // No arguments: default to mild blur
377
415
  this.options.blurSigma = -1;
378
- } else if (is.bool(sigma)) {
416
+ } else if (is.bool(options)) {
379
417
  // Boolean argument: apply mild blur?
380
- this.options.blurSigma = sigma ? -1 : 0;
418
+ this.options.blurSigma = options ? -1 : 0;
381
419
  } else if (is.number(sigma) && is.inRange(sigma, 0.3, 1000)) {
382
420
  // Numeric argument: specific sigma
383
421
  this.options.blurSigma = sigma;
384
422
  } else {
385
423
  throw is.invalidParameterError('sigma', 'number between 0.3 and 1000', sigma);
386
424
  }
425
+
387
426
  return this;
388
427
  }
389
428
 
@@ -787,24 +826,22 @@ function linear (a, b) {
787
826
  * // With this example input, a sepia filter has been applied
788
827
  * });
789
828
  *
790
- * @param {Array<Array<number>>} inputMatrix - 3x3 Recombination matrix
829
+ * @param {Array<Array<number>>} inputMatrix - 3x3 or 4x4 Recombination matrix
791
830
  * @returns {Sharp}
792
831
  * @throws {Error} Invalid parameters
793
832
  */
794
833
  function recomb (inputMatrix) {
795
- if (!Array.isArray(inputMatrix) || inputMatrix.length !== 3 ||
796
- inputMatrix[0].length !== 3 ||
797
- inputMatrix[1].length !== 3 ||
798
- inputMatrix[2].length !== 3
799
- ) {
800
- // must pass in a kernel
801
- throw new Error('Invalid recombination matrix');
834
+ if (!Array.isArray(inputMatrix)) {
835
+ throw is.invalidParameterError('inputMatrix', 'array', inputMatrix);
836
+ }
837
+ if (inputMatrix.length !== 3 && inputMatrix.length !== 4) {
838
+ throw is.invalidParameterError('inputMatrix', '3x3 or 4x4 array', inputMatrix.length);
839
+ }
840
+ const recombMatrix = inputMatrix.flat().map(Number);
841
+ if (recombMatrix.length !== 9 && recombMatrix.length !== 16) {
842
+ throw is.invalidParameterError('inputMatrix', 'cardinality of 9 or 16', recombMatrix.length);
802
843
  }
803
- this.options.recombMatrix = [
804
- inputMatrix[0][0], inputMatrix[0][1], inputMatrix[0][2],
805
- inputMatrix[1][0], inputMatrix[1][1], inputMatrix[1][2],
806
- inputMatrix[2][0], inputMatrix[2][1], inputMatrix[2][2]
807
- ].map(Number);
844
+ this.options.recombMatrix = recombMatrix;
808
845
  return this;
809
846
  }
810
847
 
package/lib/output.js CHANGED
@@ -65,6 +65,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
65
65
  * `channels` and `premultiplied` (indicating if premultiplication was used).
66
66
  * When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
67
67
  * When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region.
68
+ * Animated output will also contain `pageHeight` and `pages`.
68
69
  * May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
69
70
  * @returns {Promise<Object>} - when no callback is provided
70
71
  * @throws {Error} Invalid parameters
@@ -109,6 +110,7 @@ function toFile (fileOut, callback) {
109
110
  * - `info` contains the output image `format`, `size` (bytes), `width`, `height`,
110
111
  * `channels` and `premultiplied` (indicating if premultiplication was used).
111
112
  * When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
113
+ * Animated output will also contain `pageHeight` and `pages`.
112
114
  * May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
113
115
  *
114
116
  * A `Promise` is returned when `callback` is not provided.
package/lib/sharp.js CHANGED
@@ -73,9 +73,9 @@ if (sharp) {
73
73
  }
74
74
  if (isLinux && /(symbol not found|CXXABI_)/i.test(messages)) {
75
75
  try {
76
- const { engines } = require(`@img/sharp-libvips-${runtimePlatform}/package`);
76
+ const { config } = require(`@img/sharp-libvips-${runtimePlatform}/package`);
77
77
  const libcFound = `${familySync()} ${versionSync()}`;
78
- const libcRequires = `${engines.musl ? 'musl' : 'glibc'} ${engines.musl || engines.glibc}`;
78
+ const libcRequires = `${config.musl ? 'musl' : 'glibc'} ${config.musl || config.glibc}`;
79
79
  help.push(
80
80
  '- Update your OS:',
81
81
  ` Found ${libcFound}`,
package/lib/utility.js CHANGED
@@ -75,6 +75,13 @@ if (!libvipsVersion.isGlobal) {
75
75
  }
76
76
  versions.sharp = require('../package.json').version;
77
77
 
78
+ /* istanbul ignore next */
79
+ if (versions.heif && format.heif) {
80
+ // Prebuilt binaries provide AV1
81
+ format.heif.input.fileSuffix = ['.avif'];
82
+ format.heif.output.alias = ['avif'];
83
+ }
84
+
78
85
  /**
79
86
  * Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
80
87
  * Existing entries in the cache will be trimmed after any change in limits.
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.33.4-rc.0",
4
+ "version": "0.33.5-rc.0",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://sharp.pixelplumbing.com",
7
7
  "contributors": [
@@ -88,7 +88,8 @@
88
88
  "Mart Jansink <m.jansink@gmail.com>",
89
89
  "Lachlan Newman <lachnewman007@gmail.com>",
90
90
  "Dennis Beatty <dennis@dcbeatty.com>",
91
- "Ingvar Stepanyan <me@rreverser.com>"
91
+ "Ingvar Stepanyan <me@rreverser.com>",
92
+ "Don Denton <don@happycollision.com>"
92
93
  ],
93
94
  "scripts": {
94
95
  "install": "node install/check",
@@ -138,56 +139,58 @@
138
139
  "dependencies": {
139
140
  "color": "^4.2.3",
140
141
  "detect-libc": "^2.0.3",
141
- "semver": "^7.6.0"
142
+ "semver": "^7.6.3"
142
143
  },
143
144
  "optionalDependencies": {
144
- "@img/sharp-darwin-arm64": "0.33.4-rc.0",
145
- "@img/sharp-darwin-x64": "0.33.4-rc.0",
146
- "@img/sharp-libvips-darwin-arm64": "1.0.2",
147
- "@img/sharp-libvips-darwin-x64": "1.0.2",
148
- "@img/sharp-libvips-linux-arm": "1.0.2",
149
- "@img/sharp-libvips-linux-arm64": "1.0.2",
150
- "@img/sharp-libvips-linux-s390x": "1.0.2",
151
- "@img/sharp-libvips-linux-x64": "1.0.2",
152
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.2",
153
- "@img/sharp-libvips-linuxmusl-x64": "1.0.2",
154
- "@img/sharp-linux-arm": "0.33.4-rc.0",
155
- "@img/sharp-linux-arm64": "0.33.4-rc.0",
156
- "@img/sharp-linux-s390x": "0.33.4-rc.0",
157
- "@img/sharp-linux-x64": "0.33.4-rc.0",
158
- "@img/sharp-linuxmusl-arm64": "0.33.4-rc.0",
159
- "@img/sharp-linuxmusl-x64": "0.33.4-rc.0",
160
- "@img/sharp-wasm32": "0.33.4-rc.0",
161
- "@img/sharp-win32-ia32": "0.33.4-rc.0",
162
- "@img/sharp-win32-x64": "0.33.4-rc.0"
145
+ "@img/sharp-darwin-arm64": "0.33.5-rc.0",
146
+ "@img/sharp-darwin-x64": "0.33.5-rc.0",
147
+ "@img/sharp-libvips-darwin-arm64": "1.0.4",
148
+ "@img/sharp-libvips-darwin-x64": "1.0.4",
149
+ "@img/sharp-libvips-linux-arm": "1.0.5",
150
+ "@img/sharp-libvips-linux-arm64": "1.0.4",
151
+ "@img/sharp-libvips-linux-s390x": "1.0.4",
152
+ "@img/sharp-libvips-linux-x64": "1.0.4",
153
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
154
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
155
+ "@img/sharp-linux-arm": "0.33.5-rc.0",
156
+ "@img/sharp-linux-arm64": "0.33.5-rc.0",
157
+ "@img/sharp-linux-s390x": "0.33.5-rc.0",
158
+ "@img/sharp-linux-x64": "0.33.5-rc.0",
159
+ "@img/sharp-linuxmusl-arm64": "0.33.5-rc.0",
160
+ "@img/sharp-linuxmusl-x64": "0.33.5-rc.0",
161
+ "@img/sharp-wasm32": "0.33.5-rc.0",
162
+ "@img/sharp-win32-ia32": "0.33.5-rc.0",
163
+ "@img/sharp-win32-x64": "0.33.5-rc.0"
163
164
  },
164
165
  "devDependencies": {
165
- "@emnapi/runtime": "^1.1.1",
166
- "@img/sharp-libvips-dev": "1.0.2",
167
- "@img/sharp-libvips-dev-wasm32": "1.0.3",
168
- "@img/sharp-libvips-win32-ia32": "1.0.2",
169
- "@img/sharp-libvips-win32-x64": "1.0.2",
166
+ "@emnapi/runtime": "^1.2.0",
167
+ "@img/sharp-libvips-dev": "1.0.4",
168
+ "@img/sharp-libvips-dev-wasm32": "1.0.5",
169
+ "@img/sharp-libvips-win32-ia32": "1.0.4",
170
+ "@img/sharp-libvips-win32-x64": "1.0.4",
170
171
  "@types/node": "*",
171
172
  "async": "^3.2.5",
172
173
  "cc": "^3.0.1",
173
- "emnapi": "^1.1.1",
174
+ "emnapi": "^1.2.0",
174
175
  "exif-reader": "^2.0.1",
175
176
  "extract-zip": "^2.0.1",
176
177
  "icc": "^3.0.0",
177
- "jsdoc-to-markdown": "^8.0.1",
178
+ "jsdoc-to-markdown": "^8.0.3",
178
179
  "license-checker": "^25.0.1",
179
- "mocha": "^10.4.0",
180
- "node-addon-api": "^8.0.0",
181
- "nyc": "^15.1.0",
180
+ "mocha": "^10.7.3",
181
+ "node-addon-api": "^8.1.0",
182
+ "nyc": "^17.0.0",
182
183
  "prebuild": "^13.0.1",
183
184
  "semistandard": "^17.0.0",
184
185
  "tar-fs": "^3.0.6",
185
- "tsd": "^0.31.0"
186
+ "tsd": "^0.31.1"
186
187
  },
187
188
  "license": "Apache-2.0",
188
189
  "engines": {
189
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
190
- "libvips": ">=8.15.2"
190
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
191
+ },
192
+ "config": {
193
+ "libvips": ">=8.15.3"
191
194
  },
192
195
  "funding": {
193
196
  "url": "https://opencollective.com/libvips"
package/src/binding.gyp CHANGED
@@ -192,7 +192,7 @@
192
192
  '-Oz',
193
193
  '-sALLOW_MEMORY_GROWTH',
194
194
  '-sENVIRONMENT=node',
195
- '-sEXPORTED_FUNCTIONS=["_vips_shutdown", "_uv_library_shutdown"]',
195
+ '-sEXPORTED_FUNCTIONS=["emnapiInit", "_vips_shutdown", "_uv_library_shutdown"]',
196
196
  '-sNODERAWFS',
197
197
  '-sTEXTDECODER=0',
198
198
  '-sWASM_ASYNC_COMPILATION=0',
package/src/common.h CHANGED
@@ -16,8 +16,8 @@
16
16
 
17
17
  #if (VIPS_MAJOR_VERSION < 8) || \
18
18
  (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 15) || \
19
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 2)
20
- #error "libvips version 8.15.2+ is required - please see https://sharp.pixelplumbing.com/install"
19
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 3)
20
+ #error "libvips version 8.15.3+ is required - please see https://sharp.pixelplumbing.com/install"
21
21
  #endif
22
22
 
23
23
  #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
package/src/metadata.cc CHANGED
@@ -10,6 +10,8 @@
10
10
  #include "common.h"
11
11
  #include "metadata.h"
12
12
 
13
+ static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p);
14
+
13
15
  class MetadataWorker : public Napi::AsyncWorker {
14
16
  public:
15
17
  MetadataWorker(Napi::Function callback, MetadataBaton *baton, Napi::Function debuglog) :
@@ -131,6 +133,8 @@ class MetadataWorker : public Napi::AsyncWorker {
131
133
  memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
132
134
  baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
133
135
  }
136
+ // PNG comments
137
+ vips_image_map(image.get_image(), readPNGComment, &baton->comments);
134
138
  }
135
139
 
136
140
  // Clean up
@@ -246,6 +250,17 @@ class MetadataWorker : public Napi::AsyncWorker {
246
250
  Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
247
251
  baton->tifftagPhotoshopLength, sharp::FreeCallback));
248
252
  }
253
+ if (baton->comments.size() > 0) {
254
+ int i = 0;
255
+ Napi::Array comments = Napi::Array::New(env, baton->comments.size());
256
+ for (auto &c : baton->comments) {
257
+ Napi::Object comment = Napi::Object::New(env);
258
+ comment.Set("keyword", c.first);
259
+ comment.Set("text", c.second);
260
+ comments.Set(i++, comment);
261
+ }
262
+ info.Set("comments", comments);
263
+ }
249
264
  Callback().Call(Receiver().Value(), { env.Null(), info });
250
265
  } else {
251
266
  Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
@@ -285,3 +300,21 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
285
300
 
286
301
  return info.Env().Undefined();
287
302
  }
303
+
304
+ const char *PNG_COMMENT_START = "png-comment-";
305
+ const int PNG_COMMENT_START_LEN = strlen(PNG_COMMENT_START);
306
+
307
+ static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p) {
308
+ MetadataComments *comments = static_cast<MetadataComments *>(p);
309
+
310
+ if (vips_isprefix(PNG_COMMENT_START, field)) {
311
+ const char *keyword = strchr(field + PNG_COMMENT_START_LEN, '-');
312
+ const char *str;
313
+ if (keyword != NULL && !vips_image_get_string(image, field, &str)) {
314
+ keyword++; // Skip the hyphen
315
+ comments->push_back(std::make_pair(keyword, str));
316
+ }
317
+ }
318
+
319
+ return NULL;
320
+ }
package/src/metadata.h CHANGED
@@ -9,6 +9,8 @@
9
9
 
10
10
  #include "./common.h"
11
11
 
12
+ typedef std::vector<std::pair<std::string, std::string>> MetadataComments;
13
+
12
14
  struct MetadataBaton {
13
15
  // Input
14
16
  sharp::InputDescriptor *input;
@@ -47,6 +49,7 @@ struct MetadataBaton {
47
49
  size_t xmpLength;
48
50
  char *tifftagPhotoshop;
49
51
  size_t tifftagPhotoshopLength;
52
+ MetadataComments comments;
50
53
  std::string err;
51
54
 
52
55
  MetadataBaton():
package/src/operations.cc CHANGED
@@ -144,7 +144,7 @@ namespace sharp {
144
144
  /*
145
145
  * Gaussian blur. Use sigma of -1.0 for fast blur.
146
146
  */
147
- VImage Blur(VImage image, double const sigma) {
147
+ VImage Blur(VImage image, double const sigma, VipsPrecision precision, double const minAmpl) {
148
148
  if (sigma == -1.0) {
149
149
  // Fast, mild blur - averages neighbouring pixels
150
150
  VImage blur = VImage::new_matrixv(3, 3,
@@ -155,7 +155,9 @@ namespace sharp {
155
155
  return image.conv(blur);
156
156
  } else {
157
157
  // Slower, accurate Gaussian blur
158
- return StaySequential(image).gaussblur(sigma);
158
+ return StaySequential(image).gaussblur(sigma, VImage::option()
159
+ ->set("precision", precision)
160
+ ->set("min_ampl", minAmpl));
159
161
  }
160
162
  }
161
163
 
@@ -164,10 +166,10 @@ namespace sharp {
164
166
  */
165
167
  VImage Convolve(VImage image, int const width, int const height,
166
168
  double const scale, double const offset,
167
- std::unique_ptr<double[]> const &kernel_v
169
+ std::vector<double> const &kernel_v
168
170
  ) {
169
171
  VImage kernel = VImage::new_from_memory(
170
- kernel_v.get(),
172
+ static_cast<void*>(const_cast<double*>(kernel_v.data())),
171
173
  width * height * sizeof(double),
172
174
  width,
173
175
  height,
@@ -183,19 +185,21 @@ namespace sharp {
183
185
  * Recomb with a Matrix of the given bands/channel size.
184
186
  * Eg. RGB will be a 3x3 matrix.
185
187
  */
186
- VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
187
- double *m = matrix.get();
188
+ VImage Recomb(VImage image, std::vector<double> const& matrix) {
189
+ double* m = const_cast<double*>(matrix.data());
188
190
  image = image.colourspace(VIPS_INTERPRETATION_sRGB);
189
- return image
190
- .recomb(image.bands() == 3
191
- ? VImage::new_from_memory(
192
- m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE
193
- )
194
- : VImage::new_matrixv(4, 4,
195
- m[0], m[1], m[2], 0.0,
196
- m[3], m[4], m[5], 0.0,
197
- m[6], m[7], m[8], 0.0,
198
- 0.0, 0.0, 0.0, 1.0));
191
+ if (matrix.size() == 9) {
192
+ return image
193
+ .recomb(image.bands() == 3
194
+ ? VImage::new_matrix(3, 3, m, 9)
195
+ : VImage::new_matrixv(4, 4,
196
+ m[0], m[1], m[2], 0.0,
197
+ m[3], m[4], m[5], 0.0,
198
+ m[6], m[7], m[8], 0.0,
199
+ 0.0, 0.0, 0.0, 1.0));
200
+ } else {
201
+ return image.recomb(VImage::new_matrix(4, 4, m, 16));
202
+ }
199
203
  }
200
204
 
201
205
  VImage Modulate(VImage image, double const brightness, double const saturation,
package/src/operations.h CHANGED
@@ -47,13 +47,13 @@ namespace sharp {
47
47
  /*
48
48
  * Gaussian blur. Use sigma of -1.0 for fast blur.
49
49
  */
50
- VImage Blur(VImage image, double const sigma);
50
+ VImage Blur(VImage image, double const sigma, VipsPrecision precision, double const minAmpl);
51
51
 
52
52
  /*
53
53
  * Convolution with a kernel.
54
54
  */
55
55
  VImage Convolve(VImage image, int const width, int const height,
56
- double const scale, double const offset, std::unique_ptr<double[]> const &kernel_v);
56
+ double const scale, double const offset, std::vector<double> const &kernel_v);
57
57
 
58
58
  /*
59
59
  * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
@@ -95,7 +95,7 @@ namespace sharp {
95
95
  * Recomb with a Matrix of the given bands/channel size.
96
96
  * Eg. RGB will be a 3x3 matrix.
97
97
  */
98
- VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
98
+ VImage Recomb(VImage image, std::vector<double> const &matrix);
99
99
 
100
100
  /*
101
101
  * Modulate brightness, saturation, hue and lightness
package/src/pipeline.cc CHANGED
@@ -325,6 +325,7 @@ class PipelineWorker : public Napi::AsyncWorker {
325
325
  if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->withIccProfile.empty()) {
326
326
  // Cache input profile for use with output
327
327
  inputProfile = sharp::GetProfile(image);
328
+ baton->input->ignoreIcc = true;
328
329
  }
329
330
  char const *processingProfile = image.interpretation() == VIPS_INTERPRETATION_RGB16 ? "p3" : "srgb";
330
331
  if (
@@ -592,7 +593,7 @@ class PipelineWorker : public Napi::AsyncWorker {
592
593
 
593
594
  // Blur
594
595
  if (shouldBlur) {
595
- image = sharp::Blur(image, baton->blurSigma);
596
+ image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
596
597
  }
597
598
 
598
599
  // Unflatten the image
@@ -609,7 +610,7 @@ class PipelineWorker : public Napi::AsyncWorker {
609
610
  }
610
611
 
611
612
  // Recomb
612
- if (baton->recombMatrix != NULL) {
613
+ if (!baton->recombMatrix.empty()) {
613
614
  image = sharp::Recomb(image, baton->recombMatrix);
614
615
  }
615
616
 
@@ -849,6 +850,11 @@ class PipelineWorker : public Napi::AsyncWorker {
849
850
  image = sharp::SetAnimationProperties(
850
851
  image, nPages, targetPageHeight, baton->delay, baton->loop);
851
852
 
853
+ if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
854
+ baton->pageHeightOut = image.get_int(VIPS_META_PAGE_HEIGHT);
855
+ baton->pagesOut = image.get_int(VIPS_META_N_PAGES);
856
+ }
857
+
852
858
  // Output
853
859
  sharp::SetTimeout(image, baton->timeoutSeconds);
854
860
  if (baton->fileOut.empty()) {
@@ -1284,6 +1290,10 @@ class PipelineWorker : public Napi::AsyncWorker {
1284
1290
  if (baton->input->textAutofitDpi) {
1285
1291
  info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
1286
1292
  }
1293
+ if (baton->pageHeightOut) {
1294
+ info.Set("pageHeight", static_cast<int32_t>(baton->pageHeightOut));
1295
+ info.Set("pages", static_cast<int32_t>(baton->pagesOut));
1296
+ }
1287
1297
 
1288
1298
  if (baton->bufferOutLength > 0) {
1289
1299
  // Add buffer size to info
@@ -1532,6 +1542,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1532
1542
  baton->negate = sharp::AttrAsBool(options, "negate");
1533
1543
  baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
1534
1544
  baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
1545
+ baton->precision = sharp::AttrAsEnum<VipsPrecision>(options, "precision", VIPS_TYPE_PRECISION);
1546
+ baton->minAmpl = sharp::AttrAsDouble(options, "minAmpl");
1535
1547
  baton->brightness = sharp::AttrAsDouble(options, "brightness");
1536
1548
  baton->saturation = sharp::AttrAsDouble(options, "saturation");
1537
1549
  baton->hue = sharp::AttrAsInt32(options, "hue");
@@ -1597,17 +1609,18 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1597
1609
  baton->convKernelScale = sharp::AttrAsDouble(kernel, "scale");
1598
1610
  baton->convKernelOffset = sharp::AttrAsDouble(kernel, "offset");
1599
1611
  size_t const kernelSize = static_cast<size_t>(baton->convKernelWidth * baton->convKernelHeight);
1600
- baton->convKernel = std::unique_ptr<double[]>(new double[kernelSize]);
1612
+ baton->convKernel.resize(kernelSize);
1601
1613
  Napi::Array kdata = kernel.Get("kernel").As<Napi::Array>();
1602
1614
  for (unsigned int i = 0; i < kernelSize; i++) {
1603
1615
  baton->convKernel[i] = sharp::AttrAsDouble(kdata, i);
1604
1616
  }
1605
1617
  }
1606
1618
  if (options.Has("recombMatrix")) {
1607
- baton->recombMatrix = std::unique_ptr<double[]>(new double[9]);
1608
1619
  Napi::Array recombMatrix = options.Get("recombMatrix").As<Napi::Array>();
1609
- for (unsigned int i = 0; i < 9; i++) {
1610
- baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
1620
+ unsigned int matrixElements = recombMatrix.Length();
1621
+ baton->recombMatrix.resize(matrixElements);
1622
+ for (unsigned int i = 0; i < matrixElements; i++) {
1623
+ baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
1611
1624
  }
1612
1625
  }
1613
1626
  baton->colourspacePipeline = sharp::AttrAsEnum<VipsInterpretation>(
package/src/pipeline.h CHANGED
@@ -43,6 +43,8 @@ struct PipelineBaton {
43
43
  std::string fileOut;
44
44
  void *bufferOut;
45
45
  size_t bufferOutLength;
46
+ int pageHeightOut;
47
+ int pagesOut;
46
48
  std::vector<Composite *> composite;
47
49
  std::vector<sharp::InputDescriptor *> joinChannelIn;
48
50
  int topOffsetPre;
@@ -76,6 +78,8 @@ struct PipelineBaton {
76
78
  bool negate;
77
79
  bool negateAlpha;
78
80
  double blurSigma;
81
+ VipsPrecision precision;
82
+ double minAmpl;
79
83
  double brightness;
80
84
  double saturation;
81
85
  int hue;
@@ -195,7 +199,7 @@ struct PipelineBaton {
195
199
  std::unordered_map<std::string, std::string> withExif;
196
200
  bool withExifMerge;
197
201
  int timeoutSeconds;
198
- std::unique_ptr<double[]> convKernel;
202
+ std::vector<double> convKernel;
199
203
  int convKernelWidth;
200
204
  int convKernelHeight;
201
205
  double convKernelScale;
@@ -221,11 +225,13 @@ struct PipelineBaton {
221
225
  VipsForeignDzDepth tileDepth;
222
226
  std::string tileId;
223
227
  std::string tileBasename;
224
- std::unique_ptr<double[]> recombMatrix;
228
+ std::vector<double> recombMatrix;
225
229
 
226
230
  PipelineBaton():
227
231
  input(nullptr),
228
232
  bufferOutLength(0),
233
+ pageHeightOut(0),
234
+ pagesOut(0),
229
235
  topOffsetPre(-1),
230
236
  topOffsetPost(-1),
231
237
  channels(0),