Compare commits

..

256 Commits

Author SHA1 Message Date
Lovell Fuller
b9d4c30a9f Release v0.32.0 2023-03-24 17:05:59 +00:00
Lovell Fuller
148760fe55 Docs: clarify resize reduction/enlargement options refer to scaling
Types: options can be passed as first resize parameter
2023-03-24 15:19:21 +00:00
Lovell Fuller
98ed237734 Docs: use only first year of copyright to match code 2023-03-24 09:59:36 +00:00
Lovell Fuller
b55e58f31e Trim space from end of libvips error messages 2023-03-24 09:58:21 +00:00
Lovell Fuller
0af070ed93 Docs: update performance results, include PNG-based task 2023-03-23 18:57:43 +00:00
Lovell Fuller
9fbb4fcaef Tests: bump benchmark deps 2023-03-22 11:03:16 +00:00
Lovell Fuller
6008ff8a08 Docs: tile-based output requires libgsf 2023-03-22 09:17:07 +00:00
Lovell Fuller
cd5e11bd50 Docs: ensure parameters are indexed as they now appear in a table 2023-03-22 09:04:54 +00:00
Lovell Fuller
08d6822265 Upgrade to libvips v8.14.2 2023-03-21 21:21:24 +00:00
Lovell Fuller
8b8a815fbb Tests: tile-based output optional, will require custom libvips
The prebuilt binaries provided by v0.32.0 will not support
tile-based output, which is (hopefully) a temporary situation
until upstream licensing issues are resolved.
2023-03-21 21:19:56 +00:00
Lovell Fuller
a44da850c1 Docs: add open graph title and image 2023-03-21 12:56:44 +00:00
Lovell Fuller
c5ef4677b1 Bump tsd dep to pick up TypeScript 5 improvements 2023-03-21 12:43:29 +00:00
Lovell Fuller
f8a430bdd3 Tests: reduce CPU cost of RGBA linear test, ~2s faster 2023-03-21 12:42:50 +00:00
Lovell Fuller
cd419a261b Docs: changelog and refresh for #3583 2023-03-21 10:16:31 +00:00
LachlanNewman
d7776e3b98 Add support to normalise for lower and upper percentiles (#3583) 2023-03-21 10:13:12 +00:00
Lovell Fuller
1eefd4e562 Docs: how to provide new integrity values for custom binaries 2023-03-17 09:25:24 +00:00
cychub
0a16d26ec7 Docs: fix sharp_binary_host example by adding version (#3568) 2023-03-12 12:58:08 +00:00
Lovell Fuller
fc03fba602 Docs: clarify metadata ignores chained ops 2023-03-10 13:35:06 +00:00
Lovell Fuller
c87fe512b4 Bump devDeps 2023-03-08 16:59:20 +00:00
Lovell Fuller
2eaab59c48 Docs: add note about API Gateway integration 2023-03-08 16:54:49 +00:00
Lovell Fuller
4ec883eaa0 Wrap all async JS callbacks, help avoid possible race #3569 2023-03-01 12:41:11 +00:00
Lovell Fuller
0063df4d4f Ensure clahe op uses random read, simplify validation 2023-02-28 21:59:31 +00:00
Lovell Fuller
6c61ad256f Ensure all source code files contain SPDX licence 2023-02-28 17:01:58 +00:00
Lovell Fuller
b90474affa Docs: clarify formats that support multi-page/anim 2023-02-28 14:39:49 +00:00
Lovell Fuller
34cbc6dec3 Docs: clarify that paths are relative to process working dir 2023-02-23 10:33:13 +00:00
Lovell Fuller
bb8de0cc26 Docs: refresh search index 2023-02-18 12:51:42 +00:00
Lovell Fuller
863e37455a Docs: changelog and credit for #3556 2023-02-18 12:50:58 +00:00
Tomasz Janowski
6f0e6f2e65 Add support to extend for extendWith, allows copy/mirror/repeat (#3556) 2023-02-17 14:01:24 +00:00
Lovell Fuller
ebf4ccd124 Bump deps 2023-02-12 19:49:32 +00:00
Lovell Fuller
b96c8e8ba4 Tests: use native fs.rm instead of rimraf 2023-02-12 19:32:00 +00:00
Lovell Fuller
42d2f07e44 Add ignoreIcc input option to ignore embedded ICC profile 2023-02-12 17:51:24 +00:00
Lovell Fuller
a2988c9edc macOS: use 10.13 as minimum to match prebuilt libvips
This allows clang to use SSE4 intrinsics
2023-02-12 16:10:21 +00:00
Lovell Fuller
24b3344937 Docs: changelog for #3548 2023-02-05 09:49:06 +00:00
Jérémy Lal
9608f219bd Add support for ArrayBuffer input (#3548) 2023-02-05 09:45:17 +00:00
Pascal Jufer
4798d9da64 Docs: clarify supported bit depth for AVIF images (#3541) 2023-02-02 17:47:07 +00:00
Lovell Fuller
8d8c6b70eb Prefer integer (un)premultiply for faster RGBA resize
Add changelog, loosen modulate test thresholds
2023-01-24 15:44:39 +00:00
Lovell Fuller
9e2207f376 Prefer integer (un)premultiply for faster RGBA resize 2023-01-24 15:24:58 +00:00
Lovell Fuller
802f560b9b Test: update benchmark dependencies 2023-01-24 15:21:31 +00:00
Lovell Fuller
a532659b0f Types: changes/additions relating to new v0.32.0 features
A separate commit is required as these were not part of the
initial definitions in the v0.31.3 snapshot.

From now on, new features and updates can include the relevant
TypeScript definition changes as part of the same
code/docs/tests commits.
2023-01-17 16:11:04 +00:00
Lovell Fuller
25c6da2bcd Docs: add a couple of missing params/props 2023-01-17 15:01:52 +00:00
Lovell Fuller
02f855d57a Expose own version as sharp.versions.sharp #3471 2023-01-17 09:56:58 +00:00
Lovell Fuller
c150263ef1 Respect fastShrinkOnLoad option for WebP input #3516 2023-01-17 09:39:23 +00:00
Lovell Fuller
9f79f80a93 Docs: fastShrinkOnLoad can round-down when auto-scaling 2023-01-16 12:06:50 +00:00
Lovell Fuller
069803b83d Docs: remove Heroku install section 2023-01-16 12:06:08 +00:00
Lovell Fuller
f79760b4f2 Docs: changelog and help for TypeScript defs #3369 #3370 2023-01-16 11:12:00 +00:00
Espen Hovlandsdal
aa5f0f4e40 Include and publish TypeScript definitions (#3370)
Definitions are a snapshot taken from `@types/sharp`,
which remain under the terms of MIT licensing.
2023-01-16 10:48:37 +00:00
Lovell Fuller
286a322622 Docs: changelog and doc refresh for #3470 2023-01-16 09:27:31 +00:00
Emanuel Jöbstl
6d404f4d2c Add coords to output when using attention based crop (#3470) 2023-01-16 09:20:42 +00:00
Lovell Fuller
bdc50e1d6e Unpin node-addon-api, cast CallbackInfo access to size_t
See https://github.com/nodejs/node-addon-api/pull/1253
2023-01-16 09:00:42 +00:00
Lovell Fuller
a9bd0e79f8 Pin node-addon-api to workaround possible bug in 5.1.0 2023-01-15 19:35:27 +00:00
Lovell Fuller
a1e464cc5e Switch to sequential read as default where possible 2023-01-15 18:43:50 +00:00
Lovell Fuller
081debd055 Reduce sharpen op max sigma from 10000 to 10 #3521 2023-01-10 16:29:40 +00:00
Lovell Fuller
ef849fd639 Docs: switch to well-maintained jsdoc2md for JSDoc parsing 2023-01-08 10:15:38 +00:00
Lovell Fuller
a42a975c46 Bump devDeps 2023-01-06 19:25:27 +00:00
Lovell Fuller
e8273580af Docs: add note about use of fastShrinkOnLoad with resize kernel 2023-01-06 19:24:32 +00:00
Lovell Fuller
5be36c2deb Install: log Rosetta detection, improve related docs 2023-01-04 21:11:21 +00:00
Kleis Auke Wolthuizen
6cda090ce2 Tests: remove ICC profile from CIELAB fixture (#3510)
This ICC profile is considered incompatible with this image.

See: https://github.com/libvips/libvips/issues/730
2023-01-01 21:12:29 +00:00
Lovell Fuller
eac6e8b261 Upgrade to libvips v8.14.0-rc1
- Replace GIF 'optimise' option with 'reuse'
- Add 'progressive' option to GIF
- Add 'wrap' option to text creation
- Add 'formatMagick' property to *magick input metadata
2022-12-29 15:53:50 +00:00
Lovell Fuller
844deaf480 Release v0.31.3 2022-12-21 15:57:10 +00:00
Lovell Fuller
efbb0c22fd Docs: add image with examples of resize fit property 2022-12-21 15:47:39 +00:00
Lovell Fuller
da0b594900 Docs: update benchmarks for latest versions, add ARM64 results 2022-12-20 19:49:29 +00:00
Lovell Fuller
78dada9126 Tests: skip mapnik and tensorflow for Docker-run benchmarks
Maintainance of mapnik seems to have stalled, no ARM64 support
Memory requirements of Tensorflow too high, hangs/crashes on AMD64
2022-12-20 18:20:59 +00:00
Lovell Fuller
15f5cd4671 Tests: move mapnik to optional deps
It does not currently support ARM64
2022-12-19 19:47:46 +00:00
Lovell Fuller
9eb2e94404 Tests: update benchmark dependencies 2022-12-17 14:29:11 +00:00
Lovell Fuller
e40b068628 Tests: update leak suppresions for latest dependencies 2022-12-14 21:57:42 +00:00
Lovell Fuller
2c46528269 Docs refresh 2022-12-14 16:17:42 +00:00
Lovell Fuller
584807b4f5 Add runtime detection of V8 memory cage #3384
When using the V8 memory cage, Buffers cannot be wrapped and then
later freed via a callback. When the cage is detected via a throw,
instead fall back to copying Buffer contents to V8 memory.

This approach will be used by Electron 21+ and you should expect
reduced performance and increased memory consumption/fragmentation.
2022-12-14 16:06:04 +00:00
Lovell Fuller
a7fa7014ef Add experimental support for JPEG-XL, requires libvips with libjxl
The prebuilt binaries do not include support for this format.
2022-12-13 21:55:17 +00:00
Lovell Fuller
f92e33fbff Bump devDeps 2022-12-13 10:31:06 +00:00
Lovell Fuller
0f1e7ef6f6 Install: add support for Linux with glibc patch version #3423 2022-12-09 12:03:41 +00:00
Lennart
89e204d824 Docs: clarify failOn property applies to decoding pixel values (#3481) 2022-12-08 16:13:18 +00:00
Lovell Fuller
2a71f1830f Expand range of sharpen params to match libvips #3427 2022-12-07 09:28:01 +00:00
Lovell Fuller
def99a294a Install: log proxy use, if any, to aid with debugging 2022-12-06 19:35:47 +00:00
Lovell Fuller
9d760f3958 Improve perf of ops that introduce non-opaque background #3465 2022-12-05 20:40:41 +00:00
Lovell Fuller
0265d305fe Ensure integral output of linear op #3468 2022-12-04 21:41:15 +00:00
Lovell Fuller
a472aea025 Ignore sequentialRead option for stats #3462 2022-11-20 21:30:45 +00:00
Lovell Fuller
01ffa80338 Improve extractChannel support for 16-bit output #3453 2022-11-15 15:00:32 +00:00
Lovell Fuller
789d4851ea Tests: remove flaky font assertions
Probably due to Windows CI env font discovery
2022-11-15 10:08:43 +00:00
Lovell Fuller
4490a93430 Tests: simplify beforeEach configuration
Remove legacy settings for previous CI providers/hardware
2022-11-15 09:54:29 +00:00
Ingvar Stepanyan
ac0dc10bd5 Tests: convert mocha hooks (#3450) 2022-11-15 08:58:09 +00:00
Lovell Fuller
5740f4545e Expose GIF opts: interFrameMaxError, interPaletteMaxError #3401 2022-11-14 16:09:52 +00:00
Lovell Fuller
a9d692fb43 Reduce chance of race condition in test for... race condition 2022-11-13 10:16:47 +00:00
Lovell Fuller
df971207b8 Prevent possible race condition when reading metadata #3451 2022-11-13 10:04:55 +00:00
Ingvar Stepanyan
3a64a0529a Tests: run in parallel, move settings to config file (#3449)
This allows to easily invoke Mocha alone via `npx mocha`, or for e.g. VSCode Test Explorer to find and run tests with the correct settings automatically.
2022-11-10 21:48:18 +00:00
Peter Whidden
76cda885fb Docs: fix minor typo in resize properties (#3444) 2022-11-09 08:44:05 +00:00
Ingvar Stepanyan
1a563360c6 Fix errors for missing OpenJPEG (#3442)
Fixes couple of minor issues with JP2 errors:

1. The tests passed as false-positives even if regex is changed to arbitary pattern, because the promise returned from `assert.rejects` was ignored and the test ended prematurely. This is fixed by removing `{ ... }` around the test function body.
2. This, in turn, hid an issue with `toFile` not throwing the expected error message which was instead propagating `Error: VipsOperation: class "jp2ksave" not found` from libvips. This is now fixed by manually checking the extension before calling into libvips.
3. Pre-creating error instances like `errJp2Save` did is sometimes tempting, but is problematic for debugging because it hides the actual stacktrace of the error (the stacktrace is collected at the moment of `new Error` creation). This is now turned into a function that creates error with the right stack.
2022-11-08 19:53:14 +00:00
Lovell Fuller
ca22af203f Docs: canvas on Windows uses MSVCRT, conflicts with UCRT 2022-11-07 21:14:51 +00:00
Lovell Fuller
9fa516e849 Release v0.31.2 2022-11-04 09:44:37 +00:00
Lovell Fuller
12f472126d CI: Only pin Python version on x64 macOS and Windows
See commit 18be09f
2022-11-03 14:49:12 +00:00
Lovell Fuller
18be09f1d7 CI: Pin Python to 3.10
Python 3.11 removes support for opening files in
'universal newline' mode (e.g. 'rU'), however older
versions of node-gyp such as v6 still use it.
2022-11-03 14:40:16 +00:00
Lovell Fuller
b3c3290f90 Upgrade to libvips v8.13.3 2022-11-03 14:09:23 +00:00
Lovell Fuller
123f95c85a Bump devDeps 2022-11-03 12:50:58 +00:00
Lovell Fuller
5b0fba4c01 Ensure auto-rotate always works without resize #3422 2022-11-02 13:59:34 +00:00
Lovell Fuller
37f7ccfff4 CI: upgrade to checkout v3 2022-10-17 16:05:30 +01:00
Lovell Fuller
51811d06e2 Bump deps 2022-10-17 16:05:03 +01:00
Lovell Fuller
181731f8f4 Tests: increase timeout to 30s
Font discovery appears to be slooow on Windows
2022-10-17 15:54:43 +01:00
Gino Emiliozzi
ae79d26ead Docs: help clarify 'fit' is option name, not value (#3410) 2022-10-17 15:26:59 +01:00
Lovell Fuller
eacb8337fa Ensure manual flip, rotate, resize op order #3391 2022-10-01 11:55:29 +01:00
Lovell Fuller
99bf279de8 Release v0.31.1 2022-09-29 14:51:45 +01:00
Lovell Fuller
1b0eb6ab53 Tests: add assertion to existing scenario #3357 2022-09-29 14:21:37 +01:00
Lovell Fuller
891cf67d0b Upgrade to libvips v8.13.2 2022-09-29 14:19:58 +01:00
Lovell Fuller
eaf8a86bf2 Tests: increase timeout to 20s
Ignore unit coverage of fn used at install time
2022-09-27 14:25:34 +01:00
Lovell Fuller
3400976d61 Tests: ignore a branch for coverage check 2022-09-27 14:10:49 +01:00
Lovell Fuller
2d49f0e93e Tests: require 100% branch coverage to pass
Remove old coverage tooling, coveralls
2022-09-27 13:49:42 +01:00
Lovell Fuller
b0c69f1ee9 CI: use clearer job names 2022-09-27 10:02:20 +01:00
Lovell Fuller
27d0c35a01 CI: use clearer job names 2022-09-27 09:23:30 +01:00
Alex
32a22b5420 CI: GitHub Workflows security hardening (#3377)
Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-09-26 11:25:49 +01:00
Lovell Fuller
d1004eed02 Ensure greyscale images can be trimmed #3386 2022-09-26 10:15:25 +01:00
Lovell Fuller
70e6bb0162 Ensure close event occurs after end event #3313 2022-09-20 08:52:40 +01:00
Lovell Fuller
32aa3b4b20 Tests: bump/pin benchmark dependencies 2022-09-19 16:21:24 +01:00
Lovell Fuller
df24b30755 Tests: add tfjs to benchmark tests 2022-09-19 16:20:23 +01:00
Lovell Fuller
4de74bea94 Tests: remove assertions from benchmark code 2022-09-19 16:09:31 +01:00
Lovell Fuller
28b87db760 Ensure AVIF output is always 8-bit #3358 2022-09-14 13:33:47 +01:00
Lovell Fuller
fbd4970b57 Ensure auto-rotation works with shrink-on-load #3352
Fixes regression in 0.31.0
2022-09-07 14:17:40 +01:00
Lovell Fuller
f5da147a58 Docs: changelog and credit for #3349 2022-09-07 13:31:26 +01:00
Marcos Casagrande
eee0dd36d9 Ensure limitInputPixels uses uint64 (#3349) 2022-09-06 09:05:51 +01:00
Lovell Fuller
f7b29d7b59 CI: ensure prebuilds use Node API version 7 2022-09-05 10:14:40 +01:00
Lovell Fuller
0b806187fc Release v0.31.0 2022-09-05 09:55:27 +01:00
Lovell Fuller
c1393daa70 Expose unlimited option for HEIF input 2022-09-05 09:19:53 +01:00
Lovell Fuller
31c1cfb049 Docs: add note about GIF optimisation 2022-09-05 09:19:53 +01:00
Lovell Fuller
afc35c2208 Docs: update benchmark results for libvips v8.13.1 2022-09-05 09:19:53 +01:00
Lovell Fuller
6eb2add3bf Docs: refresh for 5cdb2b8 2022-09-04 13:17:02 +01:00
Lovell Fuller
5cdb2b83d5 Upgrade to libvips v8.13.1 2022-09-04 12:42:27 +01:00
Lovell Fuller
1eb66c0944 Tests: relax truncated PNG test assertion 2022-09-04 11:19:01 +01:00
Lovell Fuller
55c4d8807c Improve normalise op with use of histogram #200 2022-09-04 10:31:43 +01:00
Lovell Fuller
9a54a034e1 Tests: ensure truncated PNG test handles possible race 2022-09-04 10:28:24 +01:00
Lovell Fuller
f5109560d6 Standardise approach for string to enum conversion 2022-09-02 12:20:09 +01:00
Lovell Fuller
953a94885b Tests: run benchmarks in container via Docker 2022-09-01 14:58:39 +01:00
Lovell Fuller
0e3bd46ca3 Docs: clarify that metadata respects page/pages 2022-09-01 09:58:17 +01:00
Lovell Fuller
4b38f56d02 Docs: add avif and heif examples 2022-09-01 09:57:50 +01:00
Lovell Fuller
0fe857c5ac Docs: move serverless-esbuild to bundlers section 2022-08-24 17:42:56 +01:00
A. Sayef Reyadh
1bf06bd5b4 Docs: add info for serverless-esbuild users (#3235) 2022-08-24 17:37:58 +01:00
Lovell Fuller
6e3f4c3c92 Docs: changelog for #3332 2022-08-23 13:00:02 +01:00
Lovell Fuller
8583eb1235 Tests: update leak suppressions for latest versions 2022-08-23 12:32:39 +01:00
Mart
c3a852eecf Add trim option to provide a specific background colour (#3332)
Co-authored-by: Mart Jansink <mart@cinemait.nl>
2022-08-23 12:28:02 +01:00
Lovell Fuller
3a44748f49 Ensure PNG bitdepth can be set for non-palette output #3322 2022-08-22 14:57:12 +01:00
Lovell Fuller
e1bc8674fd Docs: clarify composite operation ordering 2022-08-21 19:53:04 +01:00
Lovell Fuller
a618ce7a15 Ensure image is unpremultiplied before composite #3334 2022-08-21 17:51:05 +01:00
Lovell Fuller
a44168c8c7 Docs: changelog and credit for #3303 2022-08-20 10:27:31 +01:00
Anton Marsden
74e3f73934 Expand linear operation to allow use of per-channel arrays #3303 2022-08-20 10:27:04 +01:00
Lovell Fuller
b9261c243c Bump devDeps, requires doc refresh 2022-08-20 09:23:24 +01:00
Lovell Fuller
cc9f91f37c Docs: note about macos fontconfig vs coretext 2022-08-18 16:41:33 +01:00
Lovell Fuller
212a6e7519 Ensure op ordering is respected where possible #3319
Emit warnings when previous ops might be ignored
Flip and flop now occur before rotate, if any
2022-08-18 16:41:33 +01:00
Kid
e547eaa180 Docs: fix broken link to Got stream documentation (#3323) 2022-08-07 14:25:36 +01:00
Lionel Tzatzkin
9a0d9eed74 Docs: correct syntax in sharpen example (#3310) 2022-07-29 12:11:16 +01:00
Lovell Fuller
dd56a9699e Docs: changelog and credit for #3252 2022-07-25 12:19:25 +01:00
brahima
ea7cf2a2ef Expose vips_text to create an image containing rendered text (#3252) 2022-07-25 11:32:10 +01:00
Lovell Fuller
76c4c51e2a Remove previously-deprecated reductionEffort and speed options 2022-07-24 11:18:16 +01:00
Lovell Fuller
b46ab510da Add Buffer and Stream support to tile output #2238 2022-07-24 11:06:41 +01:00
Lovell Fuller
3e327a586c Docs: add section about font discovery 2022-07-23 16:31:00 +01:00
Lovell Fuller
974fab946e Bump devDeps 2022-07-23 16:21:42 +01:00
Lovell Fuller
f998a8f249 Upgrade to libvips v8.13.0 2022-07-23 10:04:14 +01:00
Lovell Fuller
be331e958e CI: upgrade to macOS 11 2022-07-22 23:20:33 +01:00
Lovell Fuller
254944f8ab CI: install Noto font via package manager 2022-07-18 13:35:22 +01:00
Lovell Fuller
f1e640d231 Tests: catch potential failures during tile unzip 2022-07-14 11:13:59 +01:00
Lovell Fuller
c295f06a6f Ensure only props owned by EXIF Object are parsed #3292 2022-07-13 21:33:06 +01:00
Lovell Fuller
6288c7bced Expose reoptimise palette option for GIF output 2022-07-12 21:12:31 +01:00
Lovell Fuller
d247c02762 Add mixed and minSize animation options for WebP output 2022-07-12 14:51:03 +01:00
Lovell Fuller
1b84ccbbe9 Ensure fileSuffix supports suffix-less loaders 2022-07-11 20:56:19 +01:00
Lovell Fuller
e4160c684d Docs: add timeout example 2022-07-11 11:12:32 +01:00
Lovell Fuller
905518fab0 Add input fileSuffix and output alias to format #2642 2022-07-11 10:45:19 +01:00
Lovell Fuller
8ff33763ce Ensure OpenSlide+FITS input works with custom libvips #3226 2022-07-08 22:25:39 +01:00
Lovell Fuller
cbf741cac7 Ensure trim is no-op when it would reduce to nothing #3223 2022-07-08 21:06:58 +01:00
Lovell Fuller
6c2e2be41d CI: upgrade Linux arm64 environment to Node.js 14/18 2022-07-05 19:42:05 +01:00
Lovell Fuller
e0d3c6e05d Use bounding box of alpha+non-alpha for trim op #2166 2022-07-05 18:19:17 +01:00
Lovell Fuller
e3cab7f10f CI: fix path to gcc toolset 2022-06-29 12:40:04 +01:00
Lovell Fuller
204463ffbb CI: ensure gcc 11 toolset is available on the PATH 2022-06-29 12:26:16 +01:00
Lovell Fuller
1bcd3700c5 CI: Node.js 18 does not support CentOS 7, upgrade to Rocky 8 2022-06-29 12:18:41 +01:00
Lovell Fuller
c99a11cff5 CI: add Node.js 18, remove 12 2022-06-29 11:52:41 +01:00
Lovell Fuller
81c74f57e0 Re-introduce support for greyscale ICC profiles #3114 2022-06-29 11:41:36 +01:00
Lovell Fuller
7a8ab452c5 Add support for WebP and PackBits compression with TIFF output #3198 2022-06-29 11:35:58 +01:00
Lovell Fuller
bb91912883 Drop support for Node.js 12, now requires >= 14.15.0 2022-06-29 10:32:25 +01:00
Kleis Auke Wolthuizen
afc4c5bf79 Upgrade to libvips v8.13.0-rc1 (#3230)
* Switch from decompress-zip to extract-zip

The former seems to hang when unzipping a ZIP64 file that uses
the general purpose bit flag 3 as file entry.

See: https://github.com/thejoshwolfe/yauzl#no-streaming-unzip-api

* Prefer to call via static member instead

Makes it clearer that a static method is being called.

* `flatten-orange.jpg`: save without chroma subsampling

To ensure no down-scaling of the Cr/Cb channels.
2022-06-26 22:39:29 +01:00
Lovell Fuller
e40a881ab4 Release v0.30.7 2022-06-22 16:44:16 +01:00
Lovell Fuller
c1b13adac3 Bump deps 2022-06-22 11:53:22 +01:00
Lovell Fuller
29e09898f7 Docs: add examples of custom binary locations 2022-06-22 11:50:30 +01:00
Lovell Fuller
853a20358e Install: add help for possible worker thread problem #3268 2022-06-21 08:22:05 +01:00
Lovell Fuller
8bb30d7801 Docs: changelog and credit #3261 #3267 2022-06-21 07:35:28 +01:00
Blayne Chard
a333b87f5d Prevent upsampling via libwebp (#3267) 2022-06-20 10:49:53 +01:00
AlexanderTheGrey
4662527a17 Allow WebP encoding effort of 0 (#3261) 2022-06-17 08:22:51 +01:00
Lovell Fuller
b10d8f89ca Docs: add example of multi-arch within same install tree 2022-06-10 12:51:44 +01:00
Oleg Andreyev
f903e1465e Docs: clarify wording of resize background option 2022-06-08 12:56:29 +01:00
Lovell Fuller
a75718565c Ensure composite can tile with outside resize #3227 2022-06-08 12:39:00 +01:00
Fonger
4d82331bf6 docs(input): correct getNormalSize with EXIF orientation example (#3241) 2022-05-31 08:59:15 +01:00
Lovell Fuller
b91875d3d9 Release v0.30.6 2022-05-30 08:56:14 +01:00
Lovell Fuller
a0568ec0c3 Allow values for limitInputPixels larger than 32-bit #3238 2022-05-28 08:35:17 +01:00
Lovell Fuller
48e3ea5e29 Ensure brew-installed vips can be detected #3239 2022-05-26 22:58:24 +01:00
Lovell Fuller
93b29057e4 Docs: changelog and credit for #3160 2022-05-24 21:14:13 +01:00
Lovell Fuller
db654de385 Release v0.30.5 2022-05-23 12:19:02 +01:00
Lovell Fuller
a6aeef612b Install: pass PKG_CONFIG_PATH via env rather than substitution 2022-05-23 12:12:19 +01:00
Lovell Fuller
7bf6cbd669 Docs: correct links to libvips documentation 2022-05-22 12:14:24 +01:00
Lovell Fuller
04c31b35a7 Install: warn about filesystem owner running npm v8+ as root 2022-05-15 19:43:45 +01:00
Lovell Fuller
ee9cdb6598 Bump deps 2022-05-15 17:20:33 +01:00
Lovell Fuller
8960eb8309 Docs: changelog entry for #3218 2022-05-15 17:12:51 +01:00
jakob0fischl
54d9dc46f5 Fix rotate-then-extract for EXIF orientation 2 (#3218) 2022-05-15 12:26:57 +01:00
Joona Heinikoski
51b4a7c564 Add support for --libc flag to improve cross-platform install (#3160)
This deprecates the libc-as-suffix approach of --platform=linuxmusl
2022-04-28 11:24:45 +01:00
Lovell Fuller
5b03579e5c Docs: more details about concurrency, parallelism, threads 2022-04-25 14:40:55 +01:00
Lovell Fuller
58c2af3251 Docs: improve output format info for toBuffer 2022-04-25 11:41:06 +01:00
Lovell Fuller
ee948ac6fa Docs: changelog and credit for #3196 2022-04-25 11:33:05 +01:00
Ankur Parihar
66a3ce5e55 Allow installation of prebuilt libvips binary from filesystem (#3196) 2022-04-24 18:47:57 +01:00
David Humphrey
75e5afcd42 Docs: fix typo in gif example (#3201) 2022-04-22 16:09:28 +01:00
Lovell Fuller
d396a4e65d Release v0.30.4 2022-04-18 10:14:31 +01:00
Lovell Fuller
ae1dbcdf4e Bump deps 2022-04-18 10:11:30 +01:00
Lovell Fuller
4c29368b51 Docs: EXIF metadata unsupported for TIFF output #3074 2022-04-18 09:53:35 +01:00
Lovell Fuller
36e55969d2 Docs: mention npm's foreground-scripts option to aid debugging 2022-04-18 09:51:27 +01:00
Lovell Fuller
985e881e7a Bump deps 2022-04-05 21:14:09 +01:00
Lovell Fuller
0b116715aa Docs: changelog for #3178 2022-04-05 21:11:06 +01:00
Chris Hranj
9deac83322 Add missing file name to 'Input file is missing' error message (#3178) 2022-04-05 11:35:01 +01:00
Lovell Fuller
5d36f5f699 Improve error message for SVG render above limit #3167 2022-04-04 14:20:04 +01:00
Lovell Fuller
926572b41e Control sensitivity to invalid images via failOn
Deprecates failOnError, equivalent to failOn=warning
2022-04-04 12:27:45 +01:00
Lovell Fuller
d0c8e95641 Docs: expand info about use with worker threads 2022-03-30 10:09:15 +01:00
Lovell Fuller
b0ca23c3e7 Docs: changelog and credit for #3146 2022-03-28 21:32:38 +01:00
codepage949
c3a0d5f5d0 Add support for TypedArray input with byteOffset and length 2022-03-27 20:18:58 +01:00
Lovell Fuller
1d36936954 Ensure create has correct bit depth and colourspace #3139 2022-03-22 19:48:02 +00:00
Lovell Fuller
b609df4b48 Release v0.30.3 2022-03-14 11:54:15 +00:00
Lovell Fuller
c567d3b9ab Bump devDeps 2022-03-14 11:52:45 +00:00
Marvin ROGER
27d9fe2a4e Add additional debugging hint on symbol error (#3132) 2022-03-14 10:19:02 +00:00
Lovell Fuller
ac883c5215 Docs: composite image supports the animated property #3135 2022-03-14 08:53:00 +00:00
Lovell Fuller
e8720c9374 Docs: use SVG favicon with a PNG fallback
Remove unused apple-touch icons
2022-03-13 14:22:05 +00:00
Lovell Fuller
42e45d842a Docs: add more examples (composite, greyscale, tint) 2022-03-12 20:33:15 +00:00
Lovell Fuller
72fd8abe2c Docs: add section on (preventing) bundling with esbuild 2022-03-12 13:17:36 +00:00
Lovell Fuller
ac18bbbc7c Ensure backwards-compatibility of JSDoc introduced in ea599ad 2022-03-11 19:07:11 +00:00
Lovell Fuller
fcbe4e1e01 Tests: remove possible race condition
Simplify JP2 quality size check
2022-03-09 19:55:32 +00:00
Lovell Fuller
9280742385 CI: update FreeBSD environment 2022-03-09 19:26:43 +00:00
Lovell Fuller
7a1a1cf9e8 Docs: add/correct some operation examples 2022-03-09 19:18:41 +00:00
Lovell Fuller
ea599ade10 Allow sharpen options to be provided as an Object
Also exposes x1, y2, y3 parameters #2561 #2935
2022-03-09 19:07:08 +00:00
Lovell Fuller
1de49f3ed8 Docs: refresh for metadata example #3127 2022-03-09 16:02:27 +00:00
Rasmus Schultz
4ac65054bc Docs: add example of how to get the right-side-up width/height (#3127)
See #3124
2022-03-09 15:59:09 +00:00
Lovell Fuller
23033e2050 Prevent double unpremultiply with some composite blends 2022-03-04 23:17:07 +00:00
Lovell Fuller
dd3b78272a Docs: remove experimental status from existing stats properties 2022-03-04 07:58:03 +00:00
Lovell Fuller
80d169b7c2 Release v0.30.2 2022-03-02 11:24:35 +00:00
Lovell Fuller
003279a0b0 CI: switch 32-bit Windows from Appveyor to Actions 2022-03-02 11:16:21 +00:00
Lovell Fuller
af80d7e389 Improve error message for missing file that might be SVG 2022-03-02 09:58:55 +00:00
Lovell Fuller
21a960796c Ignore greyscale ICC profiles due to lcms bug #3112 2022-02-28 11:28:08 +00:00
Lovell Fuller
fc3b4a683d Expand pkgconfig search path for wider BSD support #3106 2022-02-27 09:39:21 +00:00
Lovell Fuller
808133e7dc Docs: changelog entry for #3110 2022-02-26 19:40:17 +00:00
Lovell Fuller
801b6fea6c Bump devDeps 2022-02-26 19:39:46 +00:00
Kleis Auke Wolthuizen
c2ecde6a16 Windows: ensure C++ runtime is linked statically (#3110)
And remove the empty invalid parameter handler, which should
be present in the C layer instead.

This partially reverts commit
659cdabd8e,
the added test case in that commit is still preserved.
2022-02-26 19:15:37 +00:00
Lovell Fuller
55efe5602b Bump deps 2022-02-16 19:12:27 +00:00
Lovell Fuller
c62002554b Improve performance and accuracy of multi-image composite #2286 2022-02-16 19:04:23 +00:00
Lovell Fuller
7f83ecd255 Issue templates: small formatting fixes 2022-02-15 10:54:36 +00:00
Lovell Fuller
dc5f4dcd28 Issue templates: improve guidance, increase filtering 2022-02-15 10:50:26 +00:00
Lovell Fuller
735793ba99 Release v0.30.1 2022-02-09 16:38:21 +00:00
Lovell Fuller
47792df689 Docs: update interpolator links 2022-02-09 15:45:05 +00:00
Lovell Fuller
5c6cdfaece Docs: changelog entry for #3083 2022-02-09 15:27:52 +00:00
Kleis Auke Wolthuizen
115a6b10f6 Ensure affineInterpolator is correctly finalised (#3083) 2022-02-09 14:45:11 +00:00
Lovell Fuller
4feee506cf Docs: changelog entry for #3081 2022-02-08 22:08:07 +00:00
Kleis Auke Wolthuizen
83db5f8a2a Ensure withoutReduction does not interfere with contain/crop/embed (#3081) 2022-02-08 21:22:23 +00:00
Kleis Auke Wolthuizen
7eb5efa3a3 Update tests and comments after 7faacd9 (#3080) 2022-02-08 21:12:38 +00:00
Lovell Fuller
5a9f89fe06 Docs: update references to default branch 2022-02-08 21:01:40 +00:00
Lovell Fuller
02e0c2dfc9 Tests: arm64 requires slight threshold increase 2022-02-08 21:00:30 +00:00
Lovell Fuller
968d9d7008 Bump devDeps 2022-02-08 18:29:25 +00:00
Kleis Auke Wolthuizen
7faacd91b0 Avoid fastShrinkOnLoad workaround 2022-02-08 18:20:25 +00:00
Lovell Fuller
154eaff4ec Issue template: include npm v8+ 2022-02-08 18:20:11 +00:00
Lovell Fuller
424660278d Allow use of toBuffer+toFile w/ same instance #3044 2022-02-03 22:01:46 +00:00
Lovell Fuller
2b01951306 Skip JPEG shrink-on-load for known rounding errors #3066
Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl>
2022-02-03 21:19:48 +00:00
215 changed files with 9104 additions and 3422 deletions

View File

@@ -3,76 +3,76 @@ version: 2.1
workflows:
build:
jobs:
- linux-arm64-glibc-node-12:
- linux-arm64-glibc-node-14:
filters:
tags:
only: /^v.*/
- linux-arm64-musl-node-12:
- linux-arm64-musl-node-14:
filters:
tags:
only: /^v.*/
- linux-arm64-glibc-node-16:
- linux-arm64-glibc-node-18:
filters:
tags:
only: /^v.*/
- linux-arm64-musl-node-16:
- linux-arm64-musl-node-18:
filters:
tags:
only: /^v.*/
jobs:
linux-arm64-glibc-node-12:
linux-arm64-glibc-node-14:
resource_class: arm.medium
machine:
image: ubuntu-2004:202101-01
image: ubuntu-2004:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl fonts-noto-core"
sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_12.x sid main' >/etc/apt/sources.list.d/nodesource.list"
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_14.x sid main' >/etc/apt/sources.list.d/nodesource.list"
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
- run: sudo docker exec sharp sh -c "npm test"
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 5 --upload=$prebuild_upload\" || true"
linux-arm64-glibc-node-16:
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 7 --upload=$prebuild_upload\" || true"
linux-arm64-glibc-node-18:
resource_class: arm.medium
machine:
image: ubuntu-2004:202101-01
image: ubuntu-2004:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --workdir /mnt/sharp arm64v8/debian:bullseye
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl fonts-noto-core"
sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_16.x sid main' >/etc/apt/sources.list.d/nodesource.list"
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_18.x sid main' >/etc/apt/sources.list.d/nodesource.list"
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
sudo docker cp . sharp:/mnt/sharp/.
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test"
linux-arm64-musl-node-12:
linux-arm64-musl-node-14:
resource_class: arm.medium
machine:
image: ubuntu-2004:202101-01
image: ubuntu-2004:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12-alpine3.11
sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14-alpine3.12
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
- run: sudo docker exec sharp sh -c "npm test"
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 5 --upload=$prebuild_upload\" || true"
linux-arm64-musl-node-16:
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 7 --upload=$prebuild_upload\" || true"
linux-arm64-musl-node-18:
resource_class: arm.medium
machine:
image: ubuntu-2004:202101-01
image: ubuntu-2004:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --workdir /mnt/sharp node:16-alpine3.11
sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
sudo docker run -dit --name sharp --workdir /mnt/sharp node:18-alpine3.14
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
sudo docker cp . sharp:/mnt/sharp/.
- run: sudo docker exec sharp sh -c "npm install --build-from-source"

View File

@@ -1,15 +1,15 @@
freebsd_instance:
image_family: freebsd-13-0-snap
image_family: freebsd-14-0-snap
task:
name: FreeBSD 13.0
name: FreeBSD
env:
IGNORE_OSVERSION: yes
skip_notifications: true
prerequisites_script:
- pkg update -f
- pkg upgrade -y
- pkg install -y pkgconf vips node npm
- pkg install -y devel/pkgconf graphics/vips www/node16 www/npm
install_script:
- npm install --build-from-source --unsafe-perm
test_script:

View File

@@ -16,29 +16,30 @@ If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exist
it's probably fastest to add a comment to it about your requirement.
Implementation is usually straightforward if libvips
[already supports](https://libvips.github.io/libvips/API/current/func-list.html)
[already supports](https://www.libvips.org/API/current/func-list.html)
the feature you need.
## Submit a Pull Request to fix a bug
Thank you! To prevent the problem occurring again, please add unit tests that would have failed.
Please select the `master` branch as the destination for your Pull Request so your fix can be included in the next minor release.
Please select the `main` branch as the destination for your Pull Request so your fix can be included in the next minor release.
Please squash your changes into a single commit using a command like `git rebase -i upstream/master`.
Please squash your changes into a single commit using a command like `git rebase -i upstream/main`.
To test C++ changes, you can compile the module using `npm install --build-from-source` and then run the tests using `npm test`.
## Submit a Pull Request with a new feature
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/master/test/unit) to cover your new feature.
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/main/test/unit) to cover your new feature.
A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
Please also update the [TypeScript definitions](https://github.com/lovell/sharp/tree/main/lib/index.d.ts), along with the [type definition tests](https://github.com/lovell/sharp/tree/main/test/types/sharp.test-d.ts).
Where possible, the functional tests use gradient-based perceptual hashes
based on [dHash](http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html)
to compare expected vs actual images.
You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/master/package.json#L5).
You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/main/package.json#L5).
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
@@ -93,5 +94,5 @@ Please feel free to ask any questions via a
[new issue](https://github.com/lovell/sharp/issues/new).
If you're unable to post details publicly, please
[e-mail](https://github.com/lovell/sharp/blob/master/package.json#L5)
[e-mail](https://github.com/lovell/sharp/blob/main/package.json#L5)
for private, paid consulting.

View File

@@ -5,12 +5,24 @@ labels: enhancement
---
What are you trying to achieve?
## Feature request
Have you searched for similar feature requests?
### What are you trying to achieve?
What would you expect the API to look like?
<!-- Please provide context here. -->
What alternatives have you considered?
### When you searched for similar feature requests, what did you find that might be related?
Is there a sample image that helps explain?
<!-- Please demonstrate your research here. -->
### What would you expect the API to look like?
<!-- Please provide your suggestions here. -->
### What alternatives have you considered?
<!-- Please provide your ideas here. -->
### Please provide sample image(s) that help explain this feature
<!-- Please provide links to one or more images here. -->

View File

@@ -5,18 +5,41 @@ labels: installation
---
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)?
<!-- Please try to answer as many of these questions as possible. -->
Have you ensured the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime?
## Possible install-time or require-time problem
Are you using the latest version? Is the version currently in use as reported by `npm ls sharp` the same as the latest version as reported by `npm view sharp dist-tags.latest`?
<!-- Please place an [x] in the box to confirm. -->
- [ ] I have read the [documentation relating to installation](https://sharp.pixelplumbing.com/install).
- [ ] I have ensured that the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime.
### Are you using the latest version of sharp?
<!-- Please place an [x] in the box to confirm. -->
- [ ] I am using the latest version of `sharp` as reported by `npm view sharp dist-tags.latest`.
If you cannot confirm this, please upgrade to the latest version and try again before opening an issue.
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
### Is this a problem with filesystem permissions?
If you are using npm v6 or earlier and installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
If you are using npm v7, does the user running `npm install` own the directory it is run in?
If you are using npm v7 or later, does the user running `npm install` own the directory it is run in?
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag?
What is the complete output of running `npm install --verbose sharp`? Have you checked this output for useful error messages?
### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory?
What is the output of running `npx envinfo --binaries --system`?
<details>
<!-- Please provide output of `npm install --verbose --foreground-scripts sharp` in an empty directory here. -->
</details>
### What is the output of running `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp`?
<!-- Please provide output of `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp` here. -->

View File

@@ -7,14 +7,43 @@ labels: triage
<!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. -->
Are you using the latest version? Is the version currently in use as reported by `npm ls sharp` the same as the latest version as reported by `npm view sharp dist-tags.latest`?
## Possible bug
What are the steps to reproduce?
### Is this a possible bug in a feature of sharp, unrelated to installation?
What is the expected behaviour?
<!-- Please place an [x] in the box to confirm. -->
Are you able to provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem?
- [ ] Running `npm install sharp` completes without error.
- [ ] Running `node -e "require('sharp')"` completes without error.
Are you able to provide a sample image that helps explain the problem?
If you cannot confirm both of these, please open an [installation issue](https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md) instead.
What is the output of running `npx envinfo --binaries --system`?
### Are you using the latest version of sharp?
<!-- Please place an [x] in the box to confirm. -->
- [ ] I am using the latest version of `sharp` as reported by `npm view sharp dist-tags.latest`.
If you cannot confirm this, please upgrade to the latest version and try again before opening an issue.
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
### What is the output of running `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp`?
<!-- Please provide output of the above command here. -->
### What are the steps to reproduce?
<!-- Please enter steps to reproduce here. -->
### What is the expected behaviour?
<!-- Please enter the expected behaviour here. -->
### Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem
<!-- Please provide either formatted code or a link to a repo/gist that allows someone else to reproduce here. -->
### Please provide sample image(s) that help explain this problem
<!-- Please provide links to one or more images here. -->

View File

@@ -7,10 +7,20 @@ labels: question
<!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. -->
What are you trying to achieve?
## Question about an existing feature
Have you searched for similar questions?
### What are you trying to achieve?
Are you able to provide a minimal, standalone code sample that demonstrates this question?
<!-- Please provide context here. -->
Are you able to provide a sample image that helps explain the question?
### When you searched for similar issues, what did you find that might be related?
<!-- Please demonstrate your research here. -->
### Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this question
<!-- Please provide either formatted code or a link to a repo/gist that helps someone else understand here. -->
### Please provide sample image(s) that help explain this question
<!-- Please provide links to one or more images here. -->

View File

@@ -2,29 +2,33 @@ name: CI (MacStadium)
on:
- push
- pull_request
permissions: {}
jobs:
CI:
permissions:
contents: write # for npx prebuild to make release
name: Node.js ${{ matrix.nodejs_version }} ${{ matrix.nodejs_arch }} ${{ matrix.prebuild && '- prebuild' }}
runs-on: macos-m1
strategy:
fail-fast: false
matrix:
include:
- nodejs_version: 12
nodejs_architecture: x64
- nodejs_version: 16
nodejs_architecture: arm64
- nodejs_version: 14
nodejs_arch: x64
- nodejs_version: 18
nodejs_arch: arm64
prebuild: true
defaults:
run:
shell: /usr/bin/arch -arch arm64e /bin/bash -l {0}
steps:
- name: Dependencies
uses: actions/setup-node@v2
- name: Dependencies (Node.js)
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_architecture }}
architecture: ${{ matrix.nodejs_arch }}
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install
run: npm install --build-from-source --unsafe-perm
- name: Test
@@ -33,4 +37,4 @@ jobs:
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env:
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
run: npx prebuild --runtime napi --target 5
run: npx prebuild --runtime napi --target 7

View File

@@ -2,66 +2,95 @@ name: CI (GitHub)
on:
- push
- pull_request
permissions: {}
jobs:
CI:
permissions:
contents: write # for npx prebuild to make release
name: ${{ matrix.container || matrix.os }} - Node.js ${{ matrix.nodejs_version }} ${{ matrix.nodejs_arch }} ${{ matrix.prebuild && '- prebuild' }}
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
container: centos:7
nodejs_version: 12
coverage: true
prebuild: true
- os: ubuntu-20.04
- os: ubuntu-22.04
container: centos:7
nodejs_version: 14
- os: ubuntu-20.04
prebuild: true
- os: ubuntu-22.04
container: centos:7
nodejs_version: 16
- os: ubuntu-20.04
container: node:12-alpine3.11
- os: ubuntu-22.04
container: rockylinux:8
nodejs_version: 18
- os: ubuntu-22.04
container: node:14-alpine3.12
prebuild: true
- os: ubuntu-20.04
container: node:14-alpine3.11
- os: ubuntu-20.04
container: node:14-alpine3.13
- os: ubuntu-20.04
container: node:16-alpine3.11
- os: macos-10.15
nodejs_version: 12
prebuild: true
- os: macos-10.15
- os: ubuntu-22.04
container: node:16-alpine3.12
- os: ubuntu-22.04
container: node:18-alpine3.14
- os: macos-11
nodejs_version: 14
- os: macos-10.15
nodejs_version: 16
- os: windows-2019
nodejs_version: 12
prebuild: true
nodejs_arch: x64
- os: macos-11
nodejs_version: 16
nodejs_arch: x64
- os: macos-11
nodejs_version: 18
nodejs_arch: x64
- os: windows-2019
nodejs_version: 14
nodejs_arch: x86
prebuild: true
- os: windows-2019
nodejs_version: 16
nodejs_arch: x86
- os: windows-2019
nodejs_version: 18
nodejs_arch: x86
- os: windows-2019
nodejs_version: 14
nodejs_arch: x64
prebuild: true
- os: windows-2019
nodejs_version: 16
nodejs_arch: x64
- os: windows-2019
nodejs_version: 18
nodejs_arch: x64
steps:
- name: Dependencies (Linux glibc)
if: contains(matrix.container, 'centos')
run: |
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
yum install -y centos-release-scl
yum install -y devtoolset-10-gcc-c++ make git python3 nodejs
echo "/opt/rh/devtoolset-10/root/usr/bin" >> $GITHUB_PATH
yum install -y devtoolset-11-gcc-c++ make git python3 nodejs fontconfig google-noto-sans-fonts
echo "/opt/rh/devtoolset-11/root/usr/bin" >> $GITHUB_PATH
- name: Dependencies (Rocky Linux glibc)
if: contains(matrix.container, 'rockylinux')
run: |
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
dnf install -y gcc-toolset-11-gcc-c++ make git python3 nodejs fontconfig google-noto-sans-fonts
echo "/opt/rh/gcc-toolset-11/root/usr/bin" >> $GITHUB_PATH
- name: Dependencies (Linux musl)
if: contains(matrix.container, 'alpine')
run: apk add build-base git python3 --update-cache
- name: Dependencies (macOS, Windows)
run: apk add build-base git python3 font-noto --update-cache
- name: Dependencies (Python 3.10 - macOS, Windows)
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
uses: actions/setup-node@v1
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Dependencies (Node.js - macOS, Windows)
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }}
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Fix working directory ownership
if: matrix.container
run: chown root.root .
@@ -69,13 +98,8 @@ jobs:
run: npm install --build-from-source --unsafe-perm
- name: Test
run: npm test
- name: Coverage
if: matrix.coverage
uses: coverallsapp/github-action@v1.1.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Prebuild
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env:
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
run: npx prebuild --runtime napi --target 5
run: npx prebuild --runtime napi --target 7

7
.mocharc.jsonc Normal file
View File

@@ -0,0 +1,7 @@
{
"parallel": true,
"slow": 1000,
"timeout": 30000,
"require": "./test/beforeEach.js",
"spec": "./test/unit/*.js"
}

View File

@@ -1,6 +1,6 @@
# sharp
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module
is to convert large images in common formats to
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most modern macOS, Windows and Linux systems running Node.js >= 12.13.0
Most modern macOS, Windows and Linux systems running Node.js >= 14.15.0
do not require any additional install or runtime dependencies.
## Documentation
@@ -95,15 +95,14 @@ readableStream
## Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
[![Node-API v5](https://img.shields.io/badge/Node--API-v5-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
## Licensing
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Lovell Fuller and contributors.
Copyright 2013 Lovell Fuller and others.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,17 +0,0 @@
os: Visual Studio 2019
version: "{build}"
build: off
platform: x86
environment:
matrix:
- nodejs_version: "12"
prebuild: true
- nodejs_version: "14"
- nodejs_version: "16"
install:
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)
- npm install --build-from-source
test_script:
- npm test
on_success:
- if [%prebuild%] == [true] if [%APPVEYOR_REPO_TAG%] == [true] npx prebuild --runtime napi --target 5

View File

@@ -15,10 +15,11 @@
'_ALLOW_KEYWORD_MACROS'
],
'sources': [
'src/libvips/cplusplus/VError.cpp',
'src/libvips/cplusplus/VConnection.cpp',
'src/libvips/cplusplus/VError.cpp',
'src/libvips/cplusplus/VImage.cpp',
'src/libvips/cplusplus/VInterpolate.cpp',
'src/libvips/cplusplus/VImage.cpp'
'src/libvips/cplusplus/VRegion.cpp'
],
'include_dirs': [
'<(sharp_vendor_dir)/include',
@@ -39,7 +40,6 @@
'VCCLCompilerTool': {
'ExceptionHandling': 1,
'Optimization': 1,
'RuntimeLibrary': '2', # /MD
'WholeProgramOptimization': 'true'
},
'VCLibrarianTool': {
@@ -70,7 +70,7 @@
}, {
'target_name': 'sharp-<(platform_and_arch)',
'defines': [
'NAPI_VERSION=5'
'NAPI_VERSION=7'
],
'dependencies': [
'<!(node -p "require(\'node-addon-api\').gyp")',
@@ -179,7 +179,7 @@
],
'xcode_settings': {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
'MACOSX_DEPLOYMENT_TARGET': '10.9',
'MACOSX_DEPLOYMENT_TARGET': '10.13',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'GCC_ENABLE_CPP_RTTI': 'YES',
'OTHER_CPLUSPLUSFLAGS': [
@@ -206,7 +206,6 @@
'VCCLCompilerTool': {
'ExceptionHandling': 1,
'Optimization': 1,
'RuntimeLibrary': '2', # /MD
'WholeProgramOptimization': 'true'
},
'VCLibrarianTool': {

View File

@@ -1,6 +1,6 @@
# sharp
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module
is to convert large images in common formats to
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most modern macOS, Windows and Linux systems running Node.js >= 12.13.0
Most modern macOS, Windows and Linux systems running Node.js >= 14.15.0
do not require any additional install or runtime dependencies.
### Formats
@@ -63,14 +63,18 @@ PNG filtering is disabled by default,
which for diagrams and line art often produces the same result
as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
The file size of animated GIF output is optimised
without having to use separate command line tools such as
[gifsicle](https://www.lcdf.org/gifsicle/).
### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
### Licensing
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Lovell Fuller and contributors.
Copyright 2013 Lovell Fuller and others.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,14 +1,11 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## removeAlpha
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
See also [flatten][1].
See also [flatten](/api-operation#flatten).
### Examples
```javascript
**Example**
```js
sharp('rgba.png')
.removeAlpha()
.toFile('rgb.png', function(err, info) {
@@ -16,61 +13,62 @@ sharp('rgba.png')
});
```
Returns **Sharp**
## ensureAlpha
Ensure the output image has an alpha transparency channel.
If missing, the added alpha channel will have the specified
transparency level, defaulting to fully-opaque (1).
This is a no-op if the image already has an alpha channel.
### Parameters
* `alpha` **[number][2]** alpha transparency level (0=fully-transparent, 1=fully-opaque) (optional, default `1`)
**Throws**:
### Examples
- <code>Error</code> Invalid alpha transparency level
```javascript
**Since**: 0.21.2
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [alpha] | <code>number</code> | <code>1</code> | alpha transparency level (0=fully-transparent, 1=fully-opaque) |
**Example**
```js
// rgba.png will be a 4 channel image with a fully-opaque alpha channel
await sharp('rgb.jpg')
.ensureAlpha()
.toFile('rgba.png')
```
```javascript
**Example**
```js
// rgba is a 4 channel image with a fully-transparent alpha channel
const rgba = await sharp(rgb)
.ensureAlpha(0)
.toBuffer();
```
* Throws **[Error][3]** Invalid alpha transparency level
Returns **Sharp**
**Meta**
* **since**: 0.21.2
## extractChannel
Extract a single channel from a multi-channel image.
### Parameters
* `channel` **([number][2] | [string][4])** zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
**Throws**:
### Examples
- <code>Error</code> Invalid channel
```javascript
| Param | Type | Description |
| --- | --- | --- |
| channel | <code>number</code> \| <code>string</code> | zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`. |
**Example**
```js
// green.jpg is a greyscale image containing the green channel of the input
await sharp(input)
.extractChannel('green')
.toFile('green.jpg');
```
```javascript
**Example**
```js
// red1 is the red value of the first pixel, red2 the second pixel etc.
const [red1, red2, ...] = await sharp(input)
.extractChannel(0)
@@ -78,45 +76,46 @@ const [red1, red2, ...] = await sharp(input)
.toBuffer();
```
* Throws **[Error][3]** Invalid channel
Returns **Sharp**
## joinChannel
Join one or more channels to the image.
The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
Channel ordering follows vips convention:
* sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
* CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
- sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
- CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
Buffers may be any of the image formats supported by sharp.
For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
### Parameters
* `images` **([Array][5]<([string][4] | [Buffer][6])> | [string][4] | [Buffer][6])** one or more images (file paths, Buffers).
* `options` **[Object][7]** image options, see `sharp()` constructor.
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
| Param | Type | Description |
| --- | --- | --- |
| images | <code>Array.&lt;(string\|Buffer)&gt;</code> \| <code>string</code> \| <code>Buffer</code> | one or more images (file paths, Buffers). |
| options | <code>Object</code> | image options, see `sharp()` constructor. |
* Throws **[Error][3]** Invalid parameters
Returns **Sharp**
## bandbool
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
### Parameters
* `boolOp` **[string][4]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
| Param | Type | Description |
| --- | --- | --- |
| boolOp | <code>string</code> | one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively. |
**Example**
```js
sharp('3-channel-rgb-input.png')
.bandbool(sharp.bool.and)
.toFile('1-channel-output.png', function (err, info) {
@@ -124,22 +123,4 @@ sharp('3-channel-rgb-input.png')
// If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
// then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
});
```
* Throws **[Error][3]** Invalid parameters
Returns **Sharp**
[1]: /api-operation#flatten
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[6]: https://nodejs.org/api/buffer.html
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
```

View File

@@ -1,22 +1,26 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## tint
Tint the image using the provided chroma while preserving the image luminance.
An alpha channel may be present and will be unchanged by the operation.
### Parameters
* `rgb` **([string][1] | [Object][2])** parsed by the [color][3] module to extract chroma values.
**Throws**:
<!---->
- <code>Error</code> Invalid parameter
* Throws **[Error][4]** Invalid parameter
Returns **Sharp**
| Param | Type | Description |
| --- | --- | --- |
| rgb | <code>string</code> \| <code>Object</code> | parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values. |
**Example**
```js
const output = await sharp(input)
.tint({ r: 255, g: 240, b: 16 })
.toBuffer();
```
## greyscale
Convert to 8-bit greyscale; 256 shades of grey.
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
By default the output image will be web-friendly sRGB and contain three (identical) color channels.
@@ -24,38 +28,50 @@ This may be overridden by other sharp operations such as `toColourspace('b-w')`,
which will produce an output image containing one color channel.
An alpha channel may be present, and will be unchanged by the operation.
### Parameters
* `greyscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp**
| Param | Type | Default |
| --- | --- | --- |
| [greyscale] | <code>Boolean</code> | <code>true</code> |
**Example**
```js
const output = await sharp(input).greyscale().toBuffer();
```
## grayscale
Alternative spelling of `greyscale`.
### Parameters
* `grayscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp**
| Param | Type | Default |
| --- | --- | --- |
| [grayscale] | <code>Boolean</code> | <code>true</code> |
## pipelineColourspace
Set the pipeline colourspace.
The input image will be converted to the provided colourspace at the start of the pipeline.
All operations will use this colourspace before converting to the output colourspace, as defined by [toColourspace][6].
All operations will use this colourspace before converting to the output colourspace, as defined by [toColourspace](#toColourspace).
This feature is experimental and has not yet been fully-tested with all operations.
### Parameters
* `colourspace` **[string][1]?** pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...][7]
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
**Since**: 0.29.0
| Param | Type | Description |
| --- | --- | --- |
| [colourspace] | <code>string</code> | pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774) |
**Example**
```js
// Run pipeline in 16 bits per channel RGB while converting final result to 8 bits per channel sRGB.
await sharp(input)
.pipelineColourspace('rgb16')
@@ -63,76 +79,54 @@ await sharp(input)
.toFile('16bpc-pipeline-to-8bpc-output.png')
```
* Throws **[Error][4]** Invalid parameters
Returns **Sharp**
**Meta**
* **since**: 0.29.0
## pipelineColorspace
Alternative spelling of `pipelineColourspace`.
### Parameters
* `colorspace` **[string][1]?** pipeline colorspace.
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
| Param | Type | Description |
| --- | --- | --- |
| [colorspace] | <code>string</code> | pipeline colorspace. |
* Throws **[Error][4]** Invalid parameters
Returns **Sharp**
## toColourspace
Set the output colourspace.
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
### Parameters
* `colourspace` **[string][1]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][8]
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
| Param | Type | Description |
| --- | --- | --- |
| [colourspace] | <code>string</code> | output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794) |
**Example**
```js
// Output 16 bits per pixel RGB
await sharp(input)
.toColourspace('rgb16')
.toFile('16-bpp.png')
```
* Throws **[Error][4]** Invalid parameters
Returns **Sharp**
## toColorspace
Alternative spelling of `toColourspace`.
### Parameters
* `colorspace` **[string][1]?** output colorspace.
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
* Throws **[Error][4]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[3]: https://www.npmjs.org/package/color
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[6]: #tocolourspace
[7]: https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774
[8]: https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794
| Param | Type | Description |
| --- | --- | --- |
| [colorspace] | <code>string</code> | output colorspace. |

View File

@@ -1,12 +1,12 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## composite
Composite image(s) over the processed (resized, extracted etc.) image.
The images to composite must be the same size or smaller than the processed image.
If both `top` and `left` options are provided, they take precedence over `gravity`.
Any resize or rotate operations in the same processing pipeline
will always be applied to the input image before composition.
The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
`dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
`xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
@@ -14,39 +14,70 @@ The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
`hard-light`, `soft-light`, `difference`, `exclusion`.
More information about blend modes can be found at
[https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode][1]
and [https://www.cairographics.org/operators/][2]
https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
and https://www.cairographics.org/operators/
### Parameters
* `images` **[Array][3]<[Object][4]>** Ordered list of images to composite
**Throws**:
* `images[].input` **([Buffer][5] | [String][6])?** Buffer containing image data, String containing the path to an image file, or Create object (see below)
- <code>Error</code> Invalid parameters
* `images[].input.create` **[Object][4]?** describes a blank overlay to be created.
**Since**: 0.22.0
* `images[].input.create.width` **[Number][7]?**
* `images[].input.create.height` **[Number][7]?**
* `images[].input.create.channels` **[Number][7]?** 3-4
* `images[].input.create.background` **([String][6] | [Object][4])?** parsed by the [color][8] module to extract values for red, green, blue and alpha.
* `images[].blend` **[String][6]** how to blend this image with the image below. (optional, default `'over'`)
* `images[].gravity` **[String][6]** gravity at which to place the overlay. (optional, default `'centre'`)
* `images[].top` **[Number][7]?** the pixel offset from the top edge.
* `images[].left` **[Number][7]?** the pixel offset from the left edge.
* `images[].tile` **[Boolean][9]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
* `images[].premultiplied` **[Boolean][9]** set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option. (optional, default `false`)
* `images[].density` **[Number][7]** number representing the DPI for vector overlay image. (optional, default `72`)
* `images[].raw` **[Object][4]?** describes overlay when using raw pixel data.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| images | <code>Array.&lt;Object&gt;</code> | | Ordered list of images to composite |
| [images[].input] | <code>Buffer</code> \| <code>String</code> | | Buffer containing image data, String containing the path to an image file, or Create object (see below) |
| [images[].input.create] | <code>Object</code> | | describes a blank overlay to be created. |
| [images[].input.create.width] | <code>Number</code> | | |
| [images[].input.create.height] | <code>Number</code> | | |
| [images[].input.create.channels] | <code>Number</code> | | 3-4 |
| [images[].input.create.background] | <code>String</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
| [images[].input.text] | <code>Object</code> | | describes a new text image to be created. |
| [images[].input.text.text] | <code>string</code> | | text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`. |
| [images[].input.text.font] | <code>string</code> | | font name to render with. |
| [images[].input.text.fontfile] | <code>string</code> | | absolute filesystem path to a font file that can be used by `font`. |
| [images[].input.text.width] | <code>number</code> | <code>0</code> | integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries. |
| [images[].input.text.height] | <code>number</code> | <code>0</code> | integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0. |
| [images[].input.text.align] | <code>string</code> | <code>&quot;&#x27;left&#x27;&quot;</code> | text alignment (`'left'`, `'centre'`, `'center'`, `'right'`). |
| [images[].input.text.justify] | <code>boolean</code> | <code>false</code> | set this to true to apply justification to the text. |
| [images[].input.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
| [images[].input.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`. |
| [images[].input.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
| [images[].blend] | <code>String</code> | <code>&#x27;over&#x27;</code> | how to blend this image with the image below. |
| [images[].gravity] | <code>String</code> | <code>&#x27;centre&#x27;</code> | gravity at which to place the overlay. |
| [images[].top] | <code>Number</code> | | the pixel offset from the top edge. |
| [images[].left] | <code>Number</code> | | the pixel offset from the left edge. |
| [images[].tile] | <code>Boolean</code> | <code>false</code> | set to true to repeat the overlay image across the entire image with the given `gravity`. |
| [images[].premultiplied] | <code>Boolean</code> | <code>false</code> | set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option. |
| [images[].density] | <code>Number</code> | <code>72</code> | number representing the DPI for vector overlay image. |
| [images[].raw] | <code>Object</code> | | describes overlay when using raw pixel data. |
| [images[].raw.width] | <code>Number</code> | | |
| [images[].raw.height] | <code>Number</code> | | |
| [images[].raw.channels] | <code>Number</code> | | |
| [images[].animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image. |
| [images[].failOn] | <code>string</code> | <code>&quot;&#x27;warning&#x27;&quot;</code> | @see [constructor parameters](/api-constructor#parameters) |
| [images[].limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | @see [constructor parameters](/api-constructor#parameters) |
* `images[].raw.width` **[Number][7]?**
* `images[].raw.height` **[Number][7]?**
* `images[].raw.channels` **[Number][7]?**
* `images[].failOnError` **[boolean][9]** @see [constructor parameters][10] (optional, default `true`)
* `images[].limitInputPixels` **([number][7] | [boolean][9])** @see [constructor parameters][10] (optional, default `268402689`)
### Examples
```javascript
**Example**
```js
await sharp(background)
.composite([
{ input: layer1, gravity: 'northwest' },
{ input: layer2, gravity: 'southeast' },
])
.toFile('combined.png');
```
**Example**
```js
const output = await sharp('input.gif', { animated: true })
.composite([
{ input: 'overlay.png', tile: true, blend: 'saturate' }
])
.toBuffer();
```
**Example**
```js
sharp('input.png')
.rotate(180)
.resize(300)
@@ -61,34 +92,4 @@ sharp('input.png')
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});
```
* Throws **[Error][11]** Invalid parameters
Returns **Sharp**
**Meta**
* **since**: 0.22.0
[1]: https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
[2]: https://www.cairographics.org/operators/
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[5]: https://nodejs.org/api/buffer.html
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[8]: https://www.npmjs.org/package/color
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[10]: /api-constructor#parameters
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
```

View File

@@ -1,7 +1,9 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## Sharp
**Emits**: <code>Sharp#event:info</code>, <code>Sharp#event:warning</code>
<a name="new_Sharp_new"></a>
### new
Constructor factory to create an instance of `sharp`, to which further methods are chained.
JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
@@ -9,53 +11,57 @@ When using Stream based output, derived attributes are available from the `info`
Non-critical problems encountered during processing are emitted as `warning` events.
Implements the [stream.Duplex][1] class.
Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
### Parameters
**Throws**:
* `input` **([Buffer][2] | [Uint8Array][3] | [Uint8ClampedArray][4] | [Int8Array][5] | [Uint16Array][6] | [Int16Array][7] | [Uint32Array][8] | [Int32Array][9] | [Float32Array][10] | [Float64Array][11] | [string][12])?** if present, can be
a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
a TypedArray containing raw pixel image data, or
a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* `options` **[Object][13]?** if present, is an Object with optional attributes.
- <code>Error</code> Invalid parameters
* `options.failOnError` **[boolean][14]** by default halt processing and raise an error when loading invalid images.
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
* `options.limitInputPixels` **([number][15] | [boolean][14])** Do not process input images where the number of pixels
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
* `options.unlimited` **[boolean][14]** Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG). (optional, default `false`)
* `options.sequentialRead` **[boolean][14]** Set this to `true` to use sequential rather than random access where possible.
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
* `options.density` **[number][15]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
* `options.pages` **[number][15]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
* `options.page` **[number][15]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
* `options.subifd` **[number][15]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
* `options.level` **[number][15]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
* `options.animated` **[boolean][14]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
* `options.raw` **[Object][13]?** describes raw pixel input image data. See `raw()` for pixel ordering.
* `options.raw.width` **[number][15]?** integral number of pixels wide.
* `options.raw.height` **[number][15]?** integral number of pixels high.
* `options.raw.channels` **[number][15]?** integral number of channels, between 1 and 4.
* `options.raw.premultiplied` **[boolean][14]?** specifies that the raw input has already been premultiplied, set to `true`
to avoid sharp premultiplying the image. (optional, default `false`)
* `options.create` **[Object][13]?** describes a new image to be created.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [input] | <code>Buffer</code> \| <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Uint8ClampedArray</code> \| <code>Int8Array</code> \| <code>Uint16Array</code> \| <code>Int16Array</code> \| <code>Uint32Array</code> \| <code>Int32Array</code> \| <code>Float32Array</code> \| <code>Float64Array</code> \| <code>string</code> | | if present, can be a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or a TypedArray containing raw pixel image data, or a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file. JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. |
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
| [options.failOn] | <code>string</code> | <code>&quot;&#x27;warning&#x27;&quot;</code> | when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. |
| [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). |
| [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). |
| [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. |
| [options.density] | <code>number</code> | <code>72</code> | number representing the DPI for vector images in the range 1 to 100000. |
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
| [options.pages] | <code>number</code> | <code>1</code> | Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. |
| [options.page] | <code>number</code> | <code>0</code> | Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. |
| [options.subifd] | <code>number</code> | <code>-1</code> | subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. |
| [options.level] | <code>number</code> | <code>0</code> | level to extract from a multi-level input (OpenSlide), zero based. |
| [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. |
| [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. |
| [options.raw.width] | <code>number</code> | | integral number of pixels wide. |
| [options.raw.height] | <code>number</code> | | integral number of pixels high. |
| [options.raw.channels] | <code>number</code> | | integral number of channels, between 1 and 4. |
| [options.raw.premultiplied] | <code>boolean</code> | | specifies that the raw input has already been premultiplied, set to `true` to avoid sharp premultiplying the image. (optional, default `false`) |
| [options.create] | <code>Object</code> | | describes a new image to be created. |
| [options.create.width] | <code>number</code> | | integral number of pixels wide. |
| [options.create.height] | <code>number</code> | | integral number of pixels high. |
| [options.create.channels] | <code>number</code> | | integral number of channels, either 3 (RGB) or 4 (RGBA). |
| [options.create.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
| [options.create.noise] | <code>Object</code> | | describes a noise to be created. |
| [options.create.noise.type] | <code>string</code> | | type of generated noise, currently only `gaussian` is supported. |
| [options.create.noise.mean] | <code>number</code> | | mean of pixels in generated noise. |
| [options.create.noise.sigma] | <code>number</code> | | standard deviation of pixels in generated noise. |
| [options.text] | <code>Object</code> | | describes a new text image to be created. |
| [options.text.text] | <code>string</code> | | text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`. |
| [options.text.font] | <code>string</code> | | font name to render with. |
| [options.text.fontfile] | <code>string</code> | | absolute filesystem path to a font file that can be used by `font`. |
| [options.text.width] | <code>number</code> | <code>0</code> | integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries. |
| [options.text.height] | <code>number</code> | <code>0</code> | integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0. |
| [options.text.align] | <code>string</code> | <code>&quot;&#x27;left&#x27;&quot;</code> | text alignment (`'left'`, `'centre'`, `'center'`, `'right'`). |
| [options.text.justify] | <code>boolean</code> | <code>false</code> | set this to true to apply justification to the text. |
| [options.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
| [options.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`. |
| [options.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
| [options.text.wrap] | <code>string</code> | <code>&quot;&#x27;word&#x27;&quot;</code> | word wrapping style when width is provided, one of: 'word', 'char', 'charWord' (prefer char, fallback to word) or 'none'. |
* `options.create.width` **[number][15]?** integral number of pixels wide.
* `options.create.height` **[number][15]?** integral number of pixels high.
* `options.create.channels` **[number][15]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
* `options.create.background` **([string][12] | [Object][13])?** parsed by the [color][16] module to extract values for red, green, blue and alpha.
* `options.create.noise` **[Object][13]?** describes a noise to be created.
* `options.create.noise.type` **[string][12]?** type of generated noise, currently only `gaussian` is supported.
* `options.create.noise.mean` **[number][15]?** mean of pixels in generated noise.
* `options.create.noise.sigma` **[number][15]?** standard deviation of pixels in generated noise.
### Examples
```javascript
**Example**
```js
sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg', function(err) {
@@ -63,8 +69,8 @@ sharp('input.jpg')
// containing a scaled and cropped version of input.jpg
});
```
```javascript
**Example**
```js
// Read image data from readableStream,
// resize to 300 pixels wide,
// emit an 'info' event with calculated dimensions
@@ -76,8 +82,8 @@ var transformer = sharp()
});
readableStream.pipe(transformer).pipe(writableStream);
```
```javascript
**Example**
```js
// Create a blank 300x200 PNG image of semi-transluent red pixels
sharp({
create: {
@@ -91,13 +97,13 @@ sharp({
.toBuffer()
.then( ... );
```
```javascript
**Example**
```js
// Convert an animated GIF to an animated WebP
await sharp('in.gif', { animated: true }).toFile('out.webp');
```
```javascript
**Example**
```js
// Read a raw array of pixels and save it to a png
const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
const image = sharp(input, {
@@ -111,8 +117,8 @@ const image = sharp(input, {
});
await image.toFile('my-two-pixels.png');
```
```javascript
**Example**
```js
// Generate RGB Gaussian noise
await sharp({
create: {
@@ -127,20 +133,39 @@ await sharp({
}
}).toFile('noise.png');
```
**Example**
```js
// Generate an image from text
await sharp({
text: {
text: 'Hello, world!',
width: 400, // max width
height: 300 // max height
}
}).toFile('text_bw.png');
```
**Example**
```js
// Generate an rgba image from text using pango markup and font
await sharp({
text: {
text: '<span foreground="red">Red!</span><span background="cyan">blue</span>',
font: 'sans',
rgba: true,
dpi: 300
}
}).toFile('text_rgba.png');
```
* Throws **[Error][17]** Invalid parameters
Returns **[Sharp][18]**
## clone
Take a "snapshot" of the Sharp instance, returning a new instance.
Cloned instances inherit the input of their parent instance.
This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
### Examples
```javascript
**Example**
```js
const pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
@@ -148,15 +173,13 @@ readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
```javascript
**Example**
```js
// Create a pipeline that will download an image, resize it and format it to different files
// Using Promises to know when the pipeline is complete
const fs = require("fs");
const got = require("got");
const sharpStream = sharp({
failOnError: false
});
const sharpStream = sharp({ failOn: 'none' });
const promises = [];
@@ -183,7 +206,7 @@ promises.push(
.toFile("optimized-500.webp")
);
// https://github.com/sindresorhus/got#gotstreamurl-options
// https://github.com/sindresorhus/got/blob/main/documentation/3-streams.md
got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
Promise.all(promises)
@@ -196,42 +219,4 @@ Promise.all(promises)
fs.unlinkSync("optimized-500.webp");
} catch (e) {}
});
```
Returns **[Sharp][18]**
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
[2]: https://nodejs.org/api/buffer.html
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int8Array
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int16Array
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int32Array
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float32Array
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float64Array
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[16]: https://www.npmjs.org/package/color
[17]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[18]: #sharp
```

View File

@@ -1,54 +1,57 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## metadata
Fast access to (uncached) image metadata without decoding any compressed pixel data.
This is taken from the header of the input image.
It does not include operations, such as resize, to be applied to the output image.
This is read from the header of the input image.
It does not take into consideration any operations to be applied to the output image,
such as resize or rotate.
Dimensions in the response will respect the `page` and `pages` properties of the
[constructor parameters](/api-constructor#parameters).
A `Promise` is returned when `callback` is not provided.
* `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
* `size`: Total size of image in bytes, for Stream and Buffer input only
* `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
* `height`: Number of pixels high (EXIF orientation is not taken into consideration)
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][1]
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][2]
* `density`: Number of pixels per inch (DPI), if present
* `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
* `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
* `pageHeight`: Number of pixels high each page in a multi-page image will be.
* `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
* `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
* `pagePrimary`: Number of the primary page in a HEIF image
* `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
* `subifds`: Number of Sub Image File Directories in an OME-TIFF image
* `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
* `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
* `resolutionUnit`: The unit of resolution (density), either `inch` or `cm`, if present
* `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
* `orientation`: Number value of the EXIF Orientation header, if present
* `exif`: Buffer containing raw EXIF data, if present
* `icc`: Buffer containing raw [ICC][3] profile data, if present
* `iptc`: Buffer containing raw IPTC data, if present
* `xmp`: Buffer containing raw XMP data, if present
* `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
- `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
- `size`: Total size of image in bytes, for Stream and Buffer input only
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
- `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
- `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
- `density`: Number of pixels per inch (DPI), if present
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
- `pageHeight`: Number of pixels high each page in a multi-page image will be.
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
- `pagePrimary`: Number of the primary page in a HEIF image
- `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
- `subifds`: Number of Sub Image File Directories in an OME-TIFF image
- `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
- `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
- `resolutionUnit`: The unit of resolution (density), either `inch` or `cm`, if present
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
- `orientation`: Number value of the EXIF Orientation header, if present
- `exif`: Buffer containing raw EXIF data, if present
- `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
- `iptc`: Buffer containing raw IPTC data, if present
- `xmp`: Buffer containing raw XMP data, if present
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
- `formatMagick`: String containing format for images loaded via *magick
### Parameters
* `callback` **[Function][4]?** called with the arguments `(err, metadata)`
### Examples
| Param | Type | Description |
| --- | --- | --- |
| [callback] | <code>function</code> | called with the arguments `(err, metadata)` |
```javascript
**Example**
```js
const metadata = await sharp(input).metadata();
```
```javascript
**Example**
```js
const image = sharp(inputJpg);
image
.metadata()
@@ -62,40 +65,51 @@ image
// data contains a WebP image half the width and height of the original JPEG
});
```
**Example**
```js
// Based on EXIF rotation metadata, get the right-side-up width and height:
const size = getNormalSize(await sharp(input).metadata());
function getNormalSize({ width, height, orientation }) {
return (orientation || 0) >= 5
? { width: height, height: width }
: { width, height };
}
```
Returns **([Promise][5]<[Object][6]> | Sharp)**
## stats
Access to pixel-derived image statistics for every channel in the image.
A `Promise` is returned when `callback` is not provided.
* `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
* `min` (minimum value in the channel)
* `max` (maximum value in the channel)
* `sum` (sum of all values in a channel)
* `squaresSum` (sum of squared values in a channel)
* `mean` (mean of the values in a channel)
* `stdev` (standard deviation for the values in a channel)
* `minX` (x-coordinate of one of the pixel where the minimum lies)
* `minY` (y-coordinate of one of the pixel where the minimum lies)
* `maxX` (x-coordinate of one of the pixel where the maximum lies)
* `maxY` (y-coordinate of one of the pixel where the maximum lies)
* `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
* `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
* `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
* `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
- `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
- `min` (minimum value in the channel)
- `max` (maximum value in the channel)
- `sum` (sum of all values in a channel)
- `squaresSum` (sum of squared values in a channel)
- `mean` (mean of the values in a channel)
- `stdev` (standard deviation for the values in a channel)
- `minX` (x-coordinate of one of the pixel where the minimum lies)
- `minY` (y-coordinate of one of the pixel where the minimum lies)
- `maxX` (x-coordinate of one of the pixel where the maximum lies)
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
- `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
- `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
- `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
**Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
written to a buffer in order to run `stats` on the result (see third example).
### Parameters
* `callback` **[Function][4]?** called with the arguments `(err, stats)`
### Examples
| Param | Type | Description |
| --- | --- | --- |
| [callback] | <code>function</code> | called with the arguments `(err, stats)` |
```javascript
**Example**
```js
const image = sharp(inputJpg);
image
.stats()
@@ -103,30 +117,16 @@ image
// stats contains the channel-wise statistics array and the isOpaque value
});
```
```javascript
**Example**
```js
const { entropy, sharpness, dominant } = await sharp(input).stats();
const { r, g, b } = dominant;
```
```javascript
**Example**
```js
const image = sharp(input);
// store intermediate result
const part = await image.extract(region).toBuffer();
// create new instance to obtain statistics of extracted region
const stats = await sharp(part).stats();
```
Returns **[Promise][5]<[Object][6]>**
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
[2]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat
[3]: https://www.npmjs.com/package/icc
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
```

View File

@@ -1,7 +1,4 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## rotate
Rotate the output image by either an explicit angle
or auto-orient based on the EXIF `Orientation` tag.
@@ -16,19 +13,26 @@ Mirroring is supported and may infer the use of a flip operation.
The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
Method order is important when both rotating and extracting regions,
for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
Only one rotation can occur per pipeline.
Previous calls to `rotate` in the same pipeline will be ignored.
### Parameters
Method order is important when rotating, resizing and/or extracting regions,
for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`.
* `angle` **[number][1]** angle of rotation. (optional, default `auto`)
* `options` **[Object][2]?** if present, is an Object with optional attributes.
* `options.background` **([string][3] | [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [angle] | <code>number</code> | <code>auto</code> | angle of rotation. |
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
| [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;\&quot;#000000\&quot;&quot;</code> | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
**Example**
```js
const pipeline = sharp()
.rotate()
.resize(null, 200)
@@ -39,35 +43,52 @@ const pipeline = sharp()
});
readableStream.pipe(pipeline);
```
**Example**
```js
const rotateThenResize = await sharp(input)
.rotate(90)
.resize({ width: 16, height: 8, fit: 'fill' })
.toBuffer();
const resizeThenRotate = await sharp(input)
.resize({ width: 16, height: 8, fit: 'fill' })
.rotate(90)
.toBuffer();
```
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## flip
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
Flip the image about the vertical Y axis. This always occurs before rotation, if any.
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
### Parameters
* `flip` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
| Param | Type | Default |
| --- | --- | --- |
| [flip] | <code>Boolean</code> | <code>true</code> |
**Example**
```js
const output = await sharp(input).flip().toBuffer();
```
## flop
Flop the image about the horizontal X axis. This always occurs after rotation, if any.
Flop the image about the horizontal X axis. This always occurs before rotation, if any.
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
### Parameters
* `flop` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
| Param | Type | Default |
| --- | --- | --- |
| [flop] | <code>Boolean</code> | <code>true</code> |
**Example**
```js
const output = await sharp(input).flop().toBuffer();
```
## affine
Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
You must provide an array of length 4 or a 2x2 affine transformation matrix.
@@ -75,31 +96,34 @@ By default, new pixels are filled with a black background. You can provide a bac
A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.nohalo`.
In the case of a 2x2 matrix, the transform is:
* X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
* Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
- X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
- Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
where:
- x and y are the coordinates in input image.
- X and Y are the coordinates in output image.
- (0,0) is the upper left corner.
* x and y are the coordinates in input image.
* X and Y are the coordinates in output image.
* (0,0) is the upper left corner.
### Parameters
**Throws**:
* `matrix` **([Array][7]<[Array][7]<[number][1]>> | [Array][7]<[number][1]>)** affine transformation matrix
* `options` **[Object][2]?** if present, is an Object with optional attributes.
- <code>Error</code> Invalid parameters
* `options.background` **([String][3] | [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
* `options.idx` **[Number][1]** input horizontal offset (optional, default `0`)
* `options.idy` **[Number][1]** input vertical offset (optional, default `0`)
* `options.odx` **[Number][1]** output horizontal offset (optional, default `0`)
* `options.ody` **[Number][1]** output vertical offset (optional, default `0`)
* `options.interpolator` **[String][3]** interpolator (optional, default `sharp.interpolators.bicubic`)
**Since**: 0.27.0
### Examples
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| matrix | <code>Array.&lt;Array.&lt;number&gt;&gt;</code> \| <code>Array.&lt;number&gt;</code> | | affine transformation matrix |
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
| [options.background] | <code>String</code> \| <code>Object</code> | <code>&quot;#000000&quot;</code> | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
| [options.idx] | <code>Number</code> | <code>0</code> | input horizontal offset |
| [options.idy] | <code>Number</code> | <code>0</code> | input vertical offset |
| [options.odx] | <code>Number</code> | <code>0</code> | output horizontal offset |
| [options.ody] | <code>Number</code> | <code>0</code> | output vertical offset |
| [options.interpolator] | <code>String</code> | <code>sharp.interpolators.bicubic</code> | interpolator |
```javascript
**Example**
```js
const pipeline = sharp()
.affine([[1, 0.3], [0.1, 0.7]], {
background: 'white',
@@ -114,102 +138,134 @@ inputStream
.pipe(pipeline);
```
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
**Meta**
* **since**: 0.27.0
## sharpen
Sharpen the image.
When used without parameters, performs a fast, mild sharpen of the output image.
When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
Separate control over the level of sharpening in "flat" and "jagged" areas is available.
Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
### Parameters
See [libvips sharpen](https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen) operation.
* `sigma` **[number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* `flat` **[number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
* `jagged` **[number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
<!---->
**Throws**:
* Throws **[Error][5]** Invalid parameters
- <code>Error</code> Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> \| <code>number</code> | | if present, is an Object with attributes |
| [options.sigma] | <code>number</code> | | the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10 |
| [options.m1] | <code>number</code> | <code>1.0</code> | the level of sharpening to apply to "flat" areas, between 0 and 1000000 |
| [options.m2] | <code>number</code> | <code>2.0</code> | the level of sharpening to apply to "jagged" areas, between 0 and 1000000 |
| [options.x1] | <code>number</code> | <code>2.0</code> | threshold between "flat" and "jagged", between 0 and 1000000 |
| [options.y2] | <code>number</code> | <code>10.0</code> | maximum amount of brightening, between 0 and 1000000 |
| [options.y3] | <code>number</code> | <code>20.0</code> | maximum amount of darkening, between 0 and 1000000 |
| [flat] | <code>number</code> | | (deprecated) see `options.m1`. |
| [jagged] | <code>number</code> | | (deprecated) see `options.m2`. |
**Example**
```js
const data = await sharp(input).sharpen().toBuffer();
```
**Example**
```js
const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
```
**Example**
```js
const data = await sharp(input)
.sharpen({
sigma: 2,
m1: 0,
m2: 3,
x1: 3,
y2: 15,
y3: 15,
})
.toBuffer();
```
Returns **Sharp**
## median
Apply median filter.
When used without parameters the default window is 3x3.
### Parameters
* `size` **[number][1]** square mask size: size x size (optional, default `3`)
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [size] | <code>number</code> | <code>3</code> | square mask size: size x size |
**Example**
```js
const output = await sharp(input).median().toBuffer();
```
**Example**
```js
const output = await sharp(input).median(5).toBuffer();
```
## blur
Blur the image.
When used without parameters, performs a fast 3x3 box blur (equivalent to a box linear filter).
When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
### Parameters
* `sigma` **[number][1]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
| Param | Type | Description |
| --- | --- | --- |
| [sigma] | <code>number</code> | a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`. |
**Example**
```js
const boxBlurred = await sharp(input)
.blur()
.toBuffer();
```
```javascript
**Example**
```js
const gaussianBlurred = await sharp(input)
.blur(5)
.toBuffer();
```
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## flatten
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
See also [removeAlpha][8].
See also [removeAlpha](/api-channel#removealpha).
### Parameters
* `options` **[Object][2]?**
* `options.background` **([string][3] | [Object][2])** background colour, parsed by the [color][4] module, defaults to black. (optional, default `{r:0,g:0,b:0}`)
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;{r: 0, g: 0, b: 0}&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black. |
### Examples
```javascript
**Example**
```js
await sharp(rgbaInput)
.flatten({ background: '#F0A703' })
.toBuffer();
```
Returns **Sharp**
## gamma
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
then increasing the encoding (brighten) post-resize at a factor of `gamma`.
This can improve the perceived brightness of a resized image in non-linear colour spaces.
@@ -218,93 +274,143 @@ when applying a gamma correction.
Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
### Parameters
* `gamma` **[number][1]** value between 1.0 and 3.0. (optional, default `2.2`)
* `gammaOut` **[number][1]?** value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [gamma] | <code>number</code> | <code>2.2</code> | value between 1.0 and 3.0. |
| [gammaOut] | <code>number</code> | | value between 1.0 and 3.0. (optional, defaults to same as `gamma`) |
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## negate
Produce the "negative" of the image.
### Parameters
* `options` **[Object][2]?**
* `options.alpha` **[Boolean][6]** Whether or not to negate any alpha channel (optional, default `true`)
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.alpha] | <code>Boolean</code> | <code>true</code> | Whether or not to negate any alpha channel |
**Example**
```js
const output = await sharp(input)
.negate()
.toBuffer();
```
**Example**
```js
const output = await sharp(input)
.negate({ alpha: false })
.toBuffer();
```
Returns **Sharp**
## normalise
Enhance output image contrast by stretching its luminance to cover a full dynamic range.
Enhance output image contrast by stretching its luminance to cover the full dynamic range.
Uses a histogram-based approach, taking a default range of 1% to 99% to reduce sensitivity to noise at the extremes.
### Parameters
Luminance values below the `lower` percentile will be underexposed by clipping to zero.
Luminance values above the `upper` percentile will be overexposed by clipping to the max pixel value.
* `normalise` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.lower] | <code>number</code> | <code>1</code> | Percentile below which luminance values will be underexposed. |
| [options.upper] | <code>number</code> | <code>99</code> | Percentile above which luminance values will be overexposed. |
**Example**
```js
const output = await sharp(input)
.normalise()
.toBuffer();
```
**Example**
```js
const output = await sharp(input)
.normalise({ lower: 0, upper: 100 })
.toBuffer();
```
## normalize
Alternative spelling of normalise.
### Parameters
* `normalize` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.lower] | <code>number</code> | <code>1</code> | Percentile below which luminance values will be underexposed. |
| [options.upper] | <code>number</code> | <code>99</code> | Percentile above which luminance values will be overexposed. |
**Example**
```js
const output = await sharp(input)
.normalize()
.toBuffer();
```
## clahe
Perform contrast limiting adaptive histogram equalization
[CLAHE][9].
[CLAHE](https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE).
This will, in general, enhance the clarity of the image by bringing out darker details.
### Parameters
* `options` **[Object][2]**
**Throws**:
* `options.width` **[number][1]** integer width of the region in pixels.
* `options.height` **[number][1]** integer height of the region in pixels.
* `options.maxSlope` **[number][1]** maximum value for the slope of the
cumulative histogram. A value of 0 disables contrast limiting. Valid values
are integers in the range 0-100 (inclusive) (optional, default `3`)
- <code>Error</code> Invalid parameters
<!---->
**Since**: 0.28.3
* Throws **[Error][5]** Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| options | <code>Object</code> | | |
| options.width | <code>number</code> | | Integral width of the search window, in pixels. |
| options.height | <code>number</code> | | Integral height of the search window, in pixels. |
| [options.maxSlope] | <code>number</code> | <code>3</code> | Integral level of brightening, between 0 and 100, where 0 disables contrast limiting. |
Returns **Sharp**
**Example**
```js
const output = await sharp(input)
.clahe({
width: 3,
height: 3,
})
.toBuffer();
```
**Meta**
* **since**: 0.28.3
## convolve
Convolve the image with the specified kernel.
### Parameters
* `kernel` **[Object][2]**
**Throws**:
* `kernel.width` **[number][1]** width of the kernel in pixels.
* `kernel.height` **[number][1]** height of the kernel in pixels.
* `kernel.kernel` **[Array][7]<[number][1]>** Array of length `width*height` containing the kernel values.
* `kernel.scale` **[number][1]** the scale of the kernel in pixels. (optional, default `sum`)
* `kernel.offset` **[number][1]** the offset of the kernel in pixels. (optional, default `0`)
- <code>Error</code> Invalid parameters
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| kernel | <code>Object</code> | | |
| kernel.width | <code>number</code> | | width of the kernel in pixels. |
| kernel.height | <code>number</code> | | height of the kernel in pixels. |
| kernel.kernel | <code>Array.&lt;number&gt;</code> | | Array of length `width*height` containing the kernel values. |
| [kernel.scale] | <code>number</code> | <code>sum</code> | the scale of the kernel in pixels. |
| [kernel.offset] | <code>number</code> | <code>0</code> | the offset of the kernel in pixels. |
**Example**
```js
sharp(input)
.convolve({
width: 3,
@@ -318,79 +424,99 @@ sharp(input)
});
```
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## threshold
Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
### Parameters
* `threshold` **[number][1]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
* `options` **[Object][2]?**
**Throws**:
* `options.greyscale` **[Boolean][6]** convert to single channel greyscale. (optional, default `true`)
* `options.grayscale` **[Boolean][6]** alternative spelling for greyscale. (optional, default `true`)
- <code>Error</code> Invalid parameters
<!---->
* Throws **[Error][5]** Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [threshold] | <code>number</code> | <code>128</code> | a value in the range 0-255 representing the level at which the threshold will be applied. |
| [options] | <code>Object</code> | | |
| [options.greyscale] | <code>Boolean</code> | <code>true</code> | convert to single channel greyscale. |
| [options.grayscale] | <code>Boolean</code> | <code>true</code> | alternative spelling for greyscale. |
Returns **Sharp**
## boolean
Perform a bitwise boolean operation with operand image.
This operation creates an output image where each pixel is the result of
the selected bitwise boolean `operation` between the corresponding pixels of the input images.
### Parameters
* `operand` **([Buffer][10] | [string][3])** Buffer containing image data or string containing the path to an image file.
* `operator` **[string][3]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* `options` **[Object][2]?**
**Throws**:
* `options.raw` **[Object][2]?** describes operand when using raw pixel data.
- <code>Error</code> Invalid parameters
* `options.raw.width` **[number][1]?**
* `options.raw.height` **[number][1]?**
* `options.raw.channels` **[number][1]?**
<!---->
| Param | Type | Description |
| --- | --- | --- |
| operand | <code>Buffer</code> \| <code>string</code> | Buffer containing image data or string containing the path to an image file. |
| operator | <code>string</code> | one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively. |
| [options] | <code>Object</code> | |
| [options.raw] | <code>Object</code> | describes operand when using raw pixel data. |
| [options.raw.width] | <code>number</code> | |
| [options.raw.height] | <code>number</code> | |
| [options.raw.channels] | <code>number</code> | |
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## linear
Apply the linear formula `a` * input + `b` to the image to adjust image levels.
Apply the linear formula a \* input + b to the image (levels adjustment)
When a single number is provided, it will be used for all image channels.
When an array of numbers is provided, the array length must match the number of channels.
### Parameters
* `a` **[number][1]** multiplier (optional, default `1.0`)
* `b` **[number][1]** offset (optional, default `0.0`)
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [a] | <code>number</code> \| <code>Array.&lt;number&gt;</code> | <code>[]</code> | multiplier |
| [b] | <code>number</code> \| <code>Array.&lt;number&gt;</code> | <code>[]</code> | offset |
**Example**
```js
await sharp(input)
.linear(0.5, 2)
.toBuffer();
```
**Example**
```js
await sharp(rgbInput)
.linear(
[0.25, 0.5, 0.75],
[150, 100, 50]
)
.toBuffer();
```
## recomb
Recomb the image with the specified matrix.
### Parameters
* `inputMatrix` **[Array][7]<[Array][7]<[number][1]>>** 3x3 Recombination matrix
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
**Since**: 0.21.1
| Param | Type | Description |
| --- | --- | --- |
| inputMatrix | <code>Array.&lt;Array.&lt;number&gt;&gt;</code> | 3x3 Recombination matrix |
**Example**
```js
sharp(input)
.recomb([
[0.3588, 0.7044, 0.1368],
@@ -404,78 +530,58 @@ sharp(input)
});
```
* Throws **[Error][5]** Invalid parameters
Returns **Sharp**
**Meta**
* **since**: 0.21.1
## modulate
Transforms the image using brightness, saturation, hue rotation, and lightness.
Brightness and lightness both operate on luminance, with the difference being that
brightness is multiplicative whereas lightness is additive.
### Parameters
* `options` **[Object][2]?**
**Since**: 0.22.1
* `options.brightness` **[number][1]?** Brightness multiplier
* `options.saturation` **[number][1]?** Saturation multiplier
* `options.hue` **[number][1]?** Degrees for hue rotation
* `options.lightness` **[number][1]?** Lightness addend
| Param | Type | Description |
| --- | --- | --- |
| [options] | <code>Object</code> | |
| [options.brightness] | <code>number</code> | Brightness multiplier |
| [options.saturation] | <code>number</code> | Saturation multiplier |
| [options.hue] | <code>number</code> | Degrees for hue rotation |
| [options.lightness] | <code>number</code> | Lightness addend |
### Examples
```javascript
sharp(input)
**Example**
```js
// increase brightness by a factor of 2
const output = await sharp(input)
.modulate({
brightness: 2 // increase brightness by a factor of 2
});
sharp(input)
brightness: 2
})
.toBuffer();
```
**Example**
```js
// hue-rotate by 180 degrees
const output = await sharp(input)
.modulate({
hue: 180 // hue-rotate by 180 degrees
});
sharp(input)
hue: 180
})
.toBuffer();
```
**Example**
```js
// increase lightness by +50
const output = await sharp(input)
.modulate({
lightness: 50 // increase lightness by +50
});
lightness: 50
})
.toBuffer();
```
**Example**
```js
// decreate brightness and saturation while also hue-rotating by 90 degrees
sharp(input)
const output = await sharp(input)
.modulate({
brightness: 0.5,
saturation: 0.5,
hue: 90
});
```
Returns **Sharp**
**Meta**
* **since**: 0.22.1
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[4]: https://www.npmjs.org/package/color
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[8]: /api-channel#removealpha
[9]: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE
[10]: https://nodejs.org/api/buffer.html
hue: 90,
})
.toBuffer();
```

View File

@@ -1,7 +1,4 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## toFile
Write output image data to a file.
If an explicit output format is not selected, it will be inferred from the extension,
@@ -9,88 +6,90 @@ with JPEG, PNG, WebP, AVIF, TIFF, GIF, DZI, and libvips' V format supported.
Note that raw pixel data is only supported for buffer output.
By default all metadata will be removed, which includes EXIF-based orientation.
See [withMetadata][1] for control over this.
See [withMetadata](#withMetadata) for control over this.
The caller is responsible for ensuring directory structures and permissions exist.
A `Promise` is returned when `callback` is not provided.
### Parameters
* `fileOut` **[string][2]** the path to write the image data to.
* `callback` **[Function][3]?** called on completion with two arguments `(err, info)`.
`info` contains the output image `format`, `size` (bytes), `width`, `height`,
`channels` and `premultiplied` (indicating if premultiplication was used).
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
**Returns**: <code>Promise.&lt;Object&gt;</code> - - when no callback is provided
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
| Param | Type | Description |
| --- | --- | --- |
| fileOut | <code>string</code> | the path to write the image data to. |
| [callback] | <code>function</code> | called on completion with two arguments `(err, info)`. `info` contains the output image `format`, `size` (bytes), `width`, `height`, `channels` and `premultiplied` (indicating if premultiplication was used). When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region. May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text. |
**Example**
```js
sharp(input)
.toFile('output.png', (err, info) => { ... });
```
```javascript
**Example**
```js
sharp(input)
.toFile('output.png')
.then(info => { ... })
.catch(err => { ... });
```
* Throws **[Error][4]** Invalid parameters
Returns **[Promise][5]<[Object][6]>** when no callback is provided
## toBuffer
Write output to a Buffer.
JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
Use [toFormat](#toFormat) or one of the format-specific functions such as [jpeg](#jpeg), [png](#png) etc. to set the output format.
If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
By default all metadata will be removed, which includes EXIF-based orientation.
See [withMetadata][1] for control over this.
See [withMetadata](#withMetadata) for control over this.
`callback`, if present, gets three arguments `(err, data, info)` where:
* `err` is an error, if any.
* `data` is the output image data.
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
- `err` is an error, if any.
- `data` is the output image data.
- `info` contains the output image `format`, `size` (bytes), `width`, `height`,
`channels` and `premultiplied` (indicating if premultiplication was used).
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
A `Promise` is returned when `callback` is not provided.
### Parameters
* `options` **[Object][6]?**
**Returns**: <code>Promise.&lt;Buffer&gt;</code> - - when no callback is provided
* `options.resolveWithObject` **[boolean][7]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* `callback` **[Function][3]?**
| Param | Type | Description |
| --- | --- | --- |
| [options] | <code>Object</code> | |
| [options.resolveWithObject] | <code>boolean</code> | Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`. |
| [callback] | <code>function</code> | |
### Examples
```javascript
**Example**
```js
sharp(input)
.toBuffer((err, data, info) => { ... });
```
```javascript
**Example**
```js
sharp(input)
.toBuffer()
.then(data => { ... })
.catch(err => { ... });
```
```javascript
**Example**
```js
sharp(input)
.png()
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => { ... })
.catch(err => { ... });
```
```javascript
**Example**
```js
const { data, info } = await sharp('my-image.jpg')
// output the raw pixels
.raw()
@@ -107,10 +106,8 @@ await sharp(pixelArray, { raw: { width, height, channels } })
.toFile('my-changed-image.jpg');
```
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
## withMetadata
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
This will also convert to and add a web-friendly sRGB ICC profile unless a custom
output profile is provided.
@@ -118,25 +115,31 @@ output profile is provided.
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
sRGB colour space and strip all metadata, including the removal of any ICC profile.
### Parameters
EXIF metadata is unsupported for TIFF output.
* `options` **[Object][6]?**
* `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
* `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
* `options.exif` **[Object][6]<[Object][6]>** Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. (optional, default `{}`)
* `options.density` **[number][9]?** Number of pixels per inch (DPI).
**Throws**:
### Examples
- <code>Error</code> Invalid parameters
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.orientation] | <code>number</code> | | value between 1 and 8, used to update the EXIF `Orientation` tag. |
| [options.icc] | <code>string</code> | <code>&quot;&#x27;srgb&#x27;&quot;</code> | Filesystem path to output ICC profile, relative to `process.cwd()`, defaults to built-in sRGB. |
| [options.exif] | <code>Object.&lt;Object&gt;</code> | <code>{}</code> | Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. |
| [options.density] | <code>number</code> | | Number of pixels per inch (DPI). |
**Example**
```js
sharp('input.jpg')
.withMetadata()
.toFile('output-with-metadata.jpg')
.then(info => { ... });
```
```javascript
**Example**
```js
// Set "IFD0-Copyright" in output EXIF metadata
const data = await sharp(input)
.withMetadata({
@@ -148,65 +151,66 @@ const data = await sharp(input)
})
.toBuffer();
```
```javascript
**Example**
```js
// Set output metadata to 96 DPI
const data = await sharp(input)
.withMetadata({ density: 96 })
.toBuffer();
```
* Throws **[Error][4]** Invalid parameters
Returns **Sharp**
## toFormat
Force output to a given format.
### Parameters
* `format` **([string][2] | [Object][6])** as a string or an Object with an 'id' attribute
* `options` **[Object][6]** output options
**Throws**:
### Examples
- <code>Error</code> unsupported format or options
```javascript
| Param | Type | Description |
| --- | --- | --- |
| format | <code>string</code> \| <code>Object</code> | as a string or an Object with an 'id' attribute |
| options | <code>Object</code> | output options |
**Example**
```js
// Convert any input to PNG output
const data = await sharp(input)
.toFormat('png')
.toBuffer();
```
* Throws **[Error][4]** unsupported format or options
Returns **Sharp**
## jpeg
Use these JPEG options for output image.
### Parameters
* `options` **[Object][6]?** output options
**Throws**:
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
* `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
* `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling (optional, default `'4:2:0'`)
* `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
* `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
* `options.mozjpeg` **[boolean][7]** use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }` (optional, default `false`)
* `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation (optional, default `false`)
* `options.overshootDeringing` **[boolean][7]** apply overshoot deringing (optional, default `false`)
* `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive (optional, default `false`)
* `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans (optional, default `false`)
* `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8 (optional, default `0`)
* `options.quantizationTable` **[number][9]** alternative spelling of quantisationTable (optional, default `0`)
* `options.force` **[boolean][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
- <code>Error</code> Invalid options
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.quality] | <code>number</code> | <code>80</code> | quality, integer 1-100 |
| [options.progressive] | <code>boolean</code> | <code>false</code> | use progressive (interlace) scan |
| [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:2:0&#x27;&quot;</code> | set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling |
| [options.optimiseCoding] | <code>boolean</code> | <code>true</code> | optimise Huffman coding tables |
| [options.optimizeCoding] | <code>boolean</code> | <code>true</code> | alternative spelling of optimiseCoding |
| [options.mozjpeg] | <code>boolean</code> | <code>false</code> | use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }` |
| [options.trellisQuantisation] | <code>boolean</code> | <code>false</code> | apply trellis quantisation |
| [options.overshootDeringing] | <code>boolean</code> | <code>false</code> | apply overshoot deringing |
| [options.optimiseScans] | <code>boolean</code> | <code>false</code> | optimise progressive scans, forces progressive |
| [options.optimizeScans] | <code>boolean</code> | <code>false</code> | alternative spelling of optimiseScans |
| [options.quantisationTable] | <code>number</code> | <code>0</code> | quantization table to use, integer 0-8 |
| [options.quantizationTable] | <code>number</code> | <code>0</code> | alternative spelling of quantisationTable |
| [options.force] | <code>boolean</code> | <code>true</code> | force JPEG output, otherwise attempt to use input format |
**Example**
```js
// Convert any input to very high quality JPEG output
const data = await sharp(input)
.jpeg({
@@ -215,219 +219,186 @@ const data = await sharp(input)
})
.toBuffer();
```
```javascript
**Example**
```js
// Use mozjpeg to reduce output JPEG file size (slower)
const data = await sharp(input)
.jpeg({ mozjpeg: true })
.toBuffer();
```
* Throws **[Error][4]** Invalid options
Returns **Sharp**
## png
Use these PNG options for output image.
By default, PNG output is full colour at 8 or 16 bits per pixel.
Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
Set `palette` to `true` for slower, indexed PNG output.
### Parameters
* `options` **[Object][6]?**
**Throws**:
* `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
* `options.compressionLevel` **[number][9]** zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default `6`)
* `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
* `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
* `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
* `options.effort` **[number][9]** CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true` (optional, default `7`)
* `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
* `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
* `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
- <code>Error</code> Invalid options
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.progressive] | <code>boolean</code> | <code>false</code> | use progressive (interlace) scan |
| [options.compressionLevel] | <code>number</code> | <code>6</code> | zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) |
| [options.adaptiveFiltering] | <code>boolean</code> | <code>false</code> | use adaptive row filtering |
| [options.palette] | <code>boolean</code> | <code>false</code> | quantise to a palette-based image with alpha transparency support |
| [options.quality] | <code>number</code> | <code>100</code> | use the lowest number of colours needed to achieve given quality, sets `palette` to `true` |
| [options.effort] | <code>number</code> | <code>7</code> | CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true` |
| [options.colours] | <code>number</code> | <code>256</code> | maximum number of palette entries, sets `palette` to `true` |
| [options.colors] | <code>number</code> | <code>256</code> | alternative spelling of `options.colours`, sets `palette` to `true` |
| [options.dither] | <code>number</code> | <code>1.0</code> | level of Floyd-Steinberg error diffusion, sets `palette` to `true` |
| [options.force] | <code>boolean</code> | <code>true</code> | force PNG output, otherwise attempt to use input format |
**Example**
```js
// Convert any input to full colour PNG output
const data = await sharp(input)
.png()
.toBuffer();
```
```javascript
**Example**
```js
// Convert any input to indexed PNG output (slower)
const data = await sharp(input)
.png({ palette: true })
.toBuffer();
```
* Throws **[Error][4]** Invalid options
Returns **Sharp**
## webp
Use these WebP options for output image.
### Parameters
* `options` **[Object][6]?** output options
**Throws**:
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
* `options.alphaQuality` **[number][9]** quality of alpha layer, integer 0-100 (optional, default `100`)
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
* `options.nearLossless` **[boolean][7]** use near_lossless compression mode (optional, default `false`)
* `options.smartSubsample` **[boolean][7]** use high quality chroma subsampling (optional, default `false`)
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 6 (slowest) (optional, default `4`)
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
* `options.delay` **([number][9] | [Array][10]<[number][9]>)?** delay(s) between animation frames (in milliseconds)
* `options.force` **[boolean][7]** force WebP output, otherwise attempt to use input format (optional, default `true`)
- <code>Error</code> Invalid options
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.quality] | <code>number</code> | <code>80</code> | quality, integer 1-100 |
| [options.alphaQuality] | <code>number</code> | <code>100</code> | quality of alpha layer, integer 0-100 |
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression mode |
| [options.nearLossless] | <code>boolean</code> | <code>false</code> | use near_lossless compression mode |
| [options.smartSubsample] | <code>boolean</code> | <code>false</code> | use high quality chroma subsampling |
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 6 (slowest) |
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
| [options.delay] | <code>number</code> \| <code>Array.&lt;number&gt;</code> | | delay(s) between animation frames (in milliseconds) |
| [options.minSize] | <code>boolean</code> | <code>false</code> | prevent use of animation key frames to minimise file size (slow) |
| [options.mixed] | <code>boolean</code> | <code>false</code> | allow mixture of lossy and lossless animation frames (slow) |
| [options.force] | <code>boolean</code> | <code>true</code> | force WebP output, otherwise attempt to use input format |
**Example**
```js
// Convert any input to lossless WebP output
const data = await sharp(input)
.webp({ lossless: true })
.toBuffer();
```
```javascript
**Example**
```js
// Optimise the file size of an animated WebP
const outputWebp = await sharp(inputWebp, { animated: true })
.webp({ effort: 6 })
.toBuffer();
```
* Throws **[Error][4]** Invalid options
Returns **Sharp**
## gif
Use these GIF options for the output image.
The first entry in the palette is reserved for transparency.
### Parameters
The palette of the input image will be re-used if possible.
* `options` **[Object][6]?** output options
* `options.colours` **[number][9]** maximum number of palette entries, including transparency, between 2 and 256 (optional, default `256`)
* `options.colors` **[number][9]** alternative spelling of `options.colours` (optional, default `256`)
* `options.effort` **[number][9]** CPU effort, between 1 (fastest) and 10 (slowest) (optional, default `7`)
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default `1.0`)
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
* `options.delay` **([number][9] | [Array][10]<[number][9]>)?** delay(s) between animation frames (in milliseconds)
* `options.force` **[boolean][7]** force GIF output, otherwise attempt to use input format (optional, default `true`)
**Throws**:
### Examples
- <code>Error</code> Invalid options
```javascript
**Since**: 0.30.0
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.reuse] | <code>boolean</code> | <code>true</code> | re-use existing palette, otherwise generate new (slow) |
| [options.progressive] | <code>boolean</code> | <code>false</code> | use progressive (interlace) scan |
| [options.colours] | <code>number</code> | <code>256</code> | maximum number of palette entries, including transparency, between 2 and 256 |
| [options.colors] | <code>number</code> | <code>256</code> | alternative spelling of `options.colours` |
| [options.effort] | <code>number</code> | <code>7</code> | CPU effort, between 1 (fastest) and 10 (slowest) |
| [options.dither] | <code>number</code> | <code>1.0</code> | level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) |
| [options.interFrameMaxError] | <code>number</code> | <code>0</code> | maximum inter-frame error for transparency, between 0 (lossless) and 32 |
| [options.interPaletteMaxError] | <code>number</code> | <code>3</code> | maximum inter-palette error for palette reuse, between 0 and 256 |
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
| [options.delay] | <code>number</code> \| <code>Array.&lt;number&gt;</code> | | delay(s) between animation frames (in milliseconds) |
| [options.force] | <code>boolean</code> | <code>true</code> | force GIF output, otherwise attempt to use input format |
**Example**
```js
// Convert PNG to GIF
await sharp(pngBuffer)
.gif()
.toBuffer());
.toBuffer();
```
```javascript
**Example**
```js
// Convert animated WebP to animated GIF
await sharp('animated.webp', { animated: true })
.toFile('animated.gif');
```
```javascript
**Example**
```js
// Create a 128x128, cropped, non-dithered, animated thumbnail of an animated GIF
const out = await sharp('in.gif', { animated: true })
.resize({ width: 128, height: 128 })
.gif({ dither: 0 })
.toBuffer();
```
* Throws **[Error][4]** Invalid options
Returns **Sharp**
**Meta**
* **since**: 0.30.0
## jp2
Use these JP2 options for output image.
Requires libvips compiled with support for OpenJPEG.
The prebuilt binaries do not include this - see
[installing a custom libvips][11].
### Parameters
* `options` **[Object][6]?** output options
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
* `options.tileWidth` **[number][9]** horizontal tile size (optional, default `512`)
* `options.tileHeight` **[number][9]** vertical tile size (optional, default `512`)
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
### Examples
```javascript
// Convert any input to lossless JP2 output
const data = await sharp(input)
.jp2({ lossless: true })
.toBuffer();
**Example**
```js
// Lossy file size reduction of animated GIF
await sharp('in.gif', { animated: true })
.gif({ interFrameMaxError: 8 })
.toFile('optim.gif');
```
```javascript
// Convert any input to very high quality JP2 output
const data = await sharp(input)
.jp2({
quality: 100,
chromaSubsampling: '4:4:4'
})
.toBuffer();
```
* Throws **[Error][4]** Invalid options
Returns **Sharp**
**Meta**
* **since**: 0.29.1
## tiff
Use these TIFF options for output image.
The `density` can be set in pixels/inch via [withMetadata][1] instead of providing `xres` and `yres` in pixels/mm.
The `density` can be set in pixels/inch via [withMetadata](#withMetadata) instead of providing `xres` and `yres` in pixels/mm.
### Parameters
* `options` **[Object][6]?** output options
**Throws**:
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
* `options.force` **[boolean][7]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
* `options.compression` **[string][2]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
* `options.predictor` **[string][2]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
* `options.pyramid` **[boolean][7]** write an image pyramid (optional, default `false`)
* `options.tile` **[boolean][7]** write a tiled tiff (optional, default `false`)
* `options.tileWidth` **[number][9]** horizontal tile size (optional, default `256`)
* `options.tileHeight` **[number][9]** vertical tile size (optional, default `256`)
* `options.xres` **[number][9]** horizontal resolution in pixels/mm (optional, default `1.0`)
* `options.yres` **[number][9]** vertical resolution in pixels/mm (optional, default `1.0`)
* `options.resolutionUnit` **[string][2]** resolution unit options: inch, cm (optional, default `'inch'`)
* `options.bitdepth` **[number][9]** reduce bitdepth to 1, 2 or 4 bit (optional, default `8`)
- <code>Error</code> Invalid options
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.quality] | <code>number</code> | <code>80</code> | quality, integer 1-100 |
| [options.force] | <code>boolean</code> | <code>true</code> | force TIFF output, otherwise attempt to use input format |
| [options.compression] | <code>string</code> | <code>&quot;&#x27;jpeg&#x27;&quot;</code> | compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k |
| [options.predictor] | <code>string</code> | <code>&quot;&#x27;horizontal&#x27;&quot;</code> | compression predictor options: none, horizontal, float |
| [options.pyramid] | <code>boolean</code> | <code>false</code> | write an image pyramid |
| [options.tile] | <code>boolean</code> | <code>false</code> | write a tiled tiff |
| [options.tileWidth] | <code>number</code> | <code>256</code> | horizontal tile size |
| [options.tileHeight] | <code>number</code> | <code>256</code> | vertical tile size |
| [options.xres] | <code>number</code> | <code>1.0</code> | horizontal resolution in pixels/mm |
| [options.yres] | <code>number</code> | <code>1.0</code> | vertical resolution in pixels/mm |
| [options.resolutionUnit] | <code>string</code> | <code>&quot;&#x27;inch&#x27;&quot;</code> | resolution unit options: inch, cm |
| [options.bitdepth] | <code>number</code> | <code>8</code> | reduce bitdepth to 1, 2 or 4 bit |
**Example**
```js
// Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
sharp('input.svg')
.tiff({
@@ -438,12 +409,8 @@ sharp('input.svg')
.then(info => { ... });
```
* Throws **[Error][4]** Invalid options
Returns **Sharp**
## avif
Use these AVIF options for output image.
Whilst it is possible to create AVIF images smaller than 16x16 pixels,
@@ -451,74 +418,119 @@ most web browsers do not display these properly.
AVIF image sequences are not supported.
### Parameters
* `options` **[Object][6]?** output options
**Throws**:
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
- <code>Error</code> Invalid options
<!---->
**Since**: 0.27.0
* Throws **[Error][4]** Invalid options
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.quality] | <code>number</code> | <code>50</code> | quality, integer 1-100 |
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) |
| [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:4:4&#x27;&quot;</code> | set to '4:2:0' to use chroma subsampling |
Returns **Sharp**
**Example**
```js
const data = await sharp(input)
.avif({ effort: 2 })
.toBuffer();
```
**Example**
```js
const data = await sharp(input)
.avif({ lossless: true })
.toBuffer();
```
**Meta**
* **since**: 0.27.0
## heif
Use these HEIF options for output image.
Support for patent-encumbered HEIC images requires the use of a
Support for patent-encumbered HEIC images using `hevc` compression requires the use of a
globally-installed libvips compiled with support for libheif, libde265 and x265.
### Parameters
* `options` **[Object][6]?** output options
**Throws**:
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
* `options.compression` **[string][2]** compression format: av1, hevc (optional, default `'av1'`)
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
- <code>Error</code> Invalid options
<!---->
**Since**: 0.23.0
* Throws **[Error][4]** Invalid options
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.quality] | <code>number</code> | <code>50</code> | quality, integer 1-100 |
| [options.compression] | <code>string</code> | <code>&quot;&#x27;av1&#x27;&quot;</code> | compression format: av1, hevc |
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) |
| [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:4:4&#x27;&quot;</code> | set to '4:2:0' to use chroma subsampling |
Returns **Sharp**
**Example**
```js
const data = await sharp(input)
.heif({ compression: 'hevc' })
.toBuffer();
```
## jxl
Use these JPEG-XL (JXL) options for output image.
This feature is experimental, please do not use in production systems.
Requires libvips compiled with support for libjxl.
The prebuilt binaries do not include this - see
[installing a custom libvips](https://sharp.pixelplumbing.com/install#custom-libvips).
Image metadata (EXIF, XMP) is unsupported.
**Throws**:
- <code>Error</code> Invalid options
**Since**: 0.31.3
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.distance] | <code>number</code> | <code>1.0</code> | maximum encoding error, between 0 (highest quality) and 15 (lowest quality) |
| [options.quality] | <code>number</code> | | calculate `distance` based on JPEG-like quality, between 1 and 100, overrides distance if specified |
| [options.decodingTier] | <code>number</code> | <code>0</code> | target decode speed tier, between 0 (highest quality) and 4 (lowest quality) |
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
| [options.effort] | <code>number</code> | <code>7</code> | CPU effort, between 3 (fastest) and 9 (slowest) |
**Meta**
* **since**: 0.23.0
## raw
Force output to be raw, uncompressed pixel data.
Pixel ordering is left-to-right, top-to-bottom, without padding.
Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
### Parameters
* `options` **[Object][6]?** output options
**Throws**:
* `options.depth` **[string][2]** bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex (optional, default `'uchar'`)
- <code>Error</code> Invalid options
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | output options |
| [options.depth] | <code>string</code> | <code>&quot;&#x27;uchar&#x27;&quot;</code> | bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex |
**Example**
```js
// Extract raw, unsigned 8-bit RGB pixel data from JPEG input
const { data, info } = await sharp('input.jpg')
.raw()
.toBuffer({ resolveWithObject: true });
```
```javascript
**Example**
```js
// Extract alpha channel as raw, unsigned 16-bit pixel data from PNG input
const data = await sharp('input.png')
.ensureAlpha()
@@ -528,33 +540,43 @@ const data = await sharp('input.png')
.toBuffer();
```
* Throws **[Error][4]** Invalid options
## tile
Use tile-based deep zoom (image pyramid) output.
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
### Parameters
The container will be set to `zip` when the output is a Buffer or Stream, otherwise it will default to `fs`.
* `options` **[Object][6]?**
Requires libvips compiled with support for libgsf.
The prebuilt binaries do not include this - see
[installing a custom libvips](https://sharp.pixelplumbing.com/install#custom-libvips).
* `options.size` **[number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
* `options.overlap` **[number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
* `options.angle` **[number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
* `options.background` **([string][2] | [Object][6])** background colour, parsed by the [color][12] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
* `options.depth` **[string][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* `options.skipBlanks` **[number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
* `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
* `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. (optional, default `'dz'`)
* `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`)
* `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`)
* `options.id` **[string][2]** when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json` (optional, default `'https://example.com/iiif'`)
### Examples
**Throws**:
```javascript
- <code>Error</code> Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.size] | <code>number</code> | <code>256</code> | tile size in pixels, a value between 1 and 8192. |
| [options.overlap] | <code>number</code> | <code>0</code> | tile overlap in pixels, a value between 0 and 8192. |
| [options.angle] | <code>number</code> | <code>0</code> | tile angle of rotation, must be a multiple of 90. |
| [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;{r: 255, g: 255, b: 255, alpha: 1}&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency. |
| [options.depth] | <code>string</code> | | how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout. |
| [options.skipBlanks] | <code>number</code> | <code>-1</code> | threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images |
| [options.container] | <code>string</code> | <code>&quot;&#x27;fs&#x27;&quot;</code> | tile container, with value `fs` (filesystem) or `zip` (compressed file). |
| [options.layout] | <code>string</code> | <code>&quot;&#x27;dz&#x27;&quot;</code> | filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. |
| [options.centre] | <code>boolean</code> | <code>false</code> | centre image in tile. |
| [options.center] | <code>boolean</code> | <code>false</code> | alternative spelling of centre. |
| [options.id] | <code>string</code> | <code>&quot;&#x27;https://example.com/iiif&#x27;&quot;</code> | when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json` |
| [options.basename] | <code>string</code> | | the name of the directory within the zip file when container is `zip`. |
**Example**
```js
sharp('input.tiff')
.png()
.tile({
@@ -565,51 +587,45 @@ sharp('input.tiff')
// output_files contains 512x512 tiles grouped by zoom level
});
```
**Example**
```js
const zipFileWithTiles = await sharp(input)
.tile({ basename: "tiles" })
.toBuffer();
```
**Example**
```js
const iiififier = sharp().tile({ layout: "iiif" });
readableStream
.pipe(iiififier)
.pipe(writeableStream);
```
* Throws **[Error][4]** Invalid parameters
Returns **Sharp**
## timeout
Set a timeout for processing, in seconds.
Use a value of zero to continue processing indefinitely, the default behaviour.
The clock starts when libvips opens an input image for processing.
Time spent waiting for a libuv thread to become available is not included.
### Parameters
* `options` **[Object][6]**
**Since**: 0.29.2
* `options.seconds` **[number][9]** Number of seconds after which processing will be stopped
| Param | Type | Description |
| --- | --- | --- |
| options | <code>Object</code> | |
| options.seconds | <code>number</code> | Number of seconds after which processing will be stopped |
Returns **Sharp**
**Meta**
* **since**: 0.29.2
[1]: #withmetadata
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[8]: https://nodejs.org/api/buffer.html
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[11]: https://sharp.pixelplumbing.com/install#custom-libvips
[12]: https://www.npmjs.org/package/color
**Example**
```js
// Ensure processing takes no longer than 3 seconds
try {
const data = await sharp(input)
.blur(1000)
.timeout({ seconds: 3 })
.toBuffer();
} catch (err) {
if (err.message.includes('timeout')) { ... }
}
```

View File

@@ -1,60 +1,62 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## resize
Resize image to `width`, `height` or `width x height`.
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
- `cover`: (default) Preserving aspect ratio, attempt to ensure the image covers both provided dimensions by cropping/clipping to fit.
- `contain`: Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
- `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
- `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
- `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
* `cover`: (default) Preserving aspect ratio, ensure the image covers both provided dimensions by cropping/clipping to fit.
* `contain`: Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
* `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
* `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
* `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
Some of these values are based on the [object-fit][1] CSS property.
<img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.png">
When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
- `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
- `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
* `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
* `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
* `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
Some of these values are based on the [object-position][2] CSS property.
Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
The experimental strategy-based approach resizes so one dimension is at its target length
then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
* `entropy`: focus on the region with the highest [Shannon entropy][3].
* `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
- `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
- `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
Possible interpolation kernels are:
- `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
- `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
- `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
- `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
* `nearest`: Use [nearest neighbour interpolation][4].
* `cubic`: Use a [Catmull-Rom spline][5].
* `mitchell`: Use a [Mitchell-Netravali spline][6].
* `lanczos2`: Use a [Lanczos kernel][7] with `a=2`.
* `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
Only one resize can occur per pipeline.
Previous calls to `resize` in the same pipeline will be ignored.
### Parameters
* `width` **[number][8]?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* `height` **[number][8]?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* `options` **[Object][9]?**
**Throws**:
* `options.width` **[String][10]?** alternative means of specifying `width`. If both are present this take priority.
* `options.height` **[String][10]?** alternative means of specifying `height`. If both are present this take priority.
* `options.fit` **[String][10]** how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`. (optional, default `'cover'`)
* `options.position` **[String][10]** position, gravity or strategy to use when `fit` is `cover` or `contain`. (optional, default `'centre'`)
* `options.background` **([String][10] | [Object][9])** background colour when using a `fit` of `contain`, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
* `options.kernel` **[String][10]** the kernel to use for image reduction. (optional, default `'lanczos3'`)
* `options.withoutEnlargement` **[Boolean][12]** do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option. (optional, default `false`)
* `options.withoutReduction` **[Boolean][12]** do not reduce if the width *or* height are already greater than the specified dimensions, equivalent to GraphicsMagick's `<` geometry option. (optional, default `false`)
* `options.fastShrinkOnLoad` **[Boolean][12]** take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images. (optional, default `true`)
- <code>Error</code> Invalid parameters
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [width] | <code>number</code> | | How many pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height. |
| [height] | <code>number</code> | | How many pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width. |
| [options] | <code>Object</code> | | |
| [options.width] | <code>number</code> | | An alternative means of specifying `width`. If both are present this takes priority. |
| [options.height] | <code>number</code> | | An alternative means of specifying `height`. If both are present this takes priority. |
| [options.fit] | <code>String</code> | <code>&#x27;cover&#x27;</code> | How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`. |
| [options.position] | <code>String</code> | <code>&#x27;centre&#x27;</code> | A position, gravity or strategy to use when `fit` is `cover` or `contain`. |
| [options.background] | <code>String</code> \| <code>Object</code> | <code>{r: 0, g: 0, b: 0, alpha: 1}</code> | background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency. |
| [options.kernel] | <code>String</code> | <code>&#x27;lanczos3&#x27;</code> | The kernel to use for image reduction. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load. |
| [options.withoutEnlargement] | <code>Boolean</code> | <code>false</code> | 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. |
| [options.withoutReduction] | <code>Boolean</code> | <code>false</code> | 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. |
| [options.fastShrinkOnLoad] | <code>Boolean</code> | <code>true</code> | 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. |
**Example**
```js
sharp(input)
.resize({ width: 100 })
.toBuffer()
@@ -62,8 +64,8 @@ sharp(input)
// 100 pixels wide, auto-scaled height
});
```
```javascript
**Example**
```js
sharp(input)
.resize({ height: 100 })
.toBuffer()
@@ -71,8 +73,8 @@ sharp(input)
// 100 pixels high, auto-scaled width
});
```
```javascript
**Example**
```js
sharp(input)
.resize(200, 300, {
kernel: sharp.kernel.nearest,
@@ -87,8 +89,8 @@ sharp(input)
// contained within the north-east corner of a semi-transparent white canvas
});
```
```javascript
**Example**
```js
const transformer = sharp()
.resize({
width: 200,
@@ -102,8 +104,8 @@ readableStream
.pipe(transformer)
.pipe(writableStream);
```
```javascript
**Example**
```js
sharp(input)
.resize(200, 200, {
fit: sharp.fit.inside,
@@ -117,8 +119,8 @@ sharp(input)
// and no larger than the input image
});
```
```javascript
**Example**
```js
sharp(input)
.resize(200, 200, {
fit: sharp.fit.outside,
@@ -132,8 +134,8 @@ sharp(input)
// and no smaller than the input image
});
```
```javascript
**Example**
```js
const scaleByHalf = await sharp(input)
.metadata()
.then(({ width }) => sharp(input)
@@ -142,28 +144,30 @@ const scaleByHalf = await sharp(input)
);
```
* Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## extend
Extends/pads the edges of the image with the provided background colour.
Extend / pad / extrude one or more edges of the image with either
the provided background colour or pixels derived from the image.
This operation will always occur after resizing and extraction, if any.
### Parameters
* `extend` **([number][8] | [Object][9])** single pixel count to add to all edges or an Object with per-edge counts
**Throws**:
* `extend.top` **[number][8]** (optional, default `0`)
* `extend.left` **[number][8]** (optional, default `0`)
* `extend.bottom` **[number][8]** (optional, default `0`)
* `extend.right` **[number][8]** (optional, default `0`)
* `extend.background` **([String][10] | [Object][9])** background colour, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
- <code>Error</code> Invalid parameters
### Examples
```javascript
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| extend | <code>number</code> \| <code>Object</code> | | single pixel count to add to all edges or an Object with per-edge counts |
| [extend.top] | <code>number</code> | <code>0</code> | |
| [extend.left] | <code>number</code> | <code>0</code> | |
| [extend.bottom] | <code>number</code> | <code>0</code> | |
| [extend.right] | <code>number</code> | <code>0</code> | |
| [extend.extendWith] | <code>String</code> | <code>&#x27;background&#x27;</code> | populate new pixels using this method, one of: background, copy, repeat, mirror. |
| [extend.background] | <code>String</code> \| <code>Object</code> | <code>{r: 0, g: 0, b: 0, alpha: 1}</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency. |
**Example**
```js
// Resize to 140 pixels wide, then add 10 transparent pixels
// to the top, left and right edges and 20 to the bottom edge
sharp(input)
@@ -177,8 +181,8 @@ sharp(input)
})
...
```
```javascript
**Example**
```js
// Add a row of 10 red pixels to the bottom
sharp(input)
.extend({
@@ -187,39 +191,49 @@ sharp(input)
})
...
```
**Example**
```js
// Extrude image by 8 pixels to the right, mirroring existing right hand edge
sharp(input)
.extend({
right: 8,
background: 'mirror'
})
...
```
* Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## extract
Extract/crop a region of the image.
* Use `extract` before `resize` for pre-resize extraction.
* Use `extract` after `resize` for post-resize extraction.
* Use `extract` before and after for both.
- Use `extract` before `resize` for pre-resize extraction.
- Use `extract` after `resize` for post-resize extraction.
- Use `extract` before and after for both.
### Parameters
* `options` **[Object][9]** describes the region to extract using integral pixel values
**Throws**:
* `options.left` **[number][8]** zero-indexed offset from left edge
* `options.top` **[number][8]** zero-indexed offset from top edge
* `options.width` **[number][8]** width of region to extract
* `options.height` **[number][8]** height of region to extract
- <code>Error</code> Invalid parameters
### Examples
```javascript
| Param | Type | Description |
| --- | --- | --- |
| options | <code>Object</code> | describes the region to extract using integral pixel values |
| options.left | <code>number</code> | zero-indexed offset from left edge |
| options.top | <code>number</code> | zero-indexed offset from top edge |
| options.width | <code>number</code> | width of region to extract |
| options.height | <code>number</code> | height of region to extract |
**Example**
```js
sharp(input)
.extract({ left: left, top: top, width: width, height: height })
.toFile(output, function(err) {
// Extract a region of the input image, saving in the same format.
});
```
```javascript
**Example**
```js
sharp(input)
.extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
.resize(width, height)
@@ -229,50 +243,65 @@ sharp(input)
});
```
* Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## trim
Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
If the result of this operation would trim an image to nothing then no change is made.
The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
will contain `trimOffsetLeft` and `trimOffsetTop` properties.
### Parameters
* `threshold` **[number][8]** the allowed difference from the top-left pixel, a number greater than zero. (optional, default `10`)
**Throws**:
<!---->
- <code>Error</code> Invalid parameters
* Throws **[Error][13]** Invalid parameters
Returns **Sharp**
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| trim | <code>string</code> \| <code>number</code> \| <code>Object</code> | | the specific background colour to trim, the threshold for doing so or an Object with both. |
| [trim.background] | <code>string</code> \| <code>Object</code> | <code>&quot;&#x27;top-left pixel&#x27;&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. |
| [trim.threshold] | <code>number</code> | <code>10</code> | the allowed difference from the above colour, a positive number. |
[1]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
[2]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-position
[3]: https://en.wikipedia.org/wiki/Entropy_%28information_theory%29
[4]: http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
[5]: https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
[6]: https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
[7]: https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[11]: https://www.npmjs.org/package/color
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
**Example**
```js
// Trim pixels with a colour similar to that of the top-left pixel.
sharp(input)
.trim()
.toFile(output, function(err, info) {
...
});
```
**Example**
```js
// Trim pixels with the exact same colour as that of the top-left pixel.
sharp(input)
.trim(0)
.toFile(output, function(err, info) {
...
});
```
**Example**
```js
// Trim only pixels with a similar colour to red.
sharp(input)
.trim("#FF0000")
.toFile(output, function(err, info) {
...
});
```
**Example**
```js
// Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
sharp(input)
.trim({
background: "yellow",
threshold: 42,
})
.toFile(output, function(err, info) {
...
});
```

View File

@@ -1,101 +1,100 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## format
An Object containing nested boolean values representing the available input and output formats/methods.
### Examples
```javascript
console.log(sharp.format);
```
Returns **[Object][1]**
## interpolators
An Object containing the available interpolators and their proper values
Type: [string][2]
### nearest
[Nearest neighbour interpolation][3]. Suitable for image enlargement only.
### bilinear
[Bilinear interpolation][4]. Faster than bicubic but with less smooth results.
### bicubic
[Bicubic interpolation][5] (the default).
### locallyBoundedBicubic
[LBB interpolation][6]. Prevents some "[acutance][7]" but typically reduces performance by a factor of 2.
### nohalo
[Nohalo interpolation][8]. Prevents acutance but typically reduces performance by a factor of 3.
### vertexSplitQuadraticBasisSpline
[VSQBS interpolation][9]. Prevents "staircasing" when enlarging.
## versions
An Object containing the version numbers of sharp, libvips and its dependencies.
An Object containing the version numbers of libvips and its dependencies.
### Examples
```javascript
**Example**
```js
console.log(sharp.versions);
```
## vendor
## interpolators
An Object containing the available interpolators and their proper values
**Read only**: true
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| nearest | <code>string</code> | <code>&quot;nearest&quot;</code> | [Nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). Suitable for image enlargement only. |
| bilinear | <code>string</code> | <code>&quot;bilinear&quot;</code> | [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation). Faster than bicubic but with less smooth results. |
| bicubic | <code>string</code> | <code>&quot;bicubic&quot;</code> | [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). |
| locallyBoundedBicubic | <code>string</code> | <code>&quot;lbb&quot;</code> | [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. |
| nohalo | <code>string</code> | <code>&quot;nohalo&quot;</code> | [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. |
| vertexSplitQuadraticBasisSpline | <code>string</code> | <code>&quot;vsqbs&quot;</code> | [VSQBS interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. |
## format
An Object containing nested boolean values representing the available input and output formats/methods.
**Example**
```js
console.log(sharp.format);
```
## vendor
An Object containing the platform and architecture
of the current and installed vendored binaries.
### Examples
```javascript
**Example**
```js
console.log(sharp.vendor);
```
## cache
Gets or, when options are provided, sets the limits of *libvips'* operation cache.
## queue
An EventEmitter that emits a `change` event when a task is either:
- queued, waiting for _libuv_ to provide a worker thread
- complete
**Example**
```js
sharp.queue.on('change', function(queueLength) {
console.log('Queue contains ' + queueLength + ' task(s)');
});
```
## cache
Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
Existing entries in the cache will be trimmed after any change in limits.
This method always returns cache statistics,
useful for determining how much working memory is required for a particular task.
### Parameters
* `options` **([Object][1] | [boolean][10])** Object with the following attributes, or boolean where true uses default cache settings and false removes all caching (optional, default `true`)
* `options.memory` **[number][11]** is the maximum memory in MB to use for this cache (optional, default `50`)
* `options.files` **[number][11]** is the maximum number of files to hold open (optional, default `20`)
* `options.items` **[number][11]** is the maximum number of operations to cache (optional, default `100`)
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> \| <code>boolean</code> | <code>true</code> | Object with the following attributes, or boolean where true uses default cache settings and false removes all caching |
| [options.memory] | <code>number</code> | <code>50</code> | is the maximum memory in MB to use for this cache |
| [options.files] | <code>number</code> | <code>20</code> | is the maximum number of files to hold open |
| [options.items] | <code>number</code> | <code>100</code> | is the maximum number of operations to cache |
### Examples
```javascript
**Example**
```js
const stats = sharp.cache();
```
```javascript
**Example**
```js
sharp.cache( { items: 200 } );
sharp.cache( { files: 0 } );
sharp.cache(false);
```
Returns **[Object][1]**
## concurrency
Gets or, when a concurrency is provided, sets
the number of threads *libvips'* should create to process each image.
the maximum number of threads _libvips_ should use to process _each image_.
These are from a thread pool managed by glib,
which helps avoid the overhead of creating new threads.
This method always returns the current concurrency.
The default value is the number of CPU cores,
except when using glibc-based Linux without jemalloc,
@@ -103,99 +102,67 @@ where the default is `1` to help reduce memory fragmentation.
A value of `0` will reset this to the number of CPU cores.
The maximum number of images that can be processed in parallel
is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
Some image format libraries spawn additional threads,
e.g. libaom manages its own 4 threads when encoding AVIF images,
and these are independent of the value set here.
This method always returns the current concurrency.
The maximum number of images that sharp can process in parallel
is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
which defaults to 4.
### Parameters
https://nodejs.org/api/cli.html#uv_threadpool_sizesize
* `concurrency` **[number][11]?**
For example, by default, a machine with 8 CPU cores will process
4 images in parallel and use up to 8 threads per image,
so there will be up to 32 concurrent threads.
### Examples
```javascript
**Returns**: <code>number</code> - concurrency
| Param | Type |
| --- | --- |
| [concurrency] | <code>number</code> |
**Example**
```js
const threads = sharp.concurrency(); // 4
sharp.concurrency(2); // 2
sharp.concurrency(0); // 4
```
Returns **[number][11]** concurrency
## queue
An EventEmitter that emits a `change` event when a task is either:
* queued, waiting for *libuv* to provide a worker thread
* complete
### Examples
```javascript
sharp.queue.on('change', function(queueLength) {
console.log('Queue contains ' + queueLength + ' task(s)');
});
```
## counters
Provides access to internal task counters.
- queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
- process is the number of resize tasks currently being processed.
* queue is the number of tasks this module has queued waiting for *libuv* to provide a worker thread from its pool.
* process is the number of resize tasks currently being processed.
### Examples
```javascript
**Example**
```js
const counters = sharp.counters(); // { queue: 2, process: 4 }
```
Returns **[Object][1]**
## simd
Get and set use of SIMD vector unit instructions.
Requires libvips to have been compiled with liborc support.
Improves the performance of `resize`, `blur` and `sharpen` operations
by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
### Parameters
* `simd` **[boolean][10]** (optional, default `true`)
### Examples
| Param | Type | Default |
| --- | --- | --- |
| [simd] | <code>boolean</code> | <code>true</code> |
```javascript
**Example**
```js
const simd = sharp.simd();
// simd is `true` if the runtime use of liborc is currently enabled
```
```javascript
**Example**
```js
const simd = sharp.simd(false);
// prevent libvips from using liborc at runtime
```
Returns **[boolean][10]**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
[4]: http://en.wikipedia.org/wiki/Bilinear_interpolation
[5]: http://en.wikipedia.org/wiki/Bicubic_interpolation
[6]: https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100
[7]: http://en.wikipedia.org/wiki/Acutance
[8]: http://eprints.soton.ac.uk/268086/
[9]: https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
```

View File

@@ -1,8 +1,11 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs').promises;
const path = require('path');
const documentation = require('documentation');
const jsdoc2md = require('jsdoc-to-markdown');
[
'constructor',
@@ -18,8 +21,18 @@ const documentation = require('documentation');
const input = path.join('lib', `${m}.js`);
const output = path.join('docs', `api-${m}.md`);
const ast = await documentation.build(input, { shallow: true });
const markdown = await documentation.formats.md(ast, { markdownToc: false });
const ast = await jsdoc2md.getTemplateData({ files: input });
const markdown = await jsdoc2md.render({
data: ast,
'global-index-format': 'none',
'module-index-format': 'none'
});
await fs.writeFile(output, markdown);
const cleanMarkdown = markdown
.replace(/(## [A-Za-z]+)[^\n]*/g, '$1') // simplify headings to match those of documentationjs, ensures existing URLs work
.replace(/<a name="[A-Za-z+]+"><\/a>/g, '') // remove anchors, let docute add these (at markdown to HTML render time)
.replace(/\*\*Kind\*\*: global[^\n]+/g, '') // remove all "global" Kind labels (requires JSDoc refactoring)
.trim();
await fs.writeFile(output, cleanMarkdown);
});

View File

@@ -1,9 +1,298 @@
# Changelog
## v0.32 - *flow*
Requires libvips v8.14.2
### v0.32.0 - 24th March 2023
* Default to using sequential rather than random access read where possible.
* Replace GIF output `optimise` / `optimize` option with `reuse`.
* Add `progressive` option to GIF output for interlacing.
* Add `wrap` option to text image creation.
* Add `formatMagick` property to metadata of images loaded via *magick.
* Prefer integer (un)premultiply for faster resizing of RGBA images.
* Add `ignoreIcc` input option to ignore embedded ICC profile.
* Allow use of GPS (IFD3) EXIF metadata.
[#2767](https://github.com/lovell/sharp/issues/2767)
* TypeScript definitions are now maintained and published directly, deprecating the `@types/sharp` package.
[#3369](https://github.com/lovell/sharp/issues/3369)
* Prebuilt binaries: ensure macOS 10.13+ support, as documented.
[#3438](https://github.com/lovell/sharp/issues/3438)
* Prebuilt binaries: prevent use of glib slice allocator, improves QEMU support.
[#3448](https://github.com/lovell/sharp/issues/3448)
* Add focus point coordinates to output when using attention based crop.
[#3470](https://github.com/lovell/sharp/pull/3470)
[@ejoebstl](https://github.com/ejoebstl)
* Expose sharp version as `sharp.versions.sharp`.
[#3471](https://github.com/lovell/sharp/issues/3471)
* Respect `fastShrinkOnLoad` resize option for WebP input.
[#3516](https://github.com/lovell/sharp/issues/3516)
* Reduce sharpen `sigma` maximum from 10000 to 10.
[#3521](https://github.com/lovell/sharp/issues/3521)
* Add support for `ArrayBuffer` input.
[#3548](https://github.com/lovell/sharp/pull/3548)
[@kapouer](https://github.com/kapouer)
* Add support to `extend` operation for `extendWith` to allow copy/mirror/repeat.
[#3556](https://github.com/lovell/sharp/pull/3556)
[@janaz](https://github.com/janaz)
* Ensure all async JS callbacks are wrapped to help avoid possible race condition.
[#3569](https://github.com/lovell/sharp/issues/3569)
* Prebuilt binaries: support for tile-based output temporarily removed due to licensing issue.
[#3581](https://github.com/lovell/sharp/issues/3581)
* Add support to `normalise` for `lower` and `upper` percentiles.
[#3583](https://github.com/lovell/sharp/pull/3583)
[@LachlanNewman](https://github.com/LachlanNewman)
## v0.31 - *eagle*
Requires libvips v8.13.3
### v0.31.3 - 21st December 2022
* Add experimental support for JPEG-XL images. Requires libvips compiled with libjxl.
[#2731](https://github.com/lovell/sharp/issues/2731)
* Add runtime detection of V8 memory cage, ensures compatibility with Electron 21 onwards.
[#3384](https://github.com/lovell/sharp/issues/3384)
* Expose `interFrameMaxError` and `interPaletteMaxError` GIF optimisation properties.
[#3401](https://github.com/lovell/sharp/issues/3401)
* Allow installation on Linux with glibc patch versions e.g. Fedora 38.
[#3423](https://github.com/lovell/sharp/issues/3423)
* Expand range of existing `sharpen` parameters to match libvips.
[#3427](https://github.com/lovell/sharp/issues/3427)
* Prevent possible race condition awaiting metadata of Stream-based input.
[#3451](https://github.com/lovell/sharp/issues/3451)
* Improve `extractChannel` support for 16-bit output colourspaces.
[#3453](https://github.com/lovell/sharp/issues/3453)
* Ignore `sequentialRead` option when calculating image statistics.
[#3462](https://github.com/lovell/sharp/issues/3462)
* Small performance improvement for operations that introduce a non-opaque background.
[#3465](https://github.com/lovell/sharp/issues/3465)
* Ensure integral output of `linear` operation.
[#3468](https://github.com/lovell/sharp/issues/3468)
### v0.31.2 - 4th November 2022
* Upgrade to libvips v8.13.3 for upstream bug fixes.
* Ensure manual flip, rotate, resize operation ordering (regression in 0.31.1)
[#3391](https://github.com/lovell/sharp/issues/3391)
* Ensure auto-rotation works without resize (regression in 0.31.1)
[#3422](https://github.com/lovell/sharp/issues/3422)
### v0.31.1 - 29th September 2022
* Upgrade to libvips v8.13.2 for upstream bug fixes.
* Ensure `close` event occurs after `end` event for Stream-based output.
[#3313](https://github.com/lovell/sharp/issues/3313)
* Ensure `limitInputPixels` constructor option uses uint64.
[#3349](https://github.com/lovell/sharp/pull/3349)
[@marcosc90](https://github.com/marcosc90)
* Ensure auto-rotation works with shrink-on-load and extract (regression in 0.31.0).
[#3352](https://github.com/lovell/sharp/issues/3352)
* Ensure AVIF output is always 8-bit.
[#3358](https://github.com/lovell/sharp/issues/3358)
* Ensure greyscale images can be trimmed (regression in 0.31.0).
[#3386](https://github.com/lovell/sharp/issues/3386)
### v0.31.0 - 5th September 2022
* Drop support for Node.js 12, now requires Node.js >= 14.15.0.
* GIF output now re-uses input palette if possible. Use `reoptimise` option to generate a new palette.
* Add WebP `minSize` and `mixed` options for greater control over animation frames.
* Remove previously-deprecated WebP `reductionEffort` and HEIF `speed` options. Use `effort` to control these.
* The `flip` and `flop` operations will now occur before the `rotate` operation.
* Improve `normalise` operation with use of histogram.
[#200](https://github.com/lovell/sharp/issues/200)
* Use combined bounding box of alpha and non-alpha channels for `trim` operation.
[#2166](https://github.com/lovell/sharp/issues/2166)
* Add Buffer and Stream support to tile-based output.
[#2238](https://github.com/lovell/sharp/issues/2238)
* Add input `fileSuffix` and output `alias` to `format` information.
[#2642](https://github.com/lovell/sharp/issues/2642)
* Re-introduce support for greyscale ICC profiles (temporarily removed in 0.30.2).
[#3114](https://github.com/lovell/sharp/issues/3114)
* Add support for WebP and PackBits `compression` options with TIFF output.
[#3198](https://github.com/lovell/sharp/issues/3198)
* Ensure OpenSlide and FITS input works with custom libvips.
[#3226](https://github.com/lovell/sharp/issues/3226)
* Ensure `trim` operation is a no-op when it would reduce an image to nothing.
[#3223](https://github.com/lovell/sharp/issues/3223)
* Expose `vips_text` to create an image containing rendered text.
[#3252](https://github.com/lovell/sharp/pull/3252)
[@brahima](https://github.com/brahima)
* Ensure only properties owned by the `withMetadata` EXIF Object are parsed.
[#3292](https://github.com/lovell/sharp/issues/3292)
* Expand `linear` operation to allow use of per-channel arrays.
[#3303](https://github.com/lovell/sharp/pull/3303)
[@antonmarsden](https://github.com/antonmarsden)
* Ensure the order of `rotate`, `resize` and `extend` operations is respected where possible.
Emit warnings when previous calls in the same pipeline will be ignored.
[#3319](https://github.com/lovell/sharp/issues/3319)
* Ensure PNG bitdepth can be set for non-palette output.
[#3322](https://github.com/lovell/sharp/issues/3322)
* Add trim option to provide a specific background colour.
[#3332](https://github.com/lovell/sharp/pull/3332)
[@mart-jansink](https://github.com/mart-jansink)
* Ensure resized image is unpremultiplied before composite.
[#3334](https://github.com/lovell/sharp/issues/3334)
## v0.30 - *dresser*
Requires libvips v8.12.2
### v0.30.7 - 22nd June 2022
* Ensure tiled composition always works with outside resizing.
[#3227](https://github.com/lovell/sharp/issues/3227)
* Allow WebP encoding effort of 0.
[#3261](https://github.com/lovell/sharp/pull/3261)
[@AlexanderTheGrey](https://github.com/AlexanderTheGrey)
* Prevent upsampling via libwebp.
[#3267](https://github.com/lovell/sharp/pull/3267)
[@blacha](https://github.com/blacha)
### v0.30.6 - 30th May 2022
* Allow values for `limitInputPixels` larger than 32-bit.
[#3238](https://github.com/lovell/sharp/issues/3238)
* Ensure brew-installed `vips` can be detected (regression in 0.30.5).
[#3239](https://github.com/lovell/sharp/issues/3239)
### v0.30.5 - 23rd May 2022
* Install: pass `PKG_CONFIG_PATH` via env rather than substitution.
[@dwisiswant0](https://github.com/dwisiswant0)
* Add support for `--libc` flag to improve cross-platform installation.
[#3160](https://github.com/lovell/sharp/pull/3160)
[@joonamo](https://github.com/joonamo)
* Allow installation of prebuilt libvips binaries from filesystem.
[#3196](https://github.com/lovell/sharp/pull/3196)
[@ankurparihar](https://github.com/ankurparihar)
* Fix rotate-then-extract for EXIF orientation 2.
[#3218](https://github.com/lovell/sharp/pull/3218)
[@jakob0fischl](https://github.com/jakob0fischl)
### v0.30.4 - 18th April 2022
* Increase control over sensitivity to invalid images via `failOn`, deprecate `failOnError` (equivalent to `failOn: 'warning'`).
* Ensure `create` input image has correct bit depth and colour space.
[#3139](https://github.com/lovell/sharp/issues/3139)
* Add support for `TypedArray` input with `byteOffset` and `length`.
[#3146](https://github.com/lovell/sharp/pull/3146)
[@codepage949](https://github.com/codepage949)
* Improve error message when attempting to render SVG input greater than 32767x32767.
[#3167](https://github.com/lovell/sharp/issues/3167)
* Add missing file name to 'Input file is missing' error message.
[#3178](https://github.com/lovell/sharp/pull/3178)
[@Brodan](https://github.com/Brodan)
### v0.30.3 - 14th March 2022
* Allow `sharpen` options to be provided more consistently as an Object.
[#2561](https://github.com/lovell/sharp/issues/2561)
* Expose `x1`, `y2` and `y3` parameters of `sharpen` operation.
[#2935](https://github.com/lovell/sharp/issues/2935)
* Prevent double unpremultiply with some composite blend modes (regression in 0.30.2).
[#3118](https://github.com/lovell/sharp/issues/3118)
### v0.30.2 - 2nd March 2022
* Improve performance and accuracy when compositing multiple images.
[#2286](https://github.com/lovell/sharp/issues/2286)
* Expand pkgconfig search path for wider BSD support.
[#3106](https://github.com/lovell/sharp/issues/3106)
* Ensure Windows C++ runtime is linked statically (regression in 0.30.0).
[#3110](https://github.com/lovell/sharp/pull/3110)
[@kleisauke](https://github.com/kleisauke)
* Temporarily ignore greyscale ICC profiles to workaround lcms bug.
[#3112](https://github.com/lovell/sharp/issues/3112)
### v0.30.1 - 9th February 2022
* Allow use of `toBuffer` and `toFile` on the same instance.
[#3044](https://github.com/lovell/sharp/issues/3044)
* Skip shrink-on-load for known libjpeg rounding errors.
[#3066](https://github.com/lovell/sharp/issues/3066)
[@kleisauke](https://github.com/kleisauke)
* Ensure withoutReduction does not interfere with contain/crop/embed.
[#3081](https://github.com/lovell/sharp/pull/3081)
[@kleisauke](https://github.com/kleisauke)
* Ensure affine interpolator is correctly finalised.
[#3083](https://github.com/lovell/sharp/pull/3083)
[@kleisauke](https://github.com/kleisauke)
### v0.30.0 - 1st February 2022
* Add support for GIF output to prebuilt binaries.

View File

@@ -236,3 +236,39 @@ GitHub: https://github.com/gforge
Name: Chris Banks
GitHub: https://github.com/christopherbradleybanks
Name: codepage949
GitHub: https://github.com/codepage949
Name: Chris Hranj
GitHub: https://github.com/Brodan
Name: Ankur Parihar
GitHub: https://github.com/ankurparihar
Name: Joona Heinikoski
GitHub: https://github.com/joonamo
Name: AlexanderTheGrey
GitHub: https://github.com/AlexanderTheGrey
Name: Blayne Chard
GitHub: https://github.com/blacha
Name: Brahim
GitHub: https://github.com/brahima
Name: Anton Marsden
GitHub: https://github.com/antonmarsden
Name: Marcos Casagrande
GitHub: https://github.com/marcosc90
Name: Emanuel Jöbstl
GitHub: https://github.com/ejoebstl
Name: Tomasz Janowski
GitHub: https://github.com/janaz
Name: Lachlan Newman
GitHub: https://github.com/LachlanNewman

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -5,20 +5,16 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions">
<meta property="og:title" content="sharp - High performance Node.js image processing">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo-600.png">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
img-src 'unsafe-inline' data: https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
connect-src 'self' https://www.google-analytics.com;
script-src 'self' 'unsafe-inline' 'unsafe-eval'
https://www.google-analytics.com/analytics.js;">
<link rel="icon" type="image/png" href="https://pixel.plumbing/px/32x32/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://pixel.plumbing/px/152x152/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://pixel.plumbing/px/144x144/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://pixel.plumbing/px/120x120/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://pixel.plumbing/px/114x114/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
<link rel="icon" type="image/svg+xml" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg">
<link rel="icon" type="image/png" sizes="32x32" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo-32.png">
<link rel="author" href="/humans.txt" type="text/plain">
<link rel="dns-prefetch" href="https://pixel.plumbing">
<link rel="dns-prefetch" href="https://www.google-analytics.com">
<script type="application/ld+json">
{
@@ -35,7 +31,7 @@
"@type": "Person",
"name": "Lovell Fuller"
},
"copyrightYear": [2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022],
"copyrightYear": 2013,
"license": "https://www.apache.org/licenses/LICENSE-2.0"
}
</script>
@@ -83,9 +79,7 @@
.map(function (sidebarLink) {
return sidebarLink.title;
})[0];
return title
? md.replace(/<!-- Generated by documentation.js. Update this documentation by updating the source code. -->/, '# ' + title)
: md;
return title ? `# ${title}\n${md}` : md;
});
}
};
@@ -124,7 +118,7 @@
router: { mode: 'history' },
logo: '<div style="display:flex;align-items:center">'
+ '<strong>sharp</strong> '
+ '<img src="https://pixel.plumbing/px/16x16/sharp-logo.svg" style="padding:8px" alt="#"> '
+ '<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" style="padding:8px" width="32" height="32" alt="#"> '
+ '<span style="opacity:0.8;white-space:pre" class="shorten-strapline">High performance </span> '
+ '<span style="opacity:0.8">Node.js image processing</span> '
+ '</div>',

View File

@@ -10,7 +10,7 @@ yarn add sharp
## Prerequisites
* Node.js >= 12.13.0
* Node.js >= 14.15.0
## Prebuilt binaries
@@ -28,7 +28,7 @@ is downloaded via HTTPS, verified via Subresource Integrity
and decompressed into `node_modules/sharp/vendor` during `npm install`.
This provides support for the
JPEG, PNG, WebP, AVIF, TIFF, GIF and SVG (input) image formats.
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.
The following platforms have prebuilt libvips but not sharp:
@@ -57,7 +57,7 @@ When using npm v7 or later, the user running `npm install` must own the director
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
Check the output of running `npm install --verbose sharp` for useful error messages.
Check the output of running `npm install --verbose --foreground-scripts sharp` for useful error messages.
## Apple M1
@@ -74,20 +74,36 @@ The target platform and/or architecture can be manually selected using the follo
npm install --platform=... --arch=... --arm-version=... sharp
```
* `--platform`: one of `linux`, `linuxmusl`, `darwin` or `win32`.
* `--platform`: one of `linux`, `darwin` or `win32`.
* `--arch`: one of `x64`, `ia32`, `arm` or `arm64`.
* `--arm-version`: one of `6`, `7` or `8` (`arm` defaults to `6`, `arm64` defaults to `8`).
* `--libc`: one of `glibc` or `musl`. This option only works with platform `linux`, defaults to `glibc`
* `--sharp-install-force`: skip version compatibility and Subresource Integrity checks.
These values can also be set via environment variables,
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`, `npm_config_libc`
and `SHARP_INSTALL_FORCE` respectively.
For example, if the target machine has a 64-bit ARM CPU and is running Alpine Linux,
use the following flags:
```sh
npm install --arch=arm64 --platform=linuxmusl sharp
npm install --arch=arm64 --platform=linux --libc=musl sharp
```
If the current machine is Alpine Linux and the target machine is Debian Linux on x64 cpu,
use the following flags:
```sh
npm install --arch=x64 --platform=linux --libc=glibc sharp
```
Multiple platforms and architectures can be supported within the same installation tree.
The following example for macOS installs x64 binaries then adds (via a rebuild) arm64 binaries:
```sh
npm install --platform=darwin --arch=x64 sharp
npm rebuild --platform=darwin --arch=arm64 sharp
```
## Custom libvips
@@ -96,10 +112,11 @@ To use a custom, globally-installed version of libvips instead of the provided b
make sure it is at least the version listed under `config.libvips` in the `package.json` file
and that it can be located using `pkg-config --modversion vips-cpp`.
For help compiling libvips from source, please see
[https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball](https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball).
For help compiling libvips and its dependencies, please see
[building libvips from source](https://www.libvips.org/install.html#building-libvips-from-source).
The use of a globally-installed libvips is unsupported on Windows.
The use of a globally-installed libvips is unsupported on Windows
and on macOS when running Node.js under Rosetta.
## Building from source
@@ -118,6 +135,8 @@ Building from source requires:
This is an advanced approach that most people will not require.
### Prebuilt sharp binaries
To install the prebuilt sharp binaries from a custom URL,
set the `sharp_binary_host` npm config option
or the `npm_config_sharp_binary_host` environment variable.
@@ -126,17 +145,50 @@ To install the prebuilt sharp binaries from a directory on the local filesystem,
set the `sharp_local_prebuilds` npm config option
or the `npm_config_sharp_local_prebuilds` environment variable.
URL example:
if `sharp_binary_host` is set to `https://hostname/path`
and the sharp version is `1.2.3` then the resultant URL will be
`https://hostname/path/v1.2.3/sharp-v1.2.3-napi-v5-platform-arch.tar.gz`.
Filename example:
if `sharp_local_prebuilds` is set to `/path`
and the sharp version is `1.2.3` then the resultant filename will be
`/path/sharp-v1.2.3-napi-v5-platform-arch.tar.gz`.
### Prebuilt libvips binaries
To install the prebuilt libvips binaries from a custom URL,
set the `sharp_libvips_binary_host` npm config option
or the `npm_config_sharp_libvips_binary_host` environment variable.
The version subpath and file name are appended to these.
For example, if `sharp_libvips_binary_host` is set to `https://hostname/path`
and the libvips version is `1.2.3` then the resultant URL will be
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.br`.
To install the prebuilt libvips binaries from a directory on the local filesystem,
set the `sharp_libvips_local_prebuilds` npm config option
or the `npm_config_sharp_libvips_local_prebuilds` environment variable.
The version subpath and filename are appended to these.
URL example:
if `sharp_libvips_binary_host` is set to `https://hostname/path`
and the libvips version is `4.5.6` then the resultant URL will be
`https://hostname/path/v4.5.6/libvips-4.5.6-platform-arch.tar.br`.
Filename example:
if `sharp_libvips_local_prebuilds` is set to `/path`
and the libvips version is `4.5.6` then the resultant filename will be
`/path/v4.5.6/libvips-4.5.6-platform-arch.tar.br`.
See the Chinese mirror below for a further example.
If these binaries are modified, new integrity hashes can be provided
at install time via `npm_package_config_integrity_platform_arch`
environment variables, for example set
`npm_package_config_integrity_linux_x64` to `sha512-abc...`.
The integrity hash of a file can be generated via:
```sh
sha512sum libvips-x.y.z-platform-arch.tar.br | cut -f1 -d' ' | xxd -r -p | base64 -w 0
```
## Chinese mirror
A mirror site based in China, provided by Alibaba, contains binaries for both sharp and libvips.
@@ -186,16 +238,6 @@ the use of an alternative memory allocator such as
Those using musl-based Linux (e.g. Alpine) and non-Linux systems are
unaffected.
## Heroku
Add the
[jemalloc buildpack](https://github.com/gaffneyc/heroku-buildpack-jemalloc)
to reduce the effects of memory fragmentation.
Set
[NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager.
## AWS Lambda
The `node_modules` directory of the
@@ -207,15 +249,21 @@ run the following additional command after `npm install`:
```sh
npm install
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp
rm -rf node_modules/sharp
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux --libc=glibc sharp
```
To get the best performance select the largest memory available.
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
## Webpack
When integrating with AWS API Gateway, ensure it is configured with the relevant
[binary media types](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html).
Ensure sharp is added to the
## Bundlers
### webpack
Ensure sharp is excluded from bundling via the
[externals](https://webpack.js.org/configuration/externals/)
configuration.
@@ -225,20 +273,97 @@ externals: {
}
```
### esbuild
Ensure sharp is excluded from bundling via the
[external](https://esbuild.github.io/api/#external)
configuration.
```js
buildSync({
entryPoints: ['app.js'],
bundle: true,
platform: 'node',
external: ['sharp'],
})
```
```sh
esbuild app.js --bundle --platform=node --external:sharp
```
For `serverless-esbuild`, ensure platform-specific binaries are installed
via the `serverless.yml` configuration.
```yaml
custom:
esbuild:
external:
- sharp
packagerOptions:
scripts:
- npm install --arch=x64 --platform=linux sharp
```
## TypeScript
TypeScript definitions are published as part of
the `sharp` package from v0.32.0.
Previously these were available via the `@types/sharp` package,
which is now deprecated.
When using Typescript, please ensure `devDependencies` includes
the `@types/node` package.
## Fonts
When creating text images or rendering SVG images that contain text elements,
`fontconfig` is used to find the relevant fonts.
On Windows and macOS systems, all system fonts are available for use.
On macOS systems using Homebrew, you may need to set the
`PANGOCAIRO_BACKEND` environment variable to a value of `fontconfig`
to ensure it is used for font discovery instead of Core Text.
On Linux systems, fonts that include the relevant
[`fontconfig` configuration](https://www.freedesktop.org/software/fontconfig/fontconfig-user.html)
when installed via package manager are available for use.
If `fontconfig` configuration is not found, the following error will occur:
```
Fontconfig error: Cannot load default config file
```
In serverless environments where there is no control over font packages,
use the `FONTCONFIG_PATH` environment variable to point to a custom location.
Embedded SVG fonts are unsupported.
## Worker threads
The main thread must call `require('sharp')`
before worker threads are created
to ensure shared libraries remain loaded in memory
On some platforms, including glibc-based Linux,
the main thread must call `require('sharp')`
_before_ worker threads are created.
This is to ensure shared libraries remain loaded in memory
until after all threads are complete.
Without this, the following error may occur:
```
Module did not self-register
```
## Known conflicts
### Canvas and Windows
The prebuilt binaries provided by `canvas` for Windows depend on the unmaintained GTK 2, last updated in 2011.
The prebuilt binaries provided by `canvas` for Windows
from v2.7.0 onwards depend on the Visual C++ Runtime (MSVCRT).
These conflict with the binaries provided by sharp,
which depend on the more modern Universal C Runtime (UCRT).
These conflict with the modern, up-to-date binaries provided by sharp.
See [Automattic/node-canvas#2155](https://github.com/Automattic/node-canvas/issues/2155).
If both modules are used in the same Windows process, the following error will occur:
```

View File

@@ -2,71 +2,110 @@
A test to benchmark the performance of this module relative to alternatives.
## The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.16.1 - Image processing in pure JavaScript. Provides bicubic interpolation.
* [mapnik](https://www.npmjs.org/package/mapnik) v4.5.9 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
* [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* [@squoosh/lib](https://www.npmjs.com/package/@squoosh/lib) v0.4.0 - Image libraries transpiled to WebAssembly, includes GPLv3 code.
* [@squoosh/cli](https://www.npmjs.com/package/@squoosh/cli) v0.7.2 - Command line wrapper around `@squoosh/lib`, avoids GPLv3 by spawning process.
* sharp v0.30.0 / libvips v8.12.2 - Caching within libvips disabled to ensure a fair comparison.
## The task
Decompress a 2725x2225 JPEG image,
resize to 720x588 using Lanczos 3 resampling (where available),
then compress to JPEG at a "quality" setting of 80.
## Test environment
* AWS EC2 eu-west-1 [c5ad.xlarge](https://aws.amazon.com/ec2/instance-types/c5/) (4x AMD EPYC 7R32)
* Ubuntu 21.10 (ami-0258eeb71ddf238b3)
* Node.js 16.13.2
## Results
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp | buffer | buffer | 0.84 | 1.0 |
| squoosh-cli | file | file | 1.08 | 1.3 |
| squoosh-lib | buffer | buffer | 1.85 | 2.2 |
| mapnik | buffer | buffer | 3.45 | 4.1 |
| gm | buffer | buffer | 8.60 | 10.2 |
| gm | file | file | 8.66 | 10.3 |
| imagemagick | file | file | 8.79 | 10.5 |
| sharp | stream | stream | 28.90 | 34.4 |
| sharp | file | file | 30.08 | 35.8 |
| sharp | buffer | buffer | 30.42 | 36.2 |
Greater libvips performance can be expected with caching enabled (default)
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
## Contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.22.7 - Image processing in pure JavaScript.
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
* [gm](https://www.npmjs.com/package/gm) v1.25.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* [@squoosh/lib](https://www.npmjs.com/package/@squoosh/lib) v0.4.0 - Image libraries transpiled to WebAssembly, includes GPLv3 code, but "*Project no longer maintained*".
* [@squoosh/cli](https://www.npmjs.com/package/@squoosh/cli) v0.7.3 - Command line wrapper around `@squoosh/lib`, avoids GPLv3 by spawning process, but "*Project no longer maintained*".
* sharp v0.32.0 / libvips v8.14.2 - Caching within libvips disabled to ensure a fair comparison.
## Environment
### AMD64
* AWS EC2 us-east-2 [c6a.xlarge](https://aws.amazon.com/ec2/instance-types/c6a/) (4x AMD EPYC 7R13)
* Ubuntu 22.04 20230303 (ami-0122295b0eb922138)
* Node.js 16.19.1
### ARM64
* AWS EC2 us-east-2 [c7g.xlarge](https://aws.amazon.com/ec2/instance-types/c7g/) (4x ARM Graviton3)
* Ubuntu 22.04 20230303 (ami-0af198159897e7a29)
* Node.js 16.19.1
## Task: JPEG
Decompress a 2725x2225 JPEG image,
resize to 720x588 using Lanczos 3 resampling (where available),
then compress to JPEG at a "quality" setting of 80.
Note: jimp does not support Lanczos 3, bicubic resampling used instead.
#### Results: JPEG (AMD64)
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp | buffer | buffer | 0.84 | 1.0 |
| squoosh-cli | file | file | 1.07 | 1.3 |
| squoosh-lib | buffer | buffer | 1.82 | 2.2 |
| gm | buffer | buffer | 8.41 | 10.0 |
| gm | file | file | 8.45 | 10.0 |
| imagemagick | file | file | 8.77 | 10.4 |
| sharp | stream | stream | 36.36 | 43.3 |
| sharp | file | file | 38.67 | 46.0 |
| sharp | buffer | buffer | 39.44 | 47.0 |
#### Results: JPEG (ARM64)
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp | buffer | buffer | 1.02 | 1.0 |
| squoosh-cli | file | file | 1.11 | 1.1 |
| squoosh-lib | buffer | buffer | 2.08 | 2.0 |
| gm | buffer | buffer | 8.80 | 8.6 |
| gm | file | file | 10.05 | 9.9 |
| imagemagick | file | file | 10.28 | 10.1 |
| sharp | stream | stream | 26.87 | 26.3 |
| sharp | file | file | 27.88 | 27.3 |
| sharp | buffer | buffer | 28.40 | 27.8 |
## Task: PNG
Decompress a 2048x1536 RGBA PNG image,
premultiply the alpha channel,
resize to 720x540 using Lanczos 3 resampling (where available),
unpremultiply then compress as PNG with a "default" zlib compression level of 6
and without adaptive filtering.
Note: jimp does not support premultiply/unpremultiply.
### Results: PNG (AMD64)
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| squoosh-cli | file | file | 0.40 | 1.0 |
| squoosh-lib | buffer | buffer | 0.47 | 1.2 |
| gm | file | file | 6.47 | 16.2 |
| jimp | buffer | buffer | 6.60 | 16.5 |
| imagemagick | file | file | 7.08 | 17.7 |
| sharp | file | file | 17.80 | 44.5 |
| sharp | buffer | buffer | 18.02 | 45.0 |
### Results: PNG (ARM64)
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| squoosh-cli | file | file | 0.40 | 1.0 |
| squoosh-lib | buffer | buffer | 0.48 | 1.2 |
| gm | file | file | 7.20 | 18.0 |
| jimp | buffer | buffer | 7.62 | 19.1 |
| imagemagick | file | file | 7.96 | 19.9 |
| sharp | file | file | 12.97 | 32.4 |
| sharp | buffer | buffer | 13.12 | 45.0 |
## Running the benchmark test
Requires _ImageMagick_, _GraphicsMagick_ and _Mapnik_:
```sh
brew install imagemagick
brew install graphicsmagick
brew install mapnik
```
```sh
sudo apt-get install build-essential imagemagick libmagick++-dev graphicsmagick libmapnik-dev
```
```sh
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick mapnik-devel
```
Requires Docker.
```sh
git clone https://github.com/lovell/sharp.git
cd sharp
npm install --build-from-source
cd test/bench
npm install
npm test
cd sharp/test/bench
./run-with-docker.sh
```

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');
@@ -37,7 +40,7 @@ for (const match of matches) {
].forEach((section) => {
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
const matches = contents.matchAll(
/\n## (?<title>[A-Za-z]+)\n\n(?<firstparagraph>.+?)\n\n(?<parameters>### Parameters.+?Returns)?/gs
/## (?<title>[A-Za-z]+)\n(?<firstparagraph>.+?)\n\n.+?(?<parameters>\| Param .+?\n\n)?\*\*Example/gs
);
for (const match of matches) {
const { title, firstparagraph, parameters } = match.groups;

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const stopWords = require('./stop-words');
@@ -9,12 +12,13 @@ const extractDescription = (str) =>
.replace(/\s+/g, ' ')
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
.replace(/\s+/g, ' ')
.substr(0, 180)
.substring(0, 200)
.trim();
const extractParameters = (str) =>
[...str.matchAll(/options\.(?<name>[^.`]+)/gs)]
[...str.matchAll(/options\.(?<name>[^.`\] ]+)/gs)]
.map((match) => match.groups.name)
.map((name) => name.replace(/([A-Z])/g, ' $1').toLowerCase())
.join(' ');
const extractKeywords = (str) =>

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
module.exports = [
@@ -12,15 +15,18 @@ module.exports = [
'and',
'any',
'are',
'available',
'based',
'been',
'before',
'best',
'both',
'call',
'callback',
'can',
'containing',
'contains',
'created',
'current',
'date',
'default',
@@ -43,6 +49,8 @@ module.exports = [
'how',
'image',
'implies',
'include',
'including',
'involve',
'its',
'last',
@@ -56,27 +64,37 @@ module.exports = [
'must',
'non',
'not',
'now',
'occur',
'occurs',
'one',
'options',
'other',
'out',
'over',
'part',
'perform',
'performs',
'please',
'pre',
'previously',
'produce',
'provide',
'provided',
'ready',
'requires',
'requiresharp',
'returned',
'run',
'same',
'see',
'set',
'sets',
'sharp',
'should',
'since',
'site',
'some',
'specified',
'spelling',
'such',
@@ -104,6 +122,7 @@ module.exports = [
'using',
'value',
'values',
'via',
'were',
'when',
'which',
@@ -111,5 +130,6 @@ module.exports = [
'will',
'with',
'without',
'you'
'you',
'your'
];

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const libvips = require('../lib/libvips');

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');
@@ -35,6 +38,7 @@ const hasSharpPrebuild = [
];
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
const localLibvipsDir = process.env.npm_config_sharp_libvips_local_prebuilds || '';
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
@@ -42,7 +46,9 @@ const installationForced = !!(process.env.npm_config_sharp_install_force || proc
const fail = function (err) {
libvips.log(err);
if (err.code === 'EACCES') {
libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
libvips.log('Are you trying to install as a root or sudo user?');
libvips.log('- For npm <= v6, try again with the "--unsafe-perm" flag');
libvips.log('- For npm >= v8, the user must own the directory "npm install" is run in');
}
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
process.exit(1);
@@ -125,9 +131,6 @@ try {
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (platformAndArch === 'darwin-arm64') {
throw new Error("Please run 'brew install vips' to install libvips on Apple M1 (ARM64) systems");
}
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
@@ -135,7 +138,8 @@ try {
const libcFamily = detectLibc.familySync();
const libcVersion = detectLibc.versionSync();
if (libcFamily === detectLibc.GLIBC && libcVersion && minimumGlibcVersionByArch[arch]) {
if (semverLessThan(`${libcVersion}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
const libcVersionWithoutPatch = libcVersion.split('.').slice(0, 2).join('.');
if (semverLessThan(`${libcVersionWithoutPatch}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
handleError(new Error(`Use with glibc ${libcVersion} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
}
}
@@ -149,17 +153,21 @@ try {
if (!semverSatisfies(process.versions.node, supportedNodeVersion)) {
handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
}
// Download to per-process temporary file
const tarFilename = ['libvips', minimumLibvipsVersionLabelled, platformAndArch].join('-') + '.tar.br';
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
if (fs.existsSync(tarPathCache)) {
libvips.log(`Using cached ${tarPathCache}`);
extractTarball(tarPathCache, platformAndArch);
} else if (localLibvipsDir) {
// If localLibvipsDir is given try to use binaries from local directory
const tarPathLocal = path.join(path.resolve(localLibvipsDir), `v${minimumLibvipsVersionLabelled}`, tarFilename);
libvips.log(`Using local libvips from ${tarPathLocal}`);
extractTarball(tarPathLocal, platformAndArch);
} else {
const url = distBaseUrl + tarFilename;
libvips.log(`Downloading ${url}`);
simpleGet({ url: url, agent: agent() }, function (err, response) {
simpleGet({ url: url, agent: agent(libvips.log) }, function (err, response) {
if (err) {
fail(err);
} else if (response.statusCode === 404) {

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const url = require('url');
@@ -18,7 +21,7 @@ function env (key) {
return process.env[key];
}
module.exports = function () {
module.exports = function (log) {
try {
const proxy = new url.URL(proxies.map(env).find(is.string));
const tunnel = proxy.protocol === 'https:'
@@ -27,6 +30,7 @@ module.exports = function () {
const proxyAuth = proxy.username && proxy.password
? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
: null;
log(`Via proxy ${proxy.protocol}://${proxy.hostname}:${proxy.port} ${proxyAuth ? 'with' : 'no'} credentials`);
return tunnel({
proxy: {
port: Number(proxy.port),

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const is = require('./is');
@@ -98,7 +101,7 @@ function extractChannel (channel) {
} else {
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue, alpha', channel);
}
return this.toColourspace('b-w');
return this;
}
/**

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const color = require('color');
@@ -19,6 +22,11 @@ const colourspace = {
* Tint the image using the provided chroma while preserving the image luminance.
* An alpha channel may be present and will be unchanged by the operation.
*
* @example
* const output = await sharp(input)
* .tint({ r: 255, g: 240, b: 16 })
* .toBuffer();
*
* @param {string|Object} rgb - parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values.
* @returns {Sharp}
* @throws {Error} Invalid parameter
@@ -37,6 +45,10 @@ function tint (rgb) {
* This may be overridden by other sharp operations such as `toColourspace('b-w')`,
* which will produce an output image containing one color channel.
* An alpha channel may be present, and will be unchanged by the operation.
*
* @example
* const output = await sharp(input).greyscale().toBuffer();
*
* @param {Boolean} [greyscale=true]
* @returns {Sharp}
*/

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const is = require('./is');
@@ -43,6 +46,9 @@ const blend = {
* The images to composite must be the same size or smaller than the processed image.
* If both `top` and `left` options are provided, they take precedence over `gravity`.
*
* Any resize or rotate operations in the same processing pipeline
* will always be applied to the input image before composition.
*
* The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
* `dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
* `xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
@@ -50,12 +56,27 @@ const blend = {
* `hard-light`, `soft-light`, `difference`, `exclusion`.
*
* More information about blend modes can be found at
* https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
* https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
* and https://www.cairographics.org/operators/
*
* @since 0.22.0
*
* @example
* await sharp(background)
* .composite([
* { input: layer1, gravity: 'northwest' },
* { input: layer2, gravity: 'southeast' },
* ])
* .toFile('combined.png');
*
* @example
* const output = await sharp('input.gif', { animated: true })
* .composite([
* { input: 'overlay.png', tile: true, blend: 'saturate' }
* ])
* .toBuffer();
*
* @example
* sharp('input.png')
* .rotate(180)
* .resize(300)
@@ -78,6 +99,17 @@ const blend = {
* @param {Number} [images[].input.create.height]
* @param {Number} [images[].input.create.channels] - 3-4
* @param {String|Object} [images[].input.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {Object} [images[].input.text] - describes a new text image to be created.
* @param {string} [images[].input.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
* @param {string} [images[].input.text.font] - font name to render with.
* @param {string} [images[].input.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
* @param {number} [images[].input.text.width=0] - integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
* @param {number} [images[].input.text.height=0] - integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
* @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
* @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
* @param {Number} [images[].top] - the pixel offset from the top edge.
@@ -89,7 +121,8 @@ const blend = {
* @param {Number} [images[].raw.width]
* @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels]
* @param {boolean} [images[].failOnError=true] - @see {@link /api-constructor#parameters|constructor parameters}
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
* @returns {Sharp}
* @throws {Error} Invalid parameters
@@ -162,7 +195,6 @@ function composite (images) {
throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
}
}
return composite;
});
return this;

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const util = require('util');
@@ -92,26 +95,46 @@ const debuglog = util.debuglog('sharp');
* }
* }).toFile('noise.png');
*
* @param {(Buffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
* @example
* // Generate an image from text
* await sharp({
* text: {
* text: 'Hello, world!',
* width: 400, // max width
* height: 300 // max height
* }
* }).toFile('text_bw.png');
*
* @example
* // Generate an rgba image from text using pango markup and font
* await sharp({
* text: {
* text: '<span foreground="red">Red!</span><span background="cyan">blue</span>',
* font: 'sans',
* rgba: true,
* dpi: 300
* }
* }).toFile('text_rgba.png');
*
* @param {(Buffer|ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
* a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
* a TypedArray containing raw pixel image data, or
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
* @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort.
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG).
* @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
* This can reduce memory usage and might improve performance on some systems.
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF).
* @param {boolean} [options.sequentialRead=true] - Set this to `false` to use random access rather than sequential read. Some operations will do this automatically.
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
* @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
* @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
* @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {number} [options.raw.width] - integral number of pixels wide.
* @param {number} [options.raw.height] - integral number of pixels high.
@@ -127,6 +150,18 @@ const debuglog = util.debuglog('sharp');
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
* @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
* @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
* @param {Object} [options.text] - describes a new text image to be created.
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
* @param {string} [options.text.font] - font name to render with.
* @param {string} [options.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
* @param {number} [options.text.width=0] - integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
* @param {number} [options.text.height=0] - integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
* @param {string} [options.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
* @param {boolean} [options.text.justify=false] - set this to true to apply justification to the text.
* @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
* @param {boolean} [options.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
* @param {number} [options.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
* @param {string} [options.text.wrap='word'] - word wrapping style when width is provided, one of: 'word', 'char', 'charWord' (prefer char, fallback to word) or 'none'.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -165,6 +200,7 @@ const Sharp = function (input, options) {
extendLeft: 0,
extendRight: 0,
extendBackground: [0, 0, 0, 255],
extendWith: 'background',
withoutEnlargement: false,
withoutReduction: false,
affineMatrix: [],
@@ -186,15 +222,21 @@ const Sharp = function (input, options) {
medianSize: 0,
blurSigma: 0,
sharpenSigma: 0,
sharpenFlat: 1,
sharpenJagged: 2,
sharpenM1: 1,
sharpenM2: 2,
sharpenX1: 2,
sharpenY2: 10,
sharpenY3: 20,
threshold: 0,
thresholdGrayscale: true,
trimBackground: [],
trimThreshold: 0,
gamma: 0,
gammaOut: 0,
greyscale: false,
normalise: false,
normaliseLower: 1,
normaliseUpper: 99,
claheWidth: 0,
claheHeight: 0,
claheMaxSlope: 3,
@@ -249,9 +291,15 @@ const Sharp = function (input, options) {
webpNearLossless: false,
webpSmartSubsample: false,
webpEffort: 4,
webpMinSize: false,
webpMixed: false,
gifBitdepth: 8,
gifEffort: 7,
gifDither: 1,
gifInterFrameMaxError: 0,
gifInterPaletteMaxError: 3,
gifReuse: true,
gifProgressive: false,
tiffQuality: 80,
tiffCompression: 'jpeg',
tiffPredictor: 'horizontal',
@@ -268,6 +316,10 @@ const Sharp = function (input, options) {
heifCompression: 'av1',
heifEffort: 4,
heifChromaSubsampling: '4:4:4',
jxlDistance: 1,
jxlDecodingTier: 0,
jxlEffort: 7,
jxlLossless: false,
rawDepth: 'uchar',
tileSize: 256,
tileOverlap: 0,
@@ -280,9 +332,10 @@ const Sharp = function (input, options) {
tileBackground: [255, 255, 255, 255],
tileCentre: false,
tileId: 'https://example.com/iiif',
tileBasename: '',
timeoutSeconds: 0,
linearA: 1,
linearB: 0,
linearA: [],
linearB: [],
// Function to notify of libvips warnings
debuglog: warning => {
this.emit('warning', warning);
@@ -317,9 +370,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
* // Using Promises to know when the pipeline is complete
* const fs = require("fs");
* const got = require("got");
* const sharpStream = sharp({
* failOnError: false
* });
* const sharpStream = sharp({ failOn: 'none' });
*
* const promises = [];
*
@@ -346,7 +397,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
* .toFile("optimized-500.webp")
* );
*
* // https://github.com/sindresorhus/got#gotstreamurl-options
* // https://github.com/sindresorhus/got/blob/main/documentation/3-streams.md
* got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
*
* Promise.all(promises)

1614
lib/index.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const Sharp = require('./constructor');

View File

@@ -1,17 +1,32 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const color = require('color');
const is = require('./is');
const sharp = require('./sharp');
/**
* Justication alignment
* @member
* @private
*/
const align = {
left: 'low',
center: 'centre',
centre: 'centre',
right: 'high'
};
/**
* Extract input options, if any, from an object.
* @private
*/
function _inputOptionsFromObject (obj) {
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd }
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
: undefined;
}
@@ -21,10 +36,11 @@ function _inputOptionsFromObject (obj) {
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = {
failOnError: true,
failOn: 'warning',
limitInputPixels: Math.pow(0x3FFF, 2),
ignoreIcc: false,
unlimited: false,
sequentialRead: false
sequentialRead: true
};
if (is.string(input)) {
// filesystem
@@ -35,11 +51,16 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw Error('Input Buffer is empty');
}
inputDescriptor.buffer = input;
} else if (is.arrayBuffer(input)) {
if (input.byteLength === 0) {
throw Error('Input bit Array is empty');
}
inputDescriptor.buffer = Buffer.from(input, 0, input.byteLength);
} else if (is.typedArray(input)) {
if (input.length === 0) {
throw Error('Input Bit Array is empty');
}
inputDescriptor.buffer = Buffer.from(input.buffer);
inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
// Plain Object descriptor, e.g. create
inputOptions = input;
@@ -56,14 +77,22 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
}`);
}
if (is.object(inputOptions)) {
// Fail on error
// Deprecated: failOnError
if (is.defined(inputOptions.failOnError)) {
if (is.bool(inputOptions.failOnError)) {
inputDescriptor.failOnError = inputOptions.failOnError;
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
} else {
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
}
}
// failOn
if (is.defined(inputOptions.failOn)) {
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
inputDescriptor.failOn = inputOptions.failOn;
} else {
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
}
}
// Density
if (is.defined(inputOptions.density)) {
if (is.inRange(inputOptions.density, 1, 100000)) {
@@ -72,16 +101,24 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw is.invalidParameterError('density', 'number between 1 and 100000', inputOptions.density);
}
}
// Ignore embeddded ICC profile
if (is.defined(inputOptions.ignoreIcc)) {
if (is.bool(inputOptions.ignoreIcc)) {
inputDescriptor.ignoreIcc = inputOptions.ignoreIcc;
} else {
throw is.invalidParameterError('ignoreIcc', 'boolean', inputOptions.ignoreIcc);
}
}
// limitInputPixels
if (is.defined(inputOptions.limitInputPixels)) {
if (is.bool(inputOptions.limitInputPixels)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
? Math.pow(0x3FFF, 2)
: 0;
} else if (is.integer(inputOptions.limitInputPixels) && inputOptions.limitInputPixels >= 0) {
} else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
} else {
throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
throw is.invalidParameterError('limitInputPixels', 'positive integer', inputOptions.limitInputPixels);
}
}
// unlimited
@@ -237,6 +274,88 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw new Error('Expected valid width, height and channels to create a new input image');
}
}
// Create a new image with text
if (is.defined(inputOptions.text)) {
if (is.object(inputOptions.text) && is.string(inputOptions.text.text)) {
inputDescriptor.textValue = inputOptions.text.text;
if (is.defined(inputOptions.text.height) && is.defined(inputOptions.text.dpi)) {
throw new Error('Expected only one of dpi or height');
}
if (is.defined(inputOptions.text.font)) {
if (is.string(inputOptions.text.font)) {
inputDescriptor.textFont = inputOptions.text.font;
} else {
throw is.invalidParameterError('text.font', 'string', inputOptions.text.font);
}
}
if (is.defined(inputOptions.text.fontfile)) {
if (is.string(inputOptions.text.fontfile)) {
inputDescriptor.textFontfile = inputOptions.text.fontfile;
} else {
throw is.invalidParameterError('text.fontfile', 'string', inputOptions.text.fontfile);
}
}
if (is.defined(inputOptions.text.width)) {
if (is.number(inputOptions.text.width)) {
inputDescriptor.textWidth = inputOptions.text.width;
} else {
throw is.invalidParameterError('text.textWidth', 'number', inputOptions.text.width);
}
}
if (is.defined(inputOptions.text.height)) {
if (is.number(inputOptions.text.height)) {
inputDescriptor.textHeight = inputOptions.text.height;
} else {
throw is.invalidParameterError('text.height', 'number', inputOptions.text.height);
}
}
if (is.defined(inputOptions.text.align)) {
if (is.string(inputOptions.text.align) && is.string(this.constructor.align[inputOptions.text.align])) {
inputDescriptor.textAlign = this.constructor.align[inputOptions.text.align];
} else {
throw is.invalidParameterError('text.align', 'valid alignment', inputOptions.text.align);
}
}
if (is.defined(inputOptions.text.justify)) {
if (is.bool(inputOptions.text.justify)) {
inputDescriptor.textJustify = inputOptions.text.justify;
} else {
throw is.invalidParameterError('text.justify', 'boolean', inputOptions.text.justify);
}
}
if (is.defined(inputOptions.text.dpi)) {
if (is.number(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 100000)) {
inputDescriptor.textDpi = inputOptions.text.dpi;
} else {
throw is.invalidParameterError('text.dpi', 'number between 1 and 100000', inputOptions.text.dpi);
}
}
if (is.defined(inputOptions.text.rgba)) {
if (is.bool(inputOptions.text.rgba)) {
inputDescriptor.textRgba = inputOptions.text.rgba;
} else {
throw is.invalidParameterError('text.rgba', 'bool', inputOptions.text.rgba);
}
}
if (is.defined(inputOptions.text.spacing)) {
if (is.number(inputOptions.text.spacing)) {
inputDescriptor.textSpacing = inputOptions.text.spacing;
} else {
throw is.invalidParameterError('text.spacing', 'number', inputOptions.text.spacing);
}
}
if (is.defined(inputOptions.text.wrap)) {
if (is.string(inputOptions.text.wrap) && is.inArray(inputOptions.text.wrap, ['word', 'char', 'wordChar', 'none'])) {
inputDescriptor.textWrap = inputOptions.text.wrap;
} else {
throw is.invalidParameterError('text.wrap', 'one of: word, char, wordChar, none', inputOptions.text.wrap);
}
}
delete inputDescriptor.buffer;
} else {
throw new Error('Expected a valid string to create an image with text.');
}
}
} else if (is.defined(inputOptions)) {
throw new Error('Invalid input options ' + inputOptions);
}
@@ -292,18 +411,22 @@ function _isStreamInput () {
/**
* Fast access to (uncached) image metadata without decoding any compressed pixel data.
*
* This is taken from the header of the input image.
* It does not include operations, such as resize, to be applied to the output image.
* This is read from the header of the input image.
* It does not take into consideration any operations to be applied to the output image,
* such as resize or rotate.
*
* Dimensions in the response will respect the `page` and `pages` properties of the
* {@link /api-constructor#parameters|constructor parameters}.
*
* A `Promise` is returned when `callback` is not provided.
*
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
* - `size`: Total size of image in bytes, for Stream and Buffer input only
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
* - `density`: Number of pixels per inch (DPI), if present
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
@@ -325,6 +448,7 @@ function _isStreamInput () {
* - `iptc`: Buffer containing raw IPTC data, if present
* - `xmp`: Buffer containing raw XMP data, if present
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
* - `formatMagick`: String containing format for images loaded via *magick
*
* @example
* const metadata = await sharp(input).metadata();
@@ -343,6 +467,17 @@ function _isStreamInput () {
* // data contains a WebP image half the width and height of the original JPEG
* });
*
* @example
* // Based on EXIF rotation metadata, get the right-side-up width and height:
*
* const size = getNormalSize(await sharp(input).metadata());
*
* function getNormalSize({ width, height, orientation }) {
* return (orientation || 0) >= 5
* ? { width: height, height: width }
* : { width, height };
* }
*
* @param {Function} [callback] - called with the arguments `(err, metadata)`
* @returns {Promise<Object>|Sharp}
*/
@@ -360,7 +495,7 @@ function metadata (callback) {
} else {
if (this._isStreamInput()) {
return new Promise((resolve, reject) => {
this.on('finish', () => {
const finished = () => {
this._flattenBufferIn();
sharp.metadata(this.options, (err, metadata) => {
if (err) {
@@ -369,7 +504,12 @@ function metadata (callback) {
resolve(metadata);
}
});
});
};
if (this.writableFinished) {
finished();
} else {
this.once('finish', finished);
}
});
} else {
return new Promise((resolve, reject) => {
@@ -401,9 +541,9 @@ function metadata (callback) {
* - `maxX` (x-coordinate of one of the pixel where the maximum lies)
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
* - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
*
* **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
* written to a buffer in order to run `stats` on the result (see third example).
@@ -485,4 +625,6 @@ module.exports = function (Sharp) {
metadata,
stats
});
// Class attributes
Sharp.align = align;
};

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
/**
@@ -71,6 +74,14 @@ const typedArray = function (val) {
return false;
};
/**
* Is this value an ArrayBuffer object?
* @private
*/
const arrayBuffer = function (val) {
return val instanceof ArrayBuffer;
};
/**
* Is this value a non-empty string?
* @private
@@ -134,6 +145,7 @@ module.exports = {
bool: bool,
buffer: buffer,
typedArray: typedArray,
arrayBuffer: arrayBuffer,
string: string,
number: number,
integer: integer,

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');
@@ -65,7 +68,13 @@ const isRosetta = function () {
const globalLibvipsVersion = function () {
if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
...spawnSyncOptions,
env: {
...env,
PKG_CONFIG_PATH: pkgConfigPath()
}
}).stdout;
/* istanbul ignore next */
return (globalLibvipsVersion || '').trim();
} else {
@@ -83,12 +92,21 @@ const removeVendoredLibvips = function () {
rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
};
/* istanbul ignore next */
const pkgConfigPath = function () {
if (process.platform !== 'win32') {
const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
return [brewPkgConfigPath.trim(), env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig']
.filter(function (p) { return !!p; })
.join(':');
const brewPkgConfigPath = spawnSync(
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
spawnSyncOptions
).stdout || '';
return [
brewPkgConfigPath.trim(),
env.PKG_CONFIG_PATH,
'/usr/local/lib/pkgconfig',
'/usr/lib/pkgconfig',
'/usr/local/libdata/pkgconfig',
'/usr/libdata/pkgconfig'
].filter(Boolean).join(':');
} else {
return '';
}
@@ -100,6 +118,7 @@ const useGlobalLibvips = function () {
}
/* istanbul ignore next */
if (isRosetta()) {
log('Detected Rosetta, skipping search for globally-installed libvips');
return false;
}
const globalVipsVersion = globalLibvipsVersion();

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const color = require('color');
@@ -18,8 +21,11 @@ const is = require('./is');
*
* The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
*
* Method order is important when both rotating and extracting regions,
* for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
* Only one rotation can occur per pipeline.
* Previous calls to `rotate` in the same pipeline will be ignored.
*
* Method order is important when rotating, resizing and/or extracting regions,
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`.
*
* @example
* const pipeline = sharp()
@@ -32,6 +38,16 @@ const is = require('./is');
* });
* readableStream.pipe(pipeline);
*
* @example
* const rotateThenResize = await sharp(input)
* .rotate(90)
* .resize({ width: 16, height: 8, fit: 'fill' })
* .toBuffer();
* const resizeThenRotate = await sharp(input)
* .resize({ width: 16, height: 8, fit: 'fill' })
* .rotate(90)
* .toBuffer();
*
* @param {number} [angle=auto] angle of rotation.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {string|Object} [options.background="#000000"] parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
@@ -39,6 +55,9 @@ const is = require('./is');
* @throws {Error} Invalid parameters
*/
function rotate (angle, options) {
if (this.options.useExifOrientation || this.options.angle || this.options.rotationAngle) {
this.options.debuglog('ignoring previous rotate options');
}
if (!is.defined(angle)) {
this.options.useExifOrientation = true;
} else if (is.integer(angle) && !(angle % 90)) {
@@ -61,8 +80,12 @@ function rotate (angle, options) {
}
/**
* Flip the image about the vertical Y axis. This always occurs after rotation, if any.
* Flip the image about the vertical Y axis. This always occurs before rotation, if any.
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
*
* @example
* const output = await sharp(input).flip().toBuffer();
*
* @param {Boolean} [flip=true]
* @returns {Sharp}
*/
@@ -72,8 +95,12 @@ function flip (flip) {
}
/**
* Flop the image about the horizontal X axis. This always occurs after rotation, if any.
* Flop the image about the horizontal X axis. This always occurs before rotation, if any.
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
*
* @example
* const output = await sharp(input).flop().toBuffer();
*
* @param {Boolean} [flop=true]
* @returns {Sharp}
*/
@@ -181,44 +208,113 @@ function affine (matrix, options) {
/**
* Sharpen the image.
* When used without parameters, performs a fast, mild sharpen of the output image.
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
* Separate control over the level of sharpening in "flat" and "jagged" areas is available.
*
* @param {number} [sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @param {number} [flat=1.0] - the level of sharpening to apply to "flat" areas.
* @param {number} [jagged=2.0] - the level of sharpening to apply to "jagged" areas.
* When used without parameters, performs a fast, mild sharpen of the output image.
*
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
*
* See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
*
* @example
* const data = await sharp(input).sharpen().toBuffer();
*
* @example
* const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
*
* @example
* const data = await sharp(input)
* .sharpen({
* sigma: 2,
* m1: 0,
* m2: 3,
* x1: 3,
* y2: 15,
* y3: 15,
* })
* .toBuffer();
*
* @param {Object|number} [options] - if present, is an Object with attributes
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas, between 0 and 1000000
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas, between 0 and 1000000
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged", between 0 and 1000000
* @param {number} [options.y2=10.0] - maximum amount of brightening, between 0 and 1000000
* @param {number} [options.y3=20.0] - maximum amount of darkening, between 0 and 1000000
* @param {number} [flat] - (deprecated) see `options.m1`.
* @param {number} [jagged] - (deprecated) see `options.m2`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function sharpen (sigma, flat, jagged) {
if (!is.defined(sigma)) {
function sharpen (options, flat, jagged) {
if (!is.defined(options)) {
// No arguments: default to mild sharpen
this.options.sharpenSigma = -1;
} else if (is.bool(sigma)) {
// Boolean argument: apply mild sharpen?
this.options.sharpenSigma = sigma ? -1 : 0;
} else if (is.number(sigma) && is.inRange(sigma, 0.01, 10000)) {
// Numeric argument: specific sigma
this.options.sharpenSigma = sigma;
// Control over flat areas
} else if (is.bool(options)) {
// Deprecated boolean argument: apply mild sharpen?
this.options.sharpenSigma = options ? -1 : 0;
} else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
// Deprecated numeric argument: specific sigma
this.options.sharpenSigma = options;
// Deprecated control over flat areas
if (is.defined(flat)) {
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
this.options.sharpenFlat = flat;
this.options.sharpenM1 = flat;
} else {
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
}
}
// Control over jagged areas
// Deprecated control over jagged areas
if (is.defined(jagged)) {
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
this.options.sharpenJagged = jagged;
this.options.sharpenM2 = jagged;
} else {
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
}
}
} else if (is.plainObject(options)) {
if (is.number(options.sigma) && is.inRange(options.sigma, 0.000001, 10)) {
this.options.sharpenSigma = options.sigma;
} else {
throw is.invalidParameterError('options.sigma', 'number between 0.000001 and 10', options.sigma);
}
if (is.defined(options.m1)) {
if (is.number(options.m1) && is.inRange(options.m1, 0, 1000000)) {
this.options.sharpenM1 = options.m1;
} else {
throw is.invalidParameterError('options.m1', 'number between 0 and 1000000', options.m1);
}
}
if (is.defined(options.m2)) {
if (is.number(options.m2) && is.inRange(options.m2, 0, 1000000)) {
this.options.sharpenM2 = options.m2;
} else {
throw is.invalidParameterError('options.m2', 'number between 0 and 1000000', options.m2);
}
}
if (is.defined(options.x1)) {
if (is.number(options.x1) && is.inRange(options.x1, 0, 1000000)) {
this.options.sharpenX1 = options.x1;
} else {
throw is.invalidParameterError('options.x1', 'number between 0 and 1000000', options.x1);
}
}
if (is.defined(options.y2)) {
if (is.number(options.y2) && is.inRange(options.y2, 0, 1000000)) {
this.options.sharpenY2 = options.y2;
} else {
throw is.invalidParameterError('options.y2', 'number between 0 and 1000000', options.y2);
}
}
if (is.defined(options.y3)) {
if (is.number(options.y3) && is.inRange(options.y3, 0, 1000000)) {
this.options.sharpenY3 = options.y3;
} else {
throw is.invalidParameterError('options.y3', 'number between 0 and 1000000', options.y3);
}
}
} else {
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', sigma);
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
}
return this;
}
@@ -226,6 +322,13 @@ function sharpen (sigma, flat, jagged) {
/**
* Apply median filter.
* When used without parameters the default window is 3x3.
*
* @example
* const output = await sharp(input).median().toBuffer();
*
* @example
* const output = await sharp(input).median(5).toBuffer();
*
* @param {number} [size=3] square mask size: size x size
* @returns {Sharp}
* @throws {Error} Invalid parameters
@@ -338,6 +441,17 @@ function gamma (gamma, gammaOut) {
/**
* Produce the "negative" of the image.
*
* @example
* const output = await sharp(input)
* .negate()
* .toBuffer();
*
* @example
* const output = await sharp(input)
* .negate({ alpha: false })
* .toBuffer();
*
* @param {Object} [options]
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
* @returns {Sharp}
@@ -355,22 +469,68 @@ function negate (options) {
}
/**
* Enhance output image contrast by stretching its luminance to cover the full dynamic range.
* @param {Boolean} [normalise=true]
* Enhance output image contrast by stretching its luminance to cover a full dynamic range.
*
* Uses a histogram-based approach, taking a default range of 1% to 99% to reduce sensitivity to noise at the extremes.
*
* Luminance values below the `lower` percentile will be underexposed by clipping to zero.
* Luminance values above the `upper` percentile will be overexposed by clipping to the max pixel value.
*
* @example
* const output = await sharp(input)
* .normalise()
* .toBuffer();
*
* @example
* const output = await sharp(input)
* .normalise({ lower: 0, upper: 100 })
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.lower=1] - Percentile below which luminance values will be underexposed.
* @param {number} [options.upper=99] - Percentile above which luminance values will be overexposed.
* @returns {Sharp}
*/
function normalise (normalise) {
this.options.normalise = is.bool(normalise) ? normalise : true;
function normalise (options) {
if (is.plainObject(options)) {
if (is.defined(options.lower)) {
if (is.number(options.lower) && is.inRange(options.lower, 0, 99)) {
this.options.normaliseLower = options.lower;
} else {
throw is.invalidParameterError('lower', 'number between 0 and 99', options.lower);
}
}
if (is.defined(options.upper)) {
if (is.number(options.upper) && is.inRange(options.upper, 1, 100)) {
this.options.normaliseUpper = options.upper;
} else {
throw is.invalidParameterError('upper', 'number between 1 and 100', options.upper);
}
}
}
if (this.options.normaliseLower >= this.options.normaliseUpper) {
throw is.invalidParameterError('range', 'lower to be less than upper',
`${this.options.normaliseLower} >= ${this.options.normaliseUpper}`);
}
this.options.normalise = true;
return this;
}
/**
* Alternative spelling of normalise.
* @param {Boolean} [normalize=true]
*
* @example
* const output = await sharp(input)
* .normalize()
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.lower=1] - Percentile below which luminance values will be underexposed.
* @param {number} [options.upper=99] - Percentile above which luminance values will be overexposed.
* @returns {Sharp}
*/
function normalize (normalize) {
return this.normalise(normalize);
function normalize (options) {
return this.normalise(options);
}
/**
@@ -381,36 +541,43 @@ function normalize (normalize) {
*
* @since 0.28.3
*
* @example
* const output = await sharp(input)
* .clahe({
* width: 3,
* height: 3,
* })
* .toBuffer();
*
* @param {Object} options
* @param {number} options.width - integer width of the region in pixels.
* @param {number} options.height - integer height of the region in pixels.
* @param {number} [options.maxSlope=3] - maximum value for the slope of the
* cumulative histogram. A value of 0 disables contrast limiting. Valid values
* are integers in the range 0-100 (inclusive)
* @param {number} options.width - Integral width of the search window, in pixels.
* @param {number} options.height - Integral height of the search window, in pixels.
* @param {number} [options.maxSlope=3] - Integral level of brightening, between 0 and 100, where 0 disables contrast limiting.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function clahe (options) {
if (!is.plainObject(options)) {
if (is.plainObject(options)) {
if (is.integer(options.width) && options.width > 0) {
this.options.claheWidth = options.width;
} else {
throw is.invalidParameterError('width', 'integer greater than zero', options.width);
}
if (is.integer(options.height) && options.height > 0) {
this.options.claheHeight = options.height;
} else {
throw is.invalidParameterError('height', 'integer greater than zero', options.height);
}
if (is.defined(options.maxSlope)) {
if (is.integer(options.maxSlope) && is.inRange(options.maxSlope, 0, 100)) {
this.options.claheMaxSlope = options.maxSlope;
} else {
throw is.invalidParameterError('maxSlope', 'integer between 0 and 100', options.maxSlope);
}
}
} else {
throw is.invalidParameterError('options', 'plain object', options);
}
if (!('width' in options) || !is.integer(options.width) || options.width <= 0) {
throw is.invalidParameterError('width', 'integer above zero', options.width);
} else {
this.options.claheWidth = options.width;
}
if (!('height' in options) || !is.integer(options.height) || options.height <= 0) {
throw is.invalidParameterError('height', 'integer above zero', options.height);
} else {
this.options.claheHeight = options.height;
}
if (!is.defined(options.maxSlope)) {
this.options.claheMaxSlope = 3;
} else if (!is.integer(options.maxSlope) || options.maxSlope < 0 || options.maxSlope > 100) {
throw is.invalidParameterError('maxSlope', 'integer 0-100', options.maxSlope);
} else {
this.options.claheMaxSlope = options.maxSlope;
}
return this;
}
@@ -519,26 +686,55 @@ function boolean (operand, operator, options) {
}
/**
* Apply the linear formula a * input + b to the image (levels adjustment)
* @param {number} [a=1.0] multiplier
* @param {number} [b=0.0] offset
* Apply the linear formula `a` * input + `b` to the image to adjust image levels.
*
* When a single number is provided, it will be used for all image channels.
* When an array of numbers is provided, the array length must match the number of channels.
*
* @example
* await sharp(input)
* .linear(0.5, 2)
* .toBuffer();
*
* @example
* await sharp(rgbInput)
* .linear(
* [0.25, 0.5, 0.75],
* [150, 100, 50]
* )
* .toBuffer();
*
* @param {(number|number[])} [a=[]] multiplier
* @param {(number|number[])} [b=[]] offset
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function linear (a, b) {
if (!is.defined(a) && is.number(b)) {
a = 1.0;
} else if (is.number(a) && !is.defined(b)) {
b = 0.0;
}
if (!is.defined(a)) {
this.options.linearA = 1.0;
this.options.linearA = [];
} else if (is.number(a)) {
this.options.linearA = [a];
} else if (Array.isArray(a) && a.length && a.every(is.number)) {
this.options.linearA = a;
} else {
throw is.invalidParameterError('a', 'numeric', a);
throw is.invalidParameterError('a', 'number or array of numbers', a);
}
if (!is.defined(b)) {
this.options.linearB = 0.0;
this.options.linearB = [];
} else if (is.number(b)) {
this.options.linearB = [b];
} else if (Array.isArray(b) && b.length && b.every(is.number)) {
this.options.linearB = b;
} else {
throw is.invalidParameterError('b', 'numeric', b);
throw is.invalidParameterError('b', 'number or array of numbers', b);
}
if (this.options.linearA.length !== this.options.linearB.length) {
throw new Error('Expected a and b to be arrays of the same length');
}
return this;
}
@@ -590,28 +786,38 @@ function recomb (inputMatrix) {
* @since 0.22.1
*
* @example
* sharp(input)
* // increase brightness by a factor of 2
* const output = await sharp(input)
* .modulate({
* brightness: 2 // increase brightness by a factor of 2
* });
* brightness: 2
* })
* .toBuffer();
*
* sharp(input)
* @example
* // hue-rotate by 180 degrees
* const output = await sharp(input)
* .modulate({
* hue: 180 // hue-rotate by 180 degrees
* });
* hue: 180
* })
* .toBuffer();
*
* sharp(input)
* @example
* // increase lightness by +50
* const output = await sharp(input)
* .modulate({
* lightness: 50 // increase lightness by +50
* });
* lightness: 50
* })
* .toBuffer();
*
* @example
* // decreate brightness and saturation while also hue-rotating by 90 degrees
* sharp(input)
* const output = await sharp(input)
* .modulate({
* brightness: 0.5,
* saturation: 0.5,
* hue: 90
* });
* hue: 90,
* })
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.brightness] Brightness multiplier

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const path = require('path');
@@ -10,6 +13,9 @@ const formats = new Map([
['avif', 'avif'],
['jpeg', 'jpeg'],
['jpg', 'jpeg'],
['jpe', 'jpeg'],
['tile', 'tile'],
['dz', 'tile'],
['png', 'png'],
['raw', 'raw'],
['tiff', 'tiff'],
@@ -19,10 +25,13 @@ const formats = new Map([
['jp2', 'jp2'],
['jpx', 'jp2'],
['j2k', 'jp2'],
['j2c', 'jp2']
['j2c', 'jp2'],
['jxl', 'jxl']
]);
const errJp2Save = new Error('JP2 output requires libvips with support for OpenJPEG');
const jp2Regex = /\.jp[2x]|j2[kc]$/i;
const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG');
const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
@@ -55,6 +64,8 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
* When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region.
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
* @returns {Promise<Object>} - when no callback is provided
* @throws {Error} Invalid parameters
*/
@@ -64,6 +75,8 @@ function toFile (fileOut, callback) {
err = new Error('Missing output file path');
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
err = new Error('Cannot use same file for input and output');
} else if (jp2Regex.test(fileOut) && !this.constructor.format.jp2k.output.file) {
err = errJp2Save();
}
if (err) {
if (is.fn(callback)) {
@@ -82,6 +95,8 @@ function toFile (fileOut, callback) {
* Write output to a Buffer.
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
*
* Use {@link toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
*
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
*
* By default all metadata will be removed, which includes EXIF-based orientation.
@@ -93,6 +108,7 @@ function toFile (fileOut, callback) {
* - `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
*
* A `Promise` is returned when `callback` is not provided.
*
@@ -108,6 +124,7 @@ function toFile (fileOut, callback) {
*
* @example
* sharp(input)
* .png()
* .toBuffer({ resolveWithObject: true })
* .then(({ data, info }) => { ... })
* .catch(err => { ... });
@@ -139,6 +156,7 @@ function toBuffer (options, callback) {
} else if (this.options.resolveWithObject) {
this.options.resolveWithObject = false;
}
this.options.fileOut = '';
return this._pipeline(is.fn(options) ? options : callback);
}
@@ -150,6 +168,8 @@ function toBuffer (options, callback) {
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
*
* EXIF metadata is unsupported for TIFF output.
*
* @example
* sharp('input.jpg')
* .withMetadata()
@@ -176,7 +196,7 @@ function toBuffer (options, callback) {
*
* @param {Object} [options]
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
* @param {string} [options.icc='srgb'] Filesystem path to output ICC profile, relative to `process.cwd()`, defaults to built-in sRGB.
* @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
* @param {number} [options.density] Number of pixels per inch (DPI).
* @returns {Sharp}
@@ -396,6 +416,14 @@ function png (options) {
if (is.defined(options.adaptiveFiltering)) {
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
this.options.pngBitdepth = bitdepthFromColourCount(colours);
} else {
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
}
}
if (is.defined(options.palette)) {
this._setBooleanOption('pngPalette', options.palette);
} else if ([options.quality, options.effort, options.colours, options.colors, options.dither].some(is.defined)) {
@@ -416,14 +444,6 @@ function png (options) {
throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
}
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
this.options.pngBitdepth = bitdepthFromColourCount(colours);
} else {
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
}
}
if (is.defined(options.dither)) {
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
this.options.pngDither = options.dither;
@@ -460,6 +480,8 @@ function png (options) {
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
* @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
* @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
@@ -489,14 +511,19 @@ function webp (options) {
if (is.defined(options.smartSubsample)) {
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
}
const effort = options.effort || options.reductionEffort;
if (is.defined(effort)) {
if (is.integer(effort) && is.inRange(effort, 0, 6)) {
this.options.webpEffort = effort;
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
this.options.webpEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 0 and 6', effort);
throw is.invalidParameterError('effort', 'integer between 0 and 6', options.effort);
}
}
if (is.defined(options.minSize)) {
this._setBooleanOption('webpMinSize', options.minSize);
}
if (is.defined(options.mixed)) {
this._setBooleanOption('webpMixed', options.mixed);
}
}
trySetAnimationOptions(options, this.options);
return this._updateFormatOut('webp', options);
@@ -507,13 +534,15 @@ function webp (options) {
*
* The first entry in the palette is reserved for transparency.
*
* The palette of the input image will be re-used if possible.
*
* @since 0.30.0
*
* @example
* // Convert PNG to GIF
* await sharp(pngBuffer)
* .gif()
* .toBuffer());
* .toBuffer();
*
* @example
* // Convert animated WebP to animated GIF
@@ -527,11 +556,21 @@ function webp (options) {
* .gif({ dither: 0 })
* .toBuffer();
*
* @example
* // Lossy file size reduction of animated GIF
* await sharp('in.gif', { animated: true })
* .gif({ interFrameMaxError: 8 })
* .toFile('optim.gif');
*
* @param {Object} [options] - output options
* @param {boolean} [options.reuse=true] - re-use existing palette, otherwise generate new (slow)
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {number} [options.colours=256] - maximum number of palette entries, including transparency, between 2 and 256
* @param {number} [options.colors=256] - alternative spelling of `options.colours`
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest)
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
* @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
* @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
@@ -540,6 +579,12 @@ function webp (options) {
*/
function gif (options) {
if (is.object(options)) {
if (is.defined(options.reuse)) {
this._setBooleanOption('gifReuse', options.reuse);
}
if (is.defined(options.progressive)) {
this._setBooleanOption('gifProgressive', options.progressive);
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
@@ -562,6 +607,20 @@ function gif (options) {
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
}
}
if (is.defined(options.interFrameMaxError)) {
if (is.number(options.interFrameMaxError) && is.inRange(options.interFrameMaxError, 0, 32)) {
this.options.gifInterFrameMaxError = options.interFrameMaxError;
} else {
throw is.invalidParameterError('interFrameMaxError', 'number between 0.0 and 32.0', options.interFrameMaxError);
}
}
if (is.defined(options.interPaletteMaxError)) {
if (is.number(options.interPaletteMaxError) && is.inRange(options.interPaletteMaxError, 0, 256)) {
this.options.gifInterPaletteMaxError = options.interPaletteMaxError;
} else {
throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
}
}
}
trySetAnimationOptions(options, this.options);
return this._updateFormatOut('gif', options);
@@ -603,7 +662,7 @@ function gif (options) {
/* istanbul ignore next */
function jp2 (options) {
if (!this.constructor.format.jp2k.output.buffer) {
throw errJp2Save;
throw errJp2Save();
}
if (is.object(options)) {
if (is.defined(options.quality)) {
@@ -636,7 +695,7 @@ function jp2 (options) {
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.heifChromaSubsampling = options.chromaSubsampling;
this.options.jp2ChromaSubsampling = options.chromaSubsampling;
} else {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
@@ -696,7 +755,7 @@ function trySetAnimationOptions (source, target) {
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {string} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
* @param {string} [options.compression='jpeg'] - compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {boolean} [options.pyramid=false] - write an image pyramid
* @param {boolean} [options.tile=false] - write a tiled tiff
@@ -764,10 +823,10 @@ function tiff (options) {
}
// compression
if (is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
if (is.string(options.compression) && is.inArray(options.compression, ['none', 'jpeg', 'deflate', 'packbits', 'ccittfax4', 'lzw', 'webp', 'zstd', 'jp2k'])) {
this.options.tiffCompression = options.compression;
} else {
throw is.invalidParameterError('compression', 'one of: lzw, deflate, jpeg, ccittfax4, none', options.compression);
throw is.invalidParameterError('compression', 'one of: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k', options.compression);
}
}
// predictor
@@ -798,6 +857,16 @@ function tiff (options) {
*
* AVIF image sequences are not supported.
*
* @example
* const data = await sharp(input)
* .avif({ effort: 2 })
* .toBuffer();
*
* @example
* const data = await sharp(input)
* .avif({ lossless: true })
* .toBuffer();
*
* @since 0.27.0
*
* @param {Object} [options] - output options
@@ -815,9 +884,14 @@ function avif (options) {
/**
* Use these HEIF options for output image.
*
* Support for patent-encumbered HEIC images requires the use of a
* Support for patent-encumbered HEIC images using `hevc` compression requires the use of a
* globally-installed libvips compiled with support for libheif, libde265 and x265.
*
* @example
* const data = await sharp(input)
* .heif({ compression: 'hevc' })
* .toBuffer();
*
* @since 0.23.0
*
* @param {Object} [options] - output options
@@ -858,12 +932,6 @@ function heif (options) {
} else {
throw is.invalidParameterError('effort', 'integer between 0 and 9', options.effort);
}
} else if (is.defined(options.speed)) {
if (is.integer(options.speed) && is.inRange(options.speed, 0, 9)) {
this.options.heifEffort = 9 - options.speed;
} else {
throw is.invalidParameterError('speed', 'integer between 0 and 9', options.speed);
}
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
@@ -876,6 +944,71 @@ function heif (options) {
return this._updateFormatOut('heif', options);
}
/**
* Use these JPEG-XL (JXL) options for output image.
*
* This feature is experimental, please do not use in production systems.
*
* Requires libvips compiled with support for libjxl.
* The prebuilt binaries do not include this - see
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
*
* Image metadata (EXIF, XMP) is unsupported.
*
* @since 0.31.3
*
* @param {Object} [options] - output options
* @param {number} [options.distance=1.0] - maximum encoding error, between 0 (highest quality) and 15 (lowest quality)
* @param {number} [options.quality] - calculate `distance` based on JPEG-like quality, between 1 and 100, overrides distance if specified
* @param {number} [options.decodingTier=0] - target decode speed tier, between 0 (highest quality) and 4 (lowest quality)
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=7] - CPU effort, between 3 (fastest) and 9 (slowest)
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function jxl (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
// https://github.com/libjxl/libjxl/blob/0aeea7f180bafd6893c1db8072dcb67d2aa5b03d/tools/cjxl_main.cc#L640-L644
this.options.jxlDistance = options.quality >= 30
? 0.1 + (100 - options.quality) * 0.09
: 53 / 3000 * options.quality * options.quality - 23 / 20 * options.quality + 25;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
} else if (is.defined(options.distance)) {
if (is.number(options.distance) && is.inRange(options.distance, 0, 15)) {
this.options.jxlDistance = options.distance;
} else {
throw is.invalidParameterError('distance', 'number between 0.0 and 15.0', options.distance);
}
}
if (is.defined(options.decodingTier)) {
if (is.integer(options.decodingTier) && is.inRange(options.decodingTier, 0, 4)) {
this.options.jxlDecodingTier = options.decodingTier;
} else {
throw is.invalidParameterError('decodingTier', 'integer between 0 and 4', options.decodingTier);
}
}
if (is.defined(options.lossless)) {
if (is.bool(options.lossless)) {
this.options.jxlLossless = options.lossless;
} else {
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
}
}
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 3, 9)) {
this.options.jxlEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 3 and 9', options.effort);
}
}
}
return this._updateFormatOut('jxl', options);
}
/**
* Force output to be raw, uncompressed pixel data.
* Pixel ordering is left-to-right, top-to-bottom, without padding.
@@ -917,9 +1050,16 @@ function raw (options) {
/**
* Use tile-based deep zoom (image pyramid) output.
*
* Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
* Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
*
* The container will be set to `zip` when the output is a Buffer or Stream, otherwise it will default to `fs`.
*
* Requires libvips compiled with support for libgsf.
* The prebuilt binaries do not include this - see
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
*
* @example
* sharp('input.tiff')
* .png()
@@ -931,6 +1071,17 @@ function raw (options) {
* // output_files contains 512x512 tiles grouped by zoom level
* });
*
* @example
* const zipFileWithTiles = await sharp(input)
* .tile({ basename: "tiles" })
* .toBuffer();
*
* @example
* const iiififier = sharp().tile({ layout: "iiif" });
* readableStream
* .pipe(iiififier)
* .pipe(writeableStream);
*
* @param {Object} [options]
* @param {number} [options.size=256] tile size in pixels, a value between 1 and 8192.
* @param {number} [options.overlap=0] tile overlap in pixels, a value between 0 and 8192.
@@ -943,6 +1094,7 @@ function raw (options) {
* @param {boolean} [options.centre=false] centre image in tile.
* @param {boolean} [options.center=false] alternative spelling of centre.
* @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json`
* @param {string} [options.basename] the name of the directory within the zip file when container is `zip`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -1024,6 +1176,14 @@ function tile (options) {
throw is.invalidParameterError('id', 'string', options.id);
}
}
// Basename for zip container
if (is.defined(options.basename)) {
if (is.string(options.basename)) {
this.options.tileBasename = options.basename;
} else {
throw is.invalidParameterError('basename', 'string', options.basename);
}
}
}
// Format
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
@@ -1041,6 +1201,17 @@ function tile (options) {
* The clock starts when libvips opens an input image for processing.
* Time spent waiting for a libuv thread to become available is not included.
*
* @example
* // Ensure processing takes no longer than 3 seconds
* try {
* const data = await sharp(input)
* .blur(1000)
* .timeout({ seconds: 3 })
* .toBuffer();
* } catch (err) {
* if (err.message.includes('timeout')) { ... }
* }
*
* @since 0.29.2
*
* @param {Object} options
@@ -1135,7 +1306,7 @@ function _pipeline (callback) {
this.push(data);
}
this.push(null);
this.emit('close');
this.on('end', () => this.emit('close'));
});
});
if (this.streamInFinished) {
@@ -1151,7 +1322,7 @@ function _pipeline (callback) {
this.push(data);
}
this.push(null);
this.emit('close');
this.on('end', () => this.emit('close'));
});
}
return this;
@@ -1212,6 +1383,7 @@ module.exports = function (Sharp) {
tiff,
avif,
heif,
jxl,
gif,
raw,
tile,

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const detectLibc = require('detect-libc');
@@ -7,10 +10,12 @@ const env = process.env;
module.exports = function () {
const arch = env.npm_config_arch || process.arch;
const platform = env.npm_config_platform || process.platform;
/* istanbul ignore next */
const libc = (platform === 'linux' && detectLibc.isNonGlibcLinuxSync()) ? detectLibc.familySync() : '';
const libc = process.env.npm_config_libc ||
/* istanbul ignore next */
(detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
const platformId = [`${platform}${libc}`];
const platformId = [`${platform}${libcId}`];
if (arch === 'arm') {
const fallback = process.versions.electron ? '7' : '6';

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const is = require('./is');
@@ -36,6 +39,18 @@ const position = {
'left top': 8
};
/**
* How to extend the image.
* @member
* @private
*/
const extendWith = {
background: 'background',
copy: 'copy',
repeat: 'repeat',
mirror: 'mirror'
};
/**
* Strategies for automagic cover behaviour.
* @member
@@ -92,11 +107,18 @@ function isRotationExpected (options) {
return (options.angle % 360) !== 0 || options.useExifOrientation === true || options.rotationAngle !== 0;
}
/**
* @private
*/
function isResizeExpected (options) {
return options.width !== -1 || options.height !== -1;
}
/**
* Resize image to `width`, `height` or `width x height`.
*
* When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
* - `cover`: (default) Preserving aspect ratio, ensure the image covers both provided dimensions by cropping/clipping to fit.
* - `cover`: (default) Preserving aspect ratio, attempt to ensure the image covers both provided dimensions by cropping/clipping to fit.
* - `contain`: Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
* - `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
* - `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
@@ -104,7 +126,9 @@ function isRotationExpected (options) {
*
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
*
* When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.png">
*
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
* - `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
* - `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
@@ -123,6 +147,9 @@ function isRotationExpected (options) {
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
*
* Only one resize can occur per pipeline.
* Previous calls to `resize` in the same pipeline will be ignored.
*
* @example
* sharp(input)
* .resize({ width: 100 })
@@ -204,29 +231,32 @@ function isRotationExpected (options) {
* .toBuffer()
* );
*
* @param {number} [width] - pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* @param {number} [height] - pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {number} [width] - How many pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* @param {number} [height] - How many pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {Object} [options]
* @param {String} [options.width] - alternative means of specifying `width`. If both are present this take priority.
* @param {String} [options.height] - alternative means of specifying `height`. If both are present this take priority.
* @param {String} [options.fit='cover'] - how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`.
* @param {String} [options.position='centre'] - position, gravity or strategy to use when `fit` is `cover` or `contain`.
* @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when using a `fit` of `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
* @param {Boolean} [options.withoutEnlargement=false] - do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option.
* @param {Boolean} [options.withoutReduction=false] - do not reduce if the width *or* height are already greater than the specified dimensions, equivalent to GraphicsMagick's `<` geometry option.
* @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 on some images.
* @param {number} [options.width] - An alternative means of specifying `width`. If both are present this takes priority.
* @param {number} [options.height] - An alternative means of specifying `height`. If both are present this takes priority.
* @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`.
* @param {String} [options.position='centre'] - A position, gravity or strategy to use when `fit` is `cover` or `contain`.
* @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.
* @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
* @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.
* @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.
* @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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function resize (width, height, options) {
if (is.defined(width)) {
if (is.object(width) && !is.defined(options)) {
options = width;
} else if (is.integer(width) && width > 0) {
this.options.width = width;
function resize (widthOrOptions, height, options) {
if (isResizeExpected(this.options)) {
this.options.debuglog('ignoring previous resize options');
}
if (is.defined(widthOrOptions)) {
if (is.object(widthOrOptions) && !is.defined(options)) {
options = widthOrOptions;
} else if (is.integer(widthOrOptions) && widthOrOptions > 0) {
this.options.width = widthOrOptions;
} else {
throw is.invalidParameterError('width', 'positive integer', width);
throw is.invalidParameterError('width', 'positive integer', widthOrOptions);
}
} else {
this.options.width = -1;
@@ -300,11 +330,15 @@ function resize (width, height, options) {
this._setBooleanOption('fastShrinkOnLoad', options.fastShrinkOnLoad);
}
}
if (isRotationExpected(this.options) && isResizeExpected(this.options)) {
this.options.rotateBeforePreExtract = true;
}
return this;
}
/**
* Extends/pads the edges of the image with the provided background colour.
* Extend / pad / extrude one or more edges of the image with either
* the provided background colour or pixels derived from the image.
* This operation will always occur after resizing and extraction, if any.
*
* @example
@@ -330,11 +364,21 @@ function resize (width, height, options) {
* })
* ...
*
* @example
* // Extrude image by 8 pixels to the right, mirroring existing right hand edge
* sharp(input)
* .extend({
* right: 8,
* background: 'mirror'
* })
* ...
*
* @param {(number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
* @param {number} [extend.top=0]
* @param {number} [extend.left=0]
* @param {number} [extend.bottom=0]
* @param {number} [extend.right=0]
* @param {String} [extend.extendWith='background'] - populate new pixels using this method, one of: background, copy, repeat, mirror.
* @param {String|Object} [extend.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @returns {Sharp}
* @throws {Error} Invalid parameters
@@ -375,6 +419,13 @@ function extend (extend) {
}
}
this._setBackgroundColourOption('extendBackground', extend.background);
if (is.defined(extend.extendWith)) {
if (is.string(extendWith[extend.extendWith])) {
this.options.extendWith = extendWith[extend.extendWith];
} else {
throw is.invalidParameterError('extendWith', 'one of: background, copy, repeat, mirror', extend.extendWith);
}
}
} else {
throw is.invalidParameterError('extend', 'integer or object', extend);
}
@@ -412,7 +463,10 @@ function extend (extend) {
* @throws {Error} Invalid parameters
*/
function extract (options) {
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
const suffix = isResizeExpected(this.options) || this.options.widthPre !== -1 ? 'Post' : 'Pre';
if (this.options[`width${suffix}`] !== -1) {
this.options.debuglog('ignoring previous extract options');
}
['left', 'top', 'width', 'height'].forEach(function (name) {
const value = options[name];
if (is.integer(value) && value >= 0) {
@@ -422,32 +476,87 @@ function extract (options) {
}
}, this);
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && isRotationExpected(this.options)) {
this.options.rotateBeforePreExtract = true;
if (isRotationExpected(this.options) && !isResizeExpected(this.options)) {
if (this.options.widthPre === -1 || this.options.widthPost === -1) {
this.options.rotateBeforePreExtract = true;
}
}
return this;
}
/**
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
* Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
* Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
*
* Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
*
* If the result of this operation would trim an image to nothing then no change is made.
*
* The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
*
* @param {number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
* @example
* // Trim pixels with a colour similar to that of the top-left pixel.
* sharp(input)
* .trim()
* .toFile(output, function(err, info) {
* ...
* });
* @example
* // Trim pixels with the exact same colour as that of the top-left pixel.
* sharp(input)
* .trim(0)
* .toFile(output, function(err, info) {
* ...
* });
* @example
* // Trim only pixels with a similar colour to red.
* sharp(input)
* .trim("#FF0000")
* .toFile(output, function(err, info) {
* ...
* });
* @example
* // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
* sharp(input)
* .trim({
* background: "yellow",
* threshold: 42,
* })
* .toFile(output, function(err, info) {
* ...
* });
*
* @param {string|number|Object} trim - the specific background colour to trim, the threshold for doing so or an Object with both.
* @param {string|Object} [trim.background='top-left pixel'] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
* @param {number} [trim.threshold=10] - the allowed difference from the above colour, a positive number.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function trim (threshold) {
if (!is.defined(threshold)) {
function trim (trim) {
if (!is.defined(trim)) {
this.options.trimThreshold = 10;
} else if (is.number(threshold) && threshold > 0) {
this.options.trimThreshold = threshold;
} else if (is.string(trim)) {
this._setBackgroundColourOption('trimBackground', trim);
this.options.trimThreshold = 10;
} else if (is.number(trim)) {
if (trim >= 0) {
this.options.trimThreshold = trim;
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
}
} else if (is.object(trim)) {
this._setBackgroundColourOption('trimBackground', trim.background);
if (!is.defined(trim.threshold)) {
this.options.trimThreshold = 10;
} else if (is.number(trim.threshold) && trim.threshold >= 0) {
this.options.trimThreshold = trim.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
}
} else {
throw is.invalidParameterError('threshold', 'number greater than zero', threshold);
throw is.invalidParameterError('trim', 'string, number or object', trim);
}
if (this.options.trimThreshold && isRotationExpected(this.options)) {
if (isRotationExpected(this.options)) {
this.options.rotateBeforePreExtract = true;
}
return this;

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const platformAndArch = require('./platform')();
@@ -12,8 +15,11 @@ try {
help.push('- Update Homebrew: "brew update && brew upgrade vips"');
} else {
const [platform, arch] = platformAndArch.split('-');
if (platform === 'linux' && /Module did not self-register/.test(err.message)) {
help.push('- Using worker threads? See https://sharp.pixelplumbing.com/install#worker-threads');
}
help.push(
'- Install with the --verbose flag and look for errors: "npm install --ignore-scripts=false --verbose sharp"',
'- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
`- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
);
}
@@ -21,7 +27,7 @@ try {
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
);
// Check loaded
if (process.platform === 'win32') {
if (process.platform === 'win32' || /symbol/.test(err.message)) {
const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
if (loadedModule) {
const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');
@@ -17,6 +20,10 @@ const sharp = require('./sharp');
* @returns {Object}
*/
const format = sharp.format();
format.heif.output.alias = ['avif', 'heic'];
format.jpeg.output.alias = ['jpe', 'jpg'];
format.tiff.output.alias = ['tif'];
format.jp2k.output.alias = ['j2c', 'j2k', 'jp2', 'jpx'];
/**
* An Object containing the available interpolators and their proper values
@@ -30,16 +37,16 @@ const interpolators = {
bilinear: 'bilinear',
/** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
bicubic: 'bicubic',
/** [LBB interpolation](https://github.com/jcupitt/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. */
/** [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. */
locallyBoundedBicubic: 'lbb',
/** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
nohalo: 'nohalo',
/** [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
/** [VSQBS interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
/**
* An Object containing the version numbers of libvips and its dependencies.
* An Object containing the version numbers of sharp, libvips and its dependencies.
* @member
* @example
* console.log(sharp.versions);
@@ -50,6 +57,7 @@ let versions = {
try {
versions = require(`../vendor/${versions.vips}/${platformAndArch}/versions.json`);
} catch (_err) { /* ignore */ }
versions.sharp = require('../package.json').version;
/**
* An Object containing the platform and architecture
@@ -103,7 +111,11 @@ cache(true);
/**
* Gets or, when a concurrency is provided, sets
* the number of threads _libvips'_ should create to process each image.
* the maximum number of threads _libvips_ should use to process _each image_.
* These are from a thread pool managed by glib,
* which helps avoid the overhead of creating new threads.
*
* This method always returns the current concurrency.
*
* The default value is the number of CPU cores,
* except when using glibc-based Linux without jemalloc,
@@ -111,10 +123,19 @@ cache(true);
*
* A value of `0` will reset this to the number of CPU cores.
*
* The maximum number of images that can be processed in parallel
* is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
* Some image format libraries spawn additional threads,
* e.g. libaom manages its own 4 threads when encoding AVIF images,
* and these are independent of the value set here.
*
* This method always returns the current concurrency.
* The maximum number of images that sharp can process in parallel
* is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
* which defaults to 4.
*
* https://nodejs.org/api/cli.html#uv_threadpool_sizesize
*
* For example, by default, a machine with 8 CPU cores will process
* 4 images in parallel and use up to 8 threads per image,
* so there will be up to 32 concurrent threads.
*
* @example
* const threads = sharp.concurrency(); // 4

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.30.0",
"version": "0.32.0",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -81,22 +81,28 @@
"Taneli Vatanen <taneli.vatanen@gmail.com>",
"Joris Dugué <zaruike10@gmail.com>",
"Chris Banks <christopher.bradley.banks@gmail.com>",
"Ompal Singh <ompal.hitm09@gmail.com>"
"Ompal Singh <ompal.hitm09@gmail.com>",
"Brodan <christopher.hranj@gmail.com",
"Ankur Parihar <ankur.github@gmail.com>",
"Brahim Ait elhaj <brahima@gmail.com>",
"Mart Jansink <m.jansink@gmail.com>",
"Lachlan Newman <lachnewman007@gmail.com>"
],
"scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
"test-lint": "semistandard && cpplint",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha",
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
"test-coverage": "./test/coverage/report.sh",
"test-leak": "./test/leak/leak.sh",
"docs-build": "documentation lint lib && node docs/build && node docs/search-index/build",
"test-types": "tsd",
"docs-build": "node docs/build && node docs/search-index/build",
"docs-serve": "cd docs && npx serve",
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"binding.gyp",
"install/**",
@@ -126,58 +132,59 @@
"vips"
],
"dependencies": {
"color": "^4.2.0",
"detect-libc": "^2.0.0",
"node-addon-api": "^4.3.0",
"prebuild-install": "^7.0.1",
"semver": "^7.3.5",
"color": "^4.2.3",
"detect-libc": "^2.0.1",
"node-addon-api": "^6.0.0",
"prebuild-install": "^7.1.1",
"semver": "^7.3.8",
"simple-get": "^4.0.1",
"tar-fs": "^2.1.1",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
"async": "^3.2.3",
"@types/node": "*",
"async": "^3.2.4",
"cc": "^3.0.1",
"decompress-zip": "^0.3.3",
"documentation": "^13.2.5",
"exif-reader": "^1.0.3",
"exif-reader": "^1.2.0",
"extract-zip": "^2.0.1",
"icc": "^2.0.0",
"jsdoc-to-markdown": "^8.0.0",
"license-checker": "^25.0.1",
"mocha": "^9.2.0",
"mock-fs": "^5.1.2",
"mocha": "^10.2.0",
"mock-fs": "^5.2.0",
"nyc": "^15.1.0",
"prebuild": "^11.0.2",
"rimraf": "^3.0.2",
"semistandard": "^16.0.1"
"prebuild": "^11.0.4",
"semistandard": "^16.0.1",
"tsd": "^0.28.0"
},
"license": "Apache-2.0",
"config": {
"libvips": "8.12.2",
"libvips": "8.14.2",
"integrity": {
"darwin-arm64v8": "sha512-p46s/bbJAjkOXzPISZt9HUpG9GWjwQkYnLLRLKzsBJHLtB3X6C6Y/zXI5Hd0DOojcFkks9a0kTN+uDQ/XJY19g==",
"darwin-x64": "sha512-6vOHVZnvXwe6EXRsy29jdkUzBE6ElNpXUwd+m8vV7qy32AnXu7B9YemHsZ44vWviIwPZeXF6Nhd9EFLM0wWohw==",
"linux-arm64v8": "sha512-XwZdS63yhqLtbFtx/0eoLF/Agf5qtTrI11FMnMRpuBJWd4jHB60RAH+uzYUgoChCmKIS+AeXYMLm4d8Ns2QX8w==",
"linux-armv6": "sha512-Rh0Q0kqwPG2MjXWOkPCuPEyiUKFgKJYWLgS835D4MrXgdKr8Tft/eVrc2iGIxt2re30VpDiZ1h0Rby1aCZt2zw==",
"linux-armv7": "sha512-heTS/MsmRvu4JljINxP+vDiS9ZZfuGhr3IStb5F7Gc0/QLRhllYAg4rcO8L1eTK9sIIzG5ARvI19+YUZe7WbzA==",
"linux-x64": "sha512-SSWAwBFi0hx8V/h/v82tTFGKWTFv9FiCK3Timz5OExuI+sX1Ngrd0PVQaWXOThGNdel/fcD3Bz9YjSt4feBR1g==",
"linuxmusl-arm64v8": "sha512-Rhks+5C7p7aO6AucLT1uvzo8ohlqcqCUPgZmN+LZjsPWob/Iix3MfiDYtv/+gTvdeEfXxbCU6/YuPBwHQ7/crA==",
"linuxmusl-x64": "sha512-IOyjSQqpWVntqOUpCHVWuQwACwmmjdi15H8Pc+Ma1JkhPogTfVsFQWyL7DuOTD3Yr23EuYGzovUX00duOtfy/g==",
"win32-arm64v8": "sha512-A+Qe8Ipewtvw9ldvF6nWed2J8kphzrUE04nFeKCtNx6pfGQ/MAlCKMjt/U8VgUKNjB01zJDUW9XE0+FhGZ/UpQ==",
"win32-ia32": "sha512-cMrAvwFdDeAVnLJt0IPMPRKaIFhyXYGTprsM0DND9VUHE8F7dJMR44tS5YkXsGh1QNDtjKT6YuxAVUglmiXtpA==",
"win32-x64": "sha512-vLFIfw6aW2zABa8jpgzWDhljnE6glktrddErVyazAIoHl6BFFe/Da+LK1DbXvIYHz7fyOoKhSfCJHCiJG1Vg6w=="
"darwin-arm64v8": "sha512-eUuxg6H0tXgX4z2lsaGtZ4cbPAm7yoFgkvPDd4csxoiVt+QUB25pEJwiXw7oB53VlBFIp3O8lbydSFS5zH8MQQ==",
"darwin-x64": "sha512-cMT4v76IgzSR0VoXqLk/yftRyzMEZ+SBVMLzXCgqP/lmnYisrpmHHNqrWnoZbUUBXbPXLn6KMultYOJHe/c9ZQ==",
"linux-arm64v8": "sha512-OcDJ/ly80pxwaKnw0W91sSvZczPtWsjmzrY/+6NMiQZT84LkmeaRuwErbHhorKDxnl7iZuNn9Uj5V25Xmj+LDQ==",
"linux-armv6": "sha512-hk2ohSOYTJEtVQxEQFyQ+tuayKpYqx6NiXa7AE+8MF+yscxt+g+mLJ7TjDqtmb4ttFGH4IVfsEfU2YXIqWqkpg==",
"linux-armv7": "sha512-/5Ci2Cd+yLZmTaEt9lVJ89elxX3RMJpps0ESjj43X40yrwka51QfXeg1QV38uNzZpCDIZkrbXZK0lyKldjpLuA==",
"linux-x64": "sha512-wjCKmWfBb0uz1UB7rPDLvO0s+VWuoAY/Vv/YGCRFEQUkdSLQUgHExrOMMbOM3FleuYfQqznDYCXXphkl7X44+w==",
"linuxmusl-arm64v8": "sha512-QtD2n90yi+rLE65C0gksFUU5uMUFPICI/pS3A0bgthpIcoCejAOYs3ZjVWpZbHQuV/lWahIUYO78MB9CzY860A==",
"linuxmusl-x64": "sha512-TokQ/ETCJAsPYuxIMOPYDp25rlcwtpmIMtRUR9PB75TmZEJe7abRfCEInIPYeD8F/HxxnJSLiEdlbn1z1Jfzng==",
"win32-arm64v8": "sha512-IIuj4EAgLqEVAoOuYH79C61a7TcJXlU/RBwk+5JsGWc2mr4J/Ar5J01e6XBvU4Lu3eqcU+3GPaACZEa1511buA==",
"win32-ia32": "sha512-CsZi7lrReX3B6tmYgOGJ0IiAfcN5APDC6l+3gdosxfTfwpLLO+jXaSmyNwIGeMqrdgckG/gwwc+IrUZmkmjJ/A==",
"win32-x64": "sha512-J7znmNKUK4ZKo6SnSnEtzT1xRAwvkGXxIx9/QihAadu1TFdS06yNhcENmwC4973+KZBlAdVpWbZ8sLrEoWkdCA=="
},
"runtime": "napi",
"target": 5
"target": 7
},
"engines": {
"node": ">=12.13.0"
"node": ">=14.15.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"binary": {
"napi_versions": [
5
7
]
},
"semistandard": {
@@ -190,5 +197,8 @@
"filter": [
"build/include"
]
},
"tsd": {
"directory": "test/types/"
}
}

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <cstdlib>
#include <string>
@@ -48,6 +37,9 @@ namespace sharp {
int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr) {
return obj.Get(attr).As<Napi::Number>().Int32Value();
}
int64_t AttrAsInt64(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::Number>().Int64Value();
}
double AttrAsDouble(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::Number>().DoubleValue();
}
@@ -73,6 +65,16 @@ namespace sharp {
}
return vector;
}
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len) {
try {
return Napi::Buffer<char>::New(env, data, len, FreeCallback);
} catch (Napi::Error const &err) {
static_cast<void>(err);
}
Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, data, len);
FreeCallback(nullptr, data);
return buf;
}
// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input) {
@@ -85,16 +87,18 @@ namespace sharp {
descriptor->buffer = buffer.Data();
descriptor->isBuffer = TRUE;
}
descriptor->failOnError = AttrAsBool(input, "failOnError");
descriptor->failOn = AttrAsEnum<VipsFailOn>(input, "failOn", VIPS_TYPE_FAIL_ON);
// Density for vector-based input
if (HasAttr(input, "density")) {
descriptor->density = AttrAsDouble(input, "density");
}
// Should we ignore any embedded ICC profile
if (HasAttr(input, "ignoreIcc")) {
descriptor->ignoreIcc = AttrAsBool(input, "ignoreIcc");
}
// Raw pixel input
if (HasAttr(input, "rawChannels")) {
descriptor->rawDepth = static_cast<VipsBandFormat>(
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
AttrAsStr(input, "rawDepth").data()));
descriptor->rawDepth = AttrAsEnum<VipsBandFormat>(input, "rawDepth", VIPS_TYPE_BAND_FORMAT);
descriptor->rawChannels = AttrAsUint32(input, "rawChannels");
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
@@ -128,11 +132,45 @@ namespace sharp {
descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
}
}
// Create new image with text
if (HasAttr(input, "textValue")) {
descriptor->textValue = AttrAsStr(input, "textValue");
if (HasAttr(input, "textFont")) {
descriptor->textFont = AttrAsStr(input, "textFont");
}
if (HasAttr(input, "textFontfile")) {
descriptor->textFontfile = AttrAsStr(input, "textFontfile");
}
if (HasAttr(input, "textWidth")) {
descriptor->textWidth = AttrAsUint32(input, "textWidth");
}
if (HasAttr(input, "textHeight")) {
descriptor->textHeight = AttrAsUint32(input, "textHeight");
}
if (HasAttr(input, "textAlign")) {
descriptor->textAlign = AttrAsEnum<VipsAlign>(input, "textAlign", VIPS_TYPE_ALIGN);
}
if (HasAttr(input, "textJustify")) {
descriptor->textJustify = AttrAsBool(input, "textJustify");
}
if (HasAttr(input, "textDpi")) {
descriptor->textDpi = AttrAsUint32(input, "textDpi");
}
if (HasAttr(input, "textRgba")) {
descriptor->textRgba = AttrAsBool(input, "textRgba");
}
if (HasAttr(input, "textSpacing")) {
descriptor->textSpacing = AttrAsUint32(input, "textSpacing");
}
if (HasAttr(input, "textWrap")) {
descriptor->textWrap = AttrAsEnum<VipsTextWrap>(input, "textWrap", VIPS_TYPE_TEXT_WRAP);
}
}
// Limit input images to a given number of pixels, where pixels = width * height
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
descriptor->limitInputPixels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputPixels"));
// Allow switch from random to sequential access
descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Remove safety features and allow unlimited SVG/PNG input
// Remove safety features and allow unlimited input
descriptor->unlimited = AttrAsBool(input, "unlimited");
return descriptor;
}
@@ -175,6 +213,9 @@ namespace sharp {
bool IsAvif(std::string const &str) {
return EndsWith(str, ".avif") || EndsWith(str, ".AVIF");
}
bool IsJxl(std::string const &str) {
return EndsWith(str, ".jxl") || EndsWith(str, ".JXL");
}
bool IsDz(std::string const &str) {
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
}
@@ -185,6 +226,13 @@ namespace sharp {
return EndsWith(str, ".v") || EndsWith(str, ".V") || EndsWith(str, ".vips") || EndsWith(str, ".VIPS");
}
/*
Trim space from end of string.
*/
std::string TrimEnd(std::string const &str) {
return str.substr(0, str.find_last_not_of(" \n\r\f") + 1);
}
/*
Provide a string identifier for the given image type.
*/
@@ -205,6 +253,7 @@ namespace sharp {
case ImageType::PPM: id = "ppm"; break;
case ImageType::FITS: id = "fits"; break;
case ImageType::EXR: id = "exr"; break;
case ImageType::JXL: id = "jxl"; break;
case ImageType::VIPS: id = "vips"; break;
case ImageType::RAW: id = "raw"; break;
case ImageType::UNKNOWN: id = "unknown"; break;
@@ -245,10 +294,12 @@ namespace sharp {
{ "VipsForeignLoadMagickBuffer", ImageType::MAGICK },
{ "VipsForeignLoadMagick7File", ImageType::MAGICK },
{ "VipsForeignLoadMagick7Buffer", ImageType::MAGICK },
{ "VipsForeignLoadOpenslide", ImageType::OPENSLIDE },
{ "VipsForeignLoadOpenslideFile", ImageType::OPENSLIDE },
{ "VipsForeignLoadPpmFile", ImageType::PPM },
{ "VipsForeignLoadFits", ImageType::FITS },
{ "VipsForeignLoadFitsFile", ImageType::FITS },
{ "VipsForeignLoadOpenexr", ImageType::EXR },
{ "VipsForeignLoadJxlFile", ImageType::JXL },
{ "VipsForeignLoadJxlBuffer", ImageType::JXL },
{ "VipsForeignLoadVips", ImageType::VIPS },
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
{ "VipsForeignLoadRaw", ImageType::RAW }
@@ -302,6 +353,17 @@ namespace sharp {
imageType == ImageType::PDF;
}
/*
Does this image type support removal of safety limits?
*/
bool ImageTypeSupportsUnlimited(ImageType imageType) {
return
imageType == ImageType::JPEG ||
imageType == ImageType::PNG ||
imageType == ImageType::SVG ||
imageType == ImageType::HEIF;
}
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
@@ -329,8 +391,8 @@ namespace sharp {
try {
vips::VOption *option = VImage::option()
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
->set("fail_on", descriptor->failOn);
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
option->set("unlimited", TRUE);
}
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
@@ -361,51 +423,80 @@ namespace sharp {
}
}
} else {
if (descriptor->createChannels > 0) {
int const channels = descriptor->createChannels;
if (channels > 0) {
// Create new image
if (descriptor->createNoiseType == "gaussian") {
int const channels = descriptor->createChannels;
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight);
std::vector<VImage> bands = {};
bands.reserve(channels);
for (int _band = 0; _band < channels; _band++) {
bands.push_back(image.gaussnoise(
descriptor->createWidth,
descriptor->createHeight,
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
}
image = image.bandjoin(bands);
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
if (channels < 3) {
image = image.colourspace(VIPS_INTERPRETATION_B_W);
} else {
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
bands.push_back(VImage::gaussnoise(descriptor->createWidth, descriptor->createHeight, VImage::option()
->set("mean", descriptor->createNoiseMean)
->set("sigma", descriptor->createNoiseSigma)));
}
image = VImage::bandjoin(bands).copy(VImage::option()->set("interpretation",
channels < 3 ? VIPS_INTERPRETATION_B_W: VIPS_INTERPRETATION_sRGB));
} else {
std::vector<double> background = {
descriptor->createBackground[0],
descriptor->createBackground[1],
descriptor->createBackground[2]
};
if (descriptor->createChannels == 4) {
if (channels == 4) {
background.push_back(descriptor->createBackground[3]);
}
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight)
.copy(VImage::option()->set("interpretation",
channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))
.new_from_image(background);
}
image = image.cast(VIPS_FORMAT_UCHAR);
imageType = ImageType::RAW;
} else if (descriptor->textValue.length() > 0) {
// Create a new image with text
vips::VOption *textOptions = VImage::option()
->set("align", descriptor->textAlign)
->set("justify", descriptor->textJustify)
->set("rgba", descriptor->textRgba)
->set("spacing", descriptor->textSpacing)
->set("wrap", descriptor->textWrap)
->set("autofit_dpi", &descriptor->textAutofitDpi);
if (descriptor->textWidth > 0) {
textOptions->set("width", descriptor->textWidth);
}
// Ignore dpi if height is set
if (descriptor->textWidth > 0 && descriptor->textHeight > 0) {
textOptions->set("height", descriptor->textHeight);
} else if (descriptor->textDpi > 0) {
textOptions->set("dpi", descriptor->textDpi);
}
if (descriptor->textFont.length() > 0) {
textOptions->set("font", const_cast<char*>(descriptor->textFont.data()));
}
if (descriptor->textFontfile.length() > 0) {
textOptions->set("fontfile", const_cast<char*>(descriptor->textFontfile.data()));
}
image = VImage::text(const_cast<char *>(descriptor->textValue.data()), textOptions);
if (!descriptor->textRgba) {
image = image.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));
}
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
imageType = ImageType::RAW;
} else {
// From filesystem
imageType = DetermineImageType(descriptor->file.data());
if (imageType == ImageType::MISSING) {
throw vips::VError("Input file is missing");
if (descriptor->file.find("<svg") != std::string::npos) {
throw vips::VError("Input file is missing, did you mean "
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
}
throw vips::VError("Input file is missing: " + descriptor->file);
}
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
->set("fail_on", descriptor->failOn);
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
option->set("unlimited", TRUE);
}
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
@@ -436,9 +527,10 @@ namespace sharp {
}
}
}
// Limit input images to a given number of pixels, where pixels = width * height
if (descriptor->limitInputPixels > 0 &&
static_cast<uint64_t>(image.width() * image.height()) > static_cast<uint64_t>(descriptor->limitInputPixels)) {
static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {
throw vips::VError("Input image exceeds pixel limit");
}
return std::make_tuple(image, imageType);
@@ -485,6 +577,7 @@ namespace sharp {
VImage RemoveExifOrientation(VImage image) {
VImage copy = image.copy();
copy.remove(VIPS_META_ORIENTATION);
copy.remove("exif-ifd0-Orientation");
return copy;
}
@@ -525,6 +618,15 @@ namespace sharp {
return copy;
}
/*
Remove GIF palette from image.
*/
VImage RemoveGifPalette(VImage image) {
VImage copy = image.copy();
copy.remove("gif-palette");
return copy;
}
/*
Does this image have a non-default density?
*/
@@ -633,7 +735,6 @@ namespace sharp {
Event listener for progress updates, used to detect timeout
*/
void VipsProgressCallBack(VipsImage *im, VipsProgress *progress, int *timeout) {
// printf("VipsProgressCallBack progress=%d run=%d timeout=%d\n", progress->percent, progress->run, *timeout);
if (*timeout > 0 && progress->run >= *timeout) {
vips_image_set_kill(im, TRUE);
vips_error("timeout", "%d%% complete", progress->percent);
@@ -790,22 +891,6 @@ namespace sharp {
return Is16Bit(interpretation) ? 65535.0 : 255.0;
}
/*
Get boolean operation type from string
*/
VipsOperationBoolean GetBooleanOperation(std::string const opStr) {
return static_cast<VipsOperationBoolean>(
vips_enum_from_nick(nullptr, VIPS_TYPE_OPERATION_BOOLEAN, opStr.data()));
}
/*
Get interpretation type from string
*/
VipsInterpretation GetInterpretation(std::string const typeStr) {
return static_cast<VipsInterpretation>(
vips_enum_from_nick(nullptr, VIPS_TYPE_INTERPRETATION, typeStr.data()));
}
/*
Convert RGBA value to another colourspace
*/
@@ -857,7 +942,7 @@ namespace sharp {
// Add non-transparent alpha channel, if required
if (colour[3] < 255.0 && !HasAlpha(image)) {
image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
}
return std::make_tuple(image, alphaColour);
}
@@ -885,8 +970,8 @@ namespace sharp {
}
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
Canvas canvas, bool swap, bool withoutEnlargement) {
if (swap) {
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction) {
if (swap && canvas != Canvas::IGNORE_ASPECT) {
// Swap input width and height when requested.
std::swap(width, height);
}
@@ -917,9 +1002,6 @@ namespace sharp {
}
break;
case Canvas::IGNORE_ASPECT:
if (swap) {
std::swap(hshrink, vshrink);
}
break;
}
} else if (targetWidth > 0) {
@@ -940,9 +1022,14 @@ namespace sharp {
}
}
// We should not enlarge (oversample) the output image,
// if withoutEnlargement is specified.
if (withoutEnlargement) {
// We should not reduce or enlarge the output image, if
// withoutReduction or withoutEnlargement is specified.
if (withoutReduction) {
// Equivalent of VIPS_SIZE_UP
hshrink = std::min(1.0, hshrink);
vshrink = std::min(1.0, vshrink);
} else if (withoutEnlargement) {
// Equivalent of VIPS_SIZE_DOWN
hshrink = std::max(1.0, hshrink);
vshrink = std::max(1.0, vshrink);
}

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_COMMON_H_
#define SRC_COMMON_H_
@@ -25,9 +14,9 @@
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 12) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 12 && VIPS_MICRO_VERSION < 2)
#error "libvips version 8.12.2+ is required - please see https://sharp.pixelplumbing.com/install"
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 2)
#error "libvips version 8.14.2+ is required - please see https://sharp.pixelplumbing.com/install"
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
@@ -48,13 +37,14 @@ namespace sharp {
std::string name;
std::string file;
char *buffer;
bool failOnError;
int limitInputPixels;
VipsFailOn failOn;
uint64_t limitInputPixels;
bool unlimited;
VipsAccess access;
size_t bufferLength;
bool isBuffer;
double density;
bool ignoreIcc;
VipsBandFormat rawDepth;
int rawChannels;
int rawWidth;
@@ -71,16 +61,29 @@ namespace sharp {
std::string createNoiseType;
double createNoiseMean;
double createNoiseSigma;
std::string textValue;
std::string textFont;
std::string textFontfile;
int textWidth;
int textHeight;
VipsAlign textAlign;
bool textJustify;
int textDpi;
bool textRgba;
int textSpacing;
VipsTextWrap textWrap;
int textAutofitDpi;
InputDescriptor():
buffer(nullptr),
failOnError(TRUE),
failOn(VIPS_FAIL_ON_WARNING),
limitInputPixels(0x3FFF * 0x3FFF),
unlimited(FALSE),
access(VIPS_ACCESS_RANDOM),
bufferLength(0),
isBuffer(FALSE),
density(72.0),
ignoreIcc(FALSE),
rawDepth(VIPS_FORMAT_UCHAR),
rawChannels(0),
rawWidth(0),
@@ -95,7 +98,16 @@ namespace sharp {
createHeight(0),
createBackground{ 0.0, 0.0, 0.0, 255.0 },
createNoiseMean(0.0),
createNoiseSigma(0.0) {}
createNoiseSigma(0.0),
textWidth(0),
textHeight(0),
textAlign(VIPS_ALIGN_LOW),
textJustify(FALSE),
textDpi(72),
textRgba(FALSE),
textSpacing(0),
textWrap(VIPS_TEXT_WRAP_WORD),
textAutofitDpi(0) {}
};
// Convenience methods to access the attributes of a Napi::Object
@@ -110,6 +122,11 @@ namespace sharp {
bool AttrAsBool(Napi::Object obj, std::string attr);
std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr);
std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr);
template <class T> T AttrAsEnum(Napi::Object obj, std::string attr, GType type) {
return static_cast<T>(
vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data()));
}
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len);
// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input);
@@ -129,6 +146,7 @@ namespace sharp {
PPM,
FITS,
EXR,
JXL,
VIPS,
RAW,
UNKNOWN,
@@ -159,10 +177,16 @@ namespace sharp {
bool IsHeic(std::string const &str);
bool IsHeif(std::string const &str);
bool IsAvif(std::string const &str);
bool IsJxl(std::string const &str);
bool IsDz(std::string const &str);
bool IsDzZip(std::string const &str);
bool IsV(std::string const &str);
/*
Trim space from end of string.
*/
std::string TrimEnd(std::string const &str);
/*
Provide a string identifier for the given image type.
*/
@@ -183,6 +207,11 @@ namespace sharp {
*/
bool ImageTypeSupportsPage(ImageType imageType);
/*
Does this image type support removal of safety limits?
*/
bool ImageTypeSupportsUnlimited(ImageType imageType);
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
@@ -224,6 +253,11 @@ namespace sharp {
*/
VImage RemoveAnimationProperties(VImage image);
/*
Remove GIF palette from image.
*/
VImage RemoveGifPalette(VImage image);
/*
Does this image have a non-default density?
*/
@@ -307,16 +341,6 @@ namespace sharp {
*/
double MaximumImageAlpha(VipsInterpretation const interpretation);
/*
Get boolean operation type from string
*/
VipsOperationBoolean GetBooleanOperation(std::string const opStr);
/*
Get interpretation type from string
*/
VipsInterpretation GetInterpretation(std::string const typeStr);
/*
Convert RGBA value to another colourspace
*/
@@ -345,7 +369,7 @@ namespace sharp {
the required thumbnail width/height and canvas mode.
*/
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
Canvas canvas, bool swap, bool withoutEnlargement);
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction);
} // namespace sharp

View File

@@ -31,7 +31,6 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>

View File

@@ -30,7 +30,6 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>

View File

@@ -38,7 +38,6 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>
@@ -733,7 +732,7 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
set( "in", *this )->
set( "target", target ) );
g_object_get( target.get_target(), "blob", &blob, NULL );
g_object_get( target.get_target(), "blob", &blob, (void *) NULL );
}
else if( (operation_name = vips_foreign_find_save_buffer( filename )) ) {
call_option_string( operation_name, option_string,
@@ -778,6 +777,32 @@ VImage::write_to_target( const char *suffix, VTarget target,
set( "target", target ) );
}
VRegion
VImage::region() const
{
return VRegion::new_from_image( *this );
}
VRegion
VImage::region( VipsRect *rect ) const
{
VRegion region = VRegion::new_from_image( *this );
region.prepare( rect );
return region;
}
VRegion
VImage::region( int left, int top, int width, int height ) const
{
VRegion region = VRegion::new_from_image( *this );
region.prepare( left, top, width, height );
return region;
}
#include "vips-operators.cpp"
std::vector<VImage>

View File

@@ -31,7 +31,6 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>

View File

@@ -0,0 +1,27 @@
// Object part of VRegion class
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/vips8>
#include <vips/debug.h>
VIPS_NAMESPACE_START
VRegion
VRegion::new_from_image( VImage image )
{
VipsRegion *region;
if( !(region = vips_region_new( image.get_image() )) ) {
throw VError();
}
VRegion out( region );
return( out );
}
VIPS_NAMESPACE_END

View File

@@ -1,5 +1,4 @@
// bodies for vips operations
// Mon Nov 1 03:31:09 PM CET 2021
// this file is generated automatically, do not edit!
VImage VImage::CMC2LCh( VOption *options ) const
@@ -943,6 +942,14 @@ VipsBlob *VImage::dzsave_buffer( VOption *options ) const
return( buffer );
}
void VImage::dzsave_target( VTarget target, VOption *options ) const
{
call( "dzsave_target",
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
VImage VImage::embed( int x, int y, int width, int height, VOption *options ) const
{
VImage out;
@@ -3521,6 +3528,14 @@ VipsBlob *VImage::tiffsave_buffer( VOption *options ) const
return( buffer );
}
void VImage::tiffsave_target( VTarget target, VOption *options ) const
{
call( "tiffsave_target",
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
VImage VImage::tilecache( VOption *options ) const
{
VImage out;
@@ -3664,6 +3679,13 @@ VipsBlob *VImage::webpsave_buffer( VOption *options ) const
return( buffer );
}
void VImage::webpsave_mime( VOption *options ) const
{
call( "webpsave_mime",
(options ? options : VImage::option())->
set( "in", *this ) );
}
void VImage::webpsave_target( VTarget target, VOption *options ) const
{
call( "webpsave_target",

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <numeric>
#include <vector>
@@ -80,6 +69,9 @@ class MetadataWorker : public Napi::AsyncWorker {
if (image.get_typeof(VIPS_META_RESOLUTION_UNIT) == VIPS_TYPE_REF_STRING) {
baton->resolutionUnit = image.get_string(VIPS_META_RESOLUTION_UNIT);
}
if (image.get_typeof("magick-format") == VIPS_TYPE_REF_STRING) {
baton->formatMagick = image.get_string("magick-format");
}
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
int const levels = std::stoi(image.get_string("openslide.level-count"));
for (int l = 0; l < levels; l++) {
@@ -153,7 +145,7 @@ class MetadataWorker : public Napi::AsyncWorker {
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
debuglog.Call({ Napi::String::New(env, warning) });
debuglog.MakeCallback(Receiver().Value(), { Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop();
}
@@ -204,6 +196,9 @@ class MetadataWorker : public Napi::AsyncWorker {
if (!baton->resolutionUnit.empty()) {
info.Set("resolutionUnit", baton->resolutionUnit == "in" ? "inch" : baton->resolutionUnit);
}
if (!baton->formatMagick.empty()) {
info.Set("formatMagick", baton->formatMagick);
}
if (!baton->levels.empty()) {
int i = 0;
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
@@ -235,24 +230,24 @@ class MetadataWorker : public Napi::AsyncWorker {
info.Set("orientation", baton->orientation);
}
if (baton->exifLength > 0) {
info.Set("exif", Napi::Buffer<char>::New(env, baton->exif, baton->exifLength, sharp::FreeCallback));
info.Set("exif", sharp::NewOrCopyBuffer(env, baton->exif, baton->exifLength));
}
if (baton->iccLength > 0) {
info.Set("icc", Napi::Buffer<char>::New(env, baton->icc, baton->iccLength, sharp::FreeCallback));
info.Set("icc", sharp::NewOrCopyBuffer(env, baton->icc, baton->iccLength));
}
if (baton->iptcLength > 0) {
info.Set("iptc", Napi::Buffer<char>::New(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
info.Set("iptc", sharp::NewOrCopyBuffer(env, baton->iptc, baton->iptcLength));
}
if (baton->xmpLength > 0) {
info.Set("xmp", Napi::Buffer<char>::New(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
info.Set("xmp", sharp::NewOrCopyBuffer(env, baton->xmp, baton->xmpLength));
}
if (baton->tifftagPhotoshopLength > 0) {
info.Set("tifftagPhotoshop",
Napi::Buffer<char>::New(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback));
sharp::NewOrCopyBuffer(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength));
}
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
} else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
}
delete baton->input;
@@ -270,7 +265,7 @@ class MetadataWorker : public Napi::AsyncWorker {
Napi::Value metadata(const Napi::CallbackInfo& info) {
// V8 objects are converted to non-V8 types held in the baton struct
MetadataBaton *baton = new MetadataBaton;
Napi::Object options = info[0].As<Napi::Object>();
Napi::Object options = info[size_t(0)].As<Napi::Object>();
// Input
baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
@@ -279,7 +274,7 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
// Join queue for worker thread
Napi::Function callback = info[1].As<Napi::Function>();
Napi::Function callback = info[size_t(1)].As<Napi::Function>();
MetadataWorker *worker = new MetadataWorker(callback, baton, debuglog);
worker->Receiver().Set("options", options);
worker->Queue();

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_METADATA_H_
#define SRC_METADATA_H_
@@ -41,6 +30,7 @@ struct MetadataBaton {
int pagePrimary;
std::string compression;
std::string resolutionUnit;
std::string formatMagick;
std::vector<std::pair<int, int>> levels;
int subifds;
std::vector<double> background;

View File

@@ -1,23 +1,11 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vector>
#include <vips/vips8>
#include "common.h"
@@ -57,7 +45,7 @@ namespace sharp {
/*
* Stretch luminance to cover full dynamic range.
*/
VImage Normalise(VImage image) {
VImage Normalise(VImage image, int const lower, int const upper) {
// Get original colourspace
VipsInterpretation typeBeforeNormalize = image.interpretation();
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
@@ -67,11 +55,12 @@ namespace sharp {
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
// Extract luminance
VImage luminance = lab[0];
// Find luminance range
VImage stats = luminance.stats();
double min = stats(0, 0)[0];
double max = stats(1, 0)[0];
if (min != max) {
int const min = lower == 0 ? luminance.min() : luminance.percent(lower);
int const max = upper == 100 ? luminance.max() : luminance.percent(upper);
if (std::abs(max - min) > 1) {
// Extract chroma
VImage chroma = lab.extract_band(1, VImage::option()->set("n", 2));
// Calculate multiplication factor and addition
@@ -112,6 +101,19 @@ namespace sharp {
}
}
/*
* Flatten image to remove alpha channel
*/
VImage Flatten(VImage image, std::vector<double> flattenBackground) {
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
std::vector<double> background {
flattenBackground[0] * multiplier,
flattenBackground[1] * multiplier,
flattenBackground[2] * multiplier
};
return image.flatten(VImage::option()->set("background", background));
}
/**
* Produce the "negative" of the image.
*/
@@ -209,7 +211,8 @@ namespace sharp {
/*
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
double const x1, double const y2, double const y3) {
if (sigma == -1.0) {
// Fast, mild sharpen
VImage sharpen = VImage::new_matrixv(3, 3,
@@ -224,8 +227,14 @@ namespace sharp {
if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
}
return image.sharpen(
VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged))
return image
.sharpen(VImage::option()
->set("sigma", sigma)
->set("m1", m1)
->set("m2", m2)
->set("x1", x1)
->set("y2", y2)
->set("y3", y3))
.colourspace(colourspaceBeforeSharpen);
}
}
@@ -255,45 +264,80 @@ namespace sharp {
/*
Trim an image
*/
VImage Trim(VImage image, double const threshold) {
VImage Trim(VImage image, std::vector<double> background, double threshold) {
if (image.width() < 3 && image.height() < 3) {
throw VError("Image to trim must be at least 3x3 pixels");
}
// Top-left pixel provides the background colour
VImage background = image.extract_area(0, 0, 1, 1);
if (HasAlpha(background)) {
background = background.flatten();
// Scale up 8-bit values to match 16-bit input image
double multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
threshold *= multiplier;
std::vector<double> backgroundAlpha(1);
if (background.size() == 0) {
// Top-left pixel provides the default background colour if none is given
background = image.extract_area(0, 0, 1, 1)(0, 0);
multiplier = 1.0;
}
if (HasAlpha(image) && background.size() == 4) {
// Just discard the alpha because flattening the background colour with
// itself (effectively what find_trim() does) gives the same result
backgroundAlpha[0] = background[3] * multiplier;
}
if (image.bands() > 2) {
background = {
background[0] * multiplier,
background[1] * multiplier,
background[2] * multiplier
};
} else {
background[0] = background[0] * multiplier;
}
int left, top, width, height;
left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background(0, 0))
->set("background", background)
->set("threshold", threshold));
if (width == 0 || height == 0) {
if (HasAlpha(image)) {
// Search alpha channel
VImage alpha = image[image.bands() - 1];
VImage backgroundAlpha = alpha.extract_area(0, 0, 1, 1);
left = alpha.find_trim(&top, &width, &height, VImage::option()
->set("background", backgroundAlpha(0, 0))
->set("threshold", threshold));
}
if (width == 0 || height == 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance");
if (HasAlpha(image)) {
// Search alpha channel (A)
int leftA, topA, widthA, heightA;
VImage alpha = image[image.bands() - 1];
leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option()
->set("background", backgroundAlpha)
->set("threshold", threshold));
if (widthA > 0 && heightA > 0) {
if (width > 0 && height > 0) {
// Combined bounding box (B)
int const leftB = std::min(left, leftA);
int const topB = std::min(top, topA);
int const widthB = std::max(left + width, leftA + widthA) - leftB;
int const heightB = std::max(top + height, topA + heightA) - topB;
return image.extract_area(leftB, topB, widthB, heightB);
} else {
// Use alpha only
return image.extract_area(leftA, topA, widthA, heightA);
}
}
}
return image.extract_area(left, top, width, height);
if (width > 0 && height > 0) {
return image.extract_area(left, top, width, height);
}
return image;
}
/*
* Calculate (a * in + b)
*/
VImage Linear(VImage image, double const a, double const b) {
if (HasAlpha(image)) {
VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b) {
size_t const bands = static_cast<size_t>(image.bands());
if (a.size() > bands) {
throw VError("Band expansion using linear is unsupported");
}
if (HasAlpha(image) && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
// Separate alpha channel
VImage alpha = image[image.bands() - 1];
return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
VImage alpha = image[bands - 1];
return RemoveAlpha(image).linear(a, b, VImage::option()->set("uchar", TRUE)).bandjoin(alpha);
} else {
return image.linear(a, b);
return image.linear(a, b, VImage::option()->set("uchar", TRUE));
}
}
@@ -341,11 +385,11 @@ namespace sharp {
* Split into frames, embed each frame, reassemble, and update pageHeight.
*/
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
std::vector<double> background, int nPages, int *pageHeight) {
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight) {
if (top == 0 && height == *pageHeight) {
// Fast path; no need to adjust the height of the multi-page image
return image.embed(left, 0, width, image.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("extend", extendWith)
->set("background", background));
} else if (left == 0 && width == image.width()) {
// Fast path; no need to adjust the width of the multi-page image
@@ -357,7 +401,7 @@ namespace sharp {
// Do the embed on the wide image
image = image.embed(0, top, image.width(), height, VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("extend", extendWith)
->set("background", background));
// Split the wide image into frames
@@ -387,7 +431,7 @@ namespace sharp {
// Embed each frame in the target size
for (int i = 0; i < nPages; i++) {
pages[i] = pages[i].embed(left, top, width, height, VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("extend", extendWith)
->set("background", background));
}

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_
@@ -33,7 +22,7 @@ namespace sharp {
/*
* Stretch luminance to cover full dynamic range.
*/
VImage Normalise(VImage image);
VImage Normalise(VImage image, int const lower, int const upper);
/*
* Contrast limiting adapative histogram equalization (CLAHE)
@@ -45,6 +34,11 @@ namespace sharp {
*/
VImage Gamma(VImage image, double const exponent);
/*
* Flatten image to remove alpha channel
*/
VImage Flatten(VImage image, std::vector<double> flattenBackground);
/*
* Produce the "negative" of the image.
*/
@@ -64,7 +58,8 @@ namespace sharp {
/*
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
double const x1, double const y2, double const y3);
/*
Threshold an image
@@ -84,12 +79,12 @@ namespace sharp {
/*
Trim an image
*/
VImage Trim(VImage image, double const threshold);
VImage Trim(VImage image, std::vector<double> background, double const threshold);
/*
* Linear adjustment (a * in + b)
*/
VImage Linear(VImage image, double const a, double const b);
VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b);
/*
* Recomb with a Matrix of the given bands/channel size.
@@ -118,7 +113,7 @@ namespace sharp {
* Split into frames, embed each frame, reassemble, and update pageHeight.
*/
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
std::vector<double> background, int nPages, int *pageHeight);
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
} // namespace sharp

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_PIPELINE_H_
#define SRC_PIPELINE_H_
@@ -67,15 +56,18 @@ struct PipelineBaton {
int width;
int height;
int channels;
VipsKernel kernel;
sharp::Canvas canvas;
int position;
std::vector<double> resizeBackground;
bool hasCropOffset;
int cropOffsetLeft;
int cropOffsetTop;
bool hasAttentionCenter;
int attentionX;
int attentionY;
bool premultiplied;
bool tileCentre;
std::string kernel;
bool fastShrinkOnLoad;
double tintA;
double tintB;
@@ -90,19 +82,25 @@ struct PipelineBaton {
double lightness;
int medianSize;
double sharpenSigma;
double sharpenFlat;
double sharpenJagged;
double sharpenM1;
double sharpenM2;
double sharpenX1;
double sharpenY2;
double sharpenY3;
int threshold;
bool thresholdGrayscale;
std::vector<double> trimBackground;
double trimThreshold;
int trimOffsetLeft;
int trimOffsetTop;
double linearA;
double linearB;
std::vector<double> linearA;
std::vector<double> linearB;
double gamma;
double gammaOut;
bool greyscale;
bool normalise;
int normaliseLower;
int normaliseUpper;
int claheWidth;
int claheHeight;
int claheMaxSlope;
@@ -118,6 +116,7 @@ struct PipelineBaton {
int extendLeft;
int extendRight;
std::vector<double> extendBackground;
VipsExtend extendWith;
bool withoutEnlargement;
bool withoutReduction;
std::vector<double> affineMatrix;
@@ -126,7 +125,7 @@ struct PipelineBaton {
double affineIdy;
double affineOdx;
double affineOdy;
vips::VInterpolate affineInterpolator;
std::string affineInterpolator;
int jpegQuality;
bool jpegProgressive;
std::string jpegChromaSubsampling;
@@ -154,9 +153,15 @@ struct PipelineBaton {
bool webpLossless;
bool webpSmartSubsample;
int webpEffort;
bool webpMinSize;
bool webpMixed;
int gifBitdepth;
int gifEffort;
double gifDither;
double gifInterFrameMaxError;
double gifInterPaletteMaxError;
bool gifReuse;
bool gifProgressive;
int tiffQuality;
VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor;
@@ -173,6 +178,10 @@ struct PipelineBaton {
int heifEffort;
std::string heifChromaSubsampling;
bool heifLossless;
double jxlDistance;
int jxlDecodingTier;
int jxlEffort;
bool jxlLossless;
VipsBandFormat rawDepth;
std::string err;
bool withMetadata;
@@ -206,6 +215,7 @@ struct PipelineBaton {
int tileSkipBlanks;
VipsForeignDzDepth tileDepth;
std::string tileId;
std::string tileBasename;
std::unique_ptr<double[]> recombMatrix;
PipelineBaton():
@@ -214,12 +224,16 @@ struct PipelineBaton {
topOffsetPre(-1),
topOffsetPost(-1),
channels(0),
kernel(VIPS_KERNEL_LANCZOS3),
canvas(sharp::Canvas::CROP),
position(0),
resizeBackground{ 0.0, 0.0, 0.0, 255.0 },
hasCropOffset(false),
cropOffsetLeft(0),
cropOffsetTop(0),
hasAttentionCenter(false),
attentionX(0),
attentionY(0),
premultiplied(false),
tintA(128.0),
tintB(128.0),
@@ -234,18 +248,24 @@ struct PipelineBaton {
lightness(0),
medianSize(0),
sharpenSigma(0.0),
sharpenFlat(1.0),
sharpenJagged(2.0),
sharpenM1(1.0),
sharpenM2(2.0),
sharpenX1(2.0),
sharpenY2(10.0),
sharpenY3(20.0),
threshold(0),
thresholdGrayscale(true),
trimBackground{},
trimThreshold(0.0),
trimOffsetLeft(0),
trimOffsetTop(0),
linearA(1.0),
linearB(0.0),
linearA{},
linearB{},
gamma(0.0),
greyscale(false),
normalise(false),
normaliseLower(1),
normaliseUpper(99),
claheWidth(0),
claheHeight(0),
claheMaxSlope(3),
@@ -260,6 +280,7 @@ struct PipelineBaton {
extendLeft(0),
extendRight(0),
extendBackground{ 0.0, 0.0, 0.0, 255.0 },
extendWith(VIPS_EXTEND_BACKGROUND),
withoutEnlargement(false),
withoutReduction(false),
affineMatrix{ 1.0, 0.0, 0.0, 1.0 },
@@ -268,7 +289,7 @@ struct PipelineBaton {
affineIdy(0),
affineOdx(0),
affineOdy(0),
affineInterpolator(vips::VInterpolate::new_from_name("bicubic")),
affineInterpolator("bicubic"),
jpegQuality(80),
jpegProgressive(false),
jpegChromaSubsampling("4:2:0"),
@@ -296,6 +317,15 @@ struct PipelineBaton {
webpLossless(false),
webpSmartSubsample(false),
webpEffort(4),
webpMinSize(false),
webpMixed(false),
gifBitdepth(8),
gifEffort(7),
gifDither(1.0),
gifInterFrameMaxError(0.0),
gifInterPaletteMaxError(3.0),
gifReuse(true),
gifProgressive(false),
tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
@@ -312,6 +342,10 @@ struct PipelineBaton {
heifEffort(4),
heifChromaSubsampling("4:4:4"),
heifLossless(false),
jxlDistance(1.0),
jxlDecodingTier(0),
jxlEffort(7),
jxlLossless(false),
rawDepth(VIPS_FORMAT_UCHAR),
withMetadata(false),
withMetadataOrientation(-1),

View File

@@ -1,19 +1,7 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <napi.h>
#include <cstdlib>
#include <vips/vips8>
#include "common.h"
@@ -22,16 +10,7 @@
#include "utilities.h"
#include "stats.h"
#if defined(_MSC_VER) && _MSC_VER >= 1400 // MSVC 2005/8
static void empty_invalid_parameter_handler(const wchar_t* expression,
const wchar_t* function, const wchar_t* file, unsigned int line,
uintptr_t reserved) {
// No-op.
}
#endif
static void* sharp_vips_init(void*) {
g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE);
vips_init("sharp");
return nullptr;
}
@@ -43,13 +22,6 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
// Tell the CRT to not exit the application when an invalid parameter is
// passed. The main issue is that invalid FDs will trigger this behaviour.
// See: https://github.com/libvips/libvips/pull/2571.
#if defined(_MSC_VER) && _MSC_VER >= 1400 // MSVC 2005/8
_set_invalid_parameter_handler(empty_invalid_parameter_handler);
#endif
// Methods available to JavaScript
exports.Set("metadata", Napi::Function::New(env, metadata));
exports.Set("pipeline", Napi::Function::New(env, pipeline));

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <numeric>
#include <vector>
@@ -117,7 +106,7 @@ class StatsWorker : public Napi::AsyncWorker {
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
debuglog.Call({ Napi::String::New(env, warning) });
debuglog.MakeCallback(Receiver().Value(), { Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop();
}
@@ -154,7 +143,7 @@ class StatsWorker : public Napi::AsyncWorker {
info.Set("dominant", dominant);
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
} else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
}
delete baton->input;
@@ -172,16 +161,17 @@ class StatsWorker : public Napi::AsyncWorker {
Napi::Value stats(const Napi::CallbackInfo& info) {
// V8 objects are converted to non-V8 types held in the baton struct
StatsBaton *baton = new StatsBaton;
Napi::Object options = info[0].As<Napi::Object>();
Napi::Object options = info[size_t(0)].As<Napi::Object>();
// Input
baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
baton->input->access = VIPS_ACCESS_RANDOM;
// Function to notify of libvips warnings
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
// Join queue for worker thread
Napi::Function callback = info[1].As<Napi::Function>();
Napi::Function callback = info[size_t(1)].As<Napi::Function>();
StatsWorker *worker = new StatsWorker(callback, baton, debuglog);
worker->Receiver().Set("options", options);
worker->Queue();

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_STATS_H_
#define SRC_STATS_H_

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <cmath>
#include <string>
@@ -30,16 +19,16 @@ Napi::Value cache(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Set memory limit
if (info[0].IsNumber()) {
vips_cache_set_max_mem(info[0].As<Napi::Number>().Int32Value() * 1048576);
if (info[size_t(0)].IsNumber()) {
vips_cache_set_max_mem(info[size_t(0)].As<Napi::Number>().Int32Value() * 1048576);
}
// Set file limit
if (info[1].IsNumber()) {
vips_cache_set_max_files(info[1].As<Napi::Number>().Int32Value());
if (info[size_t(1)].IsNumber()) {
vips_cache_set_max_files(info[size_t(1)].As<Napi::Number>().Int32Value());
}
// Set items limit
if (info[2].IsNumber()) {
vips_cache_set_max(info[2].As<Napi::Number>().Int32Value());
if (info[size_t(2)].IsNumber()) {
vips_cache_set_max(info[size_t(2)].As<Napi::Number>().Int32Value());
}
// Get memory stats
@@ -69,8 +58,8 @@ Napi::Value cache(const Napi::CallbackInfo& info) {
*/
Napi::Value concurrency(const Napi::CallbackInfo& info) {
// Set concurrency
if (info[0].IsNumber()) {
vips_concurrency_set(info[0].As<Napi::Number>().Int32Value());
if (info[size_t(0)].IsNumber()) {
vips_concurrency_set(info[size_t(0)].As<Napi::Number>().Int32Value());
}
// Get concurrency
return Napi::Number::New(info.Env(), vips_concurrency_get());
@@ -91,8 +80,8 @@ Napi::Value counters(const Napi::CallbackInfo& info) {
*/
Napi::Value simd(const Napi::CallbackInfo& info) {
// Set state
if (info[0].IsBoolean()) {
vips_vector_set_enabled(info[0].As<Napi::Boolean>().Value());
if (info[size_t(0)].IsBoolean()) {
vips_vector_set_enabled(info[size_t(0)].As<Napi::Boolean>().Value());
}
// Get state
return Napi::Boolean::New(info.Env(), vips_vector_isenabled());
@@ -115,17 +104,28 @@ Napi::Value format(const Napi::CallbackInfo& info) {
Napi::Object format = Napi::Object::New(env);
for (std::string const f : {
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k"
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl"
}) {
// Input
Napi::Boolean hasInputFile =
Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "load").c_str()));
const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
Napi::Boolean hasInputFile = Napi::Boolean::New(env, oc);
Napi::Boolean hasInputBuffer =
Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "load_buffer").c_str()));
Napi::Object input = Napi::Object::New(env);
input.Set("file", hasInputFile);
input.Set("buffer", hasInputBuffer);
input.Set("stream", hasInputBuffer);
if (hasInputFile) {
const VipsForeignClass *fc = VIPS_FOREIGN_CLASS(oc);
if (fc->suffs) {
Napi::Array fileSuffix = Napi::Array::New(env);
const char **suffix = fc->suffs;
for (int i = 0; *suffix; i++, suffix++) {
fileSuffix.Set(i, Napi::String::New(env, *suffix));
}
input.Set("fileSuffix", fileSuffix);
}
}
// Output
Napi::Boolean hasOutputFile =
Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "save").c_str()));
@@ -174,10 +174,10 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
// Open input files
VImage image1;
sharp::ImageType imageType1 = sharp::DetermineImageType(info[0].As<Napi::String>().Utf8Value().data());
sharp::ImageType imageType1 = sharp::DetermineImageType(info[size_t(0)].As<Napi::String>().Utf8Value().data());
if (imageType1 != sharp::ImageType::UNKNOWN) {
try {
image1 = VImage::new_from_file(info[0].As<Napi::String>().Utf8Value().c_str());
image1 = VImage::new_from_file(info[size_t(0)].As<Napi::String>().Utf8Value().c_str());
} catch (...) {
throw Napi::Error::New(env, "Input file 1 has corrupt header");
}
@@ -185,10 +185,10 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
throw Napi::Error::New(env, "Input file 1 is of an unsupported image format");
}
VImage image2;
sharp::ImageType imageType2 = sharp::DetermineImageType(info[1].As<Napi::String>().Utf8Value().data());
sharp::ImageType imageType2 = sharp::DetermineImageType(info[size_t(1)].As<Napi::String>().Utf8Value().data());
if (imageType2 != sharp::ImageType::UNKNOWN) {
try {
image2 = VImage::new_from_file(info[1].As<Napi::String>().Utf8Value().c_str());
image2 = VImage::new_from_file(info[size_t(1)].As<Napi::String>().Utf8Value().c_str());
} catch (...) {
throw Napi::Error::New(env, "Input file 2 has corrupt header");
}

View File

@@ -1,16 +1,5 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_

24
test/beforeEach.js Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const sharp = require('../');
const usingCache = !process.env.G_DEBUG;
const usingSimd = !process.env.VIPS_NOVECTOR;
const concurrency = Number(process.env.VIPS_CONCURRENCY) || 0;
exports.mochaHooks = {
beforeEach () {
sharp.cache(usingCache);
sharp.simd(usingSimd);
sharp.concurrency(concurrency);
},
afterEach () {
if (global.gc) {
global.gc();
}
}
};

26
test/bench/Dockerfile Normal file
View File

@@ -0,0 +1,26 @@
FROM ubuntu:22.04
ARG BRANCH=main
# Install basic dependencies
RUN apt-get -y update && apt-get install -y build-essential curl git
# Install latest Node.js LTS
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
RUN apt-get install -y nodejs
# Install benchmark dependencies
RUN apt-get install -y imagemagick libmagick++-dev graphicsmagick
# Install sharp
RUN mkdir /tmp/sharp
RUN cd /tmp && git clone --single-branch --branch $BRANCH https://github.com/lovell/sharp.git
RUN cd /tmp/sharp && npm install --build-from-source
# Install benchmark test
RUN cd /tmp/sharp/test/bench && npm install --omit optional
RUN cat /etc/os-release | grep VERSION=
RUN node -v
WORKDIR /tmp/sharp/test/bench
CMD [ "node", "perf" ]

View File

@@ -7,16 +7,19 @@
"scripts": {
"test": "node perf && node random && node parallel"
},
"devDependencies": {
"@squoosh/cli": "0.7.2",
"dependencies": {
"@squoosh/cli": "0.7.3",
"@squoosh/lib": "0.4.0",
"async": "3.2.3",
"async": "3.2.4",
"benchmark": "2.1.4",
"gm": "1.23.1",
"gm": "1.25.0",
"imagemagick": "0.1.3",
"jimp": "0.16.1",
"mapnik": "4.5.9",
"semver": "7.3.5"
"jimp": "0.22.7",
"semver": "7.3.8"
},
"optionalDependencies": {
"@tensorflow/tfjs-node": "4.2.0",
"mapnik": "4.5.9"
},
"license": "Apache-2.0",
"engines": {

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
process.env.UV_THREADPOOL_SIZE = 64;

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const os = require('os');
@@ -5,16 +8,24 @@ const fs = require('fs');
const { exec } = require('child_process');
const async = require('async');
const assert = require('assert');
const Benchmark = require('benchmark');
const safeRequire = (name) => {
try {
return require(name);
} catch (err) {}
return null;
};
// Contenders
const sharp = require('../../');
const gm = require('gm');
const imagemagick = require('imagemagick');
const mapnik = require('mapnik');
const mapnik = safeRequire('mapnik');
const jimp = require('jimp');
const squoosh = require('@squoosh/lib');
process.env.TF_CPP_MIN_LOG_LEVEL = 1;
const tfjs = safeRequire('@tensorflow/tfjs-node');
const fixtures = require('../fixtures');
@@ -24,6 +35,7 @@ const outputWebP = fixtures.path('output.webp');
const width = 720;
const height = 588;
const heightPng = 540;
// Disable libvips cache to ensure tests are as fair as they can be
sharp.cache(false);
@@ -137,7 +149,7 @@ async.series({
}
});
// mapnik
jpegSuite.add('mapnik-file-file', {
mapnik && jpegSuite.add('mapnik-file-file', {
defer: true,
fn: function (deferred) {
mapnik.Image.open(fixtures.inputJpg, function (err, img) {
@@ -212,11 +224,10 @@ async.series({
.filter('Lanczos')
.resize(width, height)
.quality(80)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -243,16 +254,33 @@ async.series({
.filter('Lanczos')
.resize(width, height)
.quality(80)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// tfjs
tfjs && jpegSuite.add('tfjs-node-buffer-buffer', {
defer: true,
fn: function (deferred) {
const decoded = tfjs.node.decodeJpeg(inputJpgBuffer);
const resized = tfjs.image.resizeBilinear(decoded, [height, width]);
tfjs
.node
.encodeJpeg(resized, 'rgb', 80)
.then(function () {
deferred.resolve();
tfjs.disposeVariables();
})
.catch(function (err) {
throw err;
});
}
});
// sharp
jpegSuite.add('sharp-buffer-file', {
defer: true,
@@ -272,11 +300,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -311,11 +338,10 @@ async.series({
fn: function (deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -326,8 +352,7 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.toBuffer()
.then(function (buffer) {
assert.notStrictEqual(null, buffer);
.then(function () {
deferred.resolve();
})
.catch(function (err) {
@@ -350,11 +375,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.sharpen()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -365,11 +389,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.sharpen(3, 1, 3)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -380,11 +403,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.blur()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -395,11 +417,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.blur(3)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -410,11 +431,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.gamma()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -425,11 +445,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.normalise()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -440,11 +459,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.greyscale()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -456,11 +474,10 @@ async.series({
.resize(width, height)
.gamma()
.greyscale()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -471,11 +488,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.jpeg({ progressive: true })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -486,11 +502,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.jpeg({ chromaSubsampling: '4:4:4' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -501,11 +516,10 @@ async.series({
sharp(inputJpgBuffer)
.rotate(90)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -516,26 +530,24 @@ async.series({
sharp.simd(false);
sharp(inputJpgBuffer)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
sharp.simd(true);
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-sequentialRead', {
}).add('sharp-random-access-read', {
defer: true,
fn: function (deferred) {
sharp(inputJpgBuffer, { sequentialRead: true })
sharp(inputJpgBuffer, { sequentialRead: false })
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -548,11 +560,10 @@ async.series({
fit: 'cover',
position: sharp.strategy.entropy
})
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -565,11 +576,10 @@ async.series({
fit: 'cover',
position: sharp.strategy.attention
})
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -588,11 +598,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'cubic' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -602,11 +611,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos2' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -616,11 +624,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos3' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -645,7 +652,7 @@ async.series({
throw err;
} else {
image
.resize(width, height)
.resize(width, heightPng)
.deflateLevel(6)
.filterType(0)
.getBuffer(jimp.MIME_PNG, function (err) {
@@ -666,7 +673,7 @@ async.series({
throw err;
} else {
image
.resize(width, height)
.resize(width, heightPng)
.deflateLevel(6)
.filterType(0)
.write(outputPng, function (err) {
@@ -680,21 +687,74 @@ async.series({
});
}
});
// squoosh-cli
pngSuite.add('squoosh-cli-file-file', {
defer: true,
fn: function (deferred) {
exec(`./node_modules/.bin/squoosh-cli \
--output-dir ${os.tmpdir()} \
--resize '{"enabled":true,"width":${width},"height":${heightPng},"method":"lanczos3","premultiply":true,"linearRGB":false}' \
--oxipng '{"level":1}' \
"${fixtures.inputPngAlphaPremultiplicationLarge}"`, function (err) {
if (err) {
throw err;
}
deferred.resolve();
});
}
});
// squoosh-lib (GPLv3)
pngSuite.add('squoosh-lib-buffer-buffer', {
defer: true,
fn: function (deferred) {
const pool = new squoosh.ImagePool();
const image = pool.ingestImage(inputPngBuffer);
image.decoded
.then(function () {
return image.preprocess({
resize: {
enabled: true,
width,
height: heightPng,
method: 'lanczos3',
premultiply: true,
linearRGB: false
}
});
})
.then(function () {
return image.encode({
oxipng: {
level: 1
}
});
})
.then(function () {
return pool.close();
})
.then(function () {
return image.encodedWith.oxipng;
})
.then(function () {
deferred.resolve();
});
}
});
// mapnik
pngSuite.add('mapnik-file-file', {
mapnik && pngSuite.add('mapnik-file-file', {
defer: true,
fn: function (deferred) {
mapnik.Image.open(fixtures.inputPngAlphaPremultiplicationLarge, function (err, img) {
if (err) throw err;
img.premultiply(function (err, img) {
if (err) throw err;
img.resize(width, height, {
img.resize(width, heightPng, {
scaling_method: mapnik.imageScaling.lanczos
}, function (err, img) {
if (err) throw err;
img.demultiply(function (err, img) {
if (err) throw err;
img.save(outputPng, 'png', function (err) {
img.save(outputPng, 'png32:f=no:z=6', function (err) {
if (err) throw err;
deferred.resolve();
});
@@ -710,13 +770,13 @@ async.series({
if (err) throw err;
img.premultiply(function (err, img) {
if (err) throw err;
img.resize(width, height, {
img.resize(width, heightPng, {
scaling_method: mapnik.imageScaling.lanczos
}, function (err, img) {
if (err) throw err;
img.demultiply(function (err, img) {
if (err) throw err;
img.encode('png', function (err) {
img.encode('png32:f=no:z=6', function (err) {
if (err) throw err;
deferred.resolve();
});
@@ -734,7 +794,7 @@ async.series({
srcPath: fixtures.inputPngAlphaPremultiplicationLarge,
dstPath: outputPng,
width: width,
height: height,
height: heightPng,
filter: 'Lanczos',
customArgs: [
'-define', 'PNG:compression-level=6',
@@ -755,7 +815,7 @@ async.series({
fn: function (deferred) {
gm(fixtures.inputPngAlphaPremultiplicationLarge)
.filter('Lanczos')
.resize(width, height)
.resize(width, heightPng)
.define('PNG:compression-level=6')
.define('PNG:compression-filter=0')
.write(outputPng, function (err) {
@@ -771,14 +831,13 @@ async.series({
fn: function (deferred) {
gm(fixtures.inputPngAlphaPremultiplicationLarge)
.filter('Lanczos')
.resize(width, height)
.resize(width, heightPng)
.define('PNG:compression-level=6')
.define('PNG:compression-filter=0')
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -790,7 +849,7 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.resize(width, heightPng)
.png({ compressionLevel: 6 })
.toFile(outputPng, function (err) {
if (err) {
@@ -805,13 +864,12 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.resize(width, heightPng)
.png({ compressionLevel: 6 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err, data) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -821,7 +879,7 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(width, height)
.resize(width, heightPng)
.png({ compressionLevel: 6 })
.toFile(outputPng, function (err) {
if (err) {
@@ -836,13 +894,12 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(width, height)
.resize(width, heightPng)
.png({ compressionLevel: 6 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -852,13 +909,12 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.resize(width, heightPng)
.png({ compressionLevel: 6, progressive: true })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -868,13 +924,12 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.resize(width, heightPng)
.png({ adaptiveFiltering: true, compressionLevel: 6 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -884,13 +939,12 @@ async.series({
minSamples,
fn: function (deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.resize(width, heightPng)
.png({ compressionLevel: 9 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -923,11 +977,10 @@ async.series({
fn: function (deferred) {
sharp(inputWebPBuffer)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -950,11 +1003,10 @@ async.series({
fn: function (deferred) {
sharp(fixtures.inputWebP)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -966,7 +1018,9 @@ async.series({
}).run();
}
}, function (err, results) {
assert(!err, err);
if (err) {
throw err;
}
Object.keys(results).forEach(function (format) {
if (results[format].toString().substr(0, 5) !== 'sharp') {
console.log('sharp was slower than ' + results[format] + ' for ' + format);

View File

@@ -1,3 +1,6 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const imagemagick = require('imagemagick');

13
test/bench/run-with-docker.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
BRANCH=$(git branch --show-current)
echo "Running sharp performance tests using $BRANCH branch"
docker build --build-arg "BRANCH=$BRANCH" -t sharp-test-bench .
docker run --rm -it sharp-test-bench

View File

@@ -1,6 +0,0 @@
#!/bin/sh
CPPFLAGS="--coverage" LDFLAGS="--coverage" npm rebuild
npm test
geninfo --no-external --base-directory src --output-file coverage/sharp.info build/Release/obj.target/sharp/src
genhtml --title sharp --demangle-cpp --output-directory coverage/sharp coverage/*.info

BIN
test/fixtures/65536-uint32-limit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 B

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Some files were not shown because too many files have changed in this diff Show More