1 | // Copyright 2013 Lovell Fuller and others.
|
2 | // SPDX-License-Identifier: Apache-2.0
|
3 |
|
4 | ;
|
5 |
|
6 | const events = require('node:events');
|
7 | const detectLibc = require('detect-libc');
|
8 |
|
9 | const is = require('./is');
|
10 | const { runtimePlatformArch } = require('./libvips');
|
11 | const sharp = require('./sharp');
|
12 |
|
13 | const runtimePlatform = runtimePlatformArch();
|
14 | const libvipsVersion = sharp.libvipsVersion();
|
15 |
|
16 | /**
|
17 | * An Object containing nested boolean values representing the available input and output formats/methods.
|
18 | * @member
|
19 | * @example
|
20 | * console.log(sharp.format);
|
21 | * @returns {Object}
|
22 | */
|
23 | const format = sharp.format();
|
24 | format.heif.output.alias = ['avif', 'heic'];
|
25 | format.jpeg.output.alias = ['jpe', 'jpg'];
|
26 | format.tiff.output.alias = ['tif'];
|
27 | format.jp2k.output.alias = ['j2c', 'j2k', 'jp2', 'jpx'];
|
28 |
|
29 | /**
|
30 | * An Object containing the available interpolators and their proper values
|
31 | * @readonly
|
32 | * @enum {string}
|
33 | */
|
34 | const interpolators = {
|
35 | /** [Nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). Suitable for image enlargement only. */
|
36 | nearest: 'nearest',
|
37 | /** [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation). Faster than bicubic but with less smooth results. */
|
38 | bilinear: 'bilinear',
|
39 | /** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
|
40 | bicubic: 'bicubic',
|
41 | /** [LBB interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
|
42 | locallyBoundedBicubic: 'lbb',
|
43 | /** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
|
44 | nohalo: 'nohalo',
|
45 | /** [VSQBS interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
|
46 | vertexSplitQuadraticBasisSpline: 'vsqbs'
|
47 | };
|
48 |
|
49 | /**
|
50 | * An Object containing the version numbers of sharp, libvips
|
51 | * and (when using prebuilt binaries) its dependencies.
|
52 | *
|
53 | * @member
|
54 | * @example
|
55 | * console.log(sharp.versions);
|
56 | */
|
57 | let versions = {
|
58 | vips: libvipsVersion.semver
|
59 | };
|
60 | /* istanbul ignore next */
|
61 | if (!libvipsVersion.isGlobal) {
|
62 | if (!libvipsVersion.isWasm) {
|
63 | try {
|
64 | versions = require(`@img/sharp-${runtimePlatform}/versions`);
|
65 | } catch (_) {
|
66 | try {
|
67 | versions = require(`@img/sharp-libvips-${runtimePlatform}/versions`);
|
68 | } catch (_) {}
|
69 | }
|
70 | } else {
|
71 | try {
|
72 | versions = require('@img/sharp-wasm32/versions');
|
73 | } catch (_) {}
|
74 | }
|
75 | }
|
76 | versions.sharp = require('../package.json').version;
|
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 |
|
85 | /**
|
86 | * Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
|
87 | * Existing entries in the cache will be trimmed after any change in limits.
|
88 | * This method always returns cache statistics,
|
89 | * useful for determining how much working memory is required for a particular task.
|
90 | *
|
91 | * @example
|
92 | * const stats = sharp.cache();
|
93 | * @example
|
94 | * sharp.cache( { items: 200 } );
|
95 | * sharp.cache( { files: 0 } );
|
96 | * sharp.cache(false);
|
97 | *
|
98 | * @param {Object|boolean} [options=true] - Object with the following attributes, or boolean where true uses default cache settings and false removes all caching
|
99 | * @param {number} [options.memory=50] - is the maximum memory in MB to use for this cache
|
100 | * @param {number} [options.files=20] - is the maximum number of files to hold open
|
101 | * @param {number} [options.items=100] - is the maximum number of operations to cache
|
102 | * @returns {Object}
|
103 | */
|
104 | function cache (options) {
|
105 | if (is.bool(options)) {
|
106 | if (options) {
|
107 | // Default cache settings of 50MB, 20 files, 100 items
|
108 | return sharp.cache(50, 20, 100);
|
109 | } else {
|
110 | return sharp.cache(0, 0, 0);
|
111 | }
|
112 | } else if (is.object(options)) {
|
113 | return sharp.cache(options.memory, options.files, options.items);
|
114 | } else {
|
115 | return sharp.cache();
|
116 | }
|
117 | }
|
118 | cache(true);
|
119 |
|
120 | /**
|
121 | * Gets or, when a concurrency is provided, sets
|
122 | * the maximum number of threads _libvips_ should use to process _each image_.
|
123 | * These are from a thread pool managed by glib,
|
124 | * which helps avoid the overhead of creating new threads.
|
125 | *
|
126 | * This method always returns the current concurrency.
|
127 | *
|
128 | * The default value is the number of CPU cores,
|
129 | * except when using glibc-based Linux without jemalloc,
|
130 | * where the default is `1` to help reduce memory fragmentation.
|
131 | *
|
132 | * A value of `0` will reset this to the number of CPU cores.
|
133 | *
|
134 | * Some image format libraries spawn additional threads,
|
135 | * e.g. libaom manages its own 4 threads when encoding AVIF images,
|
136 | * and these are independent of the value set here.
|
137 | *
|
138 | * The maximum number of images that sharp can process in parallel
|
139 | * is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
140 | * which defaults to 4.
|
141 | *
|
142 | * https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
143 | *
|
144 | * For example, by default, a machine with 8 CPU cores will process
|
145 | * 4 images in parallel and use up to 8 threads per image,
|
146 | * so there will be up to 32 concurrent threads.
|
147 | *
|
148 | * @example
|
149 | * const threads = sharp.concurrency(); // 4
|
150 | * sharp.concurrency(2); // 2
|
151 | * sharp.concurrency(0); // 4
|
152 | *
|
153 | * @param {number} [concurrency]
|
154 | * @returns {number} concurrency
|
155 | */
|
156 | function concurrency (concurrency) {
|
157 | return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
|
158 | }
|
159 | /* istanbul ignore next */
|
160 | if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
|
161 | // Reduce default concurrency to 1 when using glibc memory allocator
|
162 | sharp.concurrency(1);
|
163 | } else if (detectLibc.familySync() === detectLibc.MUSL && sharp.concurrency() === 1024) {
|
164 | // Reduce default concurrency when musl thread over-subscription detected
|
165 | sharp.concurrency(require('node:os').availableParallelism());
|
166 | }
|
167 |
|
168 | /**
|
169 | * An EventEmitter that emits a `change` event when a task is either:
|
170 | * - queued, waiting for _libuv_ to provide a worker thread
|
171 | * - complete
|
172 | * @member
|
173 | * @example
|
174 | * sharp.queue.on('change', function(queueLength) {
|
175 | * console.log('Queue contains ' + queueLength + ' task(s)');
|
176 | * });
|
177 | */
|
178 | const queue = new events.EventEmitter();
|
179 |
|
180 | /**
|
181 | * Provides access to internal task counters.
|
182 | * - queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
|
183 | * - process is the number of resize tasks currently being processed.
|
184 | *
|
185 | * @example
|
186 | * const counters = sharp.counters(); // { queue: 2, process: 4 }
|
187 | *
|
188 | * @returns {Object}
|
189 | */
|
190 | function counters () {
|
191 | return sharp.counters();
|
192 | }
|
193 |
|
194 | /**
|
195 | * Get and set use of SIMD vector unit instructions.
|
196 | * Requires libvips to have been compiled with highway support.
|
197 | *
|
198 | * Improves the performance of `resize`, `blur` and `sharpen` operations
|
199 | * by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
|
200 | *
|
201 | * @example
|
202 | * const simd = sharp.simd();
|
203 | * // simd is `true` if the runtime use of highway is currently enabled
|
204 | * @example
|
205 | * const simd = sharp.simd(false);
|
206 | * // prevent libvips from using highway at runtime
|
207 | *
|
208 | * @param {boolean} [simd=true]
|
209 | * @returns {boolean}
|
210 | */
|
211 | function simd (simd) {
|
212 | return sharp.simd(is.bool(simd) ? simd : null);
|
213 | }
|
214 |
|
215 | /**
|
216 | * Block libvips operations at runtime.
|
217 | *
|
218 | * This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
|
219 | * which when set will block all "untrusted" operations.
|
220 | *
|
221 | * @since 0.32.4
|
222 | *
|
223 | * @example <caption>Block all TIFF input.</caption>
|
224 | * sharp.block({
|
225 | * operation: ['VipsForeignLoadTiff']
|
226 | * });
|
227 | *
|
228 | * @param {Object} options
|
229 | * @param {Array<string>} options.operation - List of libvips low-level operation names to block.
|
230 | */
|
231 | function block (options) {
|
232 | if (is.object(options)) {
|
233 | if (Array.isArray(options.operation) && options.operation.every(is.string)) {
|
234 | sharp.block(options.operation, true);
|
235 | } else {
|
236 | throw is.invalidParameterError('operation', 'Array<string>', options.operation);
|
237 | }
|
238 | } else {
|
239 | throw is.invalidParameterError('options', 'object', options);
|
240 | }
|
241 | }
|
242 |
|
243 | /**
|
244 | * Unblock libvips operations at runtime.
|
245 | *
|
246 | * This is useful for defining a list of allowed operations.
|
247 | *
|
248 | * @since 0.32.4
|
249 | *
|
250 | * @example <caption>Block all input except WebP from the filesystem.</caption>
|
251 | * sharp.block({
|
252 | * operation: ['VipsForeignLoad']
|
253 | * });
|
254 | * sharp.unblock({
|
255 | * operation: ['VipsForeignLoadWebpFile']
|
256 | * });
|
257 | *
|
258 | * @example <caption>Block all input except JPEG and PNG from a Buffer or Stream.</caption>
|
259 | * sharp.block({
|
260 | * operation: ['VipsForeignLoad']
|
261 | * });
|
262 | * sharp.unblock({
|
263 | * operation: ['VipsForeignLoadJpegBuffer', 'VipsForeignLoadPngBuffer']
|
264 | * });
|
265 | *
|
266 | * @param {Object} options
|
267 | * @param {Array<string>} options.operation - List of libvips low-level operation names to unblock.
|
268 | */
|
269 | function unblock (options) {
|
270 | if (is.object(options)) {
|
271 | if (Array.isArray(options.operation) && options.operation.every(is.string)) {
|
272 | sharp.block(options.operation, false);
|
273 | } else {
|
274 | throw is.invalidParameterError('operation', 'Array<string>', options.operation);
|
275 | }
|
276 | } else {
|
277 | throw is.invalidParameterError('options', 'object', options);
|
278 | }
|
279 | }
|
280 |
|
281 | /**
|
282 | * Decorate the Sharp class with utility-related functions.
|
283 | * @private
|
284 | */
|
285 | module.exports = function (Sharp) {
|
286 | Sharp.cache = cache;
|
287 | Sharp.concurrency = concurrency;
|
288 | Sharp.counters = counters;
|
289 | Sharp.simd = simd;
|
290 | Sharp.format = format;
|
291 | Sharp.interpolators = interpolators;
|
292 | Sharp.versions = versions;
|
293 | Sharp.queue = queue;
|
294 | Sharp.block = block;
|
295 | Sharp.unblock = unblock;
|
296 | };
|