@revizly/sharp 0.35.0-revizly4 → 0.35.0-revizly41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +12 -18
  2. package/{lib/channel.js → dist/channel.cjs} +1 -1
  3. package/dist/channel.mjs +177 -0
  4. package/{lib/colour.js → dist/colour.cjs} +11 -7
  5. package/dist/colour.mjs +199 -0
  6. package/{lib/composite.js → dist/composite.cjs} +2 -1
  7. package/dist/composite.mjs +213 -0
  8. package/{lib/constructor.js → dist/constructor.cjs} +42 -29
  9. package/dist/constructor.mjs +512 -0
  10. package/dist/index.cjs +25 -0
  11. package/dist/index.d.cts +2001 -0
  12. package/dist/index.d.mts +2048 -0
  13. package/dist/index.mjs +25 -0
  14. package/{lib/input.js → dist/input.cjs} +26 -21
  15. package/dist/input.mjs +814 -0
  16. package/{lib/is.js → dist/is.cjs} +1 -1
  17. package/dist/is.mjs +143 -0
  18. package/{lib/libvips.js → dist/libvips.cjs} +35 -30
  19. package/dist/libvips.mjs +212 -0
  20. package/{lib/operation.js → dist/operation.cjs} +33 -51
  21. package/dist/operation.mjs +998 -0
  22. package/{lib/output.js → dist/output.cjs} +161 -28
  23. package/dist/output.mjs +1799 -0
  24. package/{lib/resize.js → dist/resize.cjs} +46 -24
  25. package/dist/resize.mjs +617 -0
  26. package/dist/sharp.cjs +125 -0
  27. package/dist/sharp.mjs +125 -0
  28. package/{lib/utility.js → dist/utility.cjs} +18 -8
  29. package/dist/utility.mjs +301 -0
  30. package/install/build.js +3 -3
  31. package/lib/index.d.ts +105 -75
  32. package/package.json +46 -27
  33. package/src/binding.gyp +18 -13
  34. package/src/common.cc +70 -17
  35. package/src/common.h +22 -3
  36. package/src/metadata.cc +66 -8
  37. package/src/metadata.h +6 -1
  38. package/src/operations.cc +25 -8
  39. package/src/operations.h +1 -1
  40. package/src/pipeline.cc +206 -69
  41. package/src/pipeline.h +16 -1
  42. package/src/stats.cc +6 -6
  43. package/src/utilities.cc +7 -6
  44. package/install/check.js +0 -14
  45. package/lib/index.js +0 -16
  46. package/lib/sharp.js +0 -121
@@ -78,7 +78,7 @@ const string = (val) => typeof val === 'string' && val.length > 0;
78
78
  * Is this value a real number?
79
79
  * @private
80
80
  */
81
- const number = (val) => typeof val === 'number' && !Number.isNaN(val);
81
+ const number = (val) => typeof val === 'number' && Number.isFinite(val);
82
82
 
83
83
  /**
84
84
  * Is this value an integer?
package/dist/is.mjs ADDED
@@ -0,0 +1,143 @@
1
+ /*!
2
+ Copyright 2013 Lovell Fuller and others.
3
+ SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Is this value defined and not null?
8
+ * @private
9
+ */
10
+ const defined = (val) => typeof val !== 'undefined' && val !== null;
11
+
12
+ /**
13
+ * Is this value an object?
14
+ * @private
15
+ */
16
+ const object = (val) => typeof val === 'object';
17
+
18
+ /**
19
+ * Is this value a plain object?
20
+ * @private
21
+ */
22
+ const plainObject = (val) => Object.prototype.toString.call(val) === '[object Object]';
23
+
24
+ /**
25
+ * Is this value a function?
26
+ * @private
27
+ */
28
+ const fn = (val) => typeof val === 'function';
29
+
30
+ /**
31
+ * Is this value a boolean?
32
+ * @private
33
+ */
34
+ const bool = (val) => typeof val === 'boolean';
35
+
36
+ /**
37
+ * Is this value a Buffer object?
38
+ * @private
39
+ */
40
+ const buffer = (val) => val instanceof Buffer;
41
+
42
+ /**
43
+ * Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
44
+ * @private
45
+ */
46
+ const typedArray = (val) => {
47
+ if (defined(val)) {
48
+ switch (val.constructor) {
49
+ case Uint8Array:
50
+ case Uint8ClampedArray:
51
+ case Int8Array:
52
+ case Uint16Array:
53
+ case Int16Array:
54
+ case Uint32Array:
55
+ case Int32Array:
56
+ case Float32Array:
57
+ case Float64Array:
58
+ return true;
59
+ }
60
+ }
61
+
62
+ return false;
63
+ };
64
+
65
+ /**
66
+ * Is this value an ArrayBuffer object?
67
+ * @private
68
+ */
69
+ const arrayBuffer = (val) => val instanceof ArrayBuffer;
70
+
71
+ /**
72
+ * Is this value a non-empty string?
73
+ * @private
74
+ */
75
+ const string = (val) => typeof val === 'string' && val.length > 0;
76
+
77
+ /**
78
+ * Is this value a real number?
79
+ * @private
80
+ */
81
+ const number = (val) => typeof val === 'number' && Number.isFinite(val);
82
+
83
+ /**
84
+ * Is this value an integer?
85
+ * @private
86
+ */
87
+ const integer = (val) => Number.isInteger(val);
88
+
89
+ /**
90
+ * Is this value within an inclusive given range?
91
+ * @private
92
+ */
93
+ const inRange = (val, min, max) => val >= min && val <= max;
94
+
95
+ /**
96
+ * Is this value within the elements of an array?
97
+ * @private
98
+ */
99
+ const inArray = (val, list) => list.includes(val);
100
+
101
+ /**
102
+ * Create an Error with a message relating to an invalid parameter.
103
+ *
104
+ * @param {string} name - parameter name.
105
+ * @param {string} expected - description of the type/value/range expected.
106
+ * @param {*} actual - the value received.
107
+ * @returns {Error} Containing the formatted message.
108
+ * @private
109
+ */
110
+ const invalidParameterError = (name, expected, actual) => new Error(
111
+ `Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
112
+ );
113
+
114
+ /**
115
+ * Ensures an Error from C++ contains a JS stack.
116
+ *
117
+ * @param {Error} native - Error with message from C++.
118
+ * @param {Error} context - Error with stack from JS.
119
+ * @returns {Error} Error with message and stack.
120
+ * @private
121
+ */
122
+ const nativeError = (native, context) => {
123
+ context.message = native.message;
124
+ return context;
125
+ };
126
+
127
+ export default {
128
+ defined,
129
+ object,
130
+ plainObject,
131
+ fn,
132
+ bool,
133
+ buffer,
134
+ typedArray,
135
+ arrayBuffer,
136
+ string,
137
+ number,
138
+ integer,
139
+ inRange,
140
+ inArray,
141
+ invalidParameterError,
142
+ nativeError
143
+ };
@@ -5,20 +5,18 @@
5
5
 
6
6
  const { spawnSync } = require('node:child_process');
7
7
  const { createHash } = require('node:crypto');
8
- const semverCoerce = require('semver/functions/coerce');
9
- const semverGreaterThanOrEqualTo = require('semver/functions/gte');
10
- const semverSatisfies = require('semver/functions/satisfies');
8
+ const semver = require('semver');
11
9
  const detectLibc = require('detect-libc');
12
-
13
- const { config, engines, optionalDependencies } = require('../package.json');
10
+ const pkg = require('../package.json');
14
11
 
15
12
  /* node:coverage ignore next */
16
- const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || config.libvips;
17
- const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
13
+ const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || pkg.config.libvips;
14
+ const minimumLibvipsVersion = semver.coerce(minimumLibvipsVersionLabelled).version;
18
15
 
19
16
  const prebuiltPlatforms = [
20
17
  'darwin-arm64', 'darwin-x64',
21
- 'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-riscv64', 'linux-s390x', 'linux-x64',
18
+ 'freebsd-arm64', 'freebsd-x64',
19
+ 'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-riscv64', 'linux-s390x', 'linux-wasm32', 'linux-x64',
22
20
  'linuxmusl-arm64', 'linuxmusl-x64',
23
21
  'win32-arm64', 'win32-ia32', 'win32-x64'
24
22
  ];
@@ -36,13 +34,13 @@ const log = (item) => {
36
34
  }
37
35
  };
38
36
 
39
- /* node:coverage ignore next */
37
+ /* node:coverage disable */
38
+
40
39
  const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
41
40
 
42
41
  const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
43
42
 
44
43
  const buildPlatformArch = () => {
45
- /* node:coverage ignore next 3 */
46
44
  if (isEmscripten()) {
47
45
  return 'wasm32';
48
46
  }
@@ -55,7 +53,6 @@ const buildSharpLibvipsIncludeDir = () => {
55
53
  try {
56
54
  return require(`@revizly/sharp-libvips-dev-${buildPlatformArch()}/include`);
57
55
  } catch {
58
- /* node:coverage ignore next 5 */
59
56
  try {
60
57
  return require('@revizly/sharp-libvips-dev/include');
61
58
  } catch {}
@@ -64,7 +61,6 @@ const buildSharpLibvipsIncludeDir = () => {
64
61
  };
65
62
 
66
63
  const buildSharpLibvipsCPlusPlusDir = () => {
67
- /* node:coverage ignore next 4 */
68
64
  try {
69
65
  return require('@revizly/sharp-libvips-dev/cplusplus');
70
66
  } catch {}
@@ -75,7 +71,6 @@ const buildSharpLibvipsLibDir = () => {
75
71
  try {
76
72
  return require(`@revizly/sharp-libvips-dev-${buildPlatformArch()}/lib`);
77
73
  } catch {
78
- /* node:coverage ignore next 5 */
79
74
  try {
80
75
  return require(`@revizly/sharp-libvips-${buildPlatformArch()}/lib`);
81
76
  } catch {}
@@ -83,12 +78,10 @@ const buildSharpLibvipsLibDir = () => {
83
78
  return '';
84
79
  };
85
80
 
86
- /* node:coverage disable */
87
-
88
81
  const isUnsupportedNodeRuntime = () => {
89
82
  if (process.release?.name === 'node' && process.versions) {
90
- if (!semverSatisfies(process.versions.node, engines.node)) {
91
- return { found: process.versions.node, expected: engines.node };
83
+ if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
84
+ return { found: process.versions.node, expected: pkg.engines.node };
92
85
  }
93
86
  }
94
87
  };
@@ -113,7 +106,7 @@ const sha512 = (s) => createHash('sha512').update(s).digest('hex');
113
106
  const yarnLocator = () => {
114
107
  try {
115
108
  const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
116
- const npmVersion = semverCoerce(optionalDependencies[`@revizly/sharp-libvips-${buildPlatformArch()}`], {
109
+ const npmVersion = semver.coerce(pkg.optionalDependencies[`@revizly/sharp-libvips-${buildPlatformArch()}`], {
117
110
  includePrerelease: true
118
111
  }).version;
119
112
  return sha512(`${identHash}npm:${npmVersion}`).slice(0, 10);
@@ -144,22 +137,34 @@ const globalLibvipsVersion = () => {
144
137
  }
145
138
  };
146
139
 
140
+ const getBrewPkgConfigPath = () => {
141
+ try {
142
+ const brewPrefix = (spawnSync('brew', ['--prefix'], { encoding: 'utf8' }).stdout || '').trim();
143
+ if (brewPrefix) {
144
+ return `${brewPrefix}/lib/pkgconfig`;
145
+ }
146
+ } catch (_err) {}
147
+ return undefined;
148
+ };
149
+
150
+ const getPkgConfigPath = () => {
151
+ try {
152
+ const pkgConfigPath = (spawnSync('pkg-config', ['--variable', 'pc_path', 'pkg-config'], { encoding: 'utf8' }).stdout || '').trim();
153
+ if (pkgConfigPath) {
154
+ return pkgConfigPath;
155
+ }
156
+ } catch (_err) {}
157
+ return undefined;
158
+ };
159
+
147
160
  /* node:coverage enable */
148
161
 
149
162
  const pkgConfigPath = () => {
150
163
  if (process.platform !== 'win32') {
151
- /* node:coverage ignore next 4 */
152
- const brewPkgConfigPath = spawnSync(
153
- 'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
154
- spawnSyncOptions
155
- ).stdout || '';
156
164
  return [
157
- brewPkgConfigPath.trim(),
158
- process.env.PKG_CONFIG_PATH,
159
- '/usr/local/lib/pkgconfig',
160
- '/usr/lib/pkgconfig',
161
- '/usr/local/libdata/pkgconfig',
162
- '/usr/libdata/pkgconfig'
165
+ getBrewPkgConfigPath(),
166
+ getPkgConfigPath(),
167
+ process.env.PKG_CONFIG_PATH
163
168
  ].filter(Boolean).join(':');
164
169
  } else {
165
170
  return '';
@@ -186,7 +191,7 @@ const useGlobalLibvips = (logger) => {
186
191
  }
187
192
  const globalVipsVersion = globalLibvipsVersion();
188
193
  /* node:coverage ignore next */
189
- return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
194
+ return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
190
195
  };
191
196
 
192
197
  module.exports = {
@@ -0,0 +1,212 @@
1
+ /*!
2
+ Copyright 2013 Lovell Fuller and others.
3
+ SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { spawnSync } from 'node:child_process';
7
+ import { createHash } from 'node:crypto';
8
+ import semver from 'semver';
9
+ import detectLibc from 'detect-libc';
10
+ import pkg from '../package.json' with { type: 'json' };
11
+
12
+ /* node:coverage ignore next */
13
+ const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || pkg.config.libvips;
14
+ const minimumLibvipsVersion = semver.coerce(minimumLibvipsVersionLabelled).version;
15
+
16
+ const prebuiltPlatforms = [
17
+ 'darwin-arm64', 'darwin-x64',
18
+ 'freebsd-arm64', 'freebsd-x64',
19
+ 'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-riscv64', 'linux-s390x', 'linux-wasm32', 'linux-x64',
20
+ 'linuxmusl-arm64', 'linuxmusl-x64',
21
+ 'win32-arm64', 'win32-ia32', 'win32-x64'
22
+ ];
23
+
24
+ const spawnSyncOptions = {
25
+ encoding: 'utf8',
26
+ shell: true
27
+ };
28
+
29
+ const log = (item) => {
30
+ if (item instanceof Error) {
31
+ console.error(`sharp: Installation error: ${item.message}`);
32
+ } else {
33
+ console.log(`sharp: ${item}`);
34
+ }
35
+ };
36
+
37
+ /* node:coverage disable */
38
+
39
+ const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
40
+
41
+ const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
42
+
43
+ const buildPlatformArch = () => {
44
+ if (isEmscripten()) {
45
+ return 'wasm32';
46
+ }
47
+ const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
48
+ const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
49
+ return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
50
+ };
51
+
52
+ const buildSharpLibvipsIncludeDir = () => {
53
+ try {
54
+ return require(`@revizly/sharp-libvips-dev-${buildPlatformArch()}/include`);
55
+ } catch {
56
+ try {
57
+ return require('@revizly/sharp-libvips-dev/include');
58
+ } catch {}
59
+ }
60
+ return '';
61
+ };
62
+
63
+ const buildSharpLibvipsCPlusPlusDir = () => {
64
+ try {
65
+ return require('@revizly/sharp-libvips-dev/cplusplus');
66
+ } catch {}
67
+ return '';
68
+ };
69
+
70
+ const buildSharpLibvipsLibDir = () => {
71
+ try {
72
+ return require(`@revizly/sharp-libvips-dev-${buildPlatformArch()}/lib`);
73
+ } catch {
74
+ try {
75
+ return require(`@revizly/sharp-libvips-${buildPlatformArch()}/lib`);
76
+ } catch {}
77
+ }
78
+ return '';
79
+ };
80
+
81
+ const isUnsupportedNodeRuntime = () => {
82
+ if (process.release?.name === 'node' && process.versions) {
83
+ if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
84
+ return { found: process.versions.node, expected: pkg.engines.node };
85
+ }
86
+ }
87
+ };
88
+
89
+ const isEmscripten = () => {
90
+ const { CC } = process.env;
91
+ return Boolean(CC?.endsWith('/emcc'));
92
+ };
93
+
94
+ const isRosetta = () => {
95
+ if (process.platform === 'darwin' && process.arch === 'x64') {
96
+ const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
97
+ return (translated || '').trim() === 'sysctl.proc_translated: 1';
98
+ }
99
+ return false;
100
+ };
101
+
102
+ /* node:coverage enable */
103
+
104
+ const sha512 = (s) => createHash('sha512').update(s).digest('hex');
105
+
106
+ const yarnLocator = () => {
107
+ try {
108
+ const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
109
+ const npmVersion = semver.coerce(pkg.optionalDependencies[`@revizly/sharp-libvips-${buildPlatformArch()}`], {
110
+ includePrerelease: true
111
+ }).version;
112
+ return sha512(`${identHash}npm:${npmVersion}`).slice(0, 10);
113
+ } catch {}
114
+ return '';
115
+ };
116
+
117
+ /* node:coverage disable */
118
+
119
+ const spawnRebuild = () =>
120
+ spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
121
+ ...spawnSyncOptions,
122
+ stdio: 'inherit'
123
+ }).status;
124
+
125
+ const globalLibvipsVersion = () => {
126
+ if (process.platform !== 'win32') {
127
+ const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
128
+ ...spawnSyncOptions,
129
+ env: {
130
+ ...process.env,
131
+ PKG_CONFIG_PATH: pkgConfigPath()
132
+ }
133
+ }).stdout;
134
+ return (globalLibvipsVersion || '').trim();
135
+ } else {
136
+ return '';
137
+ }
138
+ };
139
+
140
+ const getBrewPkgConfigPath = () => {
141
+ try {
142
+ const brewPrefix = (spawnSync('brew', ['--prefix'], { encoding: 'utf8' }).stdout || '').trim();
143
+ if (brewPrefix) {
144
+ return `${brewPrefix}/lib/pkgconfig`;
145
+ }
146
+ } catch (_err) {}
147
+ return undefined;
148
+ };
149
+
150
+ const getPkgConfigPath = () => {
151
+ try {
152
+ const pkgConfigPath = (spawnSync('pkg-config', ['--variable', 'pc_path', 'pkg-config'], { encoding: 'utf8' }).stdout || '').trim();
153
+ if (pkgConfigPath) {
154
+ return pkgConfigPath;
155
+ }
156
+ } catch (_err) {}
157
+ return undefined;
158
+ };
159
+
160
+ /* node:coverage enable */
161
+
162
+ const pkgConfigPath = () => {
163
+ if (process.platform !== 'win32') {
164
+ return [
165
+ getBrewPkgConfigPath(),
166
+ getPkgConfigPath(),
167
+ process.env.PKG_CONFIG_PATH
168
+ ].filter(Boolean).join(':');
169
+ } else {
170
+ return '';
171
+ }
172
+ };
173
+
174
+ const skipSearch = (status, reason, logger) => {
175
+ if (logger) {
176
+ logger(`Detected ${reason}, skipping search for globally-installed libvips`);
177
+ }
178
+ return status;
179
+ };
180
+
181
+ const useGlobalLibvips = (logger) => {
182
+ if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
183
+ return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS', logger);
184
+ }
185
+ if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
186
+ return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
187
+ }
188
+ /* node:coverage ignore next 3 */
189
+ if (isRosetta()) {
190
+ return skipSearch(false, 'Rosetta', logger);
191
+ }
192
+ const globalVipsVersion = globalLibvipsVersion();
193
+ /* node:coverage ignore next */
194
+ return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
195
+ };
196
+
197
+ export default {
198
+ minimumLibvipsVersion,
199
+ prebuiltPlatforms,
200
+ buildPlatformArch,
201
+ buildSharpLibvipsIncludeDir,
202
+ buildSharpLibvipsCPlusPlusDir,
203
+ buildSharpLibvipsLibDir,
204
+ isUnsupportedNodeRuntime,
205
+ runtimePlatformArch,
206
+ log,
207
+ yarnLocator,
208
+ spawnRebuild,
209
+ globalLibvipsVersion,
210
+ pkgConfigPath,
211
+ useGlobalLibvips
212
+ };
@@ -3,7 +3,7 @@
3
3
  SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- const is = require('./is');
6
+ const is = require('./is.cjs');
7
7
 
8
8
  /**
9
9
  * How accurate an operation should be.
@@ -178,7 +178,13 @@ function flop (flop) {
178
178
  * @throws {Error} Invalid parameters
179
179
  */
180
180
  function affine (matrix, options) {
181
- const flatMatrix = [].concat(...matrix);
181
+ const isValidShape = Array.isArray(matrix) && (
182
+ // 1x4 array of numbers
183
+ (matrix.length === 4 && matrix.every(is.number)) ||
184
+ // 2x2 array of arrays of numbers
185
+ (matrix.length === 2 && matrix.every((row) => Array.isArray(row) && row.length === 2))
186
+ );
187
+ const flatMatrix = isValidShape ? matrix.flat() : [];
182
188
  if (flatMatrix.length === 4 && flatMatrix.every(is.number)) {
183
189
  this.options.affineMatrix = flatMatrix;
184
190
  } else {
@@ -259,45 +265,18 @@ function affine (matrix, options) {
259
265
  * })
260
266
  * .toBuffer();
261
267
  *
262
- * @param {Object|number} [options] - if present, is an Object with attributes
268
+ * @param {Object} [options] - if present, is an Object with attributes
263
269
  * @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10
264
270
  * @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas, between 0 and 1000000
265
271
  * @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas, between 0 and 1000000
266
272
  * @param {number} [options.x1=2.0] - threshold between "flat" and "jagged", between 0 and 1000000
267
273
  * @param {number} [options.y2=10.0] - maximum amount of brightening, between 0 and 1000000
268
274
  * @param {number} [options.y3=20.0] - maximum amount of darkening, between 0 and 1000000
269
- * @param {number} [flat] - (deprecated) see `options.m1`.
270
- * @param {number} [jagged] - (deprecated) see `options.m2`.
271
275
  * @returns {Sharp}
272
276
  * @throws {Error} Invalid parameters
273
277
  */
274
- function sharpen (options, flat, jagged) {
275
- if (!is.defined(options)) {
276
- // No arguments: default to mild sharpen
277
- this.options.sharpenSigma = -1;
278
- } else if (is.bool(options)) {
279
- // Deprecated boolean argument: apply mild sharpen?
280
- this.options.sharpenSigma = options ? -1 : 0;
281
- } else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
282
- // Deprecated numeric argument: specific sigma
283
- this.options.sharpenSigma = options;
284
- // Deprecated control over flat areas
285
- if (is.defined(flat)) {
286
- if (is.number(flat) && is.inRange(flat, 0, 10000)) {
287
- this.options.sharpenM1 = flat;
288
- } else {
289
- throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
290
- }
291
- }
292
- // Deprecated control over jagged areas
293
- if (is.defined(jagged)) {
294
- if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
295
- this.options.sharpenM2 = jagged;
296
- } else {
297
- throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
298
- }
299
- }
300
- } else if (is.plainObject(options)) {
278
+ function sharpen (options) {
279
+ if (is.plainObject(options)) {
301
280
  if (is.number(options.sigma) && is.inRange(options.sigma, 0.000001, 10)) {
302
281
  this.options.sharpenSigma = options.sigma;
303
282
  } else {
@@ -339,7 +318,7 @@ function sharpen (options, flat, jagged) {
339
318
  }
340
319
  }
341
320
  } else {
342
- throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
321
+ this.options.sharpenSigma = -1;
343
322
  }
344
323
  return this;
345
324
  }
@@ -451,10 +430,10 @@ function blur (options) {
451
430
  function dilate (width) {
452
431
  if (!is.defined(width)) {
453
432
  this.options.dilateWidth = 1;
454
- } else if (is.integer(width) && width > 0) {
433
+ } else if (is.integer(width) && is.inRange(width, 1, 65536)) {
455
434
  this.options.dilateWidth = width;
456
435
  } else {
457
- throw is.invalidParameterError('dilate', 'positive integer', dilate);
436
+ throw is.invalidParameterError('width', 'integer between 1 and 65536', width);
458
437
  }
459
438
  return this;
460
439
  }
@@ -474,10 +453,10 @@ function dilate (width) {
474
453
  function erode (width) {
475
454
  if (!is.defined(width)) {
476
455
  this.options.erodeWidth = 1;
477
- } else if (is.integer(width) && width > 0) {
456
+ } else if (is.integer(width) && is.inRange(width, 1, 65536)) {
478
457
  this.options.erodeWidth = width;
479
458
  } else {
480
- throw is.invalidParameterError('erode', 'positive integer', erode);
459
+ throw is.invalidParameterError('width', 'integer between 1 and 65536', width);
481
460
  }
482
461
  return this;
483
462
  }
@@ -510,8 +489,6 @@ function flatten (options) {
510
489
  *
511
490
  * Existing alpha channel values for non-white pixels remain unchanged.
512
491
  *
513
- * This feature is experimental and the API may change.
514
- *
515
492
  * @since 0.32.1
516
493
  *
517
494
  * @example
@@ -675,23 +652,23 @@ function normalize (options) {
675
652
  * .toBuffer();
676
653
  *
677
654
  * @param {Object} options
678
- * @param {number} options.width - Integral width of the search window, in pixels.
679
- * @param {number} options.height - Integral height of the search window, in pixels.
655
+ * @param {number} options.width - Integral width of the search window, in pixels, between 1 and 65536.
656
+ * @param {number} options.height - Integral height of the search window, in pixels, between 1 and 65536.
680
657
  * @param {number} [options.maxSlope=3] - Integral level of brightening, between 0 and 100, where 0 disables contrast limiting.
681
658
  * @returns {Sharp}
682
659
  * @throws {Error} Invalid parameters
683
660
  */
684
661
  function clahe (options) {
685
662
  if (is.plainObject(options)) {
686
- if (is.integer(options.width) && options.width > 0) {
663
+ if (is.integer(options.width) && is.inRange(options.width, 1, 65536)) {
687
664
  this.options.claheWidth = options.width;
688
665
  } else {
689
- throw is.invalidParameterError('width', 'integer greater than zero', options.width);
666
+ throw is.invalidParameterError('width', 'integer between 1 and 65536', options.width);
690
667
  }
691
- if (is.integer(options.height) && options.height > 0) {
668
+ if (is.integer(options.height) && is.inRange(options.height, 1, 65536)) {
692
669
  this.options.claheHeight = options.height;
693
670
  } else {
694
- throw is.invalidParameterError('height', 'integer greater than zero', options.height);
671
+ throw is.invalidParameterError('height', 'integer between 1 and 65536', options.height);
695
672
  }
696
673
  if (is.defined(options.maxSlope)) {
697
674
  if (is.integer(options.maxSlope) && is.inRange(options.maxSlope, 0, 100)) {
@@ -735,7 +712,8 @@ function convolve (kernel) {
735
712
  if (!is.object(kernel) || !Array.isArray(kernel.kernel) ||
736
713
  !is.integer(kernel.width) || !is.integer(kernel.height) ||
737
714
  !is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) ||
738
- kernel.height * kernel.width !== kernel.kernel.length
715
+ kernel.height * kernel.width !== kernel.kernel.length ||
716
+ !kernel.kernel.every(is.number)
739
717
  ) {
740
718
  // must pass in a kernel
741
719
  throw new Error('Invalid convolution kernel');
@@ -888,12 +866,16 @@ function recomb (inputMatrix) {
888
866
  if (!Array.isArray(inputMatrix)) {
889
867
  throw is.invalidParameterError('inputMatrix', 'array', inputMatrix);
890
868
  }
891
- if (inputMatrix.length !== 3 && inputMatrix.length !== 4) {
892
- throw is.invalidParameterError('inputMatrix', '3x3 or 4x4 array', inputMatrix.length);
869
+ const dimensions = inputMatrix.length;
870
+ if (dimensions !== 3 && dimensions !== 4) {
871
+ throw is.invalidParameterError('inputMatrix', '3x3 or 4x4 array', dimensions);
872
+ }
873
+ if (!inputMatrix.every((row) => Array.isArray(row) && row.length === dimensions)) {
874
+ throw is.invalidParameterError('inputMatrix', `array of ${dimensions} arrays of length ${dimensions}`, inputMatrix);
893
875
  }
894
- const recombMatrix = inputMatrix.flat().map(Number);
895
- if (recombMatrix.length !== 9 && recombMatrix.length !== 16) {
896
- throw is.invalidParameterError('inputMatrix', 'cardinality of 9 or 16', recombMatrix.length);
876
+ const recombMatrix = inputMatrix.flat();
877
+ if (!recombMatrix.every(is.number)) {
878
+ throw is.invalidParameterError('inputMatrix', 'array of numbers', recombMatrix);
897
879
  }
898
880
  this.options.recombMatrix = recombMatrix;
899
881
  return this;