Compare commits

...

385 Commits

Author SHA1 Message Date
Lovell Fuller
dad7f1e1f6 Release v0.25.3 2020-05-17 14:21:39 +01:00
Lovell Fuller
c3a9384dcf Upgrade deps that have dropped Node.js 8 support 2020-05-17 13:29:55 +01:00
Lovell Fuller
0ee08bfe46 Docs: lint JSDoc annotations when building docs 2020-05-15 14:07:27 +01:00
Lovell Fuller
0872f495f9 Tests: temporarily skip known failure on FreeBSD 2020-05-15 12:17:45 +01:00
Lovell Fuller
33ac8b93a9 CI: allow FreeBSD to update to latest packages 2020-05-13 16:12:17 +01:00
Lovell Fuller
bbff1c222d Docs: clarify description of withMetadata
Default and non-default behaviour was mixed in the same paragraph,
leading to confusion about when an ICC profile might be expected.
2020-05-13 16:01:03 +01:00
Lovell Fuller
b534f99870 Docs: correct typo in composite 2020-05-13 15:56:44 +01:00
Lovell Fuller
86acf2460e Allow parsing of SVG attribute values >10MB #2195 2020-05-06 20:56:08 +01:00
NickNaso
40c00035ee Docs: add N-API badge 2020-05-01 11:53:10 +01:00
Lovell Fuller
4673abae7d Docs: serve logo via CDN 2020-05-01 11:41:38 +01:00
Lovell Fuller
ef8a705957 Docs: move npm install command to examples 2020-05-01 11:38:09 +01:00
Lovell Fuller
48255fa009 Docs: serve logo via CDN 2020-05-01 11:27:00 +01:00
Lovell Fuller
ed6269bc9c Bump dependencies 2020-05-01 11:26:05 +01:00
Lovell Fuller
16b8d9afe5 Docs: changelog entry and credit for #2188 2020-05-01 11:25:43 +01:00
Dumitru
f29fcb1f73 Ensure npm platform flag is respected when copying DLLs 2020-04-29 18:24:27 +01:00
Lovell Fuller
b36ad25030 CI: Upgrade FreeBSD to version 13
It currently provides only vips v8.8.3 so will (still) fail.
2020-04-23 21:39:45 +01:00
Lovell Fuller
edf3fe24c9 CI: add Node.js 14 2020-04-23 21:30:04 +01:00
Lovell Fuller
5a1bdf0eb1 Bump deps: semver, documentation, nyc 2020-04-23 20:51:12 +01:00
Lovell Fuller
d8e24d7e76 Docs: slightly reduce whitespace before headings, lists 2020-04-23 20:47:10 +01:00
Floris de Bijl
786c5330e9 docs: change 'colourspace' to 'toColourspace' (#2174) 2020-04-21 09:44:22 +01:00
Lovell Fuller
70e730bb67 Docs: add basic search feature for install and API 2020-04-14 21:26:41 +01:00
Lovell Fuller
1d6ef630a5 Docs: extract might also be referred to as 'crop' 2020-04-14 21:24:26 +01:00
Vincent Voyer
1eedb22ef5 docs(clone): add promise example 2020-04-08 09:45:04 +01:00
Lovell Fuller
6ed1f49ad3 Ensure libvips is initialised only once #2143 2020-03-29 19:44:21 +01:00
Lovell Fuller
ecd01afad3 Docs: clarify JPEG quality/chromaSubsampling interdependence 2020-03-26 20:22:47 +00:00
Lovell Fuller
8bd8709f2b Release v0.25.2 2020-03-20 19:20:34 +00:00
Lovell Fuller
e82a585cec Ensure AsyncWorker options are persisted #2130 2020-03-19 21:21:21 +00:00
Lovell Fuller
c1d4a68558 Add changelog entry, docs and credit for #2098 2020-03-18 13:54:41 +00:00
Edward Silverton
eff36dc09f Add IIIF layout support to tile-based output (#2098) 2020-03-18 13:27:54 +00:00
Lovell Fuller
031a58f817 Docs: AWS Lambda cross-platform no longer requires target flag 2020-03-17 18:33:49 +00:00
Lovell Fuller
cf39fc4fb1 Simplify tiled layout JPEG file extension logic 2020-03-14 22:08:46 +00:00
Lovell Fuller
c9bff94e17 Docs: expand descriptions of resize cover/contain 2020-03-14 21:34:48 +00:00
Lovell Fuller
e78e919925 Clarify diff between install vs bug issue templates 2020-03-10 21:36:48 +00:00
Lovell Fuller
76bb25262e Docs: ARM64/RHEL7 prebuilt binaries changelog entry 2020-03-10 21:22:30 +00:00
Lovell Fuller
d8426b1ed5 CI: Switch Linux x64 to CentOS 7 for glibc 2.17, gcc 4.8.5
Expand Linux ARM64v8 to all supported versions of Node.js
2020-03-10 19:39:26 +00:00
Lovell Fuller
4894c10dd9 Ensure consistent, correct detection of input opts #2118 2020-03-08 14:32:17 +00:00
Lovell Fuller
24285bb0e0 Release v0.25.1 2020-03-07 21:21:44 +00:00
Lovell Fuller
bf75501262 Ensure prebuilt binary based on N-API version 2020-03-07 20:59:25 +00:00
Lovell Fuller
9e214a17f0 Release v0.25.1 2020-03-07 19:12:32 +00:00
Lovell Fuller
0aae1ecd9b Ensure prebuilt binaries use N-API v4 #2117 2020-03-07 18:52:44 +00:00
Lovell Fuller
062f990315 Release v0.25.0 2020-03-07 12:44:42 +00:00
Lovell Fuller
22685e44c0 Upgrade to libvips v8.9.1 2020-03-07 10:15:51 +00:00
Lovell Fuller
8d66433e7c Docs: 32-bit Windows is supported (again) 2020-03-06 21:33:15 +00:00
Lovell Fuller
82863f48f6 Update issue templates to ask common follow-up questions 2020-03-06 21:12:59 +00:00
Lovell Fuller
51b14329d6 Simplify and future-proof tile option parsing 2020-03-06 20:06:41 +00:00
Lovell Fuller
14dc7681ed Upgrade to libvips v8.9.0-alpha2 prebuild
Improve tests for 32-bit ARM
2020-03-04 23:17:23 +00:00
Lovell Fuller
258c9e86eb Improve docs relating to single-channel raw pixel output 2020-03-01 14:22:49 +00:00
Joe Flateau
03dad5f2b2 Docs: clarify stats.isOpaque (#2094) 2020-02-24 20:35:36 +00:00
Lovell Fuller
86264e7733 Docs: update Lambda section to use Node.js 12 2020-02-24 20:31:17 +00:00
Lovell Fuller
95eae905f0 Ensure limitInputPixels, sequentialRead for composite op #2099 2020-02-24 16:02:36 +00:00
Lovell Fuller
a90dbcc808 Docs: improve a11y and metadata 2020-02-24 09:24:27 +00:00
Lovell Fuller
e9b21f2211 Ensure correct ordering of rotate-then-trim ops #2087 2020-02-23 16:59:22 +00:00
Lovell Fuller
409e5174bb Add support for 32-bit Windows (win32-ia32) 2020-02-19 23:00:23 +00:00
Lovell Fuller
8b3c0daab2 Upgrade to libvips v8.9.1-alpha1 prebuild 2020-02-19 11:41:18 +00:00
Lovell Fuller
c17807c995 Remove previously deprecated limitInputPixels, sequentialRead 2020-02-15 20:01:46 +00:00
Lovell Fuller
4abb4edf64 Migrate internals to N-API #1282 2020-02-15 19:38:15 +00:00
Lovell Fuller
d5ecc537af Release v0.24.1 2020-02-15 13:48:46 +00:00
Lovell Fuller
c7a49054fd Docs: update libvips URLs for band format/interpretation 2020-02-15 13:43:47 +00:00
Lovell Fuller
a2314c4aa0 Ensure RGBA LZW TIFF info.channel count #2064 2020-02-15 11:46:13 +00:00
Lovell Fuller
1717173f17 Tests: tighten composite offset thresholds 2020-02-15 10:53:30 +00:00
Lovell Fuller
e44c12f029 Bump dependencies, tar is now node>=10 2020-02-15 10:53:00 +00:00
Lovell Fuller
1a98c390fc Prevent sequentialRead for EXIF-based rotate op #2042 2020-01-20 21:50:43 +00:00
Lovell Fuller
91902740e4 Attempt to detect out-of-date homebrew-managed vips 2020-01-19 19:58:24 +00:00
Lovell Fuller
6aa6a93b44 Docs: add details of ignore-scripts to installation guide 2020-01-19 19:27:50 +00:00
Lovell Fuller
b4135ac9b3 Docs: fix any remaining redirects 2020-01-16 20:57:02 +00:00
Lovell Fuller
78906e6551 Update any remaining documentation links 2020-01-16 20:52:19 +00:00
Lovell Fuller
ba29ba1ab7 Release v0.24.0 2020-01-16 19:11:34 +00:00
Lovell Fuller
e0fa15f5cb Update performance test results 2020-01-16 18:56:25 +00:00
Lovell Fuller
82a1ff359d Update dev and bench dependencies 2020-01-15 21:17:25 +00:00
Lovell Fuller
18e1f10a94 Add support for input with 16-bit RGB profile #2037 2020-01-15 21:16:11 +00:00
Lovell Fuller
4828a17643 Tests: add fontconfig static data leak suppression 2020-01-15 21:08:45 +00:00
Lovell Fuller
3b4f95597a Prevent use of sequentialRead for rotate ops #2016 2020-01-14 08:34:54 +00:00
Lovell Fuller
bd52e93fca Deprecate limitInputPixels and sequentialRead, move to input options 2020-01-12 19:59:39 +00:00
Lovell Fuller
6fdc79d569 Docs: ARM64 has prebuilt libvips, not sharp 2020-01-12 10:50:14 +00:00
Lovell Fuller
7dbad7206e Drop support for undef input where opts also provided #1768 2020-01-11 12:02:31 +00:00
Lovell Fuller
a8a0c1e935 Update doc links contained within code
Throw rather than exit if require fails
2020-01-10 16:29:40 +00:00
Lovell Fuller
00bcf60566 Docs: metadata delay/loop properties 2020-01-09 23:04:06 +00:00
Lovell Fuller
4a745f2d2e Expose delay/loop metadata for animated images #1905 2020-01-09 22:51:08 +00:00
Lovell Fuller
057074b238 Docs: improve header layout on narrow screen devices 2020-01-08 23:13:33 +00:00
Lovell Fuller
4b8cc13a05 Add 2020 to list of copyright years 2020-01-08 21:20:53 +00:00
Lovell Fuller
a4e8586b59 Docs: rewrite installation guide 2020-01-08 21:04:16 +00:00
Lovell Fuller
095b37db6a Rename OS X as macOS
Bump CI to latest xcode available on maxOS 10.13
2020-01-08 21:03:50 +00:00
Lovell Fuller
efff650314 Upgrade to libvips v8.9.0 2020-01-08 16:18:43 +00:00
Lovell Fuller
0764ca8fde Changelog entry and credit for #2024 2020-01-08 16:04:42 +00:00
Brychan
403160434b Correctly bind max width and height values when using withoutEnlargement (#2024) 2020-01-07 12:10:35 +00:00
Lovell Fuller
730ab14faa Docs: correct SVG logo URL 2020-01-06 17:51:11 +00:00
Lovell Fuller
51d70c4574 Docs: switch hosting from Readthedocs to Firebase 2020-01-06 14:48:35 +00:00
Lovell Fuller
765ccd2aaa Docs: add icons 2020-01-06 14:44:07 +00:00
Lovell Fuller
5399332f81 Docs: improve section nesting levels 2020-01-06 14:26:54 +00:00
Lovell Fuller
43b7f9fc0e Docs: inject API section name as title 2020-01-04 11:11:27 +00:00
Sebin Benjamin
3925689435 Docs: initial setup for docute documentation site generator 2020-01-04 10:59:08 +00:00
Lovell Fuller
96a994a4c0 Move functions to improve logical ordering of docs 2020-01-03 20:58:57 +00:00
Lovell Fuller
84c20373ec Update leak test suppressions 2020-01-03 20:47:01 +00:00
Lovell Fuller
a216d2945b Upgrade to libvips v8.9.0-rc4, drop support for Node.js 8 2020-01-03 20:26:55 +00:00
Lovell Fuller
755a0caf3d Fix 16-bit, 2-channel PNG w/ ICC profile support #2013 2019-12-20 17:19:33 +00:00
Lovell Fuller
703d90e663 Docs: add example of using metadata and resize to scale 2019-12-16 21:15:49 +00:00
Lovell Fuller
e230ce41d7 Docs: remove references to unmaintained Docker images 2019-12-16 21:07:31 +00:00
Lovell Fuller
596b38a3bb Release v0.23.4 2019-12-05 10:00:29 +00:00
Lovell Fuller
d31a91a599 Expose raw TIFFTAG_PHOTOSHOP metadata #1600 2019-11-29 13:05:07 +00:00
Lovell Fuller
400ef71b6f Handle zero-length Buffers in Node.js v13.2.0+
These now use nullptr internally - see
https://github.com/nodejs/node/pull/30339
2019-11-28 21:20:32 +00:00
Lovell Fuller
bb15cd9067 Improve thread safety with copy-on-write for metadata #1986 2019-11-27 23:15:56 +00:00
Lovell Fuller
6ee6a226e1 Docs: add FreeBSD build status 2019-11-19 11:16:48 +00:00
Lovell Fuller
94d51a94c8 Docs: recommend jemalloc buildpack for Heroku 2019-11-19 11:13:25 +00:00
Lovell Fuller
d0feb4156c Release v0.23.3 2019-11-17 15:42:07 +00:00
Lovell Fuller
0d1278dade Improve C++ linting, move exceptions inline 2019-11-14 22:06:38 +00:00
Lovell Fuller
1b401b1195 Add FreeBSD to CI via Cirrus #1953 2019-11-14 21:55:20 +00:00
Lovell Fuller
11daa3b4d1 Tests: flatten to mid-grey before generating fingerprint 2019-11-14 13:18:14 +00:00
Lovell Fuller
88a3919ce0 Docs refresh 2019-11-14 11:32:49 +00:00
Lovell Fuller
c41b87303d Ensure trim op supports image-in-alpha #1597 2019-11-14 11:29:45 +00:00
Lovell Fuller
833aaead56 Ensure modulate can co-exist with other colour ops #1958 2019-11-11 22:16:28 +00:00
Lovell Fuller
ff98d2e44a Docs: using custom binaries with prebuild-install 2019-11-07 20:00:03 +00:00
Lovell Fuller
fcf05f608a Changelog entry for #1952 2019-11-07 19:59:18 +00:00
Pouya Eghbali
9baf38db44 Allow compilation of v0.23.x on FreeBSD and variants (#1952) 2019-11-06 23:11:44 +00:00
Lovell Fuller
69050ef1c8 Add funding link to libvips' Open Collective 2019-11-05 21:51:14 +00:00
Lovell Fuller
b35b9f7850 Ensure Travis does not cache builds #1948 2019-11-01 16:17:36 +00:00
Lovell Fuller
500ae97cac Changelog entry for #1921 2019-11-01 16:15:02 +00:00
Brendan Kennedy
d5b7040557 Ensure tile overlap option works as expected (#1921) 2019-10-30 20:02:07 +00:00
Lovell Fuller
ca52894651 Release v0.23.2 2019-10-28 18:21:11 +00:00
Lovell Fuller
4009acdd30 Docs: support added for Node.js 13 2019-10-28 18:19:33 +00:00
Lovell Fuller
147c93ecd3 Tests: increase coverage for jpeg-related logic 2019-10-27 20:03:10 +00:00
Lovell Fuller
8e04e4b07f Tests: add coverage for tiff quality option 2019-10-27 19:54:23 +00:00
Lovell Fuller
e7413ea1e5 Tests: increase coverage for metadata-related logic 2019-10-27 19:32:48 +00:00
Lovell Fuller
220bb03a32 Switch from excl npmignore to incl package.json files 2019-10-27 18:22:10 +00:00
Lovell Fuller
20f512fe5f Bump dependency patch versions 2019-10-26 23:15:41 +01:00
Lovell Fuller
efb3523eaa Remove duplicate validation from resize background 2019-10-26 23:13:12 +01:00
Lovell Fuller
2f2276e091 Changelog entries for #1924 #1932 2019-10-26 22:53:39 +01:00
Paul Neave
08a6597626 Add background option to tile output operation (#1924) 2019-10-25 14:30:33 +01:00
Nicolas Stepien
d82a6ee4fc Add Node.js 13 to CI (#1932) 2019-10-23 19:28:19 +01:00
Lovell Fuller
e627f6d68d Docs: clarify that input 'path' refers to the filesystem 2019-10-05 08:43:40 +01:00
Lovell Fuller
e650f58bd8 Improve error messaging for root/sudo permission problems 2019-10-04 12:14:08 +01:00
Lovell Fuller
5a9b6c8afd Tighten validation of page/pages constructor options 2019-10-03 16:41:32 +01:00
Lovell Fuller
075771d1e9 Improve error messaging for 404 errors on non-standard platforms 2019-10-03 15:32:15 +01:00
Lovell Fuller
4fcf091fef Bump tar dep (appears to be non-breaking despite major increment) 2019-10-03 15:30:56 +01:00
Marc Bornträger
0e66454fe4 Docs: Simplify Alpine Linux info, uses 3.10 instead of edge 2019-10-01 12:00:38 +01:00
Lovell Fuller
aa3ce760bb Release v0.23.1 2019-09-26 10:19:06 +01:00
Lovell Fuller
ba46ad1fd9 Docs: mention removal of metadata in output methods 2019-09-25 16:56:52 +01:00
Lovell Fuller
11214bab5d Bump tar dependency to ensure minipass >=2.8.6 2019-09-25 14:31:54 +01:00
Lovell Fuller
d87c289b4a Update results from latest benchmark test run 2019-09-25 14:16:51 +01:00
Lovell Fuller
af45c03b6f Bump benchmark dependencies ahead of a perf test run 2019-09-25 12:03:16 +01:00
Lovell Fuller
7d6fadce6b Ensure promise-based benchmark test is fairer
Real-world code will register a catch handler
2019-09-25 12:01:43 +01:00
Lovell Fuller
6b560f7a85 Remove imagemagick-native module from benchmarks
Unmaintained, does not compile with newer ImageMagick and Node
2019-09-25 10:13:45 +01:00
Lovell Fuller
5d4221460d Docs: Add more prominent link to libvips 2019-09-24 10:53:33 +01:00
Lovell Fuller
d42c383992 Add link from GitHub to libvips OpenCollective 2019-09-24 10:49:52 +01:00
Lovell Fuller
9c7f6fcb2b Replace deprecated URL parser
Fix up various linter errors
2019-09-22 22:46:48 +01:00
Lovell Fuller
14af0bda61 Regenerate flatten-rgb16-orange test expectation, reduce threshold 2019-09-21 20:01:15 +01:00
Raboliot le gris
fb5c393fbd Allow instance reuse with differing toBuffer options (#1860) 2019-09-08 14:35:16 +01:00
Lovell Fuller
69fe21a7ec Ensure invalid resize width/height as options throw #1817 2019-08-16 21:21:12 +01:00
Lovell Fuller
da4e05c118 Better validation and test coverage for background colours 2019-08-16 20:37:17 +01:00
Lovell Fuller
e4333ff6b0 Changelog entry, credit and doc update for #1835 2019-08-14 20:17:31 +01:00
Andargor
4ae8999f62 Add premultiplied option to composite operation (#1835) 2019-08-14 19:01:23 +01:00
Lovell Fuller
3fa91bb4ce Ensure image >= 3x3 before attempting trim operation
See https://github.com/libvips/libvips/issues/1392
2019-08-13 21:34:49 +01:00
Lovell Fuller
23b2e541ab Changelog entry for #1834 2019-08-12 21:45:29 +01:00
Julian Aubourg
5bfcf61a6f Allow use of heic/heif identifiers with toFormat (#1834) 2019-08-12 21:36:56 +01:00
Lovell Fuller
0778c112a9 Ensure sharp.format.vips is present and correct #1813 2019-08-12 21:25:10 +01:00
Lovell Fuller
2c300754a7 Expand issue templates to direct towards installation 2019-07-31 16:48:32 +01:00
Lovell Fuller
c610e306df Release v0.23.0 2019-07-29 14:45:46 +01:00
Lovell Fuller
417cca6e0d Use libvips built-in ICC profiles when required #1619 2019-07-29 14:16:21 +01:00
Lovell Fuller
2ed4b5ae83 Provide guidance on (mis)use of sudo with npm install 2019-07-29 11:48:18 +01:00
Lovell Fuller
16e429cf2c Add a few new leak test suppressions 2019-07-29 11:34:20 +01:00
Lovell Fuller
6b7ce8a605 Force V8 GC after each test during leak checks 2019-07-29 11:33:45 +01:00
Lovell Fuller
ba4ce75377 Ensure all WebP tests wait until Promises resolve 2019-07-29 11:32:31 +01:00
Lovell Fuller
76ded7fd28 Changelog entry and credit for #1755 2019-07-28 17:39:30 +01:00
Lovell Fuller
a0d1a7be50 Upgrade to libvips v8.8.1 2019-07-28 10:51:09 +01:00
Ilya Ovdin
690bc43abe Fix rotate/extract ordering for non-90 angles (#1755) 2019-07-26 20:28:45 +01:00
Lovell Fuller
50b461024d Add test coverage for single value extend operation 2019-07-26 19:31:14 +01:00
Lovell Fuller
6cf0b3240d Simplify 'this' in IO pipeline using arrow functions 2019-07-26 19:19:21 +01:00
Lovell Fuller
233b015d77 Improve consistency of validation error handling
Utilises common path of existing invalidParameterError
2019-07-26 14:58:54 +01:00
Lovell Fuller
28de243c11 Bump dependencies 2019-07-14 23:01:34 +01:00
Lovell Fuller
36e8a3da88 Expose libwebp smartSubsample and reductionEffort #1545 2019-07-14 22:52:38 +01:00
Lovell Fuller
119d16cad3 Ignore test coverage on more esoteric code paths 2019-07-14 21:52:29 +01:00
Lovell Fuller
38402d3185 Changelog entry for #1687 2019-07-12 12:15:24 +01:00
RaboliotTheGrey
6c02949fc1 Add skipBlanks support for tile layout (#1687) 2019-07-12 12:02:51 +01:00
Lovell Fuller
b737d4601e Add experimental support for HEIF images #1105
Requires a custom, globally-installed libvips compiled with libheif
2019-07-04 13:21:32 +01:00
jwater7
3ff3353550 Docs: vips is now available via the Alpine community repo (#1769) 2019-07-01 12:08:11 +01:00
Lovell Fuller
946d3c81a5 Add experimental support for Worker Threads #1558 2019-06-26 21:15:04 +01:00
Lovell Fuller
628996846d Allow use of failOnError with Stream-based input #1691 2019-06-26 19:37:27 +01:00
Lovell Fuller
631a3597c7 Upgrade to libvips v8.8.0, remove deprecated overlayWith 2019-06-26 18:32:53 +01:00
Marc Bornträger
cfa4f7d45c Docs: Alpine now provides vips package via community repo (#1730) 2019-05-30 09:02:32 +01:00
Lovell Fuller
05d76eeadf Release v0.22.1 2019-04-25 12:14:45 +01:00
Lovell Fuller
28a6c53da0 Prevent issue templates being published to npm 2019-04-25 12:14:17 +01:00
Lovell Fuller
6fcd2153c5 Docs: add support for Node 12 #1668 2019-04-25 12:01:14 +01:00
Lovell Fuller
7ae0512b9b Docs: clarify some output features require custom libvips 2019-04-25 11:39:30 +01:00
Nicolas Stepien
0890b59c32 Add Node 12 to Windows CI (#1669) 2019-04-25 11:24:35 +01:00
Lovell Fuller
df3ce450d9 Bump all dependencies 2019-04-25 09:51:59 +01:00
Lovell Fuller
bb0257b318 Remove not-yet-available Node 12 from Windows CI 2019-04-25 09:51:03 +01:00
Lovell Fuller
9c3597670d Add Node 12 to CI 2019-04-25 08:42:13 +01:00
Lovell Fuller
aa9b328778 Remove use of deprecated V8 API, swap v8::Handle for v8::Local #1668 2019-04-23 20:16:31 +01:00
tyankatsu
159e8dace2 docs: Fix path to CONTRIBUTING.md (#1666) 2019-04-20 19:19:14 +01:00
Lovell Fuller
3be4d5bb45 Ensure limitInputPixels check uses 64-bit unsigned type 2019-04-20 17:51:19 +01:00
Lovell Fuller
af7caa7b25 Docs: modernise contributing guide 2019-04-02 22:33:24 +01:00
Lovell Fuller
b4ede75522 Add issue templates (#1639) 2019-04-02 22:32:06 +01:00
Lovell Fuller
9d98114074 Move contributing info to GitHub-specific subdirectory,
which then allows for future possible issue/PR templates etc.
2019-04-02 21:03:57 +01:00
Lovell Fuller
4ac51899c3 Docs: minimum supported version of Node.js is 6 2019-04-02 20:41:23 +01:00
Lovell Fuller
90a0382317 Tests: use a concurrency of 1 on musl-based Linux
Should reduce a bit of pressure on the stack
2019-04-02 20:40:36 +01:00
Lovell Fuller
687795c801 Enhancement to and changelog entry for #1638
Remove bindings dependency as this isn't really... required
2019-04-02 20:39:06 +01:00
Sidhartha Chatterjee
2e0fbbb942 Add warning if sharp bindings aren't built correctly (#1638) 2019-04-02 17:00:55 +01:00
Lovell Fuller
0a3512d066 Unpin prebuild as 8.2.x is no longer breaking 2019-04-01 20:51:45 +01:00
Lovell Fuller
6032171f91 Docs: clarify use of integral pixel values for extract 2019-04-01 20:06:46 +01:00
Lovell Fuller
fc178de309 Changelog entry for #1601 2019-03-25 08:32:20 +00:00
Jakub Michálek
3f4398457f Change docs for composite to reflect how create works (#1623) 2019-03-25 07:45:46 +00:00
Jakub Michálek
b494b2e872 Add brightness, saturation and hue modulation #609 (#1601) 2019-03-25 07:44:07 +00:00
Lovell Fuller
18afcf5f90 Release v0.22.0 2019-03-18 23:26:39 +00:00
Lovell Fuller
87a422942d Pin prebuild due to breaking change in 8.2.0 2019-03-18 23:10:33 +00:00
Lovell Fuller
ac515121e5 Release v0.22.0 2019-03-18 21:31:46 +00:00
Lovell Fuller
2bfea0ad76 Docs: refresh usage examples 2019-03-18 21:29:17 +00:00
Lovell Fuller
83cdb558f6 Allow Stream-based input of raw pixel data #1579 2019-03-18 20:15:18 +00:00
Lovell Fuller
9ee377963e Improve error message if libvips tarball is corrupt 2019-03-17 23:07:58 +00:00
Lovell Fuller
9cc06c887b Add support for pages option for multi-page input #1566 2019-03-17 16:37:27 +00:00
Lovell Fuller
7457b50373 Remove unused shared library 2019-03-15 15:58:25 +00:00
Lovell Fuller
6387fb79b1 Small improvements to input and install docs, bump deps 2019-03-15 15:48:55 +00:00
Lovell Fuller
54e5514b9a Bump dependencies to latest 2019-03-10 18:14:43 +00:00
Lovell Fuller
1e4597c284 Changelog entry for #1595 (plus add GIF) 2019-03-10 17:26:26 +00:00
Lovell Fuller
7cafd4386c Add composite op, supporting multiple images and blend modes #728 2019-03-09 22:46:23 +00:00
Lovell Fuller
e3549ba28c Remove functions previously deprecated in v0.21.0
background, crop, embed, ignoreAspectRatio, max, min, withoutEnlargement
2019-03-01 23:43:35 +00:00
Lovell Fuller
d1bbe62e52 Rename armv8 as arm64v8 to match Node's process.arch 2019-03-01 23:43:35 +00:00
Lovell Fuller
36af74a09b Upgrade to libvips v8.7.4 2019-03-01 23:43:35 +00:00
Fabrizio Ruggeri
5afe02be60 Allow page input option to be set for PDF (#1595) 2019-03-01 23:29:34 +00:00
Jack Cross
2262959673 Docs: add missing comma to extend example (#1588) 2019-02-27 11:03:02 +00:00
Lovell Fuller
ba3f914445 Document support for animated WebP in metadata pages 2019-01-27 21:01:49 +00:00
Lovell Fuller
770be35c44 Tests: add a couple of extra leak suppressions for Node 2019-01-27 20:48:17 +00:00
Lovell Fuller
cc9f2b90fd Docs: use absolute URL for logo 2019-01-19 15:15:22 +00:00
Lovell Fuller
4aff57b071 Release v0.21.3 2019-01-19 14:25:37 +00:00
Maxime BACONNAIS
1df8d82fe0 Docs: overlay parameter of overlayWith is optional (#1547) 2019-01-19 14:19:41 +00:00
Lovell Fuller
98e90784f4 Docs: overlay parameter of overlayWith is optional 2019-01-19 14:11:54 +00:00
Lovell Fuller
87ea54cc66 Bump devDependencies 2019-01-19 14:06:16 +00:00
Lovell Fuller
d5e98bc8ad Split file-based input errors into missing vs invalid #1542 2019-01-19 11:59:36 +00:00
Lovell Fuller
fa69ff773a Input image decoding fail fast by default 2019-01-18 19:25:55 +00:00
Lovell Fuller
a183bb1cac Add valgrind memory leak suppressions 2019-01-18 12:08:28 +00:00
Lovell Fuller
cf62372cab Install: log the fallback to build from source
https://github.com/lovell/sharp-libvips/issues/18
2019-01-14 19:33:01 +00:00
Lovell Fuller
56fa9c95a1 Release v0.21.2 2019-01-13 10:26:47 +00:00
Lovell Fuller
32a34a8841 Tests: separate IO suite into per-format unit files 2019-01-13 10:11:32 +00:00
Lovell Fuller
98797445de Expose PNG output options requiring libimagequant #1484 2019-01-13 09:06:05 +00:00
Lovell Fuller
bd377438b6 Ignore colour profiles in LAB images as they are already LAB 2019-01-12 18:13:43 +00:00
Lovell Fuller
9dd6510de6 Expose underlying error message for invalid input #1505 2019-01-12 16:10:25 +00:00
Lovell Fuller
93ad9d4a4a Ensure all metadata removed from PNG unless withMetadata used 2019-01-09 21:17:53 +00:00
Lovell Fuller
4c01a099ea Add ensureAlpha op, adds alpha channel if missing #1153 2019-01-05 21:12:33 +00:00
Lovell Fuller
8e70579e47 Docs: use HTTPS links where available 2019-01-04 21:25:21 +00:00
Lovell Fuller
ee8bfa3980 Add 2019 to list of years of copyright 2019-01-04 16:05:26 +00:00
Lovell Fuller
c5dfa49cae Docs: add sharp logo to readme 2019-01-04 15:56:22 +00:00
Lovell Fuller
0822404129 Docs: expand logo viewBox to prevent clipping 2019-01-04 15:54:33 +00:00
Lovell Fuller
144f39cd45 Docs: add sharp logo, CC BY-SA 4.0 2019-01-04 15:12:53 +00:00
Lovell Fuller
87f191fd05 Node 11 now supported by nodejs/nan 2019-01-03 14:01:18 +00:00
Lovell Fuller
37ed436202 Version bump of devDependencies 2019-01-03 13:26:56 +00:00
Lovell Fuller
88e490356d Test: remove stray console.log 2019-01-03 12:35:54 +00:00
Lovell Fuller
7c631c0787 Ensure shortest resized edge is >= 1px #1003 2019-01-03 12:01:55 +00:00
Lovell Fuller
f5d3721fe0 Doc refresh for #1205 2019-01-02 19:04:49 +00:00
Lovell Fuller
cc633589d9 Expose pages metadata for multi-page input images #1205 2019-01-01 22:10:27 +00:00
Lovell Fuller
cc1d4c1a6d Expose palette-bit-depth metadata, requires upcoming libvips v8.8.0 2019-01-01 21:02:00 +00:00
Lovell Fuller
30ca424942 Apply correct forced output when chaining #1528 2019-01-01 18:40:09 +00:00
Pascal Temel
813831acf0 Docs: Update deprecated overlayWith example (#1526) 2018-12-30 20:44:36 +00:00
Lovell Fuller
a54fe9f77c Prevent mutatation of jpeg options #1516 2018-12-21 19:54:33 +00:00
Amila Welihinda
8c6da5548a Docs: change repo badge to SVG (#1498) 2018-12-10 11:03:03 +01:00
Lovell Fuller
a2aa7d69e7 Add error handler to download stream lovell/sharp-libvips#14 2018-12-08 13:56:05 +00:00
Lovell Fuller
34d5252242 Release v0.21.1 2018-12-07 19:23:54 +00:00
Lovell Fuller
f31e4d2869 Changelog, credit and doc refresh for #1483 2018-12-06 21:58:14 +00:00
Michael B. Klein
c695c40abc Expose libvips pyramid/tile options for TIFF output (#1483) 2018-12-06 22:33:46 +01:00
Lovell Fuller
fd1ca1dbb2 Ensure the tests for #1477 pass on OS X 2018-12-04 23:58:02 +01:00
Lovell Fuller
f25dbd5f61 Ensure the tests for #1477 pass on OS X 2018-12-04 23:45:08 +01:00
Keith
541e7104fd Expose libvips recombination matrix operation #1477 2018-12-04 23:06:34 +01:00
Lovell Fuller
94945cf6ac Changelog and credit for #1475 2018-12-04 07:49:41 +00:00
Lovell Fuller
db76e655f8 Ensure licensing checker works on Windows 2018-11-29 10:15:13 +00:00
Lovell Fuller
d43c7b581d Add licensing checker for production dependencies 2018-11-29 09:53:00 +00:00
Julian Aubourg
383b933e26 Build prototype with Object.assign to allow minification (#1475) 2018-11-26 19:40:06 +01:00
Lovell Fuller
d26ccf6294 Docs: update Alpine repository URLs 2018-11-21 22:42:58 +00:00
Lovell Fuller
6f9699f605 Ensure correct channel info for raw, greyscale output #1425 2018-11-19 20:00:30 +00:00
Quinn Pan
1e9093d781 Docs: correct code example in extend operation 2018-11-19 20:35:16 +01:00
Lovell Fuller
9dc6492e52 Docs: correct code example in extend operation 2018-11-19 19:34:16 +00:00
Lovell Fuller
d22f7cae6a Silence cast-function-type warnings from GCC 8+ 2018-11-19 18:47:35 +00:00
Lovell Fuller
473afaab45 Install: detect missing libvips on OpenBSD and SunOS
See https://github.com/lovell/sharp-libvips/issues/12
2018-11-19 18:46:05 +00:00
Lovell Fuller
dcd68303a4 Docs: add installation details for Lambda without Docker 2018-11-16 10:28:34 +00:00
Lovell Fuller
03394556b5 Update semistandard linter to latest 2018-11-11 18:05:40 +00:00
Lovell Fuller
1c4f6f75f3 Add Node 11 to CI, experimental only, no prebuild
Hide deprecation warnings - see nodejs/nan#811
2018-11-11 17:55:35 +00:00
Lovell Fuller
f00928dedb Doc refresh for #1438 #1439 2018-11-11 17:40:19 +00:00
Daiz
a48f8fbb61 Allow separate parameters for gamma encoding and decoding (#1439) 2018-11-11 10:15:38 +01:00
Daiz
1fa388370e Add support for the "mitchell" kernel for image reductions (#1438) 2018-10-28 15:11:27 +00:00
Christoph Tavan
95ef6b3f71 Docs: update Alpine libvips installation instructions (#1429)
With version 8.7.0 of the vips-dev Alpine package the documented installation instructions no longer work and result in:

ERROR: unsatisfiable constraints:
  pc:fftw3 (missing):
    required by: vips-dev-8.7.0-r0[pc:fftw3] vips-dev-8.7.0-r0[pc:fftw3] vips-dev-8.7.0-r0[pc:fftw3]

The fix was proposed in https://bugs.alpinelinux.org/issues/9561#note-2
2018-10-24 14:31:32 +01:00
Lovell Fuller
de11d36d00 Minor version bumps 2018-10-22 19:38:06 +01:00
Lovell Fuller
d77c2adabe Changelog and docs for #1422 2018-10-22 19:17:42 +01:00
SethWen
c89c055ae0 Install: add support for npm_config_sharp_dist_base_url (#1422) 2018-10-22 18:54:47 +01:00
Lovell Fuller
dac8117f32 Docs: ensure options are included for flatten op 2018-10-08 19:56:30 +01:00
Waylon Walker
937b091bab Docs: clear _libvips cache on Windows (#1403) 2018-10-07 16:49:05 +01:00
Lovell Fuller
019e6a1bfe Release v0.21.0 2018-10-04 13:03:30 +01:00
Lovell Fuller
1565e58fcf Update benchmark results ahead of v0.21.0
Remove lwip and images as they lack Node 10 support
2018-10-04 12:41:01 +01:00
Lovell Fuller
c22e2a17ef Update benchmark dependencies 2018-10-04 11:01:09 +01:00
Lovell Fuller
fd2a10ccea Threshold trim tests for non-turbo libjpeg 2018-10-02 20:33:26 +01:00
Lovell Fuller
0725378257 Add trimOffsetLeft, trimOffsetTop to trim response #914 2018-10-02 20:16:00 +01:00
Lovell Fuller
c431909f35 Refresh resize docs to ensure options are present 2018-10-02 18:45:08 +01:00
Lovell Fuller
db4df6f0b2 Add size to metadata response (Stream/Buffer only) #695 2018-10-02 18:05:08 +01:00
Lovell Fuller
17f942c802 Add chromaSubsampling and isProgressive to metadata #1186 2018-10-02 17:11:25 +01:00
Lovell Fuller
60438ebfe5 Ensure precision of trim threshold, update docs #914 2018-10-02 12:45:37 +01:00
Lovell Fuller
21fbe546b8 Switch from custom trim op to vips_find_trim #914 2018-10-02 11:24:32 +01:00
Lovell Fuller
11900945eb Bump dependency versions to latest 2018-10-02 11:23:49 +01:00
Lovell Fuller
ea5270221b Add new leak suppression for nodejs/libuv 2018-10-02 11:23:15 +01:00
Lovell Fuller
a64844689e Deprecate background, add op-specific prop to resize/extend/flatten #1392 2018-10-01 20:58:55 +01:00
Lovell Fuller
6007e13a22 Improve/increase installation error handling 2018-10-01 11:06:12 +01:00
Lovell Fuller
c3274e480b Deprecate crop, embed, ignoreAspectRatio, max, min, withoutEnlargement.
These become options of the resize operation instead. #1135
2018-09-30 20:16:27 +01:00
Miguel Aragón
3c54eeda5b Use more universal English to improve global understanding 2018-09-28 16:04:56 +01:00
Lovell Fuller
6236e4b97d Changelog entry and credit for #1385 2018-09-27 21:01:41 +01:00
freezy
796738da65 Add support for arbitrary rotation angle via vips_rotate (#1385) 2018-09-27 18:00:36 +01:00
freezy
37d385fafa Move background extraction into separate method (#1383) 2018-09-24 10:00:00 +01:00
Lovell Fuller
db2af42ee7 File extend, extract and trim ops under 'resize' #1135
Should make them easier to find in the docs
2018-09-22 14:52:08 +01:00
Lovell Fuller
24b42ef192 Tests: Move all setup to named file 2018-09-22 13:54:20 +01:00
Lovell Fuller
2ce166ab0a Update links to libvips, now in its own GitHub org 2018-09-21 20:33:01 +01:00
Lovell Fuller
71755b69e4 Remove duplicate libvips version/platform check 2018-09-21 20:20:40 +01:00
Lovell Fuller
1106aac2d8 Tests: tweak colour thresholds for (non-turbo) libjpeg compat 2018-09-21 19:51:47 +01:00
Lovell Fuller
93aac660a3 Tests: avoid shrink-on-load for (non-turbo) libjpeg compat 2018-09-21 19:34:52 +01:00
Lovell Fuller
0ce8ad7130 Enable CI on OS X 2018-09-20 10:41:24 +01:00
Lovell Fuller
deacd553bf Enable SIMD convolution by default #1213 2018-09-19 21:42:40 +01:00
Lovell Fuller
c8ff7e11a9 Upgrade to libvips v8.7.0
Drop Node 4 support
Add experimental musl prebuild for Node 8 and 10
2018-09-19 21:38:09 +01:00
Lovell Fuller
4cff62258c Improve smartcrop saliency testing/reporting 2018-09-05 22:49:31 +01:00
Lovell Fuller
0144358afb Release v0.20.8 2018-09-05 08:44:01 +01:00
Lovell Fuller
136097efe7 Downgrade nyc for continued Node 4 support 2018-09-04 17:07:10 +01:00
Lovell Fuller
374c6959d7 Changelog and credit for #1358 #1362 2018-09-04 16:39:24 +01:00
Axel Eirola
7d48a5ccf4 Allow floating point density input (#1362)
Metadata output will still remain integer
2018-09-01 08:58:30 +01:00
ajhool
bf3254cb16 Install: avoid race conditions when creating directories (#1358) 2018-08-29 09:20:26 +01:00
Lovell Fuller
5bed3a7d52 Release v0.20.7 2018-08-21 11:50:14 +01:00
Lovell Fuller
ece111280b Use copy+unlink if rename fails during install #1345 2018-08-20 15:14:31 +01:00
Lovell Fuller
a15a9b956b Release v0.20.6 2018-08-20 11:40:10 +01:00
Lovell Fuller
42860c2f83 Changelog, credit and doc refresh for #1342 2018-08-19 10:43:25 +01:00
Alun Davies
b5b95e5ae1 Expose depth option for tile-based output (#1342) 2018-08-18 15:09:53 +01:00
Lovell Fuller
d705cffdd6 Ensure extractChannel works with 16-bit images #1330 2018-08-12 20:22:39 +01:00
Rodrigo Alviani
23a4bc103e Docs: correct quality option in overlayWith example (#1325) 2018-08-08 08:42:18 +01:00
Lovell Fuller
c14434f9e7 Add removeAlpha op, removes alpha channel if any #1248 2018-08-07 20:32:11 +01:00
Lovell Fuller
25bd2cea3e Add experimental entropy field to stats response 2018-08-06 15:41:27 +01:00
Lovell Fuller
532de4ecab Cache libvips binaries to reduce re-install time #1301 2018-08-05 10:31:41 +01:00
Lovell Fuller
bfdd27eeef Doc refresh and dependency bumps 2018-08-05 09:42:09 +01:00
Lovell Fuller
bd9f238ab4 Improve install time error messages for FreeBSD #1310 2018-08-04 22:27:32 +01:00
Lovell Fuller
75556bb57c Ensure vendor platform mismatch throws error #1303 2018-08-04 21:34:11 +01:00
thegareth
2de062a34a Docs: update the "make a transparent image" example (#1316)
Alpha for colour is between 0-1, not 0-255.
2018-08-02 09:42:25 +01:00
Lovell Fuller
4589b15dea Changelog and credit for #1285 #1290 2018-07-10 16:12:16 +01:00
Sylvain Dumont
8b75ce6786 Allow full WebP alphaQuality range of 0-100 (#1290) 2018-07-10 15:58:17 +01:00
Espen Hovlandsdal
7bbc5176a1 Expose mozjpeg quant_table flag (#1285) 2018-07-10 15:56:05 +01:00
Lovell Fuller
5cb35485f1 Release v0.20.5 2018-06-27 08:44:31 +01:00
Lovell Fuller
80189ed689 Add changelog and credit for #1265 2018-06-27 07:54:41 +01:00
Muhammad Faheem Akhtar
3d7e8ef432 Docs: update new Buffer() to Buffer.from() (#1273) 2018-06-26 08:19:27 +01:00
Lovell Fuller
1999c7103c Docs: add CSS to improve left-hand nav nesting 2018-06-25 19:58:50 +01:00
Thomas Vantuycom
9c20ae383e Remove top of file table of contents in documentation (#1270) 2018-06-24 21:45:51 +01:00
Tom Lokhorst
76c41eaf05 Expose libjpeg optimize_coding flag (#1265) 2018-06-21 18:12:10 +01:00
Lovell Fuller
873aa6700f Release v0.20.4 2018-06-20 08:26:28 +01:00
Lovell Fuller
0d9590a9a0 Documentation refresh 2018-06-20 08:14:03 +01:00
Lovell Fuller
94607b585a Ensure extractChannel sets bw colourspace interp #1257 2018-06-19 22:47:52 +01:00
Lovell Fuller
da0b0348a2 Prevent rounding err with shrink-on-load and 90/270 rot #1241 2018-06-19 21:19:34 +01:00
Lovell Fuller
09263455b5 Release v0.20.3 2018-05-29 08:38:42 +01:00
Lovell Fuller
ddc23493d4 Add warning about possible concurrent tile race #1151 2018-05-25 19:53:23 +01:00
Lovell Fuller
54a71fc142 Fix tint op by ensuring LAB and allowing negative values #1235
Add test cases for more tint colours and input interpretations
2018-05-23 20:51:47 +01:00
Lovell Fuller
b1a9bf10a2 Pre-built binaries provided for even-numbered major versions 2018-05-21 11:44:34 +01:00
Lovell Fuller
97cfbe1b63 Bump dependency versions 2018-05-19 15:19:23 +01:00
Lovell Fuller
0ee8c63551 Replace use of libvips API deprecated since v8.6.1
See jcupitt/libvips@b085908
2018-05-19 15:03:50 +01:00
Lovell Fuller
0ac5a9ad82 Promote nw.js details to main installation docs - see 6e51f2d 2018-05-19 14:53:22 +01:00
Michael
6e51f2d608 rebuild doc for NW.js (#1229)
refers to https://github.com/lovell/sharp/issues/1223
2018-05-15 07:28:09 +01:00
Lovell Fuller
f23a8dc9dc Release v0.20.2 2018-04-28 18:29:32 +01:00
Lovell Fuller
d09fe6178c Add support for Node 10, drop support for Node 9 2018-04-28 18:12:10 +01:00
Lovell Fuller
ae2cfcc4f3 Version bumps, hold simple-get at v2 for Node 4 support 2018-04-28 18:11:20 +01:00
Lovell Fuller
853cc65e32 Changelog entry for #1208 2018-04-28 14:06:45 +01:00
Nathan Graves
5d140d949f Add support for Group4 (CCITTFAX4) compression to TIFF output (#1208) 2018-04-26 19:49:08 +01:00
Lovell Fuller
6d2da2b3ba Switch unzip test dependency for Node 10 support 2018-04-25 20:37:10 +01:00
Lovell Fuller
165e337e44 Changelog entry and doc refresh for #1204 2018-04-25 10:19:33 +01:00
Nathan Graves
b154cd0418 Add support for page selection with multi-page TIFF input (#1204) 2018-04-24 22:57:27 +01:00
Lovell Fuller
8ef1532691 Dependency version bumps 2018-04-17 20:46:22 +01:00
Lovell Fuller
771e44f2a7 Add error highlighting lack of Windows x86 node.exe support 2018-04-17 20:37:19 +01:00
Lovell Fuller
8933f1128d Changelog entry and credit for #1165 2018-04-11 20:30:16 +01:00
Rik Heywood
dbac4b9a63 Add tint operation to set image chroma 2018-04-11 20:05:48 +01:00
Lovell Fuller
bdac5b5807 Add code usage examples for output-related functions 2018-04-06 19:52:00 +01:00
Lovell Fuller
b0961b5213 Bump dependencies 2018-04-06 19:49:17 +01:00
Thomas Parisot
0d7c3fc4d8 Ignore global libvips during install via SHARP_IGNORE_GLOBAL_LIBVIPS (#1165) 2018-03-22 18:48:30 +00:00
Lovell Fuller
8dac256096 Release v0.20.1 2018-03-17 14:04:35 +00:00
Lovell Fuller
8320da39c3 Changelog entry and doc refresh for #1161 2018-03-17 11:12:43 +00:00
Andrea Bianco
875937e3d8 Expose libvips' median filter operation (#1161) 2018-03-17 10:52:44 +00:00
Lovell Fuller
f880adbaac Bump dependencies ahead of v0.20.1 2018-03-16 20:46:08 +00:00
Lovell Fuller
48c5f86adb Improve install when global libvips below min version #1148 2018-03-13 20:37:42 +00:00
Lovell Fuller
f60f7dab12 Prevent error when cumulative rounding below target #1154 2018-03-13 19:42:10 +00:00
Lovell Fuller
8f690236ed Release v0.20.0 2018-03-04 21:54:28 +00:00
Lovell Fuller
430a4297aa Add support for prebuilt sharp binaries on common platforms 2018-03-04 14:33:44 +00:00
Oskar Thornblad
69e3ab02c4 Mention EXIF orientation in metadata's jsdocs (#1142) 2018-03-04 14:19:53 +00:00
Lovell Fuller
b87a20b881 Release v0.19.1 2018-02-24 10:02:12 +00:00
Lovell Fuller
0e6f2d16ab Bump dependencies 2018-02-23 19:27:34 +00:00
Lovell Fuller
498b061819 Upgrade nan dependency, enables async hooks 2018-02-23 19:18:31 +00:00
Lovell Fuller
5ab6f599fb Prevent crop when at or below target dimensions #1134 2018-02-23 10:31:11 +00:00
Lovell Fuller
8b80626035 Changelog, credit and doc refresh for #1121 2018-02-18 19:14:37 +00:00
Andrea Bianco
f86ae79fdb Expose angle option in tile feature (#1121) 2018-02-18 20:00:08 +01:00
Lovell Fuller
1a4e68096f Changelog and credit for #1024, bump dependencies 2018-02-04 13:30:47 +00:00
Marcel
d599d1f29e Expose linear transform feature of libvips (#1024) 2018-02-04 10:36:04 +00:00
Lovell Fuller
73edfb3d2c Update list of files that npm can ignore 2018-01-13 10:28:31 +00:00
Lovell Fuller
ebae68d579 Doc refresh for #1077 2018-01-13 10:27:43 +00:00
Tony Mobily
573836e2b8 Add mention of max() to preserve ratio in withoutEnlargement (#1077) 2018-01-13 09:59:59 +00:00
217 changed files with 15152 additions and 9075 deletions

14
.cirrus.yml Normal file
View File

@@ -0,0 +1,14 @@
freebsd_instance:
image_family: freebsd-13-0-snap
task:
env:
IGNORE_OSVERSION: yes
prerequisites_script:
- pkg update -f
- pkg upgrade -y
- pkg install -y pkgconf vips node npm
install_script:
- npm install --unsafe-perm
test_script:
- npm test

View File

@@ -12,9 +12,12 @@ New bugs are assigned a `triage` label whilst under investigation.
## Submit a new feature request
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists, it's probably fastest to add a comment to it about your requirement.
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists,
it's probably fastest to add a comment to it about your requirement.
Implementation is usually straightforward if _libvips_ [already supports](https://jcupitt.github.io/libvips/API/current/) the feature you need.
Implementation is usually straightforward if libvips
[already supports](https://libvips.github.io/libvips/API/current/func-list.html)
the feature you need.
## Submit a Pull Request to fix a bug
@@ -41,18 +44,17 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch |
| ------: | :--------- |
| v0.19.0 | suit |
| v0.20.0 | teeth |
| v0.26.0 | zoom |
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
### Add a new public method
The API tries to be as fluent as possible. Image processing concepts follow the naming conventions from _libvips_ and, to a lesser extent, _ImageMagick_.
The API tries to be as fluent as possible.
Image processing concepts follow the naming conventions from libvips and, to a lesser extent, ImageMagick.
Most methods have optional parameters and assume sensible defaults. Methods with mandatory parameters often have names like `doSomethingWith(X)`.
Please ensure backwards compatibility where possible. Methods to modify previously default behaviour often have names like `withoutOptionY()` or `withExtraZ()`.
Most methods have optional parameters and assume sensible defaults.
Please ensure backwards compatibility where possible.
Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to gather feedback on a potential API change.
@@ -60,7 +62,7 @@ Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to
A method to be removed should be deprecated in the next major version then removed in the following major version.
By way of example, the [bilinearInterpolation method](https://github.com/lovell/sharp/blob/v0.6.0/index.js#L155) present in v0.5.0 was deprecated in v0.6.0 and removed in v0.7.0.
By way of example, the `background()` method present in v0.20.0 was deprecated in v0.21.0 and removed in v0.22.0.
## Documentation
@@ -68,7 +70,7 @@ The public API is documented with [JSDoc](http://usejsdoc.org/) annotated commen
These can be converted to Markdown by running:
```sh
npm run docs
npm run docs-build
```
Please include documentation updates in any Pull Request that modifies the public API.
@@ -89,20 +91,11 @@ Requires [Valgrind](http://valgrind.org/).
npm run test-leak
```
### Packaging tests
Tests the installation on a number of Linux-based operating systems.
Requires docker.
```sh
npm run test-packaging
```
## Finally
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#L4)
[e-mail](https://github.com/lovell/sharp/blob/master/package.json#L5)
for private, paid consulting.

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
open_collective: libvips

View File

@@ -0,0 +1,16 @@
---
name: Feature request
about: Suggest an idea
labels: enhancement
---
What are you trying to achieve?
Have you searched for similar feature requests?
What would you expect the API to look like?
What alternatives have you considered?
Is there a sample image that helps explain?

20
.github/ISSUE_TEMPLATE/installation.md vendored Normal file
View File

@@ -0,0 +1,20 @@
---
name: Installation
about: Something went wrong during either 'npm install sharp' or 'require("sharp")'
labels: installation
---
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)?
Have you ensured the platform and version of Node.js used for `npm install` is the same as the platform and version of Node.js used at runtime?
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`?
If you are installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
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 output of running `npx envinfo --binaries --system`?

20
.github/ISSUE_TEMPLATE/possible-bug.md vendored Normal file
View File

@@ -0,0 +1,20 @@
---
name: Possible bug
about: Installation of sharp was successful but then something unexpected occurred using one of its features
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`?
What are the steps to reproduce?
What is the expected behaviour?
Are you able to provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem?
Are you able to provide a sample image that helps explain the problem?
What is the output of running `npx envinfo --binaries --system`?

16
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,16 @@
---
name: Question
about: For help understanding an existing feature
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?
Have you searched for similar questions?
Are you able to provide a minimal, standalone code sample that demonstrates this question?
Are you able to provide a sample image that helps explain the question?

4
.gitignore vendored
View File

@@ -12,3 +12,7 @@ vendor
.gitattributes
.DS_Store
.nyc_output
.vscode/
package-lock.json
.idea
.firebase

View File

@@ -1,14 +0,0 @@
build
node_modules
coverage
.editorconfig
.gitignore
test
.travis.yml
appveyor.yml
circle.yml
mkdocs.yml
vendor
packaging
preinstall.sh
.nyc_output

4
.prebuildrc Normal file
View File

@@ -0,0 +1,4 @@
{
"include-regex": "(sharp\\.node|libvips-cpp\\.dll)",
"strip": true
}

View File

@@ -1,34 +1,178 @@
language: node_js
matrix:
jobs:
include:
- os: linux
dist: trusty
sudo: false
node_js: "4"
- os: linux
dist: trusty
sudo: false
node_js: "6"
- os: linux
dist: trusty
sudo: false
node_js: "8"
- os: linux
dist: trusty
sudo: false
node_js: "9"
- os: osx
osx_image: xcode8.3
node_js: "4"
- os: osx
osx_image: xcode8.3
node_js: "6"
- os: osx
osx_image: xcode8.3
node_js: "8"
- os: osx
osx_image: xcode8.3
node_js: "9"
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
- name: "Linux x64 (CentOS 7, glibc 2.17) - Node.js 10"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_10.x | bash -"
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
install: sudo docker exec sharp bash -c "npm install --unsafe-perm"
script: sudo docker exec sharp bash -c "npm test"
- name: "Linux x64 (CentOS 7, glibc 2.17) - Node.js 12"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_12.x | bash -"
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
install: sudo docker exec sharp bash -c "npm install --unsafe-perm"
script: sudo docker exec sharp bash -c "npm test"
- name: "Linux x64 (CentOS 7, glibc 2.17) - Node.js 13"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_13.x | bash -"
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
install: sudo docker exec sharp bash -c "npm install --unsafe-perm"
script: sudo docker exec sharp bash -c "npm test"
- name: "Linux x64 (CentOS 7, glibc 2.17) - Node.js 14"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_14.x | bash -"
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
install: sudo docker exec sharp bash -c "npm install --unsafe-perm"
script: sudo docker exec sharp bash -c "npm test"
- name: "Linux x64 (Alpine 3.9, musl 1.1.20) - Node.js 10"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10.17.0-alpine3.9 # https://github.com/nodejs/docker-node/issues/1158
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 12"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12.0-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 13"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:13.0-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 14"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14.0-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 10"
arch: arm64
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --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 python2 curl"
- 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_10.x buster main' >/etc/apt/sources.list.d/nodesource.list"
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 12"
arch: arm64
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --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 python2 curl"
- 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 buster main' >/etc/apt/sources.list.d/nodesource.list"
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 13"
arch: arm64
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --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 python2 curl"
- 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_13.x buster main' >/etc/apt/sources.list.d/nodesource.list"
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 14"
arch: arm64
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --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 python2 curl"
- 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_14.x buster main' >/etc/apt/sources.list.d/nodesource.list"
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "macOS (10.13) - Node.js 10"
os: osx
osx_image: xcode10.1
language: node_js
node_js: "10"
- name: "macOS (10.13) - Node.js 12"
os: osx
osx_image: xcode10.1
language: node_js
node_js: "12"
- name: "macOS (10.13) - Node.js 13"
os: osx
osx_image: xcode10.1
language: node_js
node_js: "13"
- name: "macOS (10.13) - Node.js 14"
os: osx
osx_image: xcode10.1
language: node_js
node_js: "14"
- name: "Unit test coverage report"
os: linux
dist: bionic
language: node_js
node_js: "13"
before_install: unset PREBUILD_TOKEN
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
cache:
npm: false

View File

@@ -1,15 +1,14 @@
# sharp
```sh
npm install 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">
The typical use case for this high speed Node.js module
is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings.
quickest ImageMagick and GraphicsMagick settings
due to its use of [libvips](https://github.com/libvips/libvips).
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
Lanczos resampling ensures quality is not sacrificed for speed.
@@ -17,40 +16,67 @@ 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.
OS X, Windows (x64), Linux (x64, ARM) systems do not require
the installation of any external runtime dependencies.
Most modern macOS, Windows and Linux systems running Node.js v10+
do not require any additional install or runtime dependencies.
## Examples
```sh
npm install sharp
```
```javascript
const sharp = require('sharp');
```
### Callback
```javascript
sharp(inputBuffer)
.resize(320, 240)
.toFile('output.webp', (err, info) => ... );
// A Promises/A+ promise is returned when callback is not provided.
.toFile('output.webp', (err, info) => { ... });
```
### Promise
```javascript
sharp('input.jpg')
.rotate()
.resize(200)
.toBuffer()
.then( data => ... )
.catch( err => ... );
.then( data => { ... })
.catch( err => { ... });
```
### Async/await
```javascript
const roundedCorners = new Buffer(
const semiTransparentRedPng = await sharp({
create: {
width: 48,
height: 48,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
}
})
.png()
.toBuffer();
```
### Stream
```javascript
const roundedCorners = Buffer.from(
'<svg><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>'
);
const roundedCornerResizer =
sharp()
.resize(200, 200)
.overlayWith(roundedCorners, { cutout: true })
.composite([{
input: roundedCorners,
blend: 'dest-in'
}])
.png();
readableStream
@@ -58,29 +84,30 @@ readableStream
.pipe(writableStream);
```
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
[![N-API v3](https://img.shields.io/badge/N--API-v3-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
### Documentation
Visit [sharp.pixelplumbing.com](http://sharp.pixelplumbing.com/) for complete
[installation instructions](http://sharp.pixelplumbing.com/page/install),
[API documentation](http://sharp.pixelplumbing.com/page/api),
[benchmark tests](http://sharp.pixelplumbing.com/page/performance) and
[changelog](http://sharp.pixelplumbing.com/page/changelog).
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
[installation instructions](https://sharp.pixelplumbing.com/install),
[API documentation](https://sharp.pixelplumbing.com/api-constructor),
[benchmark tests](https://sharp.pixelplumbing.com/performance) and
[changelog](https://sharp.pixelplumbing.com/changelog).
### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
### Licence
### Licensing
Copyright 2013, 2014, 2015, 2016, 2017, 2018 Lovell Fuller and contributors.
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](http://www.apache.org/licenses/LICENSE-2.0.html)
[https://www.apache.org/licenses/LICENSE-2.0](https://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,

View File

@@ -1,16 +1,26 @@
os: Visual Studio 2015
os: Visual Studio 2017
version: "{build}"
build: off
platform: x64
environment:
matrix:
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "8"
- nodejs_version: "9"
- nodejs_version: "10"
platform: x86
- nodejs_version: "10"
platform: x64
- nodejs_version: "12"
platform: x86
- nodejs_version: "12"
platform: x64
- nodejs_version: "13"
platform: x86
- nodejs_version: "13"
platform: x64
- nodejs_version: "14"
platform: x86
- nodejs_version: "14"
platform: x64
install:
- ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@5.3.x
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) $env:platform
- npm install
test_script:
- npm test

View File

@@ -5,15 +5,13 @@
['OS == "win"', {
# Build libvips C++ binding for Windows due to MSVC std library ABI changes
'type': 'shared_library',
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'defines': [
'VIPS_CPLUSPLUS_EXPORTS',
'_ALLOW_KEYWORD_MACROS'
],
'sources': [
'src/libvips/cplusplus/VError.cpp',
'src/libvips/cplusplus/VConnection.cpp',
'src/libvips/cplusplus/VInterpolate.cpp',
'src/libvips/cplusplus/VImage.cpp'
],
@@ -32,6 +30,9 @@
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
},
'VCLinkerTool': {
'ImageHasSafeExceptionHandlers': 'false'
}
},
'msvs_disabled_warnings': [
@@ -46,40 +47,22 @@
]
}, {
'target_name': 'sharp',
'defines': [
'NAPI_VERSION=3'
],
'dependencies': [
'<!(node -p "require(\'node-addon-api\').gyp")',
'libvips-cpp'
],
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
'variables': {
'variables': {
'variables': {
'conditions': [
['OS != "win"', {
# Build the PKG_CONFIG_PATH environment variable with all possible combinations
'pkg_config_path': '<!(which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR || true):$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig'
}, {
'pkg_config_path': ''
}]
],
},
'conditions': [
['OS != "win"', {
# Which version, if any, of libvips is available globally via pkg-config?
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips-cpp 2>/dev/null || true)'
}, {
'global_vips_version': ''
}]
],
'pkg_config_path%': '<(pkg_config_path)'
},
'pkg_config_path%': '<(pkg_config_path)',
'runtime_link%': 'shared',
'conditions': [
['OS != "win"', {
# Does the globally available version of libvips, if any, meet the minimum version requirement?
'use_global_vips': '<!(GLOBAL_VIPS_VERSION="<(global_vips_version)" node -e "require(\'./binding\').use_global_vips()")'
'pkg_config_path': '<!(node -e "console.log(require(\'./lib/libvips\').pkgConfigPath())")',
'use_global_libvips': '<!(node -e "console.log(Boolean(require(\'./lib/libvips\').useGlobalLibvips()).toString())")'
}, {
'use_global_vips': ''
'pkg_config_path': '',
'use_global_libvips': ''
}]
]
},
@@ -89,14 +72,14 @@
'src/stats.cc',
'src/operations.cc',
'src/pipeline.cc',
'src/sharp.cc',
'src/utilities.cc'
'src/utilities.cc',
'src/sharp.cc'
],
'include_dirs': [
'<!(node -e "require(\'nan\')")'
'<!@(node -p "require(\'node-addon-api\').include")',
],
'conditions': [
['use_global_vips == "true"', {
['use_global_libvips == "true"', {
# Use pkg-config for include and lib
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
'conditions': [
@@ -113,7 +96,7 @@
}]
]
}, {
# Attempt to download pre-built libvips and install locally within node_modules
# Use pre-built libvips stored locally within node_modules
'include_dirs': [
'vendor/include',
'vendor/include/glib-2.0',
@@ -122,7 +105,8 @@
'conditions': [
['OS == "win"', {
'defines': [
'_ALLOW_KEYWORD_MACROS'
'_ALLOW_KEYWORD_MACROS',
'_FILE_OFFSET_BITS=64'
],
'libraries': [
'../vendor/lib/libvips.lib',
@@ -131,9 +115,6 @@
]
}],
['OS == "mac"', {
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'libraries': [
'../vendor/lib/libvips-cpp.42.dylib',
'../vendor/lib/libvips.42.dylib',
@@ -144,9 +125,6 @@
]
}],
['OS == "linux"', {
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'defines': [
'_GLIBCXX_USE_CXX11_ABI=0'
],
@@ -157,11 +135,12 @@
'../vendor/lib/libgobject-2.0.so',
# Dependencies of dependencies, included for openSUSE support
'../vendor/lib/libcairo.so',
'../vendor/lib/libcroco-0.6.so',
'../vendor/lib/libexif.so',
'../vendor/lib/libexpat.so',
'../vendor/lib/libffi.so',
'../vendor/lib/libfontconfig.so',
'../vendor/lib/libfreetype.so',
'../vendor/lib/libfribidi.so',
'../vendor/lib/libgdk_pixbuf-2.0.so',
'../vendor/lib/libgif.so',
'../vendor/lib/libgio-2.0.so',
@@ -180,6 +159,8 @@
'../vendor/lib/librsvg-2.so',
'../vendor/lib/libtiff.so',
'../vendor/lib/libwebp.so',
'../vendor/lib/libwebpdemux.so',
'../vendor/lib/libwebpmux.so',
'../vendor/lib/libxml2.so',
'../vendor/lib/libz.so',
# Ensure runtime linking is relative to sharp.node
@@ -209,75 +190,32 @@
},
'configurations': {
'Release': {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'msvs_disabled_warnings': [
4275
'conditions': [
['OS == "linux"', {
'cflags_cc': [
'-Wno-cast-function-type'
]
}],
['target_arch == "arm"', {
'cflags_cc': [
'-Wno-psabi'
]
}],
['OS == "win"', {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
},
'VCLinkerTool': {
'ImageHasSafeExceptionHandlers': 'false'
}
},
'msvs_disabled_warnings': [
4275
]
}]
]
}
},
}, {
'target_name': 'win_copy_dlls',
'type': 'none',
'dependencies': [
'sharp'
],
'conditions': [
['OS == "win"', {
# Windows lacks support for rpath
'copies': [{
'destination': 'build/Release',
'files': [
'vendor/lib/libasprintf-0.dll',
'vendor/lib/libcairo-2.dll',
'vendor/lib/libcairo-gobject-2.dll',
'vendor/lib/libcairo-script-interpreter-2.dll',
'vendor/lib/libcharset-1.dll',
'vendor/lib/libcroco-0.6-3.dll',
'vendor/lib/libexif-12.dll',
'vendor/lib/libexpat-1.dll',
'vendor/lib/libffi-6.dll',
'vendor/lib/libfftw3-3.dll',
'vendor/lib/libfontconfig-1.dll',
'vendor/lib/libfreetype-6.dll',
'vendor/lib/libgcc_s_seh-1.dll',
'vendor/lib/libgdk_pixbuf-2.0-0.dll',
'vendor/lib/libgettextlib-0-19-8.dll',
'vendor/lib/libgettextpo-1.dll',
'vendor/lib/libgettextsrc-0-19-8.dll',
'vendor/lib/libgif-7.dll',
'vendor/lib/libgio-2.0-0.dll',
'vendor/lib/libglib-2.0-0.dll',
'vendor/lib/libgmodule-2.0-0.dll',
'vendor/lib/libgobject-2.0-0.dll',
'vendor/lib/libgsf-1-114.dll',
'vendor/lib/libgthread-2.0-0.dll',
'vendor/lib/libharfbuzz-0.dll',
'vendor/lib/libiconv-2.dll',
'vendor/lib/libintl-9.dll',
'vendor/lib/libjpeg-62.dll',
'vendor/lib/liblcms2-2.dll',
'vendor/lib/libpango-1.0-0.dll',
'vendor/lib/libpangocairo-1.0-0.dll',
'vendor/lib/libpangoft2-1.0-0.dll',
'vendor/lib/libpangowin32-1.0-0.dll',
'vendor/lib/libpixman-1-0.dll',
'vendor/lib/libpng16-16.dll',
'vendor/lib/libquadmath-0.dll',
'vendor/lib/librsvg-2-2.dll',
'vendor/lib/libssp-0.dll',
'vendor/lib/libstdc++-6.dll',
'vendor/lib/libtiff-5.dll',
'vendor/lib/libvips-42.dll',
'vendor/lib/libwebp-7.dll',
'vendor/lib/libxml2-2.dll',
'vendor/lib/zlib1.dll'
]
}]
}]
]
}]
}

View File

@@ -1,108 +0,0 @@
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const simpleGet = require('simple-get');
const semver = require('semver');
const tar = require('tar');
const detectLibc = require('detect-libc');
const agent = require('./lib/agent');
const platform = require('./lib/platform');
// Use NPM-provided environment variable where available, falling back to require-based method for Electron
const minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersion}/`;
// -- Helpers
const unpack = function (tarPath, done) {
const vendorPath = path.join(__dirname, 'vendor');
fs.mkdirSync(vendorPath);
tar
.extract({
file: tarPath,
cwd: vendorPath,
strict: true
})
.then(done)
.catch(error);
};
const error = function (msg) {
if (msg instanceof Error) {
msg = msg.message;
}
process.stderr.write(`sharp: ${msg}\n`);
process.exit(1);
};
// -- Binary downloaders
module.exports.download_vips = function () {
// Check for existing vendored binaries and verify platform matches
const currentPlatformId = platform();
try {
const vendorPlatformId = require(path.join(__dirname, 'vendor', 'platform.json'));
if (currentPlatformId === vendorPlatformId) {
return;
} else {
error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
}
} catch (err) {}
// Ensure Intel 64-bit or ARM
const arch = process.env.npm_config_arch || process.arch;
if (arch === 'ia32') {
error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion} - please see http://sharp.pixelplumbing.com/page/install`);
}
// Ensure glibc Linux
if (detectLibc.isNonGlibcLinux) {
error(`Use with ${detectLibc.family} libc requires manual installation of libvips >= ${minimumLibvipsVersion} - please see http://sharp.pixelplumbing.com/page/install`);
}
// Ensure glibc >= 2.13
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && semver.lt(`${detectLibc.version}.0`, '2.13.0')) {
error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion} - please see http://sharp.pixelplumbing.com/page/install`);
}
// Arch/platform-specific .tar.gz
const tarFilename = ['libvips', minimumLibvipsVersion, currentPlatformId].join('-') + '.tar.gz';
// Download to per-process temporary file
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
const tmpFile = fs.createWriteStream(tarPathTemp).on('close', function () {
unpack(tarPathTemp, function () {
// Attempt to remove temporary file
try {
fs.unlinkSync(tarPathTemp);
} catch (err) {}
});
});
const url = distBaseUrl + tarFilename;
const simpleGetOpt = {
url: url,
agent: agent()
};
simpleGet(simpleGetOpt, function (err, response) {
if (err) {
error(`${url} download failed: ${err.message}`);
}
if (response.statusCode !== 200) {
error(`${url} download failed: status ${response.statusCode}`);
}
response.pipe(tmpFile);
});
};
module.exports.use_global_vips = function () {
const globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
if (globalVipsVersion) {
const useGlobalVips = semver.gte(
globalVipsVersion,
minimumLibvipsVersion
);
process.stdout.write(useGlobalVips ? 'true' : 'false');
} else {
process.stdout.write('false');
}
};

80
docs/README.md Normal file
View File

@@ -0,0 +1,80 @@
# 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">
The typical use case for this high speed Node.js module
is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings
due to its use of [libvips](https://github.com/libvips/libvips).
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
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 v10+
do not require any additional install or runtime dependencies.
### Formats
This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images.
Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data.
Streams, Buffer objects and the filesystem can be used for input and output.
A single input Stream can be split into multiple processing pipelines and output Streams.
Deep Zoom image pyramids can be generated,
suitable for use with "slippy map" tile viewers like
[OpenSeadragon](https://github.com/openseadragon/openseadragon).
### Fast
This module is powered by the blazingly fast
[libvips](https://github.com/libvips/libvips) image processing library,
originally created in 1989 at Birkbeck College
and currently maintained by a small team led by
[John Cupitt](https://github.com/jcupitt).
Only small regions of uncompressed image data
are held in memory and processed at a time,
taking full advantage of multiple CPU cores and L1/L2/L3 cache.
Everything remains non-blocking thanks to _libuv_,
no child processes are spawned and Promises/async/await are supported.
### Optimal
Huffman tables are optimised when generating JPEG output images
without having to use separate command line tools like
[jpegoptim](https://github.com/tjko/jpegoptim) and
[jpegtran](http://jpegclub.org/jpegtran/).
PNG filtering is disabled by default,
which for diagrams and line art often produces the same result
as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
### Licensing
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
[https://www.apache.org/licenses/LICENSE-2.0](https://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.

View File

@@ -1,31 +1,62 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
## removeAlpha
- [extractChannel](#extractchannel)
- [joinChannel](#joinchannel)
- [bandbool](#bandbool)
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
### Examples
```javascript
sharp('rgba.png')
.removeAlpha()
.toFile('rgb.png', function(err, info) {
// rgb.png is a 3 channel image without an alpha channel
});
```
Returns **Sharp**
## ensureAlpha
Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
### Examples
```javascript
sharp('rgb.jpg')
.ensureAlpha()
.toFile('rgba.png', function(err, info) {
// rgba.png is a 4 channel image with a fully opaque alpha channel
});
```
Returns **Sharp**
**Meta**
- **since**: 0.21.2
## extractChannel
Extract a single channel from a multi-channel image.
**Parameters**
### Parameters
- `channel` **([Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) \| [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
- `channel` **([number][1] \| [string][2])** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
**Examples**
### Examples
```javascript
sharp(input)
.extractChannel('green')
.toFile('input_green.jpg', function(err, info) {
.toColourspace('b-w')
.toFile('green.jpg', function(err, info) {
// info.channels === 1
// input_green.jpg contains the green channel of the input image
// green.jpg is a greyscale image containing the green channel of the input
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid channel
- Throws **[Error][3]** Invalid channel
Returns **Sharp**
@@ -42,13 +73,13 @@ Channel ordering follows vips convention:
Buffers may be any of the image formats supported by sharp: JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data.
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**
### Parameters
- `images` **([Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;([String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Buffer](https://nodejs.org/api/buffer.html))> | [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Buffer](https://nodejs.org/api/buffer.html))** one or more images (file paths, Buffers).
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** image options, see `sharp()` constructor.
- `images` **([Array][4]&lt;([string][2] \| [Buffer][5])> | [string][2] \| [Buffer][5])** one or more images (file paths, Buffers).
- `options` **[Object][6]** image options, see `sharp()` constructor.
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][3]** Invalid parameters
Returns **Sharp**
@@ -56,11 +87,11 @@ Returns **Sharp**
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
**Parameters**
### Parameters
- `boolOp` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
- `boolOp` **[string][2]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
**Examples**
### Examples
```javascript
sharp('3-channel-rgb-input.png')
@@ -72,6 +103,18 @@ sharp('3-channel-rgb-input.png')
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][3]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[5]: https://nodejs.org/api/buffer.html
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object

View File

@@ -1,28 +1,16 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
## tint
- [background](#background)
- [greyscale](#greyscale)
- [grayscale](#grayscale)
- [toColourspace](#tocolourspace)
- [toColorspace](#tocolorspace)
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.
## background
### Parameters
Set the background for the `embed`, `flatten` and `extend` operations.
The default background is `{r: 0, g: 0, b: 0, alpha: 1}`, black without transparency.
Delegates to the _color_ module, which can throw an Error
but is liberal in what it accepts, clipping values to sensible min/max.
The alpha value is a float between `0` (transparent) and `1` (opaque).
**Parameters**
- `rgba` **([String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object))** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
- `rgb` **([string][1] \| [Object][2])** parsed by the [color][3] module to extract chroma values.
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameter
- Throws **[Error][4]** Invalid parameter
Returns **Sharp**
@@ -35,9 +23,9 @@ 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**
### Parameters
- `greyscale` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `greyscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp**
@@ -45,9 +33,9 @@ Returns **Sharp**
Alternative spelling of `greyscale`.
**Parameters**
### Parameters
- `grayscale` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `grayscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp**
@@ -56,12 +44,12 @@ Returns **Sharp**
Set the output colourspace.
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
**Parameters**
### Parameters
- `colourspace` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
- `colourspace` **[string][1]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][6]
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][4]** Invalid parameters
Returns **Sharp**
@@ -69,11 +57,23 @@ Returns **Sharp**
Alternative spelling of `toColourspace`.
**Parameters**
### Parameters
- `colorspace` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** output colorspace.
- `colorspace` **[string][1]?** output colorspace.
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** 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]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568

View File

@@ -1,51 +1,54 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
## composite
- [overlayWith](#overlaywith)
Composite image(s) over the processed (resized, extracted etc.) image.
## overlayWith
Overlay (composite) an image over the processed (resized, extracted etc.) image.
The overlay image must be the same size or smaller than the processed 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`.
If the overlay image contains an alpha channel then composition with premultiplication will occur.
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`,
`colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
`hard-light`, `soft-light`, `difference`, `exclusion`.
**Parameters**
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]
- `overlay` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))** Buffer containing image data or String containing the path to an image file.
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.gravity` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** gravity at which to place the overlay. (optional, default `'centre'`)
- `options.top` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the top edge.
- `options.left` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the left edge.
- `options.tile` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
- `options.cutout` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`)
- `options.density` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** integral number representing the DPI for vector overlay image. (optional, default `72`)
- `options.raw` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes overlay when using raw pixel data.
- `options.raw.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a blank overlay to be created.
- `options.create.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.channels` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
- `options.create.background` **([String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object))?** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
### Parameters
**Examples**
- `images` **[Array][3]&lt;[Object][4]>** Ordered list of images to composite
- `images[].input` **([Buffer][5] \| [String][6])?** Buffer containing image data, String containing the path to an image file, or Create object (see below)
- `images[].input.create` **[Object][4]?** describes a blank overlay to be created.
- `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.
- `images[].raw.width` **[Number][7]?**
- `images[].raw.height` **[Number][7]?**
- `images[].raw.channels` **[Number][7]?**
### Examples
```javascript
sharp('input.png')
.rotate(180)
.resize(300)
.flatten()
.background('#ff6600')
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
.flatten( { background: '#ff6600' } )
.composite([{ input: 'overlay.png', gravity: 'southeast' }])
.sharpen()
.withMetadata()
.quality(90)
.webp()
.webp( { quality: 90 } )
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
@@ -54,6 +57,30 @@ sharp('input.png')
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][10]** 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]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error

View File

@@ -1,36 +1,35 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [Sharp](#sharp)
- [format](#format)
- [versions](#versions)
- [queue](#queue)
## Sharp
**Parameters**
### Parameters
- `input` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))?** if present, can be
- `input` **([Buffer][1] \| [String][2])?** if present, can be
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** if present, is an Object with optional attributes.
- `options.failOnError` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** by default apply a "best effort"
to decode images, even if the data is corrupt or invalid. Set this flag to true
if you'd rather halt processing and raise an error when loading invalid images. (optional, default `false`)
- `options.density` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** integral number representing the DPI for vector images. (optional, default `72`)
- `options.raw` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 1-4
- `options.create` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a new image to be created.
- `options.create.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.channels` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
- `options.create.background` **([String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object))?** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
- `options` **[Object][3]?** if present, is an Object with optional attributes.
- `options.failOnError` **[Boolean][4]** 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][5] \| [Boolean][4])** 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.sequentialRead` **[Boolean][4]** 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][5]** number representing the DPI for vector images. (optional, default `72`)
- `options.pages` **[Number][5]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
- `options.page` **[Number][5]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
- `options.raw` **[Object][3]?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.width` **[Number][5]?**
- `options.raw.height` **[Number][5]?**
- `options.raw.channels` **[Number][5]?** 1-4
- `options.create` **[Object][3]?** describes a new image to be created.
- `options.create.width` **[Number][5]?**
- `options.create.height` **[Number][5]?**
- `options.create.channels` **[Number][5]?** 3-4
- `options.create.background` **([String][2] \| [Object][3])?** parsed by the [color][6] module to extract values for red, green, blue and alpha.
**Examples**
### Examples
```javascript
sharp('input.jpg')
@@ -61,7 +60,7 @@ sharp({
width: 300,
height: 200,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 }
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
}
})
.png()
@@ -69,43 +68,90 @@ sharp({
.then( ... );
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][7]** Invalid parameters
Returns **[Sharp](#sharp)**
Returns **[Sharp][8]**
### format
## clone
An Object containing nested boolean values representing the available input and output formats/methods.
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**
### Examples
```javascript
console.log(sharp.format);
const pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
Returns **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
### versions
An Object containing the version numbers of libvips and its dependencies.
**Examples**
```javascript
console.log(sharp.versions);
```
## 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)');
// 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 promises = [];
promises.push(
sharpStream
.clone()
.jpeg({ quality: 100 })
.toFile("originalFile.jpg")
);
promises.push(
sharpStream
.clone()
.resize({ width: 500 })
.jpeg({ quality: 80 })
.toFile("optimized-500.jpg")
);
promises.push(
sharpStream
.clone()
.resize({ width: 500 })
.webp({ quality: 80 })
.toFile("optimized-500.webp")
);
// https://github.com/sindresorhus/got#gotstreamurl-options
got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
Promise.all(promises)
.then(res => { console.log("Done!", res); })
.catch(err => {
console.error("Error processing files, let's clean it up", err);
try {
fs.unlinkSync("originalFile.jpg");
fs.unlinkSync("optimized-500.jpg");
fs.unlinkSync("optimized-500.webp");
} catch (e) {}
});
```
Returns **[Sharp][8]**
[1]: https://nodejs.org/api/buffer.html
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[6]: https://www.npmjs.org/package/color
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[8]: #sharp

View File

@@ -1,57 +1,39 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [clone](#clone)
- [metadata](#metadata)
- [stats](#stats)
- [limitInputPixels](#limitinputpixels)
- [sequentialRead](#sequentialread)
## 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
const pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
Returns **Sharp**
## metadata
Fast access to (uncached) image metadata without decoding any compressed image data.
A Promises/A+ promise is returned when `callback` is not provided.
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`
- `width`: Number of pixels wide
- `height`: Number of pixels high
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
- `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` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
- `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
- `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
- `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
**Parameters**
### Parameters
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)?** called with the arguments `(err, metadata)`
- `callback` **[Function][4]?** called with the arguments `(err, metadata)`
**Examples**
### Examples
```javascript
const image = sharp(inputJpg);
@@ -68,12 +50,12 @@ image
});
```
Returns **([Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)> | Sharp)**
Returns **([Promise][5]&lt;[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.
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)
@@ -86,13 +68,14 @@ A Promise is returned when `callback` is not provided.
- `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`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
- `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)
**Parameters**
### Parameters
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)?** called with the arguments `(err, stats)`
- `callback` **[Function][4]?** called with the arguments `(err, stats)`
**Examples**
### Examples
```javascript
const image = sharp(inputJpg);
@@ -103,32 +86,16 @@ image
});
```
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)>**
Returns **[Promise][5]&lt;[Object][6]>**
## limitInputPixels
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
Do not process input images where the number of pixels (width _ height) exceeds this limit.
Assumes image dimensions contained in the input metadata can be trusted.
The default limit is 268402689 (0x3FFF _ 0x3FFF) pixels.
[2]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat
**Parameters**
[3]: https://www.npmjs.com/package/icc
- `limit` **([Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) \| [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** an integral Number of pixels, zero or false to remove limit, true to use default limit.
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid limit
Returns **Sharp**
## sequentialRead
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
This will reduce memory usage and can improve performance on some systems.
The default behaviour _before_ function call is `false`, meaning the libvips access method is not sequential.
**Parameters**
- `sequentialRead` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp**
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object

View File

@@ -1,32 +1,16 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [rotate](#rotate)
- [extract](#extract)
- [flip](#flip)
- [flop](#flop)
- [sharpen](#sharpen)
- [blur](#blur)
- [extend](#extend)
- [flatten](#flatten)
- [trim](#trim)
- [gamma](#gamma)
- [negate](#negate)
- [normalise](#normalise)
- [normalize](#normalize)
- [convolve](#convolve)
- [threshold](#threshold)
- [boolean](#boolean)
## rotate
Rotate the output image by either an explicit angle
or auto-orient based on the EXIF `Orientation` tag.
If an angle is provided, it is converted to a valid 90/180/270deg rotation.
If an angle is provided, it is converted to a valid positive degree rotation.
For example, `-450` will produce a 270deg rotation.
When rotating by an angle other than a multiple of 90,
the background colour can be provided with the `background` option.
If no angle is provided, it is determined from the EXIF data.
Mirroring is supported and may infer the use of a flip operation.
@@ -35,11 +19,13 @@ 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)`.
**Parameters**
### Parameters
- `angle` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle of rotation, must be a multiple of 90. (optional, default `auto`)
- `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"`)
**Examples**
### Examples
```javascript
const pipeline = sharp()
@@ -53,47 +39,7 @@ const pipeline = sharp()
readableStream.pipe(pipeline);
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## extract
Extract 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.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `options.left` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** zero-indexed offset from left edge
- `options.top` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** zero-indexed offset from top edge
- `options.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** dimension of extracted image
- `options.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** dimension of extracted image
**Examples**
```javascript
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
sharp(input)
.extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
.resize(width, height)
.extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
.toFile(output, function(err) {
// Extract a region, resize, then extract from the resized image
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
@@ -102,9 +48,9 @@ Returns **Sharp**
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters**
### Parameters
- `flip` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `flip` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
@@ -113,9 +59,9 @@ Returns **Sharp**
Flop the image about the horizontal X axis. This always occurs after rotation, if any.
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters**
### Parameters
- `flop` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `flop` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
@@ -126,14 +72,28 @@ 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.
**Parameters**
### Parameters
- `sigma` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `flat` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
- `jagged` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- `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 **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
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 **[Error][5]** Invalid parameters
Returns **Sharp**
@@ -143,64 +103,23 @@ Blur the image.
When used without parameters, performs a fast, mild blur of the output image.
When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
**Parameters**
### Parameters
- `sigma` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `sigma` **[number][1]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## extend
Extends/pads the edges of the image with the colour provided to the `background` method.
This operation will always occur after resizing and extraction, if any.
**Parameters**
- `extend` **([Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) \| [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object))** single pixel count to add to all edges or an Object with per-edge counts
- `extend.top` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `extend.left` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `extend.bottom` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `extend.right` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
**Examples**
```javascript
// 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)
.resize(140)
.background({r: 0, g: 0, b: 0, alpha: 0})
.extend({top: 10, bottom: 20, left: 10, right: 10})
...
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## flatten
Merge alpha transparency channel, if any, with `background`.
Merge alpha transparency channel, if any, with a background.
**Parameters**
### Parameters
- `flatten` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp**
## trim
Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
**Parameters**
- `tolerance` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** value between 1 and 99 representing the percentage similarity. (optional, default `10`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid 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}`)
Returns **Sharp**
@@ -212,12 +131,15 @@ This can improve the perceived brightness of a resized image in non-linear colou
JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
when applying a gamma correction.
**Parameters**
Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
- `gamma` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** value between 1.0 and 3.0. (optional, default `2.2`)
### 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 **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
@@ -225,9 +147,9 @@ Returns **Sharp**
Produce the "negative" of the image.
**Parameters**
### Parameters
- `negate` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `negate` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
@@ -235,9 +157,9 @@ Returns **Sharp**
Enhance output image contrast by stretching its luminance to cover the full dynamic range.
**Parameters**
### Parameters
- `normalise` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `normalise` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
@@ -245,9 +167,9 @@ Returns **Sharp**
Alternative spelling of normalise.
**Parameters**
### Parameters
- `normalize` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `normalize` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
@@ -255,16 +177,16 @@ Returns **Sharp**
Convolve the image with the specified kernel.
**Parameters**
### Parameters
- `kernel` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `kernel.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels.
- `kernel.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels.
- `kernel.kernel` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the scale of the kernel in pixels. (optional, default `sum`)
- `kernel.offset` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the offset of the kernel in pixels. (optional, default `0`)
- `kernel` **[Object][2]**
- `kernel.width` **[number][1]** width of the kernel in pixels.
- `kernel.height` **[number][1]** width of the kernel in pixels.
- `kernel.kernel` **[Array][7]&lt;[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`)
**Examples**
### Examples
```javascript
sharp(input)
@@ -280,7 +202,7 @@ sharp(input)
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
@@ -288,15 +210,15 @@ Returns **Sharp**
Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
**Parameters**
### Parameters
- `threshold` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.greyscale` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** alternative spelling for greyscale. (optional, default `true`)
- `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]?**
- `options.greyscale` **[Boolean][6]** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean][6]** alternative spelling for greyscale. (optional, default `true`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
@@ -307,17 +229,118 @@ 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**
### Parameters
- `operand` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))** Buffer containing image data or String containing the path to an image file.
- `operator` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.raw` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes operand when using raw pixel data.
- `options.raw.width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `operand` **([Buffer][8] \| [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]?**
- `options.raw` **[Object][2]?** describes operand when using raw pixel data.
- `options.raw.width` **[number][1]?**
- `options.raw.height` **[number][1]?**
- `options.raw.channels` **[number][1]?**
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## linear
Apply the linear formula a \* input + b to the image (levels adjustment)
### Parameters
- `a` **[number][1]** multiplier (optional, default `1.0`)
- `b` **[number][1]** offset (optional, default `0.0`)
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## recomb
Recomb the image with the specified matrix.
### Parameters
- `inputMatrix` **[Array][7]&lt;[Array][7]&lt;[number][1]>>** 3x3 Recombination matrix
### Examples
```javascript
sharp(input)
.recomb([
[0.3588, 0.7044, 0.1368],
[0.2990, 0.5870, 0.1140],
[0.2392, 0.4696, 0.0912],
])
.raw()
.toBuffer(function(err, data, info) {
// data contains the raw pixel data after applying the recomb
// With this example input, a sepia filter has been applied
});
```
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
**Meta**
- **since**: 0.21.1
## modulate
Transforms the image using brightness, saturation and hue rotation.
### Parameters
- `options` **[Object][2]?**
- `options.brightness` **[number][1]?** Brightness multiplier
- `options.saturation` **[number][1]?** Saturation multiplier
- `options.hue` **[number][1]?** Degrees for hue rotation
### Examples
```javascript
sharp(input)
.modulate({
brightness: 2 // increase lightness by a factor of 2
});
sharp(input)
.modulate({
hue: 180 // hue-rotate by 180 degrees
});
// decreate brightness and saturation while also hue-rotating by 90 degrees
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]: https://nodejs.org/api/buffer.html

View File

@@ -1,18 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [toFile](#tofile)
- [toBuffer](#tobuffer)
- [withMetadata](#withmetadata)
- [jpeg](#jpeg)
- [png](#png)
- [webp](#webp)
- [tiff](#tiff)
- [raw](#raw)
- [toFormat](#toformat)
- [tile](#tile)
## toFile
Write output image data to a file.
@@ -21,26 +8,46 @@ If an explicit output format is not selected, it will be inferred from the exten
with JPEG, PNG, WebP, TIFF, 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.
A `Promise` is returned when `callback` is not provided.
**Parameters**
### Parameters
- `fileOut` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to write the image data to.
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)?** called on completion with two arguments `(err, info)`.
- `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`.
### Examples
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
```javascript
sharp(input)
.toFile('output.png', (err, info) => { ... });
```
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)>** when no callback is provided
```javascript
sharp(input)
.toFile('output.png')
.then(info => { ... })
.catch(err => { ... });
```
- Throws **[Error][4]** Invalid parameters
Returns **[Promise][5]&lt;[Object][6]>** when no callback is provided
## toBuffer
Write output to a Buffer.
JPEG, PNG, WebP, TIFF and RAW output are supported.
By default, the format will match the input image, except GIF and SVG input which become PNG output.
If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
By default all metadata will be removed, which includes EXIF-based orientation.
See [withMetadata][1] for control over this.
`callback`, if present, gets three arguments `(err, data, info)` where:
@@ -52,109 +59,58 @@ By default, the format will match the input image, except GIF and SVG input whic
A `Promise` is returned when `callback` is not provided.
**Parameters**
### Parameters
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.resolveWithObject` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)?**
- `options` **[Object][6]?**
- `options.resolveWithObject` **[boolean][7]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function][3]?**
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Buffer](https://nodejs.org/api/buffer.html)>** when no callback is provided
### Examples
```javascript
sharp(input)
.toBuffer((err, data, info) => { ... });
```
```javascript
sharp(input)
.toBuffer()
.then(data => { ... })
.catch(err => { ... });
```
```javascript
sharp(input)
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => { ... })
.catch(err => { ... });
```
Returns **[Promise][5]&lt;[Buffer][8]>** when no callback is provided
## withMetadata
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
This will also convert to and add a web-friendly sRGB ICC profile.
**Parameters**
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.
- `withMetadata` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `withMetadata.orientation` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** value between 1 and 8, used to update the EXIF `Orientation` tag.
### Parameters
- `options` **[Object][6]?**
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
### Examples
Returns **Sharp**
```javascript
sharp('input.jpg')
.withMetadata()
.toFile('output-with-metadata.jpg')
.then(info => { ... });
```
## jpeg
Use these JPEG options for output image.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** quality, integer 1-100 (optional, default `80`)
- `options.progressive` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use progressive (interlace) scan (optional, default `false`)
- `options.chromaSubsampling` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** set to '4:4:4' to prevent chroma subsampling when quality &lt;= 90 (optional, default `'4:2:0'`)
- `options.trellisQuantisation` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** apply trellis quantisation, requires mozjpeg (optional, default `false`)
- `options.overshootDeringing` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** apply overshoot deringing, requires mozjpeg (optional, default `false`)
- `options.optimiseScans` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`)
- `options.optimizeScans` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** alternative spelling of optimiseScans (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** force JPEG output, otherwise attempt to use input format (optional, default `true`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options
Returns **Sharp**
## png
Use these PNG options for output image.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.progressive` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** zlib compression level, 0-9 (optional, default `9`)
- `options.adaptiveFiltering` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use adaptive row filtering (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** force PNG output, otherwise attempt to use input format (optional, default `true`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options
Returns **Sharp**
## webp
Use these WebP options for output image.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** quality, integer 1-100 (optional, default `80`)
- `options.alphaQuality` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** quality of alpha layer, integer 0-100 (optional, default `100`)
- `options.lossless` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use lossless compression mode (optional, default `false`)
- `options.nearLossless` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use near_lossless compression mode (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** force WebP output, otherwise attempt to use input format (optional, default `true`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options
Returns **Sharp**
## tiff
Use these TIFF options for output image.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** quality, integer 1-100 (optional, default `80`)
- `options.force` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** force TIFF output, otherwise attempt to use input format (optional, default `true`)
- `options.compression` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** compression options: lzw, deflate, jpeg (optional, default `'jpeg'`)
- `options.predictor` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
- `options.xres` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** horizontal resolution in pixels/mm (optional, default `1.0`)
- `options.yres` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** vertical resolution in pixels/mm (optional, default `1.0`)
- `options.squash` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** squash 8-bit images down to 1 bit (optional, default `false`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options
Returns **Sharp**
## raw
Force output to be raw, uncompressed uint8 pixel data.
- Throws **[Error][4]** Invalid parameters
Returns **Sharp**
@@ -162,13 +118,208 @@ Returns **Sharp**
Force output to a given format.
**Parameters**
### Parameters
- `format` **([String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object))** as a String or an Object with an 'id' attribute
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** output options
- `format` **([string][2] \| [Object][6])** as a string or an Object with an 'id' attribute
- `options` **[Object][6]** output options
### Examples
```javascript
// 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
- `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]** for quality &lt; 90, set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' (use chroma subsampling); for quality >= 90 chroma is never subsampled (optional, default `'4:2:0'`)
- `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation, requires libvips compiled with support for mozjpeg (optional, default `false`)
- `options.overshootDeringing` **[boolean][7]** apply overshoot deringing, requires libvips compiled with support for mozjpeg (optional, default `false`)
- `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg (optional, default `false`)
- `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans (optional, default `false`)
- `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
- `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
- `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg (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`)
### Examples
```javascript
// Convert any input to very high quality JPEG output
const data = await sharp(input)
.jpeg({
quality: 100,
chromaSubsampling: '4:4:4'
})
.toBuffer();
```
- Throws **[Error][4]** Invalid options
Returns **Sharp**
## png
Use these PNG options for output image.
PNG output is always 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.
### Parameters
- `options` **[Object][6]?**
- `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[number][9]** zlib compression level, 0-9 (optional, default `9`)
- `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, requires libvips compiled with support for libimagequant (optional, default `false`)
- `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, requires libvips compiled with support for libimagequant (optional, default `100`)
- `options.colours` **[number][9]** maximum number of palette entries, requires libvips compiled with support for libimagequant (optional, default `256`)
- `options.colors` **[number][9]** alternative spelling of `options.colours`, requires libvips compiled with support for libimagequant (optional, default `256`)
- `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, requires libvips compiled with support for libimagequant (optional, default `1.0`)
- `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
### Examples
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.png()
.toBuffer();
```
- Throws **[Error][4]** Invalid options
Returns **Sharp**
## webp
Use these WebP options for output image.
### Parameters
- `options` **[Object][6]?** output options
- `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.reductionEffort` **[number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
- `options.force` **[boolean][7]** force WebP output, otherwise attempt to use input format (optional, default `true`)
### Examples
```javascript
// Convert any input to lossless WebP output
const data = await sharp(input)
.webp({ lossless: true })
.toBuffer();
```
- Throws **[Error][4]** Invalid options
Returns **Sharp**
## tiff
Use these TIFF options for output image.
### Parameters
- `options` **[Object][6]?** output options
- `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` **[boolean][7]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
- `options.predictor` **[boolean][7]** 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` **[boolean][7]** horizontal tile size (optional, default `256`)
- `options.tileHeight` **[boolean][7]** 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.squash` **[boolean][7]** squash 8-bit images down to 1 bit (optional, default `false`)
### Examples
```javascript
// Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
sharp('input.svg')
.tiff({
compression: 'lzw',
squash: true
})
.toFile('1-bpp-output.tiff')
.then(info => { ... });
```
- Throws **[Error][4]** Invalid options
Returns **Sharp**
## heif
Use these HEIF options for output image.
Support for HEIF (HEIC/AVIF) is experimental.
Do not use this in production systems.
Requires a custom, globally-installed libvips compiled with support for libheif.
Most versions of libheif support only the patent-encumbered HEVC compression format.
### Parameters
- `options` **[Object][6]?** output options
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
- `options.compression` **[boolean][7]** compression format: hevc, avc, jpeg, av1 (optional, default `'hevc'`)
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** unsupported format or options
- Throws **[Error][4]** Invalid options
Returns **Sharp**
**Meta**
- **since**: 0.23.0
## raw
Force output to be raw, uncompressed, 8-bit unsigned integer (unit8) pixel data.
Pixel ordering is left-to-right, top-to-bottom, without padding.
Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
### Examples
```javascript
// Extract raw RGB pixel data from JPEG input
const { data, info } = await sharp('input.jpg')
.raw()
.toBuffer({ resolveWithObject: true });
```
```javascript
// Extract alpha channel as raw pixel data from PNG input
const data = await sharp('input.png')
.ensureAlpha()
.extractChannel(3)
.toColourspace('b-w')
.raw()
.toBuffer();
```
Returns **Sharp**
@@ -178,15 +329,21 @@ 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**
Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
- `tile` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `tile.size` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `tile.overlap` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `tile.container` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
- `tile.layout` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
### Parameters
**Examples**
- `options` **[Object][6]?**
- `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][10] 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`, `zoomify` or `google`. (optional, default `'dz'`)
### Examples
```javascript
sharp('input.tiff')
@@ -200,6 +357,26 @@ sharp('input.tiff')
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
- Throws **[Error][4]** Invalid parameters
Returns **Sharp**
[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://www.npmjs.org/package/color

View File

@@ -1,173 +1,247 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [resize](#resize)
- [crop](#crop)
- [embed](#embed)
- [max](#max)
- [min](#min)
- [ignoreAspectRatio](#ignoreaspectratio)
- [withoutEnlargement](#withoutenlargement)
## resize
Resize image to `width` x `height`.
By default, the resized image is centre cropped to the exact size specified.
Resize image to `width`, `height` or `width x height`.
Possible kernels are:
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these 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).
- `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).
- `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.
**Parameters**
Some of these values are based on the [object-fit][1] CSS property.
- `width` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
- `height` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
- `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.kernel` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** the kernel to use for image reduction. (optional, default `'lanczos3'`)
- `options.fastShrinkOnLoad` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** 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`)
When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
**Examples**
- `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.
```javascript
sharp(inputBuffer)
.resize(200, 300, {
kernel: sharp.kernel.nearest
})
.background('white')
.embed()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a nearest-neighbour scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## crop
Crop the resized image to the exact size specified, the default behaviour.
Possible attributes of the optional `sharp.gravity` are `north`, `northeast`, `east`, `southeast`, `south`,
`southwest`, `west`, `northwest`, `center` and `centre`.
Some of these values are based on the [object-position][2] 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](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
- `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.
**Parameters**
Possible interpolation kernels are:
- `crop` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically. (optional, default `'centre'`)
- `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).
**Examples**
### 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]?**
- `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.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`)
### Examples
```javascript
sharp(input)
.resize({ width: 100 })
.toBuffer()
.then(data => {
// 100 pixels wide, auto-scaled height
});
```
```javascript
sharp(input)
.resize({ height: 100 })
.toBuffer()
.then(data => {
// 100 pixels high, auto-scaled width
});
```
```javascript
sharp(input)
.resize(200, 300, {
kernel: sharp.kernel.nearest,
fit: 'contain',
position: 'right top',
background: { r: 255, g: 255, b: 255, alpha: 0.5 }
})
.toFile('output.png')
.then(() => {
// output.png is a 200 pixels wide and 300 pixels high image
// containing a nearest-neighbour scaled version
// contained within the north-east corner of a semi-transparent white canvas
});
```
```javascript
const transformer = sharp()
.resize(200, 200)
.crop(sharp.strategy.entropy)
.on('error', function(err) {
console.log(err);
.resize({
width: 200,
height: 200,
fit: sharp.fit.cover,
position: sharp.strategy.entropy
});
// Read image data from readableStream
// Write 200px square auto-cropped image data to writableStream
readableStream.pipe(transformer).pipe(writableStream);
readableStream
.pipe(transformer)
.pipe(writableStream);
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## embed
Preserving aspect ratio, resize the image to the maximum `width` or `height` specified
then embed on a background of the exact `width` and `height` specified.
If the background contains an alpha value then WebP and PNG format output images will
contain an alpha channel, even when the input image does not.
**Parameters**
- `embed` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** A member of `sharp.gravity` to embed to an edge/corner. (optional, default `'centre'`)
**Examples**
```javascript
sharp('input.gif')
.resize(200, 300)
.background({r: 0, g: 0, b: 0, alpha: 0})
.embed()
.toFormat(sharp.format.webp)
.toBuffer(function(err, outputBuffer) {
if (err) {
throw err;
}
// outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
// containing a scaled version, embedded on a transparent canvas, of input.gif
});
```
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## max
Preserving aspect ratio, resize the image to be as large as possible
while ensuring its dimensions are less than or equal to the `width` and `height` specified.
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
**Examples**
```javascript
sharp(inputBuffer)
.resize(200, 200)
.max()
sharp(input)
.resize(200, 200, {
fit: sharp.fit.inside,
withoutEnlargement: true
})
.toFormat('jpeg')
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains JPEG image data no wider than 200 pixels and no higher
// than 200 pixels regardless of the inputBuffer image dimensions
// outputBuffer contains JPEG image data
// no wider and no higher than 200 pixels
// and no larger than the input image
});
```
Returns **Sharp**
```javascript
const scaleByHalf = await sharp(input)
.metadata()
.then(({ width }) => sharp(input)
.resize(Math.round(width * 0.5))
.toBuffer()
);
```
## min
Preserving aspect ratio, resize the image to be as small as possible
while ensuring its dimensions are greater than or equal to the `width` and `height` specified.
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## ignoreAspectRatio
## extend
Ignoring the aspect ratio of the input, stretch the image to
the exact `width` and/or `height` provided via `resize`.
Extends/pads the edges of the image with the provided background colour.
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
- `extend.top` **[number][8]?**
- `extend.left` **[number][8]?**
- `extend.bottom` **[number][8]?**
- `extend.right` **[number][8]?**
- `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}`)
### Examples
```javascript
// 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)
.resize(140)
.extend({
top: 10,
bottom: 20,
left: 10,
right: 10,
background: { r: 0, g: 0, b: 0, alpha: 0 }
})
...
```
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## withoutEnlargement
## extract
Do not enlarge the output image if the input image width _or_ height are already less than the required dimensions.
This is equivalent to GraphicsMagick's `>` geometry option:
"_change the dimensions of the image only if its width or height exceeds the geometry specification_".
Extract/crop a region of the image.
The default behaviour _before_ function call is `false`, meaning the image will be enlarged.
- Use `extract` before `resize` for pre-resize extraction.
- Use `extract` after `resize` for post-resize extraction.
- Use `extract` before and after for both.
**Parameters**
### Parameters
- `withoutEnlargement` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
- `options` **[Object][9]** describes the region to extract using integral pixel values
- `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
### Examples
```javascript
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
sharp(input)
.extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
.resize(width, height)
.extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
.toFile(output, function(err) {
// Extract a region, resize, then extract from the resized image
});
```
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## trim
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.
The `info` response Object 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 **[Error][13]** Invalid parameters
Returns **Sharp**
[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

View File

@@ -1,11 +1,26 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
## format
- [cache](#cache)
- [concurrency](#concurrency)
- [counters](#counters)
- [simd](#simd)
An Object containing nested boolean values representing the available input and output formats/methods.
### Examples
```javascript
console.log(sharp.format);
```
Returns **[Object][1]**
## versions
An Object containing the version numbers of libvips and its dependencies.
### Examples
```javascript
console.log(sharp.versions);
```
## cache
@@ -14,14 +29,14 @@ 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**
### Parameters
- `options` **([Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) \| [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** Object with the following attributes, or Boolean where true uses default cache settings and false removes all caching (optional, default `true`)
- `options.memory` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** is the maximum memory in MB to use for this cache (optional, default `50`)
- `options.files` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** is the maximum number of files to hold open (optional, default `20`)
- `options.items` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** is the maximum number of operations to cache (optional, default `100`)
- `options` **([Object][1] \| [boolean][2])** Object with the following attributes, or boolean where true uses default cache settings and false removes all caching (optional, default `true`)
- `options.memory` **[number][3]** is the maximum memory in MB to use for this cache (optional, default `50`)
- `options.files` **[number][3]** is the maximum number of files to hold open (optional, default `20`)
- `options.items` **[number][3]** is the maximum number of operations to cache (optional, default `100`)
**Examples**
### Examples
```javascript
const stats = sharp.cache();
@@ -33,7 +48,7 @@ sharp.cache( { files: 0 } );
sharp.cache(false);
```
Returns **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
Returns **[Object][1]**
## concurrency
@@ -47,11 +62,11 @@ is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
This method always returns the current concurrency.
**Parameters**
### Parameters
- `concurrency` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `concurrency` **[number][3]?**
**Examples**
### Examples
```javascript
const threads = sharp.concurrency(); // 4
@@ -59,7 +74,22 @@ sharp.concurrency(2); // 2
sharp.concurrency(0); // 4
```
Returns **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** concurrency
Returns **[number][3]** 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
@@ -68,13 +98,13 @@ 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.
**Examples**
### Examples
```javascript
const counters = sharp.counters(); // { queue: 2, process: 4 }
```
Returns **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
Returns **[Object][1]**
## simd
@@ -84,23 +114,26 @@ 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.
This feature is currently off by default but future versions may reverse this.
Versions of liborc prior to 0.4.25 are known to segfault under heavy load.
### Parameters
**Parameters**
- `simd` **[boolean][2]** (optional, default `true`)
- `simd` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `false`)
**Examples**
### Examples
```javascript
const simd = sharp.simd();
// simd is `true` if SIMD is currently enabled
// simd is `true` if the runtime use of liborc is currently enabled
```
```javascript
const simd = sharp.simd(true);
// attempts to enable the use of SIMD, returning true if available
const simd = sharp.simd(false);
// prevent libvips from using liborc at runtime
```
Returns **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
Returns **[boolean][2]**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number

View File

@@ -1,10 +1,453 @@
# Changelog
### v0.19 - "*suit*"
## v0.25 - *yield*
Requires libvips v8.9.1
### v0.25.3 - 17th May 2020
* Ensure libvips is initialised only once, improves worker thread safety.
[#2143](https://github.com/lovell/sharp/issues/2143)
* Ensure npm platform flag is respected when copying DLLs.
[#2188](https://github.com/lovell/sharp/pull/2188)
[@dimadeveatii](https://github.com/dimadeveatii)
* Allow SVG input with large inline images to be parsed.
[#2195](https://github.com/lovell/sharp/issues/2195)
### v0.25.2 - 20th March 2020
* Provide prebuilt binaries for Linux ARM64v8.
* Add IIIF layout support to tile-based output.
[#2098](https://github.com/lovell/sharp/pull/2098)
[@edsilv](https://github.com/edsilv)
* Ensure input options are consistently and correctly detected.
[#2118](https://github.com/lovell/sharp/issues/2118)
* Ensure N-API prebuilt binaries work on RHEL7 and its derivatives.
[#2119](https://github.com/lovell/sharp/issues/2119)
* Ensure AsyncWorker options are persisted.
[#2130](https://github.com/lovell/sharp/issues/2130)
### v0.25.1 - 7th March 2020
* Ensure prebuilt binaries are fetched based on N-API version.
[#2117](https://github.com/lovell/sharp/issues/2117)
### v0.25.0 - 7th March 2020
* Remove `limitInputPixels` and `sequentialRead` previously deprecated in v0.24.0.
* Migrate internals to N-API.
[#1282](https://github.com/lovell/sharp/issues/1282)
* Add support for 32-bit Windows.
[#2088](https://github.com/lovell/sharp/issues/2088)
* Ensure correct ordering of rotate-then-trim operations.
[#2087](https://github.com/lovell/sharp/issues/2087)
* Ensure composite accepts `limitInputPixels` and `sequentialRead` input options.
[#2099](https://github.com/lovell/sharp/issues/2099)
## v0.24 - "*wit*"
Requires libvips v8.9.0.
### v0.24.1 - 15<sup>th</sup> February 2020
* Prevent use of sequentialRead for EXIF-based rotate operation.
[#2042](https://github.com/lovell/sharp/issues/2042)
* Ensure RGBA LZW TIFF returns correct channel count.
[#2064](https://github.com/lovell/sharp/issues/2064)
### v0.24.0 - 16<sup>th</sup> January 2020
* Drop support for Node.js 8.
[#1910](https://github.com/lovell/sharp/issues/1910)
* Drop support for undefined input where options also provided.
[#1768](https://github.com/lovell/sharp/issues/1768)
* Move `limitInputPixels` and `sequentialRead` to input options, deprecating functions of the same name.
* Expose `delay` and `loop` metadata for animated images.
[#1905](https://github.com/lovell/sharp/issues/1905)
* Ensure correct colour output for 16-bit, 2-channel PNG input with ICC profile.
[#2013](https://github.com/lovell/sharp/issues/2013)
* Prevent use of sequentialRead for rotate operations.
[#2016](https://github.com/lovell/sharp/issues/2016)
* Correctly bind max width and height values when using withoutEnlargement.
[#2024](https://github.com/lovell/sharp/pull/2024)
[@BrychanOdlum](https://github.com/BrychanOdlum)
* Add support for input with 16-bit RGB profile.
[#2037](https://github.com/lovell/sharp/issues/2037)
## v0.23 - "*vision*"
Requires libvips v8.8.1.
### v0.23.4 - 5<sup>th</sup> December 2019
* Handle zero-length Buffer objects when using Node.js v13.2.0+.
* Expose raw TIFFTAG_PHOTOSHOP metadata.
[#1600](https://github.com/lovell/sharp/issues/1600)
* Improve thread safety by using copy-on-write when updating metadata.
[#1986](https://github.com/lovell/sharp/issues/1986)
### v0.23.3 - 17<sup>th</sup> November 2019
* Ensure `trim` operation supports images contained in the alpha channel.
[#1597](https://github.com/lovell/sharp/issues/1597)
* Ensure tile `overlap` option works as expected.
[#1921](https://github.com/lovell/sharp/pull/1921)
[@rustyguts](https://github.com/rustyguts)
* Allow compilation on FreeBSD and variants (broken since v0.23.0)
[#1952](https://github.com/lovell/sharp/pull/1952)
[@pouya-eghbali](https://github.com/pouya-eghbali)
* Ensure `modulate` and other colour-based operations can co-exist.
[#1958](https://github.com/lovell/sharp/issues/1958)
### v0.23.2 - 28<sup>th</sup> October 2019
* Add `background` option to tile output operation.
[#1924](https://github.com/lovell/sharp/pull/1924)
[@neave](https://github.com/neave)
* Add support for Node.js 13.
[#1932](https://github.com/lovell/sharp/pull/1932)
[@MayhemYDG](https://github.com/MayhemYDG)
### v0.23.1 - 26<sup>th</sup> September 2019
* Ensure `sharp.format.vips` is present and correct (filesystem only).
[#1813](https://github.com/lovell/sharp/issues/1813)
* Ensure invalid `width` and `height` provided as options to `resize` throw.
[#1817](https://github.com/lovell/sharp/issues/1817)
* Allow use of 'heic' and 'heif' identifiers with `toFormat`.
[#1834](https://github.com/lovell/sharp/pull/1834)
[@jaubourg](https://github.com/jaubourg)
* Add `premultiplied` option to `composite` operation.
[#1835](https://github.com/lovell/sharp/pull/1835)
[@Andargor](https://github.com/Andargor)
* Allow instance reuse with differing `toBuffer` options.
[#1860](https://github.com/lovell/sharp/pull/1860)
[@RaboliotTheGrey](https://github.com/RaboliotTheGrey)
* Ensure image is at least 3x3 pixels before attempting trim operation.
### v0.23.0 - 29<sup>th</sup> July 2019
* Remove `overlayWith` previously deprecated in v0.22.0.
* Add experimental support for HEIF images. Requires libvips compiled with libheif.
[#1105](https://github.com/lovell/sharp/issues/1105)
* Expose libwebp `smartSubsample` and `reductionEffort` options.
[#1545](https://github.com/lovell/sharp/issues/1545)
* Add experimental support for Worker Threads.
[#1558](https://github.com/lovell/sharp/issues/1558)
* Use libvips' built-in CMYK and sRGB profiles when required.
[#1619](https://github.com/lovell/sharp/issues/1619)
* Drop support for Node.js versions 6 and 11.
[#1674](https://github.com/lovell/sharp/issues/1674)
* Expose `skipBlanks` option for tile-based output.
[#1687](https://github.com/lovell/sharp/pull/1687)
[@RaboliotTheGrey](https://github.com/RaboliotTheGrey)
* Allow use of `failOnError` option with Stream-based input.
[#1691](https://github.com/lovell/sharp/issues/1691)
* Fix rotate/extract ordering for non-90 angles.
[#1755](https://github.com/lovell/sharp/pull/1755)
[@iovdin](https://github.com/iovdin)
## v0.22 - "*uptake*"
Requires libvips v8.7.4.
### v0.22.1 - 25<sup>th</sup> April 2019
* Add `modulate` operation for brightness, saturation and hue.
[#1601](https://github.com/lovell/sharp/pull/1601)
[@Goues](https://github.com/Goues)
* Improve help messaging should `require("sharp")` fail.
[#1638](https://github.com/lovell/sharp/pull/1638)
[@sidharthachatterjee](https://github.com/sidharthachatterjee)
* Add support for Node 12.
[#1668](https://github.com/lovell/sharp/issues/1668)
### v0.22.0 - 18<sup>th</sup> March 2019
* Remove functions previously deprecated in v0.21.0:
`background`, `crop`, `embed`, `ignoreAspectRatio`, `max`, `min` and `withoutEnlargement`.
* Add `composite` operation supporting multiple images and blend modes; deprecate `overlayWith`.
[#728](https://github.com/lovell/sharp/issues/728)
* Add support for `pages` input option for multi-page input.
[#1566](https://github.com/lovell/sharp/issues/1566)
* Allow Stream-based input of raw pixel data.
[#1579](https://github.com/lovell/sharp/issues/1579)
* Add support for `page` input option to GIF and PDF.
[#1595](https://github.com/lovell/sharp/pull/1595)
[@ramiel](https://github.com/ramiel)
## v0.21 - "*teeth*"
Requires libvips v8.7.0.
### v0.21.3 - 19<sup>th</sup> January 2019
* Input image decoding now fails fast, set `failOnError` to change this behaviour.
* Failed filesystem-based input now separates missing file and invalid format errors.
[#1542](https://github.com/lovell/sharp/issues/1542)
### v0.21.2 - 13<sup>th</sup> January 2019
* Ensure all metadata is removed from PNG output unless `withMetadata` used.
* Ensure shortest edge is at least one pixel after resizing.
[#1003](https://github.com/lovell/sharp/issues/1003)
* Add `ensureAlpha` operation to add an alpha channel, if missing.
[#1153](https://github.com/lovell/sharp/issues/1153)
* Expose `pages` and `pageHeight` metadata for multi-page input images.
[#1205](https://github.com/lovell/sharp/issues/1205)
* Expose PNG output options requiring libimagequant.
[#1484](https://github.com/lovell/sharp/issues/1484)
* Expose underlying error message for invalid input.
[#1505](https://github.com/lovell/sharp/issues/1505)
* Prevent mutatation of options passed to `jpeg`.
[#1516](https://github.com/lovell/sharp/issues/1516)
* Ensure forced output format applied correctly when output chaining.
[#1528](https://github.com/lovell/sharp/issues/1528)
### v0.21.1 - 7<sup>th</sup> December 2018
* Install: support `sharp_dist_base_url` npm config, like existing `SHARP_DIST_BASE_URL`.
[#1422](https://github.com/lovell/sharp/pull/1422)
[@SethWen](https://github.com/SethWen)
* Ensure `channel` metadata is correct for raw, greyscale output.
[#1425](https://github.com/lovell/sharp/issues/1425)
* Add support for the "mitchell" kernel for image reductions.
[#1438](https://github.com/lovell/sharp/pull/1438)
[@Daiz](https://github.com/Daiz)
* Allow separate parameters for gamma encoding and decoding.
[#1439](https://github.com/lovell/sharp/pull/1439)
[@Daiz](https://github.com/Daiz)
* Build prototype with `Object.assign` to allow minification.
[#1475](https://github.com/lovell/sharp/pull/1475)
[@jaubourg](https://github.com/jaubourg)
* Expose libvips' recombination matrix operation.
[#1477](https://github.com/lovell/sharp/pull/1477)
[@fromkeith](https://github.com/fromkeith)
* Expose libvips' pyramid/tile options for TIFF output.
[#1483](https://github.com/lovell/sharp/pull/1483)
[@mbklein](https://github.com/mbklein)
### v0.21.0 - 4<sup>th</sup> October 2018
* Deprecate the following resize-related functions:
`crop`, `embed`, `ignoreAspectRatio`, `max`, `min` and `withoutEnlargement`.
Access to these is now via options passed to the `resize` function.
For example:
`embed('north')` is now `resize(width, height, { fit: 'contain', position: 'north' })`,
`crop('attention')` is now `resize(width, height, { fit: 'cover', position: 'attention' })`,
`max().withoutEnlargement()` is now `resize(width, height, { fit: 'inside', withoutEnlargement: true })`.
[#1135](https://github.com/lovell/sharp/issues/1135)
* Deprecate the `background` function.
Per-operation `background` options added to `resize`, `extend` and `flatten` operations.
[#1392](https://github.com/lovell/sharp/issues/1392)
* Add `size` to `metadata` response (Stream and Buffer input only).
[#695](https://github.com/lovell/sharp/issues/695)
* Switch from custom trim operation to `vips_find_trim`.
[#914](https://github.com/lovell/sharp/issues/914)
* Add `chromaSubsampling` and `isProgressive` properties to `metadata` response.
[#1186](https://github.com/lovell/sharp/issues/1186)
* Drop Node 4 support.
[#1212](https://github.com/lovell/sharp/issues/1212)
* Enable SIMD convolution by default.
[#1213](https://github.com/lovell/sharp/issues/1213)
* Add experimental prebuilt binaries for musl-based Linux.
[#1379](https://github.com/lovell/sharp/issues/1379)
* Add support for arbitrary rotation angle via vips_rotate.
[#1385](https://github.com/lovell/sharp/pull/1385)
[@freezy](https://github.com/freezy)
## v0.20 - "*prebuild*"
Requires libvips v8.6.1.
#### v0.19.0 - 11<sup>th</sup> January 2018
### v0.20.8 - 5<sup>th</sup> September 2018
* Avoid race conditions when creating directories during installation.
[#1358](https://github.com/lovell/sharp/pull/1358)
[@ajhool](https://github.com/ajhool)
* Accept floating point values for input density parameter.
[#1362](https://github.com/lovell/sharp/pull/1362)
[@aeirola](https://github.com/aeirola)
### v0.20.7 - 21<sup>st</sup> August 2018
* Use copy+unlink if rename operation fails during installation.
[#1345](https://github.com/lovell/sharp/issues/1345)
### v0.20.6 - 20<sup>th</sup> August 2018
* Add removeAlpha operation to remove alpha channel, if any.
[#1248](https://github.com/lovell/sharp/issues/1248)
* Expose mozjpeg quant_table flag.
[#1285](https://github.com/lovell/sharp/pull/1285)
[@rexxars](https://github.com/rexxars)
* Allow full WebP alphaQuality range of 0-100.
[#1290](https://github.com/lovell/sharp/pull/1290)
[@sylvaindumont](https://github.com/sylvaindumont)
* Cache libvips binaries to reduce re-install time.
[#1301](https://github.com/lovell/sharp/issues/1301)
* Ensure vendor platform mismatch throws error at install time.
[#1303](https://github.com/lovell/sharp/issues/1303)
* Improve install time error messages for FreeBSD users.
[#1310](https://github.com/lovell/sharp/issues/1310)
* Ensure extractChannel works with 16-bit images.
[#1330](https://github.com/lovell/sharp/issues/1330)
* Expose depth option for tile-based output.
[#1342](https://github.com/lovell/sharp/pull/1342)
[@alundavies](https://github.com/alundavies)
* Add experimental entropy field to stats response.
### v0.20.5 - 27<sup>th</sup> June 2018
* Expose libjpeg optimize_coding flag.
[#1265](https://github.com/lovell/sharp/pull/1265)
[@tomlokhorst](https://github.com/tomlokhorst)
### v0.20.4 - 20<sup>th</sup> June 2018
* Prevent possible rounding error when using shrink-on-load and 90/270 degree rotation.
[#1241](https://github.com/lovell/sharp/issues/1241)
[@anahit42](https://github.com/anahit42)
* Ensure extractChannel sets correct single-channel colour space interpretation.
[#1257](https://github.com/lovell/sharp/issues/1257)
[@jeremychone](https://github.com/jeremychone)
### v0.20.3 - 29<sup>th</sup> May 2018
* Fix tint operation by ensuring LAB interpretation and allowing negative values.
[#1235](https://github.com/lovell/sharp/issues/1235)
[@wezside](https://github.com/wezside)
### v0.20.2 - 28<sup>th</sup> April 2018
* Add tint operation to set image chroma.
[#825](https://github.com/lovell/sharp/pull/825)
[@rikh42](https://github.com/rikh42)
* Add environment variable to ignore globally-installed libvips.
[#1165](https://github.com/lovell/sharp/pull/1165)
[@oncletom](https://github.com/oncletom)
* Add support for page selection with multi-page input (GIF/TIFF).
[#1204](https://github.com/lovell/sharp/pull/1204)
[@woolite64](https://github.com/woolite64)
* Add support for Group4 (CCITTFAX4) compression with TIFF output.
[#1208](https://github.com/lovell/sharp/pull/1208)
[@woolite64](https://github.com/woolite64)
### v0.20.1 - 17<sup>th</sup> March 2018
* Improve installation experience when a globally-installed libvips below the minimum required version is found.
[#1148](https://github.com/lovell/sharp/issues/1148)
* Prevent smartcrop error when cumulative rounding is below target size.
[#1154](https://github.com/lovell/sharp/issues/1154)
[@ralrom](https://github.com/ralrom)
* Expose libvips' median filter operation.
[#1161](https://github.com/lovell/sharp/pull/1161)
[@BiancoA](https://github.com/BiancoA)
### v0.20.0 - 5<sup>th</sup> March 2018
* Add support for prebuilt sharp binaries on common platforms.
[#186](https://github.com/lovell/sharp/issues/186)
## v0.19 - "*suit*"
Requires libvips v8.6.1.
### v0.19.1 - 24<sup>th</sup> February 2018
* Expose libvips' linear transform feature.
[#1024](https://github.com/lovell/sharp/pull/1024)
[@3epnm](https://github.com/3epnm)
* Expose angle option for tile-based output.
[#1121](https://github.com/lovell/sharp/pull/1121)
[@BiancoA](https://github.com/BiancoA)
* Prevent crop operation when image already at or below target dimensions.
[#1134](https://github.com/lovell/sharp/issues/1134)
[@pieh](https://github.com/pieh)
### v0.19.0 - 11<sup>th</sup> January 2018
* Expose offset coordinates of strategy-based crop.
[#868](https://github.com/lovell/sharp/issues/868)
@@ -48,17 +491,17 @@ Requires libvips v8.6.1.
* TIFF output: switch default predictor from 'none' to 'horizontal' to match libvips' behaviour.
### v0.18 - "*ridge*"
## v0.18 - "*ridge*"
Requires libvips v8.5.5.
#### v0.18.4 - 18<sup>th</sup> September 2017
### v0.18.4 - 18<sup>th</sup> September 2017
* Ensure input Buffer really is marked as Persistent, prevents mark-sweep GC.
[#950](https://github.com/lovell/sharp/issues/950)
[@lfdoherty](https://github.com/lfdoherty)
#### v0.18.3 - 13<sup>th</sup> September 2017
### v0.18.3 - 13<sup>th</sup> September 2017
* Skip shrink-on-load when trimming.
[#888](https://github.com/lovell/sharp/pull/888)
@@ -68,7 +511,7 @@ Requires libvips v8.5.5.
[#945](https://github.com/lovell/sharp/pull/945)
[@pbomb](https://github.com/pbomb)
#### v0.18.2 - 1<sup>st</sup> July 2017
### v0.18.2 - 1<sup>st</sup> July 2017
* Expose libvips' xres and yres properties for TIFF output.
[#828](https://github.com/lovell/sharp/pull/828)
@@ -85,13 +528,13 @@ Requires libvips v8.5.5.
[#857](https://github.com/lovell/sharp/pull/857)
[@ekremkaraca](https://github.com/ekremkaraca)
#### v0.18.1 - 30<sup>th</sup> May 2017
### v0.18.1 - 30<sup>th</sup> May 2017
* Remove regression from #781 that could cause incorrect shrink calculation.
[#831](https://github.com/lovell/sharp/issues/831)
[@suprMax](https://github.com/suprMax)
#### v0.18.0 - 30<sup>th</sup> May 2017
### v0.18.0 - 30<sup>th</sup> May 2017
* Remove the previously-deprecated output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
@@ -149,11 +592,11 @@ Requires libvips v8.5.5.
[#814](https://github.com/lovell/sharp/pull/814)
[@jingsam](https://github.com/jingsam)
### v0.17 - "*quill*"
## v0.17 - "*quill*"
Requires libvips v8.4.2.
#### v0.17.3 - 1<sup>st</sup> April 2017
### v0.17.3 - 1<sup>st</sup> April 2017
* Allow toBuffer to optionally resolve a Promise with both info and data.
[#143](https://github.com/lovell/sharp/issues/143)
@@ -171,7 +614,7 @@ Requires libvips v8.4.2.
[#738](https://github.com/lovell/sharp/pull/738)
[@kristojorg](https://github.com/kristojorg)
#### v0.17.2 - 11<sup>th</sup> February 2017
### v0.17.2 - 11<sup>th</sup> February 2017
* Ensure Readable side of Stream can start flowing after Writable side has finished.
[#671](https://github.com/lovell/sharp/issues/671)
@@ -181,7 +624,7 @@ Requires libvips v8.4.2.
[#685](https://github.com/lovell/sharp/pull/685)
[@rnanwani](https://github.com/rnanwani)
#### v0.17.1 - 15<sup>th</sup> January 2017
### v0.17.1 - 15<sup>th</sup> January 2017
* Improve error messages for invalid parameters.
[@spikeon](https://github.com/spikeon)
@@ -194,7 +637,7 @@ Requires libvips v8.4.2.
[@wangzhiwei1888](https://github.com/wangzhiwei1888)
[#679](https://github.com/lovell/sharp/issues/679)
#### v0.17.0 - 11<sup>th</sup> December 2016
### v0.17.0 - 11<sup>th</sup> December 2016
* Drop support for versions of Node prior to v4.
@@ -231,17 +674,17 @@ Requires libvips v8.4.2.
[#646](https://github.com/lovell/sharp/issues/646)
[@DaGaMs](https://github.com/DaGaMs)
### v0.16 - "*pencil*"
## v0.16 - "*pencil*"
Requires libvips v8.3.3
#### v0.16.2 - 22<sup>nd</sup> October 2016
### v0.16.2 - 22<sup>nd</sup> October 2016
* Restrict readelf usage to Linux only when detecting global libvips version.
[#602](https://github.com/lovell/sharp/issues/602)
[@caoko](https://github.com/caoko)
#### v0.16.1 - 13<sup>th</sup> October 2016
### v0.16.1 - 13<sup>th</sup> October 2016
* C++11 ABI version is now auto-detected, remove sharp-cxx11 installation flag.
@@ -260,7 +703,7 @@ Requires libvips v8.3.3
[#566](https://github.com/lovell/sharp/issues/566)
[@Nateowami](https://github.com/Nateowami)
#### v0.16.0 - 18<sup>th</sup> August 2016
### v0.16.0 - 18<sup>th</sup> August 2016
* Add pre-compiled libvips for OS X, ARMv7 and ARMv8.
[#312](https://github.com/lovell/sharp/issues/312)
@@ -300,11 +743,11 @@ Requires libvips v8.3.3
* Remove deprecated interpolateWith method - use resize(w, h, { interpolator: ... })
[#310](https://github.com/lovell/sharp/issues/310)
### v0.15 - "*outfit*"
## v0.15 - "*outfit*"
Requires libvips v8.3.1
#### v0.15.1 - 12<sup>th</sup> July 2016
### v0.15.1 - 12<sup>th</sup> July 2016
* Concat Stream-based input in single operation for ~+3% perf and less GC.
[#429](https://github.com/lovell/sharp/issues/429)
@@ -370,7 +813,7 @@ Requires libvips v8.3.1
[#501](https://github.com/lovell/sharp/pull/501)
[@mhirsch](https://github.com/mhirsch)
#### v0.15.0 - 21<sup>st</sup> May 2016
### v0.15.0 - 21<sup>st</sup> May 2016
* Use libvips' new Lanczos 3 kernel as default for image reduction.
Deprecate interpolateWith method, now provided as a resize option.
@@ -388,11 +831,11 @@ Requires libvips v8.3.1
[#413](https://github.com/lovell/sharp/issues/413)
[@jardakotesovec](https://github.com/jardakotesovec)
### v0.14 - "*needle*"
## v0.14 - "*needle*"
Requires libvips v8.2.3
#### v0.14.1 - 16<sup>th</sup> April 2016
### v0.14.1 - 16<sup>th</sup> April 2016
* Allow removal of limitation on input pixel count via limitInputPixels. Use with care.
[#250](https://github.com/lovell/sharp/issues/250)
@@ -416,7 +859,7 @@ Requires libvips v8.2.3
[#412](https://github.com/lovell/sharp/issues/412)
[@nouh](https://github.com/nouh)
#### v0.14.0 - 2<sup>nd</sup> April 2016
### v0.14.0 - 2<sup>nd</sup> April 2016
* Add ability to extend (pad) the edges of an image.
[#128](https://github.com/lovell/sharp/issues/128)
@@ -453,17 +896,17 @@ Requires libvips v8.2.3
* Remove deprecated style of calling extract API. Breaks calls using positional arguments.
[#276](https://github.com/lovell/sharp/issues/276)
### v0.13 - "*mind*"
## v0.13 - "*mind*"
Requires libvips v8.2.2
#### v0.13.1 - 27<sup>th</sup> February 2016
### v0.13.1 - 27<sup>th</sup> February 2016
* Fix embedding onto transparent backgrounds; regression introduced in v0.13.0.
[#366](https://github.com/lovell/sharp/issues/366)
[@diegocsandrim](https://github.com/diegocsandrim)
#### v0.13.0 - 15<sup>th</sup> February 2016
### v0.13.0 - 15<sup>th</sup> February 2016
* Improve vector image support by allowing control of density/DPI.
Switch pre-built libs from Imagemagick to Graphicsmagick.
@@ -508,11 +951,11 @@ Requires libvips v8.2.2
* Add support for gamma correction of images with an alpha channel.
### v0.12 - "*look*"
## v0.12 - "*look*"
Requires libvips v8.2.0
#### v0.12.2 - 16<sup>th</sup> January 2016
### v0.12.2 - 16<sup>th</sup> January 2016
* Upgrade libvips to v8.2.0 for improved vips_shrink.
@@ -530,7 +973,7 @@ Requires libvips v8.2.0
[#331](https://github.com/lovell/sharp/issues/331)
[@dtoubelis](https://github.com/dtoubelis)
#### v0.12.1 - 12<sup>th</sup> December 2015
### v0.12.1 - 12<sup>th</sup> December 2015
* Allow use of SIMD vector instructions (via liborc) to be toggled on/off.
[#172](https://github.com/lovell/sharp/issues/172)
@@ -543,7 +986,7 @@ Requires libvips v8.2.0
* Use the NPM-configured HTTPS proxy, if any, for binary downloads.
#### v0.12.0 - 23<sup>rd</sup> November 2015
### v0.12.0 - 23<sup>rd</sup> November 2015
* Bundle pre-compiled libvips and its dependencies for 64-bit Linux and Windows.
[#42](https://github.com/lovell/sharp/issues/42)
@@ -579,9 +1022,9 @@ Requires libvips v8.2.0
[#309](https://github.com/lovell/sharp/pull/309)
[@papandreou](https://github.com/papandreou)
### v0.11 - "*knife*"
## v0.11 - "*knife*"
#### v0.11.4 - 5<sup>th</sup> November 2015
### v0.11.4 - 5<sup>th</sup> November 2015
* Add corners, e.g. `northeast`, to existing `gravity` option.
[#291](https://github.com/lovell/sharp/pull/291)
@@ -595,13 +1038,13 @@ Requires libvips v8.2.0
[#287](https://github.com/lovell/sharp/pull/287)
[@vlapo](https://github.com/vlapo)
#### v0.11.3 - 8<sup>th</sup> September 2015
### v0.11.3 - 8<sup>th</sup> September 2015
* Intrepret blurSigma, sharpenFlat, and sharpenJagged as double precision.
[#263](https://github.com/lovell/sharp/pull/263)
[@chrisriley](https://github.com/chrisriley)
#### v0.11.2 - 28<sup>th</sup> August 2015
### v0.11.2 - 28<sup>th</sup> August 2015
* Allow crop gravity to be provided as a String.
[#255](https://github.com/lovell/sharp/pull/255)
@@ -609,7 +1052,7 @@ Requires libvips v8.2.0
* Add support for io.js v3 and Node v4.
[#246](https://github.com/lovell/sharp/issues/246)
#### v0.11.1 - 12<sup>th</sup> August 2015
### v0.11.1 - 12<sup>th</sup> August 2015
* Silence MSVC warning: "C4530: C++ exception handler used, but unwind semantics are not enabled".
[#244](https://github.com/lovell/sharp/pull/244)
@@ -619,7 +1062,7 @@ Requires libvips v8.2.0
[#249](https://github.com/lovell/sharp/issues/249)
[@compeak](https://github.com/compeak)
#### v0.11.0 - 15<sup>th</sup> July 2015
### v0.11.0 - 15<sup>th</sup> July 2015
* Allow alpha transparency compositing via new `overlayWith` method.
[#97](https://github.com/lovell/sharp/issues/97)
@@ -647,9 +1090,9 @@ Requires libvips v8.2.0
[#238](https://github.com/lovell/sharp/issues/238)
[@richardadjogah](https://github.com/richardadjogah)
### v0.10 - "*judgment*"
## v0.10 - "*judgment*"
#### v0.10.1 - 1<sup>st</sup> June 2015
### v0.10.1 - 1<sup>st</sup> June 2015
* Allow embed of image with alpha transparency onto non-transparent background.
[#204](https://github.com/lovell/sharp/issues/204)
@@ -659,7 +1102,7 @@ Requires libvips v8.2.0
[#228](https://github.com/lovell/sharp/issues/228)
[@doggan](https://github.com/doggan)
#### v0.10.0 - 23<sup>rd</sup> April 2015
### v0.10.0 - 23<sup>rd</sup> April 2015
* Add support for Windows (x86).
[#19](https://github.com/lovell/sharp/issues/19)

117
docs/firebase.json Normal file
View File

@@ -0,0 +1,117 @@
{
"hosting": {
"site": "pixelplumbing-sharp",
"public": ".",
"ignore": [
".*",
"firebase.json",
"*.md",
"image/**",
"search-index/**"
],
"headers": [
{
"source": "**",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=86400"
}
]
}
],
"redirects": [
{
"source": "**/install/**",
"destination": "/install",
"type": 301
},
{
"source": "/page/install",
"destination": "/install",
"type": 301
},
{
"source": "**/api-constructor/**",
"destination": "/api-constructor",
"type": 301
},
{
"source": "**/api-input/**",
"destination": "/api-input",
"type": 301
},
{
"source": "**/api-output/**",
"destination": "/api-output",
"type": 301
},
{
"source": "**/api-resize/**",
"destination": "/api-resize",
"type": 301
},
{
"source": "**/api-compsite/**",
"destination": "/api-compsite",
"type": 301
},
{
"source": "**/api-operation/**",
"destination": "/api-operation",
"type": 301
},
{
"source": "**/api-colour/**",
"destination": "/api-colour",
"type": 301
},
{
"source": "**/api-channel/**",
"destination": "/api-channel",
"type": 301
},
{
"source": "**/api-utility/**",
"destination": "/api-utility",
"type": 301
},
{
"source": "/page/api",
"destination": "/api-constructor",
"type": 301
},
{
"source": "**/performance/**",
"destination": "/performance",
"type": 301
},
{
"source": "/page/performance",
"destination": "/performance",
"type": 301
},
{
"source": "**/changelog/**",
"destination": "/changelog",
"type": 301
},
{
"source": "/page/changelog",
"destination": "/changelog",
"type": 301
},
{
"source": "/en/**",
"destination": "/",
"type": 301
}
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}

184
docs/humans.txt Normal file
View File

@@ -0,0 +1,184 @@
/* THANKS */
Name: John Cupitt
GitHub: https://github.com/jcupitt
Name: Pierre Inglebert
GitHub: https://github.com/pierreinglebert
Name: Jonathan Ong
GitHub: https://github.com/jonathanong
Name: Chanon Sajjamanochai
GitHub: https://github.com/chanon
Name: Juliano Julio
GitHub: https://github.com/julianojulio
Name: Daniel Gasienica
GitHub: https://github.com/gasi
Name: Julian Walker
GitHub: https://github.com/julianwa
Name: Amit Pitaru
GitHub: https://github.com/apitaru
Name: Brandon Aaron
GitHub: https://github.com/brandonaaron
Name: Andreas Lind
GitHub: https://github.com/papandreouGitHub:
Name: Maurus Cuelenaere
GitHub: https://github.com/mcuelenaere
Name: Linus Unnebäck
GitHub: https://github.com/LinusU
Name: Victor Mateevitsi
GitHub: https://github.com/mvictoras
Name: Alaric Holloway
GitHub: https://github.com/skedastik
Name: Bernhard K. Weisshuhn
GitHub: https://github.com/bkw
Name: David A. Carley
GitHub: https://github.com/dacarley
Name: John Tobin
GitHub: https://github.com/jtobinisaniceguy
Name: Kenton Gray
GitHub: https://github.com/kentongray
Name: Felix Bünemann
GitHub: https://github.com/felixbuenemann
Name: Samy Al Zahrani
GitHub: https://github.com/salzhrani
Name: Chintan Thakkar
GitHub: https://github.com/lemnisk8
Name: F. Orlando Galashan
GitHub: https://github.com/frulo
Name: Kleis Auke Wolthuizen
GitHub: https://github.com/kleisauke
Name: Matt Hirsch
GitHub: https://github.com/mhirsch
Name: Rahul Nanwani
GitHub: https://github.com/rnanwani
Name: Matthias Thoemmes
GitHub: https://github.com/cmtt
Name: Patrick Paskaris
GitHub: https://github.com/ppaskaris
Name: Jérémy Lal
GitHub: https://github.com/kapouer
Name: Alice Monday
GitHub: https://github.com/alice0meta
Name: Kristo Jorgenson
GitHub: https://github.com/kristojorg
Name: Yves Bos
GitHub: https://github.com/YvesBos
Name: Nicolas Coden
GitHub: https://github.com/ncoden
Name: Matt Parrish
GitHub: https://github.com/pbomb
Name: Matthew McEachen
GitHub: https://github.com/mceachen
Name: Jarda Kotěšovec
GitHub: https://github.com/jardakotesovec
Name: Kenric D'Souza
GitHub: https://github.com/AzureByte
Name: Oleh Aleinyk
GitHub: https://github.com/oaleynik
Name: Marcel Bretschneider
GitHub: https://github.com/3epnm
Name: Andrea Bianco
GitHub: https://github.com/BiancoA
Name: Rik Heywood
GitHub: https://github.com/rikh42
Name: Thomas Parisot
GitHub: https://github.com/oncletom
Name: Nathan Graves
GitHub: https://github.com/woolite64
Name: Tom Lokhorst
GitHub: https://github.com/tomlokhorst
Name: Espen Hovlandsdal
GitHub: https://github.com/rexxars
Name: Sylvain Dumont
GitHub: https://github.com/sylvaindumont
Name: Alun Davies
GitHub: https://github.com/alundavies
Name: Aidan Hoolachan
GitHub: https://github.com/ajhool
Name: Axel Eirola
GitHub: https://github.com/aeirola
Name: Freezy
GitHub: https://github.com/freezy
Name: Julian Aubourg
GitHub: https://github.com/jaubourg
Name: Keith Belovay
GitHub: https://github.com/fromkeith
Name: Michael B. Klein
GitHub: https://github.com/mbklein
Name: Jakub Michálek
GitHub: https://github.com/Goues
Name: Ilya Ovdin
GitHub: https://github.com/iovdin
Name: Andargor
GitHub: https://github.com/Andargor
Name: Nicolas Stepien
GitHub: https://github.com/MayhemYDG
Name: Paul Neave
GitHub: https://github.com/neave
Name: Brendan Kennedy
GitHub: https://github.com/rustyguts
Name: Brychan Bennett-Odlum
GitHub: https://github.com/BrychanOdlum
Name: Edward Silverton
GitHub: https://github.com/edsilv
Name: Dumitru Deveatii
GitHub: https://github.com/dimadeveatii

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="86 86 550 550">
<!-- Copyright 2019 Lovell Fuller. This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) License. -->
<path fill="none" stroke="#9c0" stroke-width="80" d="M258.411 285.777l200.176-26.8M244.113 466.413L451.44 438.66M451.441 438.66V238.484M451.441 88.363v171.572l178.725-23.917M270.323 255.602V477.22M272.71 634.17V462.591L93.984 486.515"/>
<path fill="none" stroke="#090" stroke-width="80" d="M451.441 610.246V438.66l178.725-23.91M269.688 112.59v171.58L90.964 308.093"/>
</svg>

After

Width:  |  Height:  |  Size: 592 B

208
docs/index.html Normal file
View File

@@ -0,0 +1,208 @@
<!DOCTYPE html>
<html lang="en">
<head>
<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 and WebP images of varying dimensions">
<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="stylesheet" href="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.css">
<link rel="author" href="/humans.txt" type="text/plain">
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.3/docs/README.md" as="fetch" type="text/markdown" crossorigin>
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
<link rel="dns-prefetch" href="https://www.google-analytics.com">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareSourceCode",
"name": "sharp",
"description": "High performance Node.js image processing",
"url": "https://sharp.pixelplumbing.com",
"codeRepository": "https://github.com/lovell/sharp",
"programmingLanguage": ["JavaScript", "C++"],
"runtimePlatform": "Node.js",
"copyrightHolder": {
"@context": "https://schema.org",
"@type": "Person",
"name": "Lovell Fuller"
},
"copyrightYear": [2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020],
"license": "https://www.apache.org/licenses/LICENSE-2.0"
}
</script>
<style>
:root {
--link-color: #077;
}
@media (max-width: 576px) {
.shorten-strapline {
display: none;
}
}
img[alt='sharp logo'] {
margin: 0 0 16px 16px;
}
.page-content > h2,
.page-content.has-page-title > h2:first-child {
margin-top: 3rem;
}
.page-content h3 {
font-size: 1.4rem;
}
ul ul {
padding-left: 20px;
}
</style>
<title>sharp - High performance Node.js image processing</title>
</head>
<body>
<div id="docute"></div>
<script src="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docute-google-analytics@1/dist/index.min.js"></script>
<script>
const docuteApiTitlePlugin = {
name: 'apiTitle',
extend(api) {
api.processMarkdown(function (md) {
const title = api.store.getters.sidebarLinks
.filter(function (sidebarLink) {
return sidebarLink.link === api.router.history.current.path
})
.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;
});
}
};
const docuteApiSearchPlugin = {
name: 'apiSearch',
extend(api) {
api.enableSearch({
handler: function (keyword) {
if (keyword.trim().length > 2) {
return fetch('/search-index.json')
.then(function (res) {
return res.json();
})
.then(function (entries) {
const results = [];
entries.forEach(function (entry) {
if (entry.k.includes(keyword)) {
results.push({
title: entry.t,
link: entry.l,
description: entry.d,
label: entry.l.startsWith("/api") ? "API" : "Install"
});
}
})
return results.slice(0, 3);
})
} else {
return [];
}
}
});
}
};
new Docute({
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="#"> '
+ '<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>',
darkThemeToggler: true,
detectSystemDarkTheme: true,
footer: '<a href="https://pixelplumbing.com/" target="_blank">pixelplumbing.com<a>',
plugins: [
docuteGoogleAnalytics('UA-13034748-12'),
docuteApiTitlePlugin,
docuteApiSearchPlugin
],
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.3/docs',
nav: [
{
title: 'Funding',
link: 'https://opencollective.com/libvips'
},
{
title: 'GitHub',
link: 'https://github.com/lovell/sharp'
}
],
sidebar: [
{
title: 'Home',
link: '/'
},
{
title: 'Installation',
link: '/install'
},
{
title: 'API',
children: [
{
title: 'Constructor',
link: '/api-constructor'
},
{
title: 'Input metadata',
link: '/api-input'
},
{
title: 'Output options',
link: '/api-output'
},
{
title: 'Resizing images',
link: '/api-resize'
},
{
title: 'Compositing images',
link: '/api-composite'
},
{
title: 'Image operations',
link: '/api-operation'
},
{
title: 'Colour manipulation',
link: '/api-colour'
},
{
title: 'Channel manipulation',
link: '/api-channel'
},
{
title: 'Global properties',
link: '/api-utility'
},
],
},
{
title: 'Performance',
link: '/performance'
},
{
title: 'Changelog',
link: '/changelog'
}
]
});
</script>
</body>
</html>

View File

@@ -1,124 +0,0 @@
# sharp
The typical use case for this high speed Node.js module
is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
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.
OS X, Windows (x64), Linux (x64, ARM) systems do not require
the installation of any external runtime dependencies.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
### Formats
This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images.
Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data.
Streams, Buffer objects and the filesystem can be used for input and output.
A single input Stream can be split into multiple processing pipelines and output Streams.
Deep Zoom image pyramids can be generated,
suitable for use with "slippy map" tile viewers like
[OpenSeadragon](https://github.com/openseadragon/openseadragon)
and [Leaflet](https://github.com/turban/Leaflet.Zoomify).
### Fast
This module is powered by the blazingly fast
[libvips](https://github.com/jcupitt/libvips) image processing library,
originally created in 1989 at Birkbeck College
and currently maintained by
[John Cupitt](https://github.com/jcupitt).
Only small regions of uncompressed image data
are held in memory and processed at a time,
taking full advantage of multiple CPU cores and L1/L2/L3 cache.
Everything remains non-blocking thanks to _libuv_,
no child processes are spawned and Promises/A+ are supported.
### Optimal
Huffman tables are optimised when generating JPEG output images
without having to use separate command line tools like
[jpegoptim](https://github.com/tjko/jpegoptim) and
[jpegtran](http://jpegclub.org/jpegtran/).
PNG filtering is disabled by default,
which for diagrams and line art often produces the same result
as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
### Credits
This module would never have been possible without
the help and code contributions of the following people:
* [John Cupitt](https://github.com/jcupitt)
* [Pierre Inglebert](https://github.com/pierreinglebert)
* [Jonathan Ong](https://github.com/jonathanong)
* [Chanon Sajjamanochai](https://github.com/chanon)
* [Juliano Julio](https://github.com/julianojulio)
* [Daniel Gasienica](https://github.com/gasi)
* [Julian Walker](https://github.com/julianwa)
* [Amit Pitaru](https://github.com/apitaru)
* [Brandon Aaron](https://github.com/brandonaaron)
* [Andreas Lind](https://github.com/papandreou)
* [Maurus Cuelenaere](https://github.com/mcuelenaere)
* [Linus Unnebäck](https://github.com/LinusU)
* [Victor Mateevitsi](https://github.com/mvictoras)
* [Alaric Holloway](https://github.com/skedastik)
* [Bernhard K. Weisshuhn](https://github.com/bkw)
* [David A. Carley](https://github.com/dacarley)
* [John Tobin](https://github.com/jtobinisaniceguy)
* [Kenton Gray](https://github.com/kentongray)
* [Felix Bünemann](https://github.com/felixbuenemann)
* [Samy Al Zahrani](https://github.com/salzhrani)
* [Chintan Thakkar](https://github.com/lemnisk8)
* [F. Orlando Galashan](https://github.com/frulo)
* [Kleis Auke Wolthuizen](https://github.com/kleisauke)
* [Matt Hirsch](https://github.com/mhirsch)
* [Rahul Nanwani](https://github.com/rnanwani)
* [Matthias Thoemmes](https://github.com/cmtt)
* [Patrick Paskaris](https://github.com/ppaskaris)
* [Jérémy Lal](https://github.com/kapouer)
* [Alice Monday](https://github.com/alice0meta)
* [Kristo Jorgenson](https://github.com/kristojorg)
* [Yves Bos](https://github.com/YvesBos)
* [Nicolas Coden](https://github.com/ncoden)
* [Matt Parrish](https://github.com/pbomb)
* [Matthew McEachen](https://github.com/mceachen)
* [Jarda Kotěšovec](https://github.com/jardakotesovec)
* [Kenric D'Souza](https://github.com/AzureByte)
Thank you!
### Licence
Copyright 2013, 2014, 2015, 2016, 2017, 2018 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](http://www.apache.org/licenses/LICENSE-2.0.html)
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.

View File

@@ -8,84 +8,101 @@ npm install sharp
yarn add sharp
```
### Prerequisites
## Prerequisites
* Node v4.5.0+
* C++11 compatible compiler such as gcc 4.8+, clang 3.0+ or MSVC 2013+
* [node-gyp](https://github.com/TooTallNate/node-gyp#installation) and its dependencies (includes Python)
* Node.js v10+
### Linux
## Prebuilt binaries
[![Ubuntu 16.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
Ready-compiled sharp and libvips binaries are provided for use with
Node.js v10+ on the most common platforms:
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 7MB.
* macOS x64 (>= 10.13)
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
* Linux ARM64 (glibc >= 2.29)
* Windows
Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.:
A ~10MB tarball containing libvips and its most commonly used dependencies
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
* Debian 7, 8
* Ubuntu 14.04, 16.04
* Centos 7
* Fedora
* openSUSE 13.2
* Archlinux
* Raspbian Jessie
* Amazon Linux 2017.03.1
* Solus
This provides support for the
JPEG, PNG, WebP, TIFF, GIF (input) and SVG (input) image formats.
To use a globally-installed version of libvips instead of the provided binaries,
The following platforms have prebuilt libvips but not sharp:
* Linux ARMv6
* Linux ARMv7 (glibc >= 2.28)
The following platforms require compilation of both libvips and sharp from source:
* Linux x86
* Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6)
* Linux ARM64 (glibc <= 2.28, musl)
* Linux PowerPC
* FreeBSD
* OpenBSD
## Common problems
The architecture and platform of Node.js used for `npm install`
must be the same as the architecture and platform of Node.js used at runtime.
The `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
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.
## Custom libvips
To use a custom, globally-installed version of libvips instead of the provided binaries,
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`.
If you are using non-stadard paths (anything other than `/usr` or `/usr/local`),
you might need to set `PKG_CONFIG_PATH` during `npm install`
and `LD_LIBRARY_PATH` at runtime.
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).
This allows the use of newer versions of libvips with older versions of sharp.
The use of a globally-installed libvips is unsupported on Windows.
For 32-bit Intel CPUs and older Linux-based operating systems such as Centos 6,
it is recommended to install a system-wide installation of libvips from source:
## Building from source
https://jcupitt.github.io/libvips/install.html#building-libvips-from-a-source-tarball
This module will be compiled from source at `npm install` time when:
#### Alpine Linux
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this),
* prebuilt binaries do not exist for the current platform and Node.js version, or
* when the `npm install --build-from-source` flag is used.
libvips is available in the
[testing repository](https://pkgs.alpinelinux.org/packages?name=vips-dev):
Building from source requires:
* C++11 compiler
* [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies
## Custom prebuilt binaries
This is an advanced approach that most people will not require.
To install the prebuilt libvips binaries from a custom URL,
set the `sharp_dist_base_url` npm config option
or the `SHARP_DIST_BASE_URL` environment variable.
For example, both of the following will result in an attempt to download the file located at
`https://hostname/path/libvips-x.y.z-platform.tar.gz`.
```sh
apk add vips-dev fftw-dev --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/
npm config set sharp_dist_base_url "https://hostname/path/"
npm install sharp
```
The smaller stack size of musl libc means
libvips may need to be used without a cache
via `sharp.cache(false)` to avoid a stack overflow.
```sh
SHARP_DIST_BASE_URL="https://hostname/path/" npm install sharp
```
### Mac OS
To install the prebuilt sharp binaries from a custom URL, please see
[https://github.com/prebuild/prebuild-install#custom-binaries](https://github.com/prebuild/prebuild-install#custom-binaries)
[![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
## FreeBSD
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 7MB.
To use your own version of libvips instead of the provided binaries, 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`.
### Windows x64
[![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 12MB.
Only 64-bit (x64) `node.exe` is supported.
### FreeBSD
libvips must be installed before `npm install` is run.
This can be achieved via package or ports:
The `vips` package must be installed before `npm install` is run.
```sh
pkg install -y pkgconf vips
@@ -95,155 +112,51 @@ pkg install -y pkgconf vips
cd /usr/ports/graphics/vips/ && make install clean
```
FreeBSD's gcc v4 and v5 need `CXXFLAGS=-D_GLIBCXX_USE_C99` set for C++11 support due to
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=193528
## Heroku
### Heroku
Add the
[jemalloc buildpack](https://github.com/gaffneyc/heroku-buildpack-jemalloc)
to reduce the effects of memory fragmentation.
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 7MB.
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
Set
[NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager.
### Docker
## AWS Lambda
[Marc Bachmann](https://github.com/marcbachmann) maintains an
[Ubuntu-based Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
Set the Lambda runtime to `nodejs12.x`.
```sh
docker pull marcbachmann/libvips
```
The binaries in the `node_modules` directory of the
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
must be for the Linux x64 platform.
[Will Jordan](https://github.com/wjordan) maintains an
[Alpine-based Dockerfile for libvips](https://github.com/wjordan/dockerfile-libvips).
```sh
docker pull wjordan/libvips
```
[Tailor Brands](https://github.com/TailorBrands) maintain
[Debian-based Dockerfiles for libvips and nodejs](https://github.com/TailorBrands/docker-libvips).
```sh
docker pull tailor/docker-libvips
```
### AWS Lambda
A [deployment package](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html) for the
[Lambda Execution Environment](http://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html)
can be built using Docker.
On machines other than Linux x64, such as macOS and Windows, run the following:
```sh
rm -rf node_modules/sharp
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs6.10 npm install
npm install --arch=x64 --platform=linux sharp
```
Set the Lambda runtime to Node.js 6.10.
To get the best performance select the largest memory available. A 1536 MB function provides ~12x more CPU time than a 128 MB function.
### Build tools
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)
### Coding tools
* [Sharp TypeScript Types](https://www.npmjs.com/package/@types/sharp)
### CLI tools
* [sharp-cli](https://www.npmjs.com/package/sharp-cli)
### Security
Many users of this module process untrusted, user-supplied images,
but there are aspects of security to consider when doing so.
It is possible to compile libvips with support for various third-party image loaders.
Each of these libraries has undergone differing levels of security testing.
Whilst tools such as [American Fuzzy Lop](http://lcamtuf.coredump.cx/afl/)
and [Valgrind](http://valgrind.org/) have been used to test
the most popular web-based formats, as well as libvips itself,
you are advised to perform your own testing and sandboxing.
ImageMagick in particular has a relatively large attack surface,
which can be partially mitigated with a
[policy.xml](http://www.imagemagick.org/script/resources.php)
configuration file to prevent the use of coders known to be vulnerable.
```xml
<policymap>
<policy domain="coder" rights="none" pattern="EPHEMERAL" />
<policy domain="coder" rights="none" pattern="URL" />
<policy domain="coder" rights="none" pattern="HTTPS" />
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
<policy domain="coder" rights="none" pattern="TEXT" />
<policy domain="coder" rights="none" pattern="SHOW" />
<policy domain="coder" rights="none" pattern="WIN" />
<policy domain="coder" rights="none" pattern="PLT" />
</policymap>
```
Set the `MAGICK_CONFIGURE_PATH` environment variable
to the directory containing the `policy.xml` file.
### Pre-compiled libvips binaries
If a global installation of libvips that meets the
minimum version requirement cannot be found,
this module will attempt to download a pre-compiled bundle of libvips
and its dependencies on Linux and Windows machines.
Should you need to manually download and inspect these files,
you can do so via https://github.com/lovell/sharp-libvips/releases
Should you wish to install these from your own location,
set the `SHARP_DIST_BASE_URL` environment variable, e.g.
Alternatively a Docker container closely matching the Lambda runtime can be used:
```sh
SHARP_DIST_BASE_URL="https://hostname/path/" npm install sharp
rm -rf node_modules/sharp
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs12.x npm install sharp
```
to use `https://hostname/path/libvips-x.y.z-platform.tar.gz`.
To get the best performance select the largest memory available.
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
### Licences
## Electron
This module is licensed under the terms of the
[Apache 2.0 Licence](https://github.com/lovell/sharp/blob/master/LICENSE).
Electron provides versions of the V8 JavaScript engine
that are incompatible with Node.js.
To ensure the correct binaries are used, run the following:
The libraries downloaded and used by this module
are done so under the terms of the following licences,
all of which are compatible with the Apache 2.0 Licence.
```sh
npm install
npx electron-rebuild
```
Use of libraries under the terms of the LGPLv3 is via the
"any later version" clause of the LGPLv2 or LGPLv2.1.
| Library | Used under the terms of |
|---------------|----------------------------------------------------------------------------------------------------------|
| cairo | Mozilla Public License 2.0 |
| expat | MIT Licence |
| fontconfig | [fontconfig Licence](https://cgit.freedesktop.org/fontconfig/tree/COPYING) (BSD-like) |
| freetype | [freetype Licence](http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) |
| giflib | MIT Licence |
| glib | LGPLv3 |
| harfbuzz | MIT Licence |
| lcms | MIT Licence |
| libcroco | LGPLv3 |
| libexif | LGPLv3 |
| libffi | MIT Licence |
| libgsf | LGPLv3 |
| libjpeg-turbo | [zlib License, IJG License](https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/LICENSE.md) |
| libpng | [libpng License](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt) |
| librsvg | LGPLv3 |
| libtiff | [libtiff License](http://www.libtiff.org/misc.html) (BSD-like) |
| libvips | LGPLv3 |
| libwebp | New BSD License |
| libxml2 | MIT Licence |
| pango | LGPLv3 |
| pixman | MIT Licence |
| zlib | [zlib Licence](https://github.com/madler/zlib/blob/master/zlib.h) |
Further help can be found at
[https://electronjs.org/docs/tutorial/using-native-node-modules](https://electronjs.org/docs/tutorial/using-native-node-modules)

View File

@@ -1,52 +1,46 @@
# Performance
### Test environment
A test to benchmark the performance of this module relative to alternatives.
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
* Ubuntu 17.10 (hvm:ebs-ssd, 20180102, ami-0741d47e)
* Node.js v8.9.4
## The contenders
### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.28 - Image processing in pure JavaScript. Bilinear interpolation only.
* [pajk-lwip](https://www.npmjs.com/package/pajk-lwip) v0.2.0 (fork) - Wrapper around CImg that compiles dependencies from source.
* [mapnik](https://www.npmjs.org/package/mapnik) v3.6.2 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) v1.9.3 - Wrapper around libmagick++, supports Buffers only.
* [jimp](https://www.npmjs.com/package/jimp) v0.9.3 - Image processing in pure JavaScript. Provides bicubic interpolation.
* [mapnik](https://www.npmjs.org/package/mapnik) v4.3.1 - 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.
* [images](https://www.npmjs.com/package/images) v3.0.1 - Compiles dependencies from source. Provides bicubic interpolation.
* sharp v0.19.0 / libvips v8.6.1 - Caching within libvips disabled to ensure a fair comparison.
* sharp v0.24.0 / libvips v8.9.0 - Caching within libvips disabled to ensure a fair comparison.
### The task
## 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.
### Results
## Test environment
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
* Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
* Node.js v12.14.1
## Results
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp (bilinear) | buffer | buffer | 1.14 | 1.0 |
| lwip | buffer | buffer | 1.86 | 1.6 |
| mapnik | buffer | buffer | 3.34 | 2.9 |
| imagemagick-native | buffer | buffer | 4.13 | 3.6 |
| gm | buffer | buffer | 4.21 | 3.7 |
| gm | file | file | 4.27 | 3.7 |
| imagemagick | file | file | 4.67 | 4.1 |
| images (bicubic) | file | file | 6.22 | 5.5 |
| sharp | stream | stream | 24.43 | 21.4 |
| sharp | file | file | 25.97 | 22.7 |
| sharp | file | buffer | 26.00 | 22.8 |
| sharp | buffer | file | 26.33 | 23.0 |
| sharp | buffer | buffer | 26.43 | 23.1 |
| jimp | buffer | buffer | 0.72 | 1.0 |
| mapnik | buffer | buffer | 3.02 | 4.2 |
| gm | buffer | buffer | 3.90 | 5.4 |
| gm | file | file | 3.94 | 5.5 |
| imagemagick | file | file | 4.30 | 6.0 |
| sharp | stream | stream | 23.65 | 32.8 |
| sharp | file | file | 24.66 | 34.3 |
| sharp | buffer | buffer | 25.14 | 34.9 |
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.
### Benchmark test prerequisites
## Running the benchmark test
Requires _ImageMagick_, _GraphicsMagick_ and _Mapnik_:
@@ -57,15 +51,13 @@ brew install mapnik
```
```sh
sudo apt-get install imagemagick libmagick++-dev graphicsmagick mapnik-dev
sudo apt-get install imagemagick libmagick++-dev graphicsmagick libmapnik-dev
```
```sh
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick mapnik-devel
```
### Running the benchmark test
```sh
git clone https://github.com/lovell/sharp.git
cd sharp

2
docs/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

1
docs/search-index.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,59 @@
'use strict';
const fs = require('fs');
const { extractDescription, extractKeywords } = require('./extract');
const searchIndex = [];
// Install
const contents = fs.readFileSync(`${__dirname}/../install.md`, 'utf8');
const matches = contents.matchAll(
/## (?<title>[A-Za-z ]+)\n\n(?<body>[^#]+)/gs
);
for (const match of matches) {
const { title, body } = match.groups;
const description = extractDescription(body);
searchIndex.push({
t: title,
d: description,
k: extractKeywords(`${title} ${description}`),
l: `/install#${title.toLowerCase().replace(/ /g, '-')}`
});
}
// API
[
'constructor',
'input',
'output',
'resize',
'composite',
'operation',
'channel',
'colour',
'utility'
].forEach((section) => {
const contents = fs.readFileSync(`${__dirname}/../api-${section}.md`, 'utf8');
const matches = contents.matchAll(
/\n## (?<title>[A-Za-z]+)\n\n(?<firstparagraph>.+?)\n\n/gs
);
for (const match of matches) {
const { title, firstparagraph } = match.groups;
const description = firstparagraph.startsWith('###')
? 'Constructor'
: extractDescription(firstparagraph);
searchIndex.push({
t: title,
d: description,
k: extractKeywords(`${title} ${description}`),
l: `/api-${section}#${title.toLowerCase()}`
});
}
});
fs.writeFileSync(
`${__dirname}/../search-index.json`,
JSON.stringify(searchIndex)
);

View File

@@ -0,0 +1,80 @@
'use strict';
const stopWords = [
'a',
'about',
'all',
'already',
'always',
'an',
'and',
'any',
'are',
'as',
'at',
'be',
'been',
'by',
'can',
'do',
'does',
'each',
'either',
'etc',
'for',
'from',
'get',
'gets',
'has',
'have',
'how',
'if',
'in',
'is',
'it',
'its',
'may',
'more',
'much',
'no',
'not',
'of',
'on',
'or',
'over',
'set',
'sets',
'should',
'that',
'the',
'their',
'there',
'therefore',
'these',
'this',
'to',
'use',
'using',
'when',
'which',
'will',
'with'
];
const extractDescription = (str) =>
str
.replace(/\(http[^)]+/g, '')
.replace(/\s+/g, ' ')
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
.replace(/\s+/g, ' ')
.substr(0, 140)
.trim();
const extractKeywords = (str) =>
str
.split(/[ -/]/)
.map((word) => word.toLowerCase().replace(/[^a-z]/g, ''))
.filter((word) => word.length > 2 && !stopWords.includes(word))
.join(' ');
module.exports = { extractDescription, extractKeywords };

35
install/dll-copy.js Normal file
View File

@@ -0,0 +1,35 @@
'use strict';
const fs = require('fs');
const path = require('path');
const libvips = require('../lib/libvips');
const npmLog = require('npmlog');
const platform = process.env.npm_config_platform || process.platform;
if (platform === 'win32') {
const buildDir = path.join(__dirname, '..', 'build');
const buildReleaseDir = path.join(buildDir, 'Release');
npmLog.info('sharp', `Creating ${buildReleaseDir}`);
try {
libvips.mkdirSync(buildDir);
libvips.mkdirSync(buildReleaseDir);
} catch (err) {}
const vendorLibDir = path.join(__dirname, '..', 'vendor', 'lib');
npmLog.info('sharp', `Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
try {
fs
.readdirSync(vendorLibDir)
.filter(function (filename) {
return /\.dll$/.test(filename);
})
.forEach(function (filename) {
fs.copyFileSync(
path.join(vendorLibDir, filename),
path.join(buildReleaseDir, filename)
);
});
} catch (err) {
npmLog.error('sharp', err.message);
}
}

118
install/libvips.js Normal file
View File

@@ -0,0 +1,118 @@
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const detectLibc = require('detect-libc');
const npmLog = require('npmlog');
const semver = require('semver');
const simpleGet = require('simple-get');
const tar = require('tar');
const agent = require('../lib/agent');
const libvips = require('../lib/libvips');
const platform = require('../lib/platform');
const minimumGlibcVersionByArch = {
arm: '2.28',
arm64: '2.29',
x64: '2.17'
};
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersionLabelled}/`;
const fail = function (err) {
npmLog.error('sharp', err.message);
if (err.code === 'EACCES') {
npmLog.info('sharp', 'Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
}
npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/install for required dependencies');
process.exit(1);
};
const extractTarball = function (tarPath) {
const vendorPath = path.join(__dirname, '..', 'vendor');
libvips.mkdirSync(vendorPath);
tar
.extract({
file: tarPath,
cwd: vendorPath,
strict: true
})
.catch(function (err) {
if (/unexpected end of file/.test(err.message)) {
npmLog.error('sharp', `Please delete ${tarPath} as it is not a valid tarball`);
}
fail(err);
});
};
try {
const useGlobalLibvips = libvips.useGlobalLibvips();
if (useGlobalLibvips) {
const globalLibvipsVersion = libvips.globalLibvipsVersion();
npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`);
npmLog.info('sharp', 'Building from source via node-gyp');
process.exit(1);
} else if (libvips.hasVendoredLibvips()) {
npmLog.info('sharp', `Using existing vendored libvips v${minimumLibvipsVersion}`);
} else {
// Is this arch/platform supported?
const arch = process.env.npm_config_arch || process.arch;
const platformAndArch = platform();
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version) {
if (semver.lt(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
}
}
// Download to per-process temporary file
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.gz';
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
if (fs.existsSync(tarPathCache)) {
npmLog.info('sharp', `Using cached ${tarPathCache}`);
extractTarball(tarPathCache);
} else {
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
const tmpFile = fs.createWriteStream(tarPathTemp);
const url = distBaseUrl + tarFilename;
npmLog.info('sharp', `Downloading ${url}`);
simpleGet({ url: url, agent: agent() }, function (err, response) {
if (err) {
fail(err);
} else if (response.statusCode === 404) {
fail(new Error(`Prebuilt libvips ${minimumLibvipsVersion} binaries are not yet available for ${platformAndArch}`));
} else if (response.statusCode !== 200) {
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
} else {
response
.on('error', fail)
.pipe(tmpFile);
}
});
tmpFile
.on('error', fail)
.on('close', function () {
try {
// Attempt to rename
fs.renameSync(tarPathTemp, tarPathCache);
} catch (err) {
// Fall back to copy and unlink
fs.copyFileSync(tarPathTemp, tarPathCache);
fs.unlinkSync(tarPathTemp);
}
extractTarball(tarPathCache);
});
}
}
} catch (err) {
fail(err);
}

View File

@@ -20,15 +20,18 @@ function env (key) {
module.exports = function () {
try {
const proxy = url.parse(proxies.map(env).find(is.string));
const proxy = new url.URL(proxies.map(env).find(is.string));
const tunnel = proxy.protocol === 'https:'
? tunnelAgent.httpsOverHttps
: tunnelAgent.httpsOverHttp;
const proxyAuth = proxy.username && proxy.password
? `${proxy.username}:${proxy.password}`
: null;
return tunnel({
proxy: {
port: Number(proxy.port),
host: proxy.hostname,
proxyAuth: proxy.auth
proxyAuth
}
});
} catch (err) {

View File

@@ -12,18 +12,55 @@ const bool = {
eor: 'eor'
};
/**
* Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
*
* @example
* sharp('rgba.png')
* .removeAlpha()
* .toFile('rgb.png', function(err, info) {
* // rgb.png is a 3 channel image without an alpha channel
* });
*
* @returns {Sharp}
*/
function removeAlpha () {
this.options.removeAlpha = true;
return this;
}
/**
* Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
*
* @since 0.21.2
*
* @example
* sharp('rgb.jpg')
* .ensureAlpha()
* .toFile('rgba.png', function(err, info) {
* // rgba.png is a 4 channel image with a fully opaque alpha channel
* });
*
* @returns {Sharp}
*/
function ensureAlpha () {
this.options.ensureAlpha = true;
return this;
}
/**
* Extract a single channel from a multi-channel image.
*
* @example
* sharp(input)
* .extractChannel('green')
* .toFile('input_green.jpg', function(err, info) {
* .toColourspace('b-w')
* .toFile('green.jpg', function(err, info) {
* // info.channels === 1
* // input_green.jpg contains the green channel of the input image
* // green.jpg is a greyscale image containing the green channel of the input
* });
*
* @param {Number|String} channel - zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
* @param {number|string} channel - zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
* @returns {Sharp}
* @throws {Error} Invalid channel
*/
@@ -38,7 +75,7 @@ function extractChannel (channel) {
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
this.options.extractChannel = channel;
} else {
throw new Error('Cannot extract invalid channel ' + channel);
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue', channel);
}
return this;
}
@@ -54,7 +91,7 @@ function extractChannel (channel) {
* Buffers may be any of the image formats supported by sharp: JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data.
* 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.
*
* @param {Array<String|Buffer>|String|Buffer} images - one or more images (file paths, Buffers).
* @param {Array<string|Buffer>|string|Buffer} images - one or more images (file paths, Buffers).
* @param {Object} options - image options, see `sharp()` constructor.
* @returns {Sharp}
* @throws {Error} Invalid parameters
@@ -82,7 +119,7 @@ function joinChannel (images, options) {
* // then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
* });
*
* @param {String} boolOp - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @param {string} boolOp - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -90,7 +127,7 @@ function bandbool (boolOp) {
if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) {
this.options.bandBoolOp = boolOp;
} else {
throw new Error('Invalid bandbool operation ' + boolOp);
throw is.invalidParameterError('boolOp', 'one of: and, or, eor', boolOp);
}
return this;
}
@@ -100,13 +137,13 @@ function bandbool (boolOp) {
* @private
*/
module.exports = function (Sharp) {
// Public instance functions
[
Object.assign(Sharp.prototype, {
// Public instance functions
removeAlpha,
ensureAlpha,
extractChannel,
joinChannel,
bandbool
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
// Class attributes
Sharp.bool = bool;

View File

@@ -16,25 +16,17 @@ const colourspace = {
};
/**
* Set the background for the `embed`, `flatten` and `extend` operations.
* The default background is `{r: 0, g: 0, b: 0, alpha: 1}`, black without transparency.
* 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.
*
* Delegates to the _color_ module, which can throw an Error
* but is liberal in what it accepts, clipping values to sensible min/max.
* The alpha value is a float between `0` (transparent) and `1` (opaque).
*
* @param {String|Object} rgba - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @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
*/
function background (rgba) {
const colour = color(rgba);
this.options.background = [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
function tint (rgb) {
const colour = color(rgb);
this.options.tintA = colour.a();
this.options.tintB = colour.b();
return this;
}
@@ -65,13 +57,13 @@ function grayscale (grayscale) {
/**
* Set the output colourspace.
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
* @param {String} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColourspace (colourspace) {
if (!is.string(colourspace)) {
throw new Error('Invalid output colourspace ' + colourspace);
throw is.invalidParameterError('colourspace', 'string', colourspace);
}
this.options.colourspace = colourspace;
return this;
@@ -79,7 +71,7 @@ function toColourspace (colourspace) {
/**
* Alternative spelling of `toColourspace`.
* @param {String} [colorspace] - output colorspace.
* @param {string} [colorspace] - output colorspace.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -87,20 +79,43 @@ function toColorspace (colorspace) {
return this.toColourspace(colorspace);
}
/**
* Update a colour attribute of the this.options Object.
* @private
* @param {string} key
* @param {string|Object} value
* @throws {Error} Invalid value
*/
function _setBackgroundColourOption (key, value) {
if (is.defined(value)) {
if (is.object(value) || is.string(value)) {
const colour = color(value);
this.options[key] = [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
} else {
throw is.invalidParameterError('background', 'object or string', value);
}
}
}
/**
* Decorate the Sharp prototype with colour-related functions.
* @private
*/
module.exports = function (Sharp) {
// Public instance functions
[
background,
Object.assign(Sharp.prototype, {
// Public
tint,
greyscale,
grayscale,
toColourspace,
toColorspace
].forEach(function (f) {
Sharp.prototype[f.name] = f;
toColorspace,
// Private
_setBackgroundColourOption
});
// Class attributes
Sharp.colourspace = colourspace;

View File

@@ -3,24 +3,67 @@
const is = require('./is');
/**
* Overlay (composite) an image over the processed (resized, extracted etc.) image.
* Blend modes.
* @member
* @private
*/
const blend = {
clear: 'clear',
source: 'source',
over: 'over',
in: 'in',
out: 'out',
atop: 'atop',
dest: 'dest',
'dest-over': 'dest-over',
'dest-in': 'dest-in',
'dest-out': 'dest-out',
'dest-atop': 'dest-atop',
xor: 'xor',
add: 'add',
saturate: 'saturate',
multiply: 'multiply',
screen: 'screen',
overlay: 'overlay',
darken: 'darken',
lighten: 'lighten',
'colour-dodge': 'colour-dodge',
'color-dodge': 'colour-dodge',
'colour-burn': 'colour-burn',
'color-burn': 'colour-burn',
'hard-light': 'hard-light',
'soft-light': 'soft-light',
difference: 'difference',
exclusion: 'exclusion'
};
/**
* Composite image(s) over the processed (resized, extracted etc.) image.
*
* The overlay image must be the same size or smaller than the processed 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`.
*
* If the overlay image contains an alpha channel then composition with premultiplication will occur.
* 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`,
* `colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
* `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
* and https://www.cairographics.org/operators/
*
* @since 0.22.0
*
* @example
* sharp('input.png')
* .rotate(180)
* .resize(300)
* .flatten()
* .background('#ff6600')
* .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
* .flatten( { background: '#ff6600' } )
* .composite([{ input: 'overlay.png', gravity: 'southeast' }])
* .sharpen()
* .withMetadata()
* .quality(90)
* .webp()
* .webp( { quality: 90 } )
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains upside down, 300px wide, alpha channel flattened
@@ -28,63 +71,95 @@ const is = require('./is');
* // sharpened, with metadata, 90% quality WebP image data. Phew!
* });
*
* @param {(Buffer|String)} overlay - Buffer containing image data or String containing the path to an image file.
* @param {Object} [options]
* @param {String} [options.gravity='centre'] - gravity at which to place the overlay.
* @param {Number} [options.top] - the pixel offset from the top edge.
* @param {Number} [options.left] - the pixel offset from the left edge.
* @param {Boolean} [options.tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
* @param {Boolean} [options.cutout=false] - set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another.
* @param {Number} [options.density=72] - integral number representing the DPI for vector overlay image.
* @param {Object} [options.raw] - describes overlay when using raw pixel data.
* @param {Number} [options.raw.width]
* @param {Number} [options.raw.height]
* @param {Number} [options.raw.channels]
* @param {Object} [options.create] - describes a blank overlay to be created.
* @param {Number} [options.create.width]
* @param {Number} [options.create.height]
* @param {Number} [options.create.channels] - 3-4
* @param {String|Object} [options.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 - Ordered list of images to composite
* @param {Buffer|String} [images[].input] - Buffer containing image data, String containing the path to an image file, or Create object (see below)
* @param {Object} [images[].input.create] - describes a blank overlay to be created.
* @param {Number} [images[].input.create.width]
* @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 {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.
* @param {Number} [images[].left] - the pixel offset from the left edge.
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
* @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option.
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
* @param {Number} [images[].raw.width]
* @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels]
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function overlayWith (overlay, options) {
this.options.overlay = this._createInputDescriptor(overlay, options, {
allowStream: false
});
if (is.object(options)) {
if (is.defined(options.tile)) {
if (is.bool(options.tile)) {
this.options.overlayTile = options.tile;
} else {
throw new Error('Invalid overlay tile ' + options.tile);
}
}
if (is.defined(options.cutout)) {
if (is.bool(options.cutout)) {
this.options.overlayCutout = options.cutout;
} else {
throw new Error('Invalid overlay cutout ' + options.cutout);
}
}
if (is.defined(options.left) || is.defined(options.top)) {
if (is.integer(options.left) && options.left >= 0 && is.integer(options.top) && options.top >= 0) {
this.options.overlayXOffset = options.left;
this.options.overlayYOffset = options.top;
} else {
throw new Error('Invalid overlay left ' + options.left + ' and/or top ' + options.top);
}
}
if (is.defined(options.gravity)) {
if (is.integer(options.gravity) && is.inRange(options.gravity, 0, 8)) {
this.options.overlayGravity = options.gravity;
} else if (is.string(options.gravity) && is.integer(this.constructor.gravity[options.gravity])) {
this.options.overlayGravity = this.constructor.gravity[options.gravity];
} else {
throw new Error('Unsupported overlay gravity ' + options.gravity);
}
}
function composite (images) {
if (!Array.isArray(images)) {
throw is.invalidParameterError('images to composite', 'array', images);
}
this.options.composite = images.map(image => {
if (!is.object(image)) {
throw is.invalidParameterError('image to composite', 'object', image);
}
const inputOptions = this._inputOptionsFromObject(image);
const composite = {
input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }),
blend: 'over',
tile: false,
left: -1,
top: -1,
gravity: 0,
premultiplied: false
};
if (is.defined(image.blend)) {
if (is.string(blend[image.blend])) {
composite.blend = blend[image.blend];
} else {
throw is.invalidParameterError('blend', 'valid blend name', image.blend);
}
}
if (is.defined(image.tile)) {
if (is.bool(image.tile)) {
composite.tile = image.tile;
} else {
throw is.invalidParameterError('tile', 'boolean', image.tile);
}
}
if (is.defined(image.left)) {
if (is.integer(image.left) && image.left >= 0) {
composite.left = image.left;
} else {
throw is.invalidParameterError('left', 'positive integer', image.left);
}
}
if (is.defined(image.top)) {
if (is.integer(image.top) && image.top >= 0) {
composite.top = image.top;
} else {
throw is.invalidParameterError('top', 'positive integer', image.top);
}
}
if (composite.left !== composite.top && Math.min(composite.left, composite.top) === -1) {
throw new Error('Expected both left and top to be set');
}
if (is.defined(image.gravity)) {
if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
composite.gravity = image.gravity;
} else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
composite.gravity = this.constructor.gravity[image.gravity];
} else {
throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
}
}
if (is.defined(image.premultiplied)) {
if (is.bool(image.premultiplied)) {
composite.premultiplied = image.premultiplied;
} else {
throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
}
}
return composite;
});
return this;
}
@@ -93,5 +168,6 @@ function overlayWith (overlay, options) {
* @private
*/
module.exports = function (Sharp) {
Sharp.prototype.overlayWith = overlayWith;
Sharp.prototype.composite = composite;
Sharp.blend = blend;
};

View File

@@ -1,51 +1,44 @@
'use strict';
const path = require('path');
const util = require('util');
const stream = require('stream');
const events = require('events');
const semver = require('semver');
const is = require('./is');
const platform = require('./platform');
const sharp = require('../build/Release/sharp.node');
// Vendor platform
(function () {
let vendorPlatformId;
try {
vendorPlatformId = require('../vendor/platform.json');
} catch (err) {
return;
}
const currentPlatformId = platform();
/* istanbul ignore if */
if (currentPlatformId !== vendorPlatformId) {
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm rebuild'.`);
}
})();
require('./libvips').hasVendoredLibvips();
// Versioning
let versions = {
vips: sharp.libvipsVersion()
};
(function () {
// Does libvips meet minimum requirement?
const libvipsVersionMin = require('../package.json').config.libvips;
/* istanbul ignore if */
if (semver.lt(versions.vips, libvipsVersionMin)) {
throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin);
/* istanbul ignore next */
try {
require('../build/Release/sharp.node');
} catch (err) {
// Bail early if bindings aren't available
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, ''];
if (/NODE_MODULE_VERSION/.test(err.message)) {
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
} else if (/invalid ELF header/.test(err.message)) {
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
} else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
help.push('- Run "brew update && brew upgrade vips"');
} else if (/Cannot find module/.test(err.message)) {
help.push('- Run "npm rebuild --verbose sharp" and look for errors');
} else {
help.push(
'- Remove the "node_modules/sharp" directory then run',
' "npm install --ignore-scripts=false --verbose" and look for errors'
);
}
// Include versions of dependencies, if present
try {
versions = require('../vendor/versions.json');
} catch (err) {}
})();
help.push(
'- Consult the installation documentation at https://sharp.pixelplumbing.com/install',
'- Search for this error at https://github.com/lovell/sharp/issues', ''
);
const error = help.join('\n');
throw new Error(error);
}
// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
/**
* @class Sharp
* @constructs sharp
*
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
*
@@ -81,7 +74,7 @@ const debuglog = util.debuglog('sharp');
* width: 300,
* height: 200,
* channels: 4,
* background: { r: 255, g: 0, b: 0, alpha: 128 }
* background: { r: 255, g: 0, b: 0, alpha: 0.5 }
* }
* })
* .png()
@@ -90,13 +83,19 @@ const debuglog = util.debuglog('sharp');
*
* @param {(Buffer|String)} [input] - if present, can be
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, 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=false] - by default apply a "best effort"
* to decode images, even if the data is corrupt or invalid. Set this flag to true
* if you'd rather halt processing and raise an error when loading invalid images.
* @param {Number} [options.density=72] - integral number representing the DPI for vector images.
* @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 {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.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 {Number} [options.density=72] - number representing the DPI for vector images.
* @param {Number} [options.pages=1] - number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages.
* @param {Number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {Number} [options.raw.width]
* @param {Number} [options.raw.height]
@@ -118,11 +117,6 @@ const Sharp = function (input, options) {
}
stream.Duplex.call(this);
this.options = {
// input options
sequentialRead: false,
limitInputPixels: Math.pow(0x3FFF, 2),
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
@@ -135,10 +129,12 @@ const Sharp = function (input, options) {
width: -1,
height: -1,
canvas: 'crop',
crop: 0,
embed: 0,
position: 0,
resizeBackground: [0, 0, 0, 255],
useExifOrientation: false,
angle: 0,
rotationAngle: 0,
rotationBackground: [0, 0, 0, 255],
rotateBeforePreExtract: false,
flip: false,
flop: false,
@@ -146,34 +142,39 @@ const Sharp = function (input, options) {
extendBottom: 0,
extendLeft: 0,
extendRight: 0,
extendBackground: [0, 0, 0, 255],
withoutEnlargement: false,
kernel: 'lanczos3',
fastShrinkOnLoad: true,
// operations
background: [0, 0, 0, 255],
tintA: 128,
tintB: 128,
flatten: false,
flattenBackground: [0, 0, 0],
negate: false,
medianSize: 0,
blurSigma: 0,
sharpenSigma: 0,
sharpenFlat: 1,
sharpenJagged: 2,
threshold: 0,
thresholdGrayscale: true,
trimTolerance: 0,
trimThreshold: 0,
gamma: 0,
gammaOut: 0,
greyscale: false,
normalise: 0,
normalise: false,
brightness: 1,
saturation: 1,
hue: 0,
booleanBufferIn: null,
booleanFileIn: '',
joinChannelIn: [],
extractChannel: -1,
removeAlpha: false,
ensureAlpha: false,
colourspace: 'srgb',
// overlay
overlayGravity: 0,
overlayXOffset: -1,
overlayYOffset: -1,
overlayTile: false,
overlayCutout: false,
composite: [],
// output
fileOut: '',
formatOut: 'input',
@@ -188,26 +189,50 @@ const Sharp = function (input, options) {
jpegTrellisQuantisation: false,
jpegOvershootDeringing: false,
jpegOptimiseScans: false,
jpegOptimiseCoding: true,
jpegQuantisationTable: 0,
pngProgressive: false,
pngCompressionLevel: 9,
pngAdaptiveFiltering: false,
pngPalette: false,
pngQuality: 100,
pngColours: 256,
pngDither: 1,
webpQuality: 80,
webpAlphaQuality: 100,
webpLossless: false,
webpNearLossless: false,
webpSmartSubsample: false,
webpReductionEffort: 4,
tiffQuality: 80,
tiffCompression: 'jpeg',
tiffPredictor: 'horizontal',
tiffPyramid: false,
tiffSquash: false,
tiffTile: false,
tiffTileHeight: 256,
tiffTileWidth: 256,
tiffXres: 1.0,
tiffYres: 1.0,
heifQuality: 80,
heifLossless: false,
heifCompression: 'hevc',
tileSize: 256,
tileOverlap: 0,
tileContainer: 'fs',
tileLayout: 'dz',
tileFormat: 'last',
tileDepth: 'last',
tileAngle: 0,
tileSkipBlanks: -1,
tileBackground: [255, 255, 255, 255],
linearA: 1,
linearB: 0,
// Function to notify of libvips warnings
debuglog: debuglog,
// Function to notify of queue length changes
queueListener: function (queueLength) {
queue.emit('change', queueLength);
Sharp.queue.emit('change', queueLength);
}
};
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
@@ -216,33 +241,84 @@ const Sharp = function (input, options) {
util.inherits(Sharp, stream.Duplex);
/**
* An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread
* - complete
* @member
* 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.
*
* @example
* sharp.queue.on('change', function(queueLength) {
* console.log('Queue contains ' + queueLength + ' task(s)');
* const pipeline = sharp().rotate();
* pipeline.clone().resize(800, 600).pipe(firstWritableStream);
* pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
* readableStream.pipe(pipeline);
* // firstWritableStream receives auto-rotated, resized readableStream
* // secondWritableStream receives auto-rotated, extracted region of readableStream
*
* @example
* // 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 promises = [];
*
* promises.push(
* sharpStream
* .clone()
* .jpeg({ quality: 100 })
* .toFile("originalFile.jpg")
* );
*
* promises.push(
* sharpStream
* .clone()
* .resize({ width: 500 })
* .jpeg({ quality: 80 })
* .toFile("optimized-500.jpg")
* );
*
* promises.push(
* sharpStream
* .clone()
* .resize({ width: 500 })
* .webp({ quality: 80 })
* .toFile("optimized-500.webp")
* );
*
* // https://github.com/sindresorhus/got#gotstreamurl-options
* got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
*
* Promise.all(promises)
* .then(res => { console.log("Done!", res); })
* .catch(err => {
* console.error("Error processing files, let's clean it up", err);
* try {
* fs.unlinkSync("originalFile.jpg");
* fs.unlinkSync("optimized-500.jpg");
* fs.unlinkSync("optimized-500.webp");
* } catch (e) {}
* });
*
* @returns {Sharp}
*/
const queue = new events.EventEmitter();
Sharp.queue = queue;
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @example
* console.log(sharp.format);
* @returns {Object}
*/
Sharp.format = sharp.format();
/**
* An Object containing the version numbers of libvips and its dependencies.
* @member
* @example
* console.log(sharp.versions);
*/
Sharp.versions = versions;
function clone () {
// Clone existing options
const clone = this.constructor.call();
clone.options = Object.assign({}, this.options);
// Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) {
this.on('finish', () => {
// Clone inherits input data
this._flattenBufferIn();
clone.options.bufferIn = this.options.bufferIn;
clone.emit('finish');
});
}
return clone;
}
Object.assign(Sharp.prototype, { clone });
/**
* Export constructor.

Binary file not shown.

Binary file not shown.

View File

@@ -4,12 +4,27 @@ const color = require('color');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* Extract input options, if any, from an object.
* @private
*/
function _inputOptionsFromObject (obj) {
const { raw, density, limitInputPixels, sequentialRead, failOnError } = obj;
return [raw, density, limitInputPixels, sequentialRead, failOnError].some(is.defined)
? { raw, density, limitInputPixels, sequentialRead, failOnError }
: undefined;
}
/**
* Create Object containing input and input-related options.
* @private
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = { failOnError: false };
const inputDescriptor = {
failOnError: true,
limitInputPixels: Math.pow(0x3FFF, 2),
sequentialRead: false
};
if (is.string(input)) {
// filesystem
inputDescriptor.file = input;
@@ -19,11 +34,17 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
// Plain Object descriptor, e.g. create
inputOptions = input;
} else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream
if (_inputOptionsFromObject(inputOptions)) {
// Stream with options
inputDescriptor.buffer = [];
}
} else if (!is.defined(input) && !is.defined(inputOptions) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream without options
inputDescriptor.buffer = [];
} else {
throw new Error('Unsupported input ' + typeof input);
throw new Error(`Unsupported input '${input}' of type ${typeof input}${
is.defined(inputOptions) ? ` when also providing options of type ${typeof inputOptions}` : ''
}`);
}
if (is.object(inputOptions)) {
// Fail on error
@@ -31,15 +52,35 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (is.bool(inputOptions.failOnError)) {
inputDescriptor.failOnError = inputOptions.failOnError;
} else {
throw new Error('Invalid failOnError (boolean) ' + inputOptions.failOnError);
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
}
}
// Density
if (is.defined(inputOptions.density)) {
if (is.integer(inputOptions.density) && is.inRange(inputOptions.density, 1, 2400)) {
if (is.inRange(inputOptions.density, 1, 2400)) {
inputDescriptor.density = inputOptions.density;
} else {
throw new Error('Invalid density (1 to 2400) ' + inputOptions.density);
throw is.invalidParameterError('density', 'number between 1 and 2400', inputOptions.density);
}
}
// 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) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
} else {
throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
}
}
// sequentialRead
if (is.defined(inputOptions.sequentialRead)) {
if (is.bool(inputOptions.sequentialRead)) {
inputDescriptor.sequentialRead = inputOptions.sequentialRead;
} else {
throw is.invalidParameterError('sequentialRead', 'boolean', inputOptions.sequentialRead);
}
}
// Raw pixel input
@@ -57,6 +98,21 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw new Error('Expected width, height and channels for raw pixel input');
}
}
// Multi-page input (GIF, TIFF, PDF)
if (is.defined(inputOptions.pages)) {
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
inputDescriptor.pages = inputOptions.pages;
} else {
throw is.invalidParameterError('pages', 'integer between -1 and 100000', inputOptions.pages);
}
}
if (is.defined(inputOptions.page)) {
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
inputDescriptor.page = inputOptions.page;
} else {
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
}
}
// Create new image
if (is.defined(inputOptions.create)) {
if (
@@ -91,7 +147,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
* Handle incoming Buffer chunk on Writable Stream.
* @private
* @param {Buffer} chunk
* @param {String} encoding - unused
* @param {string} encoding - unused
* @param {Function} callback
*/
function _write (chunk, encoding, callback) {
@@ -100,9 +156,8 @@ function _write (chunk, encoding, callback) {
/* istanbul ignore else */
if (is.buffer(chunk)) {
if (this.options.input.buffer.length === 0) {
const that = this;
this.on('finish', function () {
that.streamInFinished = true;
this.on('finish', () => {
this.streamInFinished = true;
});
}
this.options.input.buffer.push(chunk);
@@ -128,55 +183,31 @@ function _flattenBufferIn () {
/**
* Are we expecting Stream-based input?
* @private
* @returns {Boolean}
* @returns {boolean}
*/
function _isStreamInput () {
return Array.isArray(this.options.input.buffer);
}
/**
* 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.
*
* @example
* const pipeline = sharp().rotate();
* pipeline.clone().resize(800, 600).pipe(firstWritableStream);
* pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
* readableStream.pipe(pipeline);
* // firstWritableStream receives auto-rotated, resized readableStream
* // secondWritableStream receives auto-rotated, extracted region of readableStream
*
* @returns {Sharp}
*/
function clone () {
const that = this;
// Clone existing options
const clone = this.constructor.call();
clone.options = Object.assign({}, this.options);
// Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) {
this.on('finish', function () {
// Clone inherits input data
that._flattenBufferIn();
clone.options.bufferIn = that.options.bufferIn;
clone.emit('finish');
});
}
return clone;
}
/**
* Fast access to (uncached) image metadata without decoding any compressed image data.
* A Promises/A+ promise is returned when `callback` is not provided.
* 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`
* - `width`: Number of pixels wide
* - `height`: Number of pixels high
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
* - `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)
* - `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://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/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
* - `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
@@ -184,6 +215,7 @@ function clone () {
* - `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
*
* @example
* const image = sharp(inputJpg);
@@ -203,12 +235,11 @@ function clone () {
* @returns {Promise<Object>|Sharp}
*/
function metadata (callback) {
const that = this;
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', function () {
that._flattenBufferIn();
sharp.metadata(that.options, callback);
this.on('finish', () => {
this._flattenBufferIn();
sharp.metadata(this.options, callback);
});
} else {
sharp.metadata(this.options, callback);
@@ -216,10 +247,10 @@ function metadata (callback) {
return this;
} else {
if (this._isStreamInput()) {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.metadata(that.options, function (err, metadata) {
return new Promise((resolve, reject) => {
this.on('finish', () => {
this._flattenBufferIn();
sharp.metadata(this.options, (err, metadata) => {
if (err) {
reject(err);
} else {
@@ -229,8 +260,8 @@ function metadata (callback) {
});
});
} else {
return new Promise(function (resolve, reject) {
sharp.metadata(that.options, function (err, metadata) {
return new Promise((resolve, reject) => {
sharp.metadata(this.options, (err, metadata) => {
if (err) {
reject(err);
} else {
@@ -244,7 +275,7 @@ function metadata (callback) {
/**
* Access to pixel-derived image statistics for every channel in the image.
* A Promise is returned when `callback` is not provided.
* 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)
@@ -257,7 +288,8 @@ function metadata (callback) {
* - `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`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
* - `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)
*
* @example
* const image = sharp(inputJpg);
@@ -271,12 +303,11 @@ function metadata (callback) {
* @returns {Promise<Object>}
*/
function stats (callback) {
const that = this;
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', function () {
that._flattenBufferIn();
sharp.stats(that.options, callback);
this.on('finish', () => {
this._flattenBufferIn();
sharp.stats(this.options, callback);
});
} else {
sharp.stats(this.options, callback);
@@ -284,10 +315,10 @@ function stats (callback) {
return this;
} else {
if (this._isStreamInput()) {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.stats(that.options, function (err, stats) {
return new Promise((resolve, reject) => {
this.on('finish', function () {
this._flattenBufferIn();
sharp.stats(this.options, (err, stats) => {
if (err) {
reject(err);
} else {
@@ -297,8 +328,8 @@ function stats (callback) {
});
});
} else {
return new Promise(function (resolve, reject) {
sharp.stats(that.options, function (err, stats) {
return new Promise((resolve, reject) => {
sharp.stats(this.options, (err, stats) => {
if (err) {
reject(err);
} else {
@@ -310,61 +341,20 @@ function stats (callback) {
}
}
/**
* Do not process input images where the number of pixels (width * height) exceeds this limit.
* Assumes image dimensions contained in the input metadata can be trusted.
* The default limit is 268402689 (0x3FFF * 0x3FFF) pixels.
* @param {(Number|Boolean)} limit - an integral Number of pixels, zero or false to remove limit, true to use default limit.
* @returns {Sharp}
* @throws {Error} Invalid limit
*/
function limitInputPixels (limit) {
// if we pass in false we represent the integer as 0 to disable
if (limit === false) {
limit = 0;
} else if (limit === true) {
limit = Math.pow(0x3FFF, 2);
}
if (is.integer(limit) && limit >= 0) {
this.options.limitInputPixels = limit;
} else {
throw is.invalidParameterError('limitInputPixels', 'integer', limit);
}
return this;
}
/**
* An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
* This will reduce memory usage and can improve performance on some systems.
*
* The default behaviour *before* function call is `false`, meaning the libvips access method is not sequential.
*
* @param {Boolean} [sequentialRead=true]
* @returns {Sharp}
*/
function sequentialRead (sequentialRead) {
this.options.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true;
return this;
}
/**
* Decorate the Sharp prototype with input-related functions.
* @private
*/
module.exports = function (Sharp) {
[
Object.assign(Sharp.prototype, {
// Private
_inputOptionsFromObject,
_createInputDescriptor,
_write,
_flattenBufferIn,
_isStreamInput,
// Public
clone,
metadata,
stats,
limitInputPixels,
sequentialRead
].forEach(function (f) {
Sharp.prototype[f.name] = f;
stats
});
};

View File

@@ -91,8 +91,8 @@ const inArray = function (val, list) {
/**
* Create an Error with a message relating to an invalid parameter.
*
* @param {String} name - parameter name.
* @param {String} expected - description of the type/value/range expected.
* @param {string} name - parameter name.
* @param {string} expected - description of the type/value/range expected.
* @param {*} actual - the value received.
* @returns {Error} Containing the formatted message.
* @private

105
lib/libvips.js Normal file
View File

@@ -0,0 +1,105 @@
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const spawnSync = require('child_process').spawnSync;
const semver = require('semver');
const platform = require('./platform');
const env = process.env;
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
require('../package.json').config.libvips;
const minimumLibvipsVersion = semver.coerce(minimumLibvipsVersionLabelled).version;
const spawnSyncOptions = {
encoding: 'utf8',
shell: true
};
const mkdirSync = function (dirPath) {
try {
fs.mkdirSync(dirPath);
} catch (err) {
/* istanbul ignore if */
if (err.code !== 'EEXIST') {
throw err;
}
}
};
const cachePath = function () {
const npmCachePath = env.npm_config_cache || /* istanbul ignore next */
(env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
mkdirSync(npmCachePath);
const libvipsCachePath = path.join(npmCachePath, '_libvips');
mkdirSync(libvipsCachePath);
return libvipsCachePath;
};
const globalLibvipsVersion = function () {
if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout || '';
return globalLibvipsVersion.trim();
} else {
return '';
}
};
const hasVendoredLibvips = function () {
const currentPlatformId = platform();
const vendorPath = path.join(__dirname, '..', 'vendor');
let vendorVersionId;
let vendorPlatformId;
try {
vendorVersionId = require(path.join(vendorPath, 'versions.json')).vips;
vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
} catch (err) {}
/* istanbul ignore if */
if (vendorVersionId && vendorVersionId !== minimumLibvipsVersion) {
throw new Error(`Found vendored libvips v${vendorVersionId} but require v${minimumLibvipsVersion}. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
}
/* istanbul ignore else */
if (vendorPlatformId) {
/* istanbul ignore else */
if (currentPlatformId === vendorPlatformId) {
return true;
} else {
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
}
} else {
return false;
}
};
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(':');
} else {
return '';
}
};
const useGlobalLibvips = function () {
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
return false;
}
const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && /* istanbul ignore next */
semver.gte(globalVipsVersion, minimumLibvipsVersion);
};
module.exports = {
minimumLibvipsVersion,
minimumLibvipsVersionLabelled,
cachePath,
globalLibvipsVersion,
hasVendoredLibvips,
pkgConfigPath,
useGlobalLibvips,
mkdirSync
};

View File

@@ -1,14 +1,18 @@
'use strict';
const color = require('color');
const is = require('./is');
/**
* Rotate the output image by either an explicit angle
* or auto-orient based on the EXIF `Orientation` tag.
*
* If an angle is provided, it is converted to a valid 90/180/270deg rotation.
* If an angle is provided, it is converted to a valid positive degree rotation.
* For example, `-450` will produce a 270deg rotation.
*
* When rotating by an angle other than a multiple of 90,
* the background colour can be provided with the `background` option.
*
* If no angle is provided, it is determined from the EXIF data.
* Mirroring is supported and may infer the use of a flip operation.
*
@@ -28,64 +32,30 @@ const is = require('./is');
* });
* readableStream.pipe(pipeline);
*
* @param {Number} [angle=auto] angle of rotation, must be a multiple of 90.
* @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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function rotate (angle) {
function rotate (angle, options) {
if (!is.defined(angle)) {
this.options.useExifOrientation = true;
} else if (is.integer(angle) && !(angle % 90)) {
this.options.angle = angle;
} else {
throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + angle);
}
return this;
}
/**
* Extract 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.
*
* @example
* 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.
* });
* @example
* sharp(input)
* .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
* .resize(width, height)
* .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
* .toFile(output, function(err) {
* // Extract a region, resize, then extract from the resized image
* });
*
* @param {Object} options
* @param {Number} options.left - zero-indexed offset from left edge
* @param {Number} options.top - zero-indexed offset from top edge
* @param {Number} options.width - dimension of extracted image
* @param {Number} options.height - dimension of extracted image
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extract (options) {
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
['left', 'top', 'width', 'height'].forEach(function (name) {
const value = options[name];
if (is.integer(value) && value >= 0) {
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else {
throw new Error('Non-integer value for ' + name + ' of ' + value);
} else if (is.number(angle)) {
this.options.rotationAngle = angle;
if (is.object(options) && options.background) {
const backgroundColour = color(options.background);
this.options.rotationBackground = [
backgroundColour.red(),
backgroundColour.green(),
backgroundColour.blue(),
Math.round(backgroundColour.alpha() * 255)
];
}
}, this);
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
this.options.rotateBeforePreExtract = true;
} else {
throw is.invalidParameterError('angle', 'numeric', angle);
}
return this;
}
@@ -118,9 +88,9 @@ function flop (flop) {
* 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.
* @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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -139,7 +109,7 @@ function sharpen (sigma, flat, jagged) {
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
this.options.sharpenFlat = flat;
} else {
throw new Error('Invalid sharpen level for flat areas (0.0 - 10000.0) ' + flat);
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
}
}
// Control over jagged areas
@@ -147,11 +117,31 @@ function sharpen (sigma, flat, jagged) {
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
this.options.sharpenJagged = jagged;
} else {
throw new Error('Invalid sharpen level for jagged areas (0.0 - 10000.0) ' + jagged);
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
}
}
} else {
throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', sigma);
}
return this;
}
/**
* Apply median filter.
* When used without parameters the default window is 3x3.
* @param {number} [size=3] square mask size: size x size
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function median (size) {
if (!is.defined(size)) {
// No arguments: default to 3x3
this.options.medianSize = 3;
} else if (is.integer(size) && is.inRange(size, 1, 1000)) {
// Numeric argument: specific sigma
this.options.medianSize = size;
} else {
throw is.invalidParameterError('size', 'integer between 1 and 1000', size);
}
return this;
}
@@ -160,7 +150,7 @@ function sharpen (sigma, flat, jagged) {
* Blur the image.
* When used without parameters, performs a fast, mild blur of the output image.
* When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
* @param {Number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @param {number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -175,78 +165,21 @@ function blur (sigma) {
// Numeric argument: specific sigma
this.options.blurSigma = sigma;
} else {
throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
throw is.invalidParameterError('sigma', 'number between 0.3 and 1000', sigma);
}
return this;
}
/**
* Extends/pads the edges of the image with the colour provided to the `background` method.
* This operation will always occur after resizing and extraction, if any.
*
* @example
* // 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)
* .resize(140)
* .background({r: 0, g: 0, b: 0, alpha: 0})
* .extend({top: 10, bottom: 20, left: 10, right: 10})
* ...
*
* @param {(Number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
* @param {Number} [extend.top]
* @param {Number} [extend.left]
* @param {Number} [extend.bottom]
* @param {Number} [extend.right]
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extend (extend) {
if (is.integer(extend) && extend > 0) {
this.options.extendTop = extend;
this.options.extendBottom = extend;
this.options.extendLeft = extend;
this.options.extendRight = extend;
} else if (
is.object(extend) &&
is.integer(extend.top) && extend.top >= 0 &&
is.integer(extend.bottom) && extend.bottom >= 0 &&
is.integer(extend.left) && extend.left >= 0 &&
is.integer(extend.right) && extend.right >= 0
) {
this.options.extendTop = extend.top;
this.options.extendBottom = extend.bottom;
this.options.extendLeft = extend.left;
this.options.extendRight = extend.right;
} else {
throw new Error('Invalid edge extension ' + extend);
}
return this;
}
/**
* Merge alpha transparency channel, if any, with `background`.
* @param {Boolean} [flatten=true]
* Merge alpha transparency channel, if any, with a background.
* @param {Object} [options]
* @param {string|Object} [options.background={r: 0, g: 0, b: 0}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black.
* @returns {Sharp}
*/
function flatten (flatten) {
this.options.flatten = is.bool(flatten) ? flatten : true;
return this;
}
/**
* Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
* @param {Number} [tolerance=10] value between 1 and 99 representing the percentage similarity.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function trim (tolerance) {
if (!is.defined(tolerance)) {
this.options.trimTolerance = 10;
} else if (is.integer(tolerance) && is.inRange(tolerance, 1, 99)) {
this.options.trimTolerance = tolerance;
} else {
throw new Error('Invalid trim tolerance (1 to 99) ' + tolerance);
function flatten (options) {
this.options.flatten = is.bool(options) ? options : true;
if (is.object(options)) {
this._setBackgroundColourOption('flattenBackground', options.background);
}
return this;
}
@@ -257,18 +190,30 @@ function trim (tolerance) {
* This can improve the perceived brightness of a resized image in non-linear colour spaces.
* JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
* when applying a gamma correction.
* @param {Number} [gamma=2.2] value between 1.0 and 3.0.
*
* Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
*
* @param {number} [gamma=2.2] value between 1.0 and 3.0.
* @param {number} [gammaOut] value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function gamma (gamma) {
function gamma (gamma, gammaOut) {
if (!is.defined(gamma)) {
// Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2;
} else if (is.number(gamma) && is.inRange(gamma, 1, 3)) {
this.options.gamma = gamma;
} else {
throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
throw is.invalidParameterError('gamma', 'number between 1.0 and 3.0', gamma);
}
if (!is.defined(gammaOut)) {
// Default gamma correction for output is same as input
this.options.gammaOut = this.options.gamma;
} else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) {
this.options.gammaOut = gammaOut;
} else {
throw is.invalidParameterError('gammaOut', 'number between 1.0 and 3.0', gammaOut);
}
return this;
}
@@ -319,11 +264,11 @@ function normalize (normalize) {
* });
*
* @param {Object} kernel
* @param {Number} kernel.width - width of the kernel in pixels.
* @param {Number} kernel.height - width of the kernel in pixels.
* @param {Array<Number>} kernel.kernel - Array of length `width*height` containing the kernel values.
* @param {Number} [kernel.scale=sum] - the scale of the kernel in pixels.
* @param {Number} [kernel.offset=0] - the offset of the kernel in pixels.
* @param {number} kernel.width - width of the kernel in pixels.
* @param {number} kernel.height - width of the kernel in pixels.
* @param {Array<number>} kernel.kernel - Array of length `width*height` containing the kernel values.
* @param {number} [kernel.scale=sum] - the scale of the kernel in pixels.
* @param {number} [kernel.offset=0] - the offset of the kernel in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -355,7 +300,7 @@ function convolve (kernel) {
/**
* Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
* @param {Number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
* @param {number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
* @param {Object} [options]
* @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
* @param {Boolean} [options.grayscale=true] - alternative spelling for greyscale.
@@ -370,7 +315,7 @@ function threshold (threshold, options) {
} else if (is.integer(threshold) && is.inRange(threshold, 0, 255)) {
this.options.threshold = threshold;
} else {
throw new Error('Invalid threshold (0 to 255) ' + threshold);
throw is.invalidParameterError('threshold', 'integer between 0 and 255', threshold);
}
if (!is.object(options) || options.greyscale === true || options.grayscale === true) {
this.options.thresholdGrayscale = true;
@@ -386,13 +331,13 @@ function threshold (threshold, options) {
* 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.
*
* @param {Buffer|String} operand - Buffer containing image data or String containing the path to an image file.
* @param {String} operator - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @param {Buffer|string} operand - Buffer containing image data or string containing the path to an image file.
* @param {string} operator - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @param {Object} [options]
* @param {Object} [options.raw] - describes operand when using raw pixel data.
* @param {Number} [options.raw.width]
* @param {Number} [options.raw.height]
* @param {Number} [options.raw.channels]
* @param {number} [options.raw.width]
* @param {number} [options.raw.height]
* @param {number} [options.raw.channels]
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@@ -401,7 +346,129 @@ function boolean (operand, operator, options) {
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
this.options.booleanOp = operator;
} else {
throw new Error('Invalid boolean operator ' + operator);
throw is.invalidParameterError('operator', 'one of: and, or, eor', operator);
}
return this;
}
/**
* Apply the linear formula a * input + b to the image (levels adjustment)
* @param {number} [a=1.0] multiplier
* @param {number} [b=0.0] offset
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function linear (a, b) {
if (!is.defined(a)) {
this.options.linearA = 1.0;
} else if (is.number(a)) {
this.options.linearA = a;
} else {
throw is.invalidParameterError('a', 'numeric', a);
}
if (!is.defined(b)) {
this.options.linearB = 0.0;
} else if (is.number(b)) {
this.options.linearB = b;
} else {
throw is.invalidParameterError('b', 'numeric', b);
}
return this;
}
/**
* Recomb the image with the specified matrix.
*
* @since 0.21.1
*
* @example
* sharp(input)
* .recomb([
* [0.3588, 0.7044, 0.1368],
* [0.2990, 0.5870, 0.1140],
* [0.2392, 0.4696, 0.0912],
* ])
* .raw()
* .toBuffer(function(err, data, info) {
* // data contains the raw pixel data after applying the recomb
* // With this example input, a sepia filter has been applied
* });
*
* @param {Array<Array<number>>} inputMatrix - 3x3 Recombination matrix
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function recomb (inputMatrix) {
if (!Array.isArray(inputMatrix) || inputMatrix.length !== 3 ||
inputMatrix[0].length !== 3 ||
inputMatrix[1].length !== 3 ||
inputMatrix[2].length !== 3
) {
// must pass in a kernel
throw new Error('Invalid recombination matrix');
}
this.options.recombMatrix = [
inputMatrix[0][0], inputMatrix[0][1], inputMatrix[0][2],
inputMatrix[1][0], inputMatrix[1][1], inputMatrix[1][2],
inputMatrix[2][0], inputMatrix[2][1], inputMatrix[2][2]
].map(Number);
return this;
}
/**
* Transforms the image using brightness, saturation and hue rotation.
*
* @since 0.22.1
*
* @example
* sharp(input)
* .modulate({
* brightness: 2 // increase lightness by a factor of 2
* });
*
* sharp(input)
* .modulate({
* hue: 180 // hue-rotate by 180 degrees
* });
*
* // decreate brightness and saturation while also hue-rotating by 90 degrees
* sharp(input)
* .modulate({
* brightness: 0.5,
* saturation: 0.5,
* hue: 90
* });
*
* @param {Object} [options]
* @param {number} [options.brightness] Brightness multiplier
* @param {number} [options.saturation] Saturation multiplier
* @param {number} [options.hue] Degrees for hue rotation
* @returns {Sharp}
*/
function modulate (options) {
if (!is.plainObject(options)) {
throw is.invalidParameterError('options', 'plain object', options);
}
if ('brightness' in options) {
if (is.number(options.brightness) && options.brightness >= 0) {
this.options.brightness = options.brightness;
} else {
throw is.invalidParameterError('brightness', 'number above zero', options.brightness);
}
}
if ('saturation' in options) {
if (is.number(options.saturation) && options.saturation >= 0) {
this.options.saturation = options.saturation;
} else {
throw is.invalidParameterError('saturation', 'number above zero', options.saturation);
}
}
if ('hue' in options) {
if (is.integer(options.hue)) {
this.options.hue = options.hue % 360;
} else {
throw is.invalidParameterError('hue', 'number', options.hue);
}
}
return this;
}
@@ -411,24 +478,23 @@ function boolean (operand, operator, options) {
* @private
*/
module.exports = function (Sharp) {
[
Object.assign(Sharp.prototype, {
rotate,
extract,
flip,
flop,
sharpen,
median,
blur,
extend,
flatten,
trim,
gamma,
negate,
normalise,
normalize,
convolve,
threshold,
boolean
].forEach(function (f) {
Sharp.prototype[f.name] = f;
boolean,
linear,
recomb,
modulate
});
};

View File

@@ -3,6 +3,17 @@
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
const formats = new Map([
['heic', 'heif'],
['heif', 'heif'],
['jpeg', 'jpeg'],
['jpg', 'jpeg'],
['png', 'png'],
['raw', 'raw'],
['tiff', 'tiff'],
['webp', 'webp']
]);
/**
* Write output image data to a file.
*
@@ -10,9 +21,22 @@ const sharp = require('../build/Release/sharp.node');
* with JPEG, PNG, WebP, TIFF, 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 {@link withMetadata} for control over this.
*
* A `Promise` is returned when `callback` is not provided.
*
* @param {String} fileOut - the path to write the image data to.
* @example
* sharp(input)
* .toFile('output.png', (err, info) => { ... });
*
* @example
* sharp(input)
* .toFile('output.png')
* .then(info => { ... })
* .catch(err => { ... });
*
* @param {string} fileOut - the path to write the image data to.
* @param {Function} [callback] - 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).
@@ -22,7 +46,7 @@ const sharp = require('../build/Release/sharp.node');
*/
function toFile (fileOut, callback) {
if (!fileOut || fileOut.length === 0) {
const errOutputInvalid = new Error('Invalid output');
const errOutputInvalid = new Error('Missing output file path');
if (is.fn(callback)) {
callback(errOutputInvalid);
} else {
@@ -47,7 +71,11 @@ function toFile (fileOut, callback) {
/**
* Write output to a Buffer.
* JPEG, PNG, WebP, TIFF and RAW output are supported.
* By default, the format will match the input image, except GIF and SVG input which become PNG output.
*
* If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
*
* By default all metadata will be removed, which includes EXIF-based orientation.
* See {@link withMetadata} for control over this.
*
* `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any.
@@ -58,54 +86,115 @@ function toFile (fileOut, callback) {
*
* A `Promise` is returned when `callback` is not provided.
*
* @example
* sharp(input)
* .toBuffer((err, data, info) => { ... });
*
* @example
* sharp(input)
* .toBuffer()
* .then(data => { ... })
* .catch(err => { ... });
*
* @example
* sharp(input)
* .toBuffer({ resolveWithObject: true })
* .then(({ data, info }) => { ... })
* .catch(err => { ... });
*
* @param {Object} [options]
* @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {Function} [callback]
* @returns {Promise<Buffer>} - when no callback is provided
*/
function toBuffer (options, callback) {
if (is.object(options)) {
if (is.bool(options.resolveWithObject)) {
this.options.resolveWithObject = options.resolveWithObject;
}
this._setBooleanOption('resolveWithObject', options.resolveWithObject);
} else if (this.options.resolveWithObject) {
this.options.resolveWithObject = false;
}
return this._pipeline(is.fn(options) ? options : callback);
}
/**
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
* The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
* This will also convert to and add a web-friendly sRGB ICC profile.
* @param {Object} [withMetadata]
* @param {Number} [withMetadata.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
*
* 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.
*
* @example
* sharp('input.jpg')
* .withMetadata()
* .toFile('output-with-metadata.jpg')
* .then(info => { ... });
*
* @param {Object} [options]
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withMetadata (withMetadata) {
this.options.withMetadata = is.bool(withMetadata) ? withMetadata : true;
if (is.object(withMetadata)) {
if (is.defined(withMetadata.orientation)) {
if (is.integer(withMetadata.orientation) && is.inRange(withMetadata.orientation, 1, 8)) {
this.options.withMetadataOrientation = withMetadata.orientation;
function withMetadata (options) {
this.options.withMetadata = is.bool(options) ? options : true;
if (is.object(options)) {
if (is.defined(options.orientation)) {
if (is.integer(options.orientation) && is.inRange(options.orientation, 1, 8)) {
this.options.withMetadataOrientation = options.orientation;
} else {
throw new Error('Invalid orientation (1 to 8) ' + withMetadata.orientation);
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
}
}
}
return this;
}
/**
* Force output to a given format.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .toFormat('png')
* .toBuffer();
*
* @param {(string|Object)} format - as a string or an Object with an 'id' attribute
* @param {Object} options - output options
* @returns {Sharp}
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
if (!actualFormat) {
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
}
return this[actualFormat](options);
}
/**
* Use these JPEG options for output image.
*
* @example
* // Convert any input to very high quality JPEG output
* const data = await sharp(input)
* .jpeg({
* quality: 100,
* chromaSubsampling: '4:4:4'
* })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {String} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling when quality <= 90
* @param {Boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires mozjpeg
* @param {Boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires mozjpeg
* @param {Boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires mozjpeg
* @param {Boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
* @param {Boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {string} [options.chromaSubsampling='4:2:0'] - for quality < 90, set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' (use chroma subsampling); for quality >= 90 chroma is never subsampled
* @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
* @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires libvips compiled with support for mozjpeg
* @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg
* @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
* @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
* @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
* @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg
* @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable
* @param {boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
@@ -115,7 +204,7 @@ function jpeg (options) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.jpegQuality = options.quality;
} else {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.progressive)) {
@@ -125,34 +214,61 @@ function jpeg (options) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.jpegChromaSubsampling = options.chromaSubsampling;
} else {
throw new Error('Invalid chromaSubsampling (4:2:0, 4:4:4) ' + options.chromaSubsampling);
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
}
options.trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
if (is.defined(options.trellisQuantisation)) {
this._setBooleanOption('jpegTrellisQuantisation', options.trellisQuantisation);
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
if (is.defined(trellisQuantisation)) {
this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
}
if (is.defined(options.overshootDeringing)) {
this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
}
options.optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
if (is.defined(options.optimiseScans)) {
this._setBooleanOption('jpegOptimiseScans', options.optimiseScans);
if (options.optimiseScans) {
const optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
if (is.defined(optimiseScans)) {
this._setBooleanOption('jpegOptimiseScans', optimiseScans);
if (optimiseScans) {
this.options.jpegProgressive = true;
}
}
const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
if (is.defined(optimiseCoding)) {
this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
}
const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
if (is.defined(quantisationTable)) {
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
this.options.jpegQuantisationTable = quantisationTable;
} else {
throw is.invalidParameterError('quantisationTable', 'integer between 0 and 8', quantisationTable);
}
}
}
return this._updateFormatOut('jpeg', options);
}
/**
* Use these PNG options for output image.
*
* PNG output is always 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.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .png()
* .toBuffer();
*
* @param {Object} [options]
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {number} [options.compressionLevel=9] - zlib compression level, 0-9
* @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
* @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libvips compiled with support for libimagequant
* @param {number} [options.colours=256] - maximum number of palette entries, requires libvips compiled with support for libimagequant
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, requires libvips compiled with support for libimagequant
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libvips compiled with support for libimagequant
* @param {boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
@@ -165,24 +281,60 @@ function png (options) {
if (is.integer(options.compressionLevel) && is.inRange(options.compressionLevel, 0, 9)) {
this.options.pngCompressionLevel = options.compressionLevel;
} else {
throw new Error('Invalid compressionLevel (integer, 0-9) ' + options.compressionLevel);
throw is.invalidParameterError('compressionLevel', 'integer between 0 and 9', options.compressionLevel);
}
}
if (is.defined(options.adaptiveFiltering)) {
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
}
if (is.defined(options.palette)) {
this._setBooleanOption('pngPalette', options.palette);
if (this.options.pngPalette) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
this.options.pngQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
}
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
this.options.pngColours = 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;
} else {
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
}
}
}
}
}
return this._updateFormatOut('png', options);
}
/**
* Use these WebP options for output image.
*
* @example
* // Convert any input to lossless WebP output
* const data = await sharp(input)
* .webp({ lossless: true })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
* @param {Boolean} [options.lossless=false] - use lossless compression mode
* @param {Boolean} [options.nearLossless=false] - use near_lossless compression mode
* @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
* @param {boolean} [options.lossless=false] - use lossless compression mode
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
* @param {number} [options.reductionEffort=4] - level of CPU effort to reduce file size, integer 0-6
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
@@ -191,14 +343,14 @@ function webp (options) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.webpQuality = options.quality;
} else {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.object(options) && is.defined(options.alphaQuality)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 1, 100)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
this.options.webpAlphaQuality = options.alphaQuality;
} else {
throw new Error('Invalid webp alpha quality (integer, 1-100) ' + options.alphaQuality);
throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
}
}
if (is.object(options) && is.defined(options.lossless)) {
@@ -207,104 +359,198 @@ function webp (options) {
if (is.object(options) && is.defined(options.nearLossless)) {
this._setBooleanOption('webpNearLossless', options.nearLossless);
}
if (is.object(options) && is.defined(options.smartSubsample)) {
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
}
if (is.object(options) && is.defined(options.reductionEffort)) {
if (is.integer(options.reductionEffort) && is.inRange(options.reductionEffort, 0, 6)) {
this.options.webpReductionEffort = options.reductionEffort;
} else {
throw is.invalidParameterError('reductionEffort', 'integer between 0 and 6', options.reductionEffort);
}
}
return this._updateFormatOut('webp', options);
}
/**
* Use these TIFF options for output image.
*
* @example
* // Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
* sharp('input.svg')
* .tiff({
* compression: 'lzw',
* squash: true
* })
* .toFile('1-bpp-output.tiff')
* .then(info => { ... });
*
* @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 {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg
* @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
* @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
* @param {boolean} [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
* @param {boolean} [options.tileWidth=256] - horizontal tile size
* @param {boolean} [options.tileHeight=256] - vertical tile size
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
* @param {boolean} [options.squash=false] - squash 8-bit images down to 1 bit
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function tiff (options) {
if (is.object(options) && is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.tiffQuality = options.quality;
} else {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.tiffQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
}
if (is.object(options) && is.defined(options.squash)) {
if (is.bool(options.squash)) {
this.options.tiffSquash = options.squash;
} else {
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
if (is.defined(options.squash)) {
this._setBooleanOption('tiffSquash', options.squash);
}
}
// resolution
if (is.object(options) && is.defined(options.xres)) {
if (is.number(options.xres)) {
this.options.tiffXres = options.xres;
} else {
throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
// tiling
if (is.defined(options.tile)) {
this._setBooleanOption('tiffTile', options.tile);
}
}
if (is.object(options) && is.defined(options.yres)) {
if (is.number(options.yres)) {
this.options.tiffYres = options.yres;
} else {
throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
if (is.defined(options.tileWidth)) {
if (is.integer(options.tileWidth) && options.tileWidth > 0) {
this.options.tiffTileWidth = options.tileWidth;
} else {
throw is.invalidParameterError('tileWidth', 'integer greater than zero', options.tileWidth);
}
}
}
// compression
if (is.defined(options) && is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'none'])) {
this.options.tiffCompression = options.compression;
} else {
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, none`;
throw new Error(message);
if (is.defined(options.tileHeight)) {
if (is.integer(options.tileHeight) && options.tileHeight > 0) {
this.options.tiffTileHeight = options.tileHeight;
} else {
throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
}
}
}
// predictor
if (is.defined(options) && is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
this.options.tiffPredictor = options.predictor;
} else {
const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
throw new Error(message);
// pyramid
if (is.defined(options.pyramid)) {
this._setBooleanOption('tiffPyramid', options.pyramid);
}
// resolution
if (is.defined(options.xres)) {
if (is.number(options.xres) && options.xres > 0) {
this.options.tiffXres = options.xres;
} else {
throw is.invalidParameterError('xres', 'number greater than zero', options.xres);
}
}
if (is.defined(options.yres)) {
if (is.number(options.yres) && options.yres > 0) {
this.options.tiffYres = options.yres;
} else {
throw is.invalidParameterError('yres', 'number greater than zero', options.yres);
}
}
// compression
if (is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
this.options.tiffCompression = options.compression;
} else {
throw is.invalidParameterError('compression', 'one of: lzw, deflate, jpeg, ccittfax4, none', options.compression);
}
}
// predictor
if (is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
this.options.tiffPredictor = options.predictor;
} else {
throw is.invalidParameterError('predictor', 'one of: none, horizontal, float', options.predictor);
}
}
}
return this._updateFormatOut('tiff', options);
}
/**
* Force output to be raw, uncompressed uint8 pixel data.
* Use these HEIF options for output image.
*
* Support for HEIF (HEIC/AVIF) is experimental.
* Do not use this in production systems.
*
* Requires a custom, globally-installed libvips compiled with support for libheif.
*
* Most versions of libheif support only the patent-encumbered HEVC compression format.
*
* @since 0.23.0
*
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
* @param {boolean} [options.lossless=false] - use lossless compression
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function heif (options) {
if (!this.constructor.format.heif.output.buffer) {
throw new Error('The heif operation requires libvips to have been installed with support for libheif');
}
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.heifQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.lossless)) {
if (is.bool(options.lossless)) {
this.options.heifLossless = options.lossless;
} else {
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
}
}
if (is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['hevc', 'avc', 'jpeg', 'av1'])) {
this.options.heifCompression = options.compression;
} else {
throw is.invalidParameterError('compression', 'one of: hevc, avc, jpeg, av1', options.compression);
}
}
}
return this._updateFormatOut('heif', options);
}
/**
* Force output to be raw, uncompressed, 8-bit unsigned integer (unit8) pixel data.
* Pixel ordering is left-to-right, top-to-bottom, without padding.
* Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
*
* @example
* // Extract raw RGB pixel data from JPEG input
* const { data, info } = await sharp('input.jpg')
* .raw()
* .toBuffer({ resolveWithObject: true });
*
* @example
* // Extract alpha channel as raw pixel data from PNG input
* const data = await sharp('input.png')
* .ensureAlpha()
* .extractChannel(3)
* .toColourspace('b-w')
* .raw()
* .toBuffer();
*
* @returns {Sharp}
*/
function raw () {
return this._updateFormatOut('raw');
}
/**
* Force output to a given format.
* @param {(String|Object)} format - as a String or an Object with an 'id' attribute
* @param {Object} options - output options
* @returns {Sharp}
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
if (is.object(format) && is.string(format.id)) {
format = format.id;
}
if (format === 'jpg') format = 'jpeg';
if (!is.inArray(format, ['jpeg', 'png', 'webp', 'tiff', 'raw'])) {
throw new Error('Unsupported output format ' + format);
}
return this[format](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.
*
* Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
*
* @example
* sharp('input.tiff')
* .png()
@@ -316,57 +562,89 @@ function toFormat (format, options) {
* // output_files contains 512x512 tiles grouped by zoom level
* });
*
* @param {Object} [tile]
* @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192.
* @param {Number} [tile.overlap=0] tile overlap in pixels, a value between 0 and 8192.
* @param {String} [tile.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
* @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.
* @param {number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
* @param {string|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
* @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function tile (tile) {
if (is.object(tile)) {
function tile (options) {
if (is.object(options)) {
// Size of square tiles, in pixels
if (is.defined(tile.size)) {
if (is.integer(tile.size) && is.inRange(tile.size, 1, 8192)) {
this.options.tileSize = tile.size;
if (is.defined(options.size)) {
if (is.integer(options.size) && is.inRange(options.size, 1, 8192)) {
this.options.tileSize = options.size;
} else {
throw new Error('Invalid tile size (1 to 8192) ' + tile.size);
throw is.invalidParameterError('size', 'integer between 1 and 8192', options.size);
}
}
// Overlap of tiles, in pixels
if (is.defined(tile.overlap)) {
if (is.integer(tile.overlap) && is.inRange(tile.overlap, 0, 8192)) {
if (tile.overlap > this.options.tileSize) {
throw new Error('Tile overlap ' + tile.overlap + ' cannot be larger than tile size ' + this.options.tileSize);
if (is.defined(options.overlap)) {
if (is.integer(options.overlap) && is.inRange(options.overlap, 0, 8192)) {
if (options.overlap > this.options.tileSize) {
throw is.invalidParameterError('overlap', `<= size (${this.options.tileSize})`, options.overlap);
}
this.options.tileOverlap = tile.overlap;
this.options.tileOverlap = options.overlap;
} else {
throw new Error('Invalid tile overlap (0 to 8192) ' + tile.overlap);
throw is.invalidParameterError('overlap', 'integer between 0 and 8192', options.overlap);
}
}
// Container
if (is.defined(tile.container)) {
if (is.string(tile.container) && is.inArray(tile.container, ['fs', 'zip'])) {
this.options.tileContainer = tile.container;
if (is.defined(options.container)) {
if (is.string(options.container) && is.inArray(options.container, ['fs', 'zip'])) {
this.options.tileContainer = options.container;
} else {
throw new Error('Invalid tile container ' + tile.container);
throw is.invalidParameterError('container', 'one of: fs, zip', options.container);
}
}
// Layout
if (is.defined(tile.layout)) {
if (is.string(tile.layout) && is.inArray(tile.layout, ['dz', 'google', 'zoomify'])) {
this.options.tileLayout = tile.layout;
if (is.defined(options.layout)) {
if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'iiif', 'zoomify'])) {
this.options.tileLayout = options.layout;
} else {
throw new Error('Invalid tile layout ' + tile.layout);
throw is.invalidParameterError('layout', 'one of: dz, google, iiif, zoomify', options.layout);
}
}
// Angle of rotation,
if (is.defined(options.angle)) {
if (is.integer(options.angle) && !(options.angle % 90)) {
this.options.tileAngle = options.angle;
} else {
throw is.invalidParameterError('angle', 'positive/negative multiple of 90', options.angle);
}
}
// Background colour
this._setBackgroundColourOption('tileBackground', options.background);
// Depth of tiles
if (is.defined(options.depth)) {
if (is.string(options.depth) && is.inArray(options.depth, ['onepixel', 'onetile', 'one'])) {
this.options.tileDepth = options.depth;
} else {
throw is.invalidParameterError('depth', 'one of: onepixel, onetile, one', options.depth);
}
}
// Threshold to skip blank tiles
if (is.defined(options.skipBlanks)) {
if (is.integer(options.skipBlanks) && is.inRange(options.skipBlanks, -1, 65535)) {
this.options.tileSkipBlanks = options.skipBlanks;
} else {
throw is.invalidParameterError('skipBlanks', 'integer between -1 and 255/65535', options.skipBlanks);
}
} else if (is.defined(options.layout) && options.layout === 'google') {
this.options.tileSkipBlanks = 5;
}
}
// Format
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
this.options.tileFormat = this.options.formatOut;
} else if (this.options.formatOut !== 'input') {
throw new Error('Invalid tile format ' + this.options.formatOut);
throw is.invalidParameterError('format', 'one of: jpeg, png, webp', this.options.formatOut);
}
return this._updateFormatOut('dz');
}
@@ -375,28 +653,30 @@ function tile (tile) {
* Update the output format unless options.force is false,
* in which case revert to input format.
* @private
* @param {String} formatOut
* @param {string} formatOut
* @param {Object} [options]
* @param {Boolean} [options.force=true] - force output format, otherwise attempt to use input format
* @param {boolean} [options.force=true] - force output format, otherwise attempt to use input format
* @returns {Sharp}
*/
function _updateFormatOut (formatOut, options) {
this.options.formatOut = (is.object(options) && options.force === false) ? 'input' : formatOut;
if (!(is.object(options) && options.force === false)) {
this.options.formatOut = formatOut;
}
return this;
}
/**
* Update a Boolean attribute of the this.options Object.
* Update a boolean attribute of the this.options Object.
* @private
* @param {String} key
* @param {Boolean} val
* @param {string} key
* @param {boolean} val
* @throws {Error} Invalid key
*/
function _setBooleanOption (key, val) {
if (is.bool(val)) {
this.options[key] = val;
} else {
throw new Error('Invalid ' + key + ' (boolean) ' + val);
throw is.invalidParameterError(key, 'boolean', val);
}
}
@@ -405,6 +685,7 @@ function _setBooleanOption (key, val) {
* @private
*/
function _read () {
/* istanbul ignore else */
if (!this.options.streamOut) {
this.options.streamOut = true;
this._pipeline();
@@ -417,14 +698,13 @@ function _read () {
* @private
*/
function _pipeline (callback) {
const that = this;
if (typeof callback === 'function') {
// output=file/buffer
if (this._isStreamInput()) {
// output=file/buffer, input=stream
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, callback);
this.on('finish', () => {
this._flattenBufferIn();
sharp.pipeline(this.options, callback);
});
} else {
// output=file/buffer, input=file/buffer
@@ -435,41 +715,31 @@ function _pipeline (callback) {
// output=stream
if (this._isStreamInput()) {
// output=stream, input=stream
if (this.streamInFinished) {
this.once('finish', () => {
this._flattenBufferIn();
sharp.pipeline(this.options, function (err, data, info) {
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
that.emit('error', err);
this.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
this.emit('info', info);
this.push(data);
}
that.push(null);
});
} else {
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
}
that.push(null);
});
this.push(null);
});
});
if (this.streamInFinished) {
this.emit('finish');
}
} else {
// output=stream, input=file/buffer
sharp.pipeline(this.options, function (err, data, info) {
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
that.emit('error', err);
this.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
this.emit('info', info);
this.push(data);
}
that.push(null);
this.push(null);
});
}
return this;
@@ -477,15 +747,15 @@ function _pipeline (callback) {
// output=promise
if (this._isStreamInput()) {
// output=promise, input=stream
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data, info) {
return new Promise((resolve, reject) => {
this.once('finish', () => {
this._flattenBufferIn();
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
reject(err);
} else {
if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
if (this.options.resolveWithObject) {
resolve({ data, info });
} else {
resolve(data);
}
@@ -495,12 +765,12 @@ function _pipeline (callback) {
});
} else {
// output=promise, input=file/buffer
return new Promise(function (resolve, reject) {
sharp.pipeline(that.options, function (err, data, info) {
return new Promise((resolve, reject) => {
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
reject(err);
} else {
if (that.options.resolveWithObject) {
if (this.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
@@ -517,24 +787,23 @@ function _pipeline (callback) {
* @private
*/
module.exports = function (Sharp) {
[
Object.assign(Sharp.prototype, {
// Public
toFile,
toBuffer,
withMetadata,
toFormat,
jpeg,
png,
webp,
tiff,
heif,
raw,
toFormat,
tile,
// Private
_updateFormatOut,
_setBooleanOption,
_read,
_pipeline
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
};

View File

@@ -1,15 +1,24 @@
'use strict';
module.exports = function () {
const arch = process.env.npm_config_arch || process.arch;
const platform = process.env.npm_config_platform || process.platform;
const detectLibc = require('detect-libc');
const platformId = [platform];
if (arch === 'arm' || arch === 'armhf' || arch === 'arm64') {
const armVersion = (arch === 'arm64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
platformId.push(`armv${armVersion}`);
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.isNonGlibcLinux) ? detectLibc.family : '';
const platformId = [`${platform}${libc}`];
if (arch === 'arm') {
platformId.push(`armv${env.npm_config_arm_version || process.config.variables.arm_version || '6'}`);
} else if (arch === 'arm64') {
platformId.push(`arm64v${env.npm_config_arm_version || '8'}`);
} else {
platformId.push(arch);
}
return platformId.join('-');
};

View File

@@ -3,7 +3,7 @@
const is = require('./is');
/**
* Weighting to apply to image crop.
* Weighting to apply when using contain/cover fit.
* @member
* @private
*/
@@ -21,7 +21,23 @@ const gravity = {
};
/**
* Strategies for automagic crop behaviour.
* Position to apply when using contain/cover fit.
* @member
* @private
*/
const position = {
top: 1,
right: 2,
bottom: 3,
left: 4,
'right top': 5,
'right bottom': 6,
'left bottom': 7,
'left top': 8
};
/**
* Strategies for automagic cover behaviour.
* @member
* @private
*/
@@ -38,45 +54,161 @@ const strategy = {
const kernel = {
nearest: 'nearest',
cubic: 'cubic',
mitchell: 'mitchell',
lanczos2: 'lanczos2',
lanczos3: 'lanczos3'
};
/**
* Resize image to `width` x `height`.
* By default, the resized image is centre cropped to the exact size specified.
* Methods by which an image can be resized to fit the provided dimensions.
* @member
* @private
*/
const fit = {
contain: 'contain',
cover: 'cover',
fill: 'fill',
inside: 'inside',
outside: 'outside'
};
/**
* Map external fit property to internal canvas property.
* @member
* @private
*/
const mapFitToCanvas = {
contain: 'embed',
cover: 'crop',
fill: 'ignore_aspect',
inside: 'max',
outside: 'min'
};
/**
* @private
*/
function isRotationExpected (options) {
return (options.angle % 360) !== 0 || options.useExifOrientation === true || options.rotationAngle !== 0;
}
/**
* Resize image to `width`, `height` or `width x height`.
*
* Possible kernels are:
* 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.
* - `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.
*
* 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.
*
* 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](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).
*
* @example
* sharp(inputBuffer)
* .resize(200, 300, {
* kernel: sharp.kernel.nearest
* })
* .background('white')
* .embed()
* .toFile('output.tiff')
* .then(function() {
* // output.tiff is a 200 pixels wide and 300 pixels high image
* // containing a nearest-neighbour scaled version, embedded on a white canvas,
* // of the image data in inputBuffer
* sharp(input)
* .resize({ width: 100 })
* .toBuffer()
* .then(data => {
* // 100 pixels wide, auto-scaled height
* });
*
* @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.
* @example
* sharp(input)
* .resize({ height: 100 })
* .toBuffer()
* .then(data => {
* // 100 pixels high, auto-scaled width
* });
*
* @example
* sharp(input)
* .resize(200, 300, {
* kernel: sharp.kernel.nearest,
* fit: 'contain',
* position: 'right top',
* background: { r: 255, g: 255, b: 255, alpha: 0.5 }
* })
* .toFile('output.png')
* .then(() => {
* // output.png is a 200 pixels wide and 300 pixels high image
* // containing a nearest-neighbour scaled version
* // contained within the north-east corner of a semi-transparent white canvas
* });
*
* @example
* const transformer = sharp()
* .resize({
* width: 200,
* height: 200,
* fit: sharp.fit.cover,
* position: sharp.strategy.entropy
* });
* // Read image data from readableStream
* // Write 200px square auto-cropped image data to writableStream
* readableStream
* .pipe(transformer)
* .pipe(writableStream);
*
* @example
* sharp(input)
* .resize(200, 200, {
* fit: sharp.fit.inside,
* withoutEnlargement: true
* })
* .toFormat('jpeg')
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains JPEG image data
* // no wider and no higher than 200 pixels
* // and no larger than the input image
* });
*
* @example
* const scaleByHalf = await sharp(input)
* .metadata()
* .then(({ width }) => sharp(input)
* .resize(Math.round(width * 0.5))
* .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 {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.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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function resize (width, height, options) {
if (is.defined(width)) {
if (is.integer(width) && width > 0) {
if (is.object(width) && !is.defined(options)) {
options = width;
} else if (is.integer(width) && width > 0) {
this.options.width = width;
} else {
throw is.invalidParameterError('width', 'positive integer', width);
@@ -94,6 +226,44 @@ function resize (width, height, options) {
this.options.height = -1;
}
if (is.object(options)) {
// Width
if (is.defined(options.width)) {
if (is.integer(options.width) && options.width > 0) {
this.options.width = options.width;
} else {
throw is.invalidParameterError('width', 'positive integer', options.width);
}
}
// Height
if (is.defined(options.height)) {
if (is.integer(options.height) && options.height > 0) {
this.options.height = options.height;
} else {
throw is.invalidParameterError('height', 'positive integer', options.height);
}
}
// Fit
if (is.defined(options.fit)) {
const canvas = mapFitToCanvas[options.fit];
if (is.string(canvas)) {
this.options.canvas = canvas;
} else {
throw is.invalidParameterError('fit', 'valid fit', options.fit);
}
}
// Position
if (is.defined(options.position)) {
const pos = is.integer(options.position)
? options.position
: strategy[options.position] || position[options.position] || gravity[options.position];
if (is.integer(pos) && (is.inRange(pos, 0, 8) || is.inRange(pos, 16, 17))) {
this.options.position = pos;
} else {
throw is.invalidParameterError('position', 'valid position/gravity/strategy', options.position);
}
}
// Background
this._setBackgroundColourOption('resizeBackground', options.background);
// Kernel
if (is.defined(options.kernel)) {
if (is.string(kernel[options.kernel])) {
@@ -102,6 +272,10 @@ function resize (width, height, options) {
throw is.invalidParameterError('kernel', 'valid kernel name', options.kernel);
}
}
// Without enlargement
if (is.defined(options.withoutEnlargement)) {
this._setBooleanOption('withoutEnlargement', options.withoutEnlargement);
}
// Shrink on load
if (is.defined(options.fastShrinkOnLoad)) {
this._setBooleanOption('fastShrinkOnLoad', options.fastShrinkOnLoad);
@@ -111,156 +285,124 @@ function resize (width, height, options) {
}
/**
* Crop the resized image to the exact size specified, the default behaviour.
*
* Possible attributes of the optional `sharp.gravity` are `north`, `northeast`, `east`, `southeast`, `south`,
* `southwest`, `west`, `northwest`, `center` and `centre`.
*
* 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](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.
* Extends/pads the edges of the image with the provided background colour.
* This operation will always occur after resizing and extraction, if any.
*
* @example
* const transformer = sharp()
* .resize(200, 200)
* .crop(sharp.strategy.entropy)
* .on('error', function(err) {
* console.log(err);
* });
* // Read image data from readableStream
* // Write 200px square auto-cropped image data to writableStream
* readableStream.pipe(transformer).pipe(writableStream);
* // 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)
* .resize(140)
* .extend({
* top: 10,
* bottom: 20,
* left: 10,
* right: 10,
* background: { r: 0, g: 0, b: 0, alpha: 0 }
* })
* ...
*
* @param {String} [crop='centre'] - A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically.
* @param {(number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
* @param {number} [extend.top]
* @param {number} [extend.left]
* @param {number} [extend.bottom]
* @param {number} [extend.right]
* @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
*/
function crop (crop) {
this.options.canvas = 'crop';
if (!is.defined(crop)) {
// Default
this.options.crop = gravity.center;
} else if (is.integer(crop) && is.inRange(crop, 0, 8)) {
// Gravity (numeric)
this.options.crop = crop;
} else if (is.string(crop) && is.integer(gravity[crop])) {
// Gravity (string)
this.options.crop = gravity[crop];
} else if (is.integer(crop) && crop >= strategy.entropy) {
// Strategy
this.options.crop = crop;
} else if (is.string(crop) && is.integer(strategy[crop])) {
// Strategy (string)
this.options.crop = strategy[crop];
} else {
throw is.invalidParameterError('crop', 'valid crop id/name/strategy', crop);
}
return this;
}
/**
* Preserving aspect ratio, resize the image to the maximum `width` or `height` specified
* then embed on a background of the exact `width` and `height` specified.
*
* If the background contains an alpha value then WebP and PNG format output images will
* contain an alpha channel, even when the input image does not.
*
* @example
* sharp('input.gif')
* .resize(200, 300)
* .background({r: 0, g: 0, b: 0, alpha: 0})
* .embed()
* .toFormat(sharp.format.webp)
* .toBuffer(function(err, outputBuffer) {
* if (err) {
* throw err;
* }
* // outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
* // containing a scaled version, embedded on a transparent canvas, of input.gif
* });
* @param {String} [embed='centre'] - A member of `sharp.gravity` to embed to an edge/corner.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function embed (embed) {
this.options.canvas = 'embed';
if (!is.defined(embed)) {
// Default
this.options.embed = gravity.center;
} else if (is.integer(embed) && is.inRange(embed, 0, 8)) {
// Gravity (numeric)
this.options.embed = embed;
} else if (is.string(embed) && is.integer(gravity[embed])) {
// Gravity (string)
this.options.embed = gravity[embed];
} else {
throw is.invalidParameterError('embed', 'valid embed id/name', embed);
}
return this;
}
/**
* Preserving aspect ratio, resize the image to be as large as possible
* while ensuring its dimensions are less than or equal to the `width` and `height` specified.
*
* Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
*
* @example
* sharp(inputBuffer)
* .resize(200, 200)
* .max()
* .toFormat('jpeg')
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains JPEG image data no wider than 200 pixels and no higher
* // than 200 pixels regardless of the inputBuffer image dimensions
* });
*
* @returns {Sharp}
*/
function max () {
this.options.canvas = 'max';
return this;
}
/**
* Preserving aspect ratio, resize the image to be as small as possible
* while ensuring its dimensions are greater than or equal to the `width` and `height` specified.
*
* Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
*
* @returns {Sharp}
*/
function min () {
this.options.canvas = 'min';
return this;
}
/**
* Ignoring the aspect ratio of the input, stretch the image to
* the exact `width` and/or `height` provided via `resize`.
* @returns {Sharp}
*/
function ignoreAspectRatio () {
this.options.canvas = 'ignore_aspect';
return this;
}
/**
* Do not enlarge the output image if the input image width *or* height are already less than the required dimensions.
* This is equivalent to GraphicsMagick's `>` geometry option:
* "*change the dimensions of the image only if its width or height exceeds the geometry specification*".
*
* The default behaviour *before* function call is `false`, meaning the image will be enlarged.
*
* @param {Boolean} [withoutEnlargement=true]
* @returns {Sharp}
*/
function withoutEnlargement (withoutEnlargement) {
this.options.withoutEnlargement = is.bool(withoutEnlargement) ? withoutEnlargement : true;
function extend (extend) {
if (is.integer(extend) && extend > 0) {
this.options.extendTop = extend;
this.options.extendBottom = extend;
this.options.extendLeft = extend;
this.options.extendRight = extend;
} else if (
is.object(extend) &&
is.integer(extend.top) && extend.top >= 0 &&
is.integer(extend.bottom) && extend.bottom >= 0 &&
is.integer(extend.left) && extend.left >= 0 &&
is.integer(extend.right) && extend.right >= 0
) {
this.options.extendTop = extend.top;
this.options.extendBottom = extend.bottom;
this.options.extendLeft = extend.left;
this.options.extendRight = extend.right;
this._setBackgroundColourOption('extendBackground', extend.background);
} else {
throw is.invalidParameterError('extend', 'integer or object', extend);
}
return this;
}
/**
* 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.
*
* @example
* 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.
* });
* @example
* sharp(input)
* .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
* .resize(width, height)
* .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
* .toFile(output, function(err) {
* // Extract a region, resize, then extract from the resized image
* });
*
* @param {Object} options - describes the region to extract using integral pixel values
* @param {number} options.left - zero-indexed offset from left edge
* @param {number} options.top - zero-indexed offset from top edge
* @param {number} options.width - width of region to extract
* @param {number} options.height - height of region to extract
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extract (options) {
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
['left', 'top', 'width', 'height'].forEach(function (name) {
const value = options[name];
if (is.integer(value) && value >= 0) {
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else {
throw is.invalidParameterError(name, 'integer', value);
}
}, this);
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && isRotationExpected(this.options)) {
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.
*
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
*
* @param {number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function trim (threshold) {
if (!is.defined(threshold)) {
this.options.trimThreshold = 10;
} else if (is.number(threshold) && threshold > 0) {
this.options.trimThreshold = threshold;
} else {
throw is.invalidParameterError('threshold', 'number greater than zero', threshold);
}
if (this.options.trimThreshold && isRotationExpected(this.options)) {
this.options.rotateBeforePreExtract = true;
}
return this;
}
@@ -269,19 +411,16 @@ function withoutEnlargement (withoutEnlargement) {
* @private
*/
module.exports = function (Sharp) {
[
Object.assign(Sharp.prototype, {
resize,
crop,
embed,
max,
min,
ignoreAspectRatio,
withoutEnlargement
].forEach(function (f) {
Sharp.prototype[f.name] = f;
extend,
extract,
trim
});
// Class attributes
Sharp.gravity = gravity;
Sharp.strategy = strategy;
Sharp.kernel = kernel;
Sharp.fit = fit;
Sharp.position = position;
};

View File

@@ -1,8 +1,31 @@
'use strict';
const events = require('events');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @member
* @example
* console.log(sharp.format);
* @returns {Object}
*/
const format = sharp.format();
/**
* An Object containing the version numbers of libvips and its dependencies.
* @member
* @example
* console.log(sharp.versions);
*/
let versions = {
vips: sharp.libvipsVersion()
};
try {
versions = require('../vendor/versions.json');
} catch (err) {}
/**
* 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.
@@ -16,10 +39,10 @@ const sharp = require('../build/Release/sharp.node');
* sharp.cache( { files: 0 } );
* sharp.cache(false);
*
* @param {Object|Boolean} [options=true] - Object with the following attributes, or Boolean where true uses default cache settings and false removes all caching
* @param {Number} [options.memory=50] - is the maximum memory in MB to use for this cache
* @param {Number} [options.files=20] - is the maximum number of files to hold open
* @param {Number} [options.items=100] - is the maximum number of operations to cache
* @param {Object|boolean} [options=true] - Object with the following attributes, or boolean where true uses default cache settings and false removes all caching
* @param {number} [options.memory=50] - is the maximum memory in MB to use for this cache
* @param {number} [options.files=20] - is the maximum number of files to hold open
* @param {number} [options.items=100] - is the maximum number of operations to cache
* @returns {Object}
*/
function cache (options) {
@@ -54,13 +77,25 @@ cache(true);
* sharp.concurrency(2); // 2
* sharp.concurrency(0); // 4
*
* @param {Number} [concurrency]
* @returns {Number} concurrency
* @param {number} [concurrency]
* @returns {number} concurrency
*/
function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}
/**
* An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread
* - complete
* @member
* @example
* sharp.queue.on('change', function(queueLength) {
* console.log('Queue contains ' + queueLength + ' task(s)');
* });
*/
const queue = new events.EventEmitter();
/**
* 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.
@@ -82,23 +117,20 @@ function counters () {
* 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.
*
* This feature is currently off by default but future versions may reverse this.
* Versions of liborc prior to 0.4.25 are known to segfault under heavy load.
*
* @example
* const simd = sharp.simd();
* // simd is `true` if SIMD is currently enabled
* // simd is `true` if the runtime use of liborc is currently enabled
* @example
* const simd = sharp.simd(true);
* // attempts to enable the use of SIMD, returning true if available
* const simd = sharp.simd(false);
* // prevent libvips from using liborc at runtime
*
* @param {Boolean} [simd=false]
* @returns {Boolean}
* @param {boolean} [simd=true]
* @returns {boolean}
*/
function simd (simd) {
return sharp.simd(is.bool(simd) ? simd : null);
}
simd(false);
simd(true);
/**
* Decorate the Sharp class with utility-related functions.
@@ -113,4 +145,7 @@ module.exports = function (Sharp) {
].forEach(function (f) {
Sharp[f.name] = f;
});
Sharp.format = format;
Sharp.versions = versions;
Sharp.queue = queue;
};

View File

@@ -1,25 +0,0 @@
site_name: sharp
site_url: http://sharp.pixelplumbing.com/
repo_url: https://github.com/lovell/sharp
site_description: High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images
copyright: <a href="https://pixelplumbing.com/">pixelplumbing.com</a>
google_analytics: ['UA-13034748-12', 'sharp.pixelplumbing.com']
theme: readthedocs
markdown_extensions:
- toc:
permalink: True
pages:
- Home: index.md
- Installation: install.md
- API:
- Constructor: api-constructor.md
- Input: api-input.md
- Output: api-output.md
- "Resizing images": api-resize.md
- "Compositing images": api-composite.md
- "Image operations": api-operation.md
- "Colour manipulation": api-colour.md
- "Channel manipulation": api-channel.md
- Utilities: api-utility.md
- Performance: performance.md
- Changelog: changelog.md

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.19.0",
"version": "0.25.3",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -39,19 +39,54 @@
"Guy Maliar <guy@tailorbrands.com>",
"Nicolas Coden <nicolas@ncoden.fr>",
"Matt Parrish <matt.r.parrish@gmail.com>",
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
"Matthew McEachen <matthew+github@mceachen.org>",
"Jarda Kotěšovec <jarda.kotesovec@gmail.com>",
"Kenric D'Souza <kenric.dsouza@gmail.com>",
"Oleh Aleinyk <oleg.aleynik@gmail.com>"
"Oleh Aleinyk <oleg.aleynik@gmail.com>",
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
"Andrea Bianco <andrea.bianco@unibas.ch>",
"Rik Heywood <rik@rik.org>",
"Thomas Parisot <hi@oncletom.io>",
"Nathan Graves <nathanrgraves+github@gmail.com>",
"Tom Lokhorst <tom@lokhorst.eu>",
"Espen Hovlandsdal <espen@hovlandsdal.com>",
"Sylvain Dumont <sylvain.dumont35@gmail.com>",
"Alun Davies <alun.owain.davies@googlemail.com>",
"Aidan Hoolachan <ajhoolachan21@gmail.com>",
"Axel Eirola <axel.eirola@iki.fi>",
"Freezy <freezy@xbmc.org>",
"Daiz <taneli.vatanen@gmail.com>",
"Julian Aubourg <j@ubourg.net>",
"Keith Belovay <keith@picthrive.com>",
"Michael B. Klein <mbklein@gmail.com>",
"Jordan Prudhomme <jordan@raboland.fr>",
"Ilya Ovdin <iovdin@gmail.com>",
"Andargor <andargor@yahoo.com>",
"Paul Neave <paul.neave@gmail.com>",
"Brendan Kennedy <brenwken@gmail.com>",
"Brychan Bennett-Odlum <git@brychan.io>",
"Edward Silverton <e.silverton@gmail.com>"
],
"scripts": {
"clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*",
"test": "semistandard && cc && nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"coverage": "./test/coverage/report.sh",
"install": "(node install/libvips && node install/dll-copy && prebuild-install --runtime=napi) || (node-gyp rebuild && node install/dll-copy)",
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing && prebuild-ci",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"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": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md lib/$m.js >docs/api-$m.md; done"
"docs-build": "documentation lint lib && for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done && 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",
"files": [
"binding.gyp",
"install/**",
"lib/**",
"src/**"
],
"repository": {
"type": "git",
"url": "git://github.com/lovell/sharp"
@@ -73,32 +108,46 @@
"vips"
],
"dependencies": {
"color": "^2.0.1",
"color": "^3.1.2",
"detect-libc": "^1.0.3",
"nan": "^2.8.0",
"semver": "^5.4.1",
"simple-get": "^2.7.0",
"tar": "^4.2.0",
"node-addon-api": "^3.0.0",
"npmlog": "^4.1.2",
"prebuild-install": "^5.3.3",
"semver": "^7.3.2",
"simple-get": "^4.0.0",
"tar": "^6.0.2",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
"async": "^2.6.0",
"cc": "^1.0.1",
"documentation": "^5.3.5",
"exif-reader": "^1.0.2",
"async": "^3.2.0",
"cc": "^2.0.1",
"decompress-zip": "^0.3.2",
"documentation": "^13.0.0",
"exif-reader": "^1.0.3",
"icc": "^1.0.0",
"mocha": "^4.1.0",
"nyc": "^11.4.1",
"rimraf": "^2.6.2",
"semistandard": "^12.0.0",
"unzip": "^0.1.11"
"license-checker": "^25.0.1",
"mocha": "^7.1.2",
"mock-fs": "^4.12.0",
"nyc": "^15.0.1",
"prebuild": "^10.0.0",
"prebuild-ci": "^3.1.0",
"rimraf": "^3.0.2",
"semistandard": "^14.2.0"
},
"license": "Apache-2.0",
"config": {
"libvips": "8.6.1"
"libvips": "8.9.1"
},
"engines": {
"node": ">=4.5.0"
"node": ">=10"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"binary": {
"napi_versions": [
3
]
},
"semistandard": {
"env": [
@@ -108,10 +157,7 @@
"cc": {
"linelength": "120",
"filter": [
"build/c++11",
"build/include",
"runtime/indentation_namespace",
"runtime/references"
"build/include"
]
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -17,11 +17,9 @@
#include <string.h>
#include <vector>
#include <queue>
#include <mutex>
#include <mutex> // NOLINT(build/c++11)
#include <node.h>
#include <node_buffer.h>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
#include "common.h"
@@ -30,49 +28,77 @@ using vips::VImage;
namespace sharp {
// Convenience methods to access the attributes of a v8::Object
bool HasAttr(v8::Handle<v8::Object> obj, std::string attr) {
return Nan::Has(obj, Nan::New(attr).ToLocalChecked()).FromJust();
// Convenience methods to access the attributes of a Napi::Object
bool HasAttr(Napi::Object obj, std::string attr) {
return obj.Has(attr);
}
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr) {
return *Nan::Utf8String(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked());
std::string AttrAsStr(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::String>();
}
uint32_t AttrAsUint32(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::Number>().Uint32Value();
}
int32_t AttrAsInt32(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::Number>().Int32Value();
}
double AttrAsDouble(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::Number>().DoubleValue();
}
double AttrAsDouble(Napi::Object obj, unsigned int const attr) {
return obj.Get(attr).As<Napi::Number>().DoubleValue();
}
bool AttrAsBool(Napi::Object obj, std::string attr) {
return obj.Get(attr).As<Napi::Boolean>().Value();
}
std::vector<double> AttrAsRgba(Napi::Object obj, std::string attr) {
Napi::Array background = obj.Get(attr).As<Napi::Array>();
std::vector<double> rgba(background.Length());
for (unsigned int i = 0; i < background.Length(); i++) {
rgba[i] = AttrAsDouble(background, i);
}
return rgba;
}
// Create an InputDescriptor instance from a v8::Object describing an input image
InputDescriptor* CreateInputDescriptor(
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist
) {
Nan::HandleScope();
// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input) {
InputDescriptor *descriptor = new InputDescriptor;
if (HasAttr(input, "file")) {
descriptor->file = AttrAsStr(input, "file");
} else if (HasAttr(input, "buffer")) {
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
descriptor->bufferLength = node::Buffer::Length(buffer);
descriptor->buffer = node::Buffer::Data(buffer);
buffersToPersist.push_back(buffer);
Napi::Buffer<char> buffer = input.Get("buffer").As<Napi::Buffer<char>>();
descriptor->bufferLength = buffer.Length();
descriptor->buffer = buffer.Data();
descriptor->isBuffer = TRUE;
}
descriptor->failOnError = AttrTo<bool>(input, "failOnError");
descriptor->failOnError = AttrAsBool(input, "failOnError");
// Density for vector-based input
if (HasAttr(input, "density")) {
descriptor->density = AttrTo<uint32_t>(input, "density");
descriptor->density = AttrAsDouble(input, "density");
}
// Raw pixel input
if (HasAttr(input, "rawChannels")) {
descriptor->rawChannels = AttrTo<uint32_t>(input, "rawChannels");
descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth");
descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight");
descriptor->rawChannels = AttrAsUint32(input, "rawChannels");
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
}
// Multi-page input (GIF, TIFF, PDF)
if (HasAttr(input, "pages")) {
descriptor->pages = AttrAsInt32(input, "pages");
}
if (HasAttr(input, "page")) {
descriptor->page = AttrAsUint32(input, "page");
}
// Create new image
if (HasAttr(input, "createChannels")) {
descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels");
descriptor->createWidth = AttrTo<uint32_t>(input, "createWidth");
descriptor->createHeight = AttrTo<uint32_t>(input, "createHeight");
v8::Local<v8::Object> createBackground = AttrAs<v8::Object>(input, "createBackground");
for (unsigned int i = 0; i < 4; i++) {
descriptor->createBackground[i] = AttrTo<double>(createBackground, i);
}
descriptor->createChannels = AttrAsUint32(input, "createChannels");
descriptor->createWidth = AttrAsUint32(input, "createWidth");
descriptor->createHeight = AttrAsUint32(input, "createHeight");
descriptor->createBackground = AttrAsRgba(input, "createBackground");
}
// Limit input images to a given number of pixels, where pixels = width * height
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
// Allow switch from random to sequential access
descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
return descriptor;
}
@@ -98,6 +124,15 @@ namespace sharp {
bool IsTiff(std::string const &str) {
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
}
bool IsHeic(std::string const &str) {
return EndsWith(str, ".heic") || EndsWith(str, ".HEIC");
}
bool IsHeif(std::string const &str) {
return EndsWith(str, ".heif") || EndsWith(str, ".HEIF") || IsHeic(str) || IsAvif(str);
}
bool IsAvif(std::string const &str) {
return EndsWith(str, ".avif") || EndsWith(str, ".AVIF");
}
bool IsDz(std::string const &str) {
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
}
@@ -120,14 +155,16 @@ namespace sharp {
case ImageType::TIFF: id = "tiff"; break;
case ImageType::GIF: id = "gif"; break;
case ImageType::SVG: id = "svg"; break;
case ImageType::HEIF: id = "heif"; break;
case ImageType::PDF: id = "pdf"; break;
case ImageType::MAGICK: id = "magick"; break;
case ImageType::OPENSLIDE: id = "openslide"; break;
case ImageType::PPM: id = "ppm"; break;
case ImageType::FITS: id = "fits"; break;
case ImageType::VIPS: id = "v"; break;
case ImageType::VIPS: id = "vips"; break;
case ImageType::RAW: id = "raw"; break;
case ImageType::UNKNOWN: id = "unknown"; break;
case ImageType::MISSING: id = "missing"; break;
}
return id;
}
@@ -152,6 +189,8 @@ namespace sharp {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgBuffer")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "HeifBuffer")) {
imageType = ImageType::HEIF;
} else if (EndsWith(loader, "PdfBuffer")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "MagickBuffer")) {
@@ -171,7 +210,7 @@ namespace sharp {
std::string const loader = load;
if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) {
} else if (EndsWith(loader, "PngFile")) {
imageType = ImageType::PNG;
} else if (EndsWith(loader, "WebpFile")) {
imageType = ImageType::WEBP;
@@ -183,6 +222,8 @@ namespace sharp {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgFile")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "HeifFile")) {
imageType = ImageType::HEIF;
} else if (EndsWith(loader, "PdfFile")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "Ppm")) {
@@ -194,17 +235,32 @@ namespace sharp {
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
imageType = ImageType::MAGICK;
}
} else {
if (EndsWith(vips::VError().what(), " not found\n")) {
imageType = ImageType::MISSING;
}
}
return imageType;
}
/*
Does this image type support multiple pages?
*/
bool ImageTypeSupportsPage(ImageType imageType) {
return
imageType == ImageType::GIF ||
imageType == ImageType::TIFF ||
imageType == ImageType::HEIF ||
imageType == ImageType::PDF;
}
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor) {
VImage image;
ImageType imageType;
if (descriptor->buffer != nullptr) {
if (descriptor->isBuffer) {
if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
@@ -221,20 +277,27 @@ namespace sharp {
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", accessMethod)
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG) {
option->set("unlimited", TRUE);
}
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density));
option->set("dpi", descriptor->density);
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
if (ImageTypeSupportsPage(imageType)) {
option->set("n", descriptor->pages);
option->set("page", descriptor->page);
}
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
image = SetDensity(image, descriptor->density);
}
} catch (...) {
throw vips::VError("Input buffer has corrupt header");
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
}
} else {
throw vips::VError("Input buffer contains unsupported image format");
@@ -257,29 +320,44 @@ namespace sharp {
} else {
// From filesystem
imageType = DetermineImageType(descriptor->file.data());
if (imageType == ImageType::MISSING) {
throw vips::VError("Input file is missing");
}
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", accessMethod)
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG) {
option->set("unlimited", TRUE);
}
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density));
option->set("dpi", descriptor->density);
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
if (ImageTypeSupportsPage(imageType)) {
option->set("n", descriptor->pages);
option->set("page", descriptor->page);
}
image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
image = SetDensity(image, descriptor->density);
}
} catch (...) {
throw vips::VError("Input file has corrupt header");
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
}
} else {
throw vips::VError("Input file is missing or of an unsupported image format");
throw vips::VError("Input file contains unsupported image format");
}
}
}
// 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)) {
throw vips::VError("Input image exceeds pixel limit");
}
return std::make_tuple(image, imageType);
}
@@ -317,15 +395,19 @@ namespace sharp {
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VImage image, int const orientation) {
image.set(VIPS_META_ORIENTATION, orientation);
VImage SetExifOrientation(VImage image, int const orientation) {
VImage copy = image.copy();
copy.set(VIPS_META_ORIENTATION, orientation);
return copy;
}
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image) {
vips_image_remove(image.get_image(), VIPS_META_ORIENTATION);
VImage RemoveExifOrientation(VImage image) {
VImage copy = image.copy();
copy.remove(VIPS_META_ORIENTATION);
return copy;
}
/*
@@ -345,11 +427,13 @@ namespace sharp {
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const int density) {
const double pixelsPerMm = static_cast<double>(density) / 25.4;
image.set("Xres", pixelsPerMm);
image.set("Yres", pixelsPerMm);
image.set(VIPS_META_RESOLUTION_UNIT, "in");
VImage SetDensity(VImage image, const double density) {
const double pixelsPerMm = density / 25.4;
VImage copy = image.copy();
copy.set("Xres", pixelsPerMm);
copy.set("Yres", pixelsPerMm);
copy.set(VIPS_META_RESOLUTION_UNIT, "in");
return copy;
}
/*
@@ -360,10 +444,6 @@ namespace sharp {
if (image.width() > 65535 || image.height() > 65535) {
throw vips::VError("Processed image is too large for the JPEG format");
}
} else if (imageType == ImageType::PNG) {
if (image.width() > 2147483647 || image.height() > 2147483647) {
throw vips::VError("Processed image is too large for the PNG format");
}
} else if (imageType == ImageType::WEBP) {
if (image.width() > 16383 || image.height() > 16383) {
throw vips::VError("Processed image is too large for the WebP format");
@@ -374,11 +454,9 @@ namespace sharp {
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
void FreeCallback(char* data, void* hint) {
if (data != nullptr) {
g_free(data);
}
}
std::function<void(void*, char*)> FreeCallback = [](void*, char* data) {
g_free(data);
};
/*
Temporary buffer of warnings
@@ -596,4 +674,40 @@ namespace sharp {
}
}
/*
Apply the alpha channel to a given colour
*/
std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour) {
// Scale up 8-bit values to match 16-bit input image
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
// Create alphaColour colour
std::vector<double> alphaColour;
if (image.bands() > 2) {
alphaColour = {
multiplier * colour[0],
multiplier * colour[1],
multiplier * colour[2]
};
} else {
// Convert sRGB to greyscale
alphaColour = { multiplier * (
0.2126 * colour[0] +
0.7152 * colour[1] +
0.0722 * colour[2])
};
}
// Add alpha channel to alphaColour colour
if (colour[3] < 255.0 || HasAlpha(image)) {
alphaColour.push_back(colour[3] * multiplier);
}
// Ensure alphaColour colour uses correct colourspace
alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation());
// 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));
}
return std::make_tuple(image, alphaColour);
}
} // namespace sharp

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -19,23 +19,22 @@
#include <tuple>
#include <vector>
#include <node.h>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 6))
#error libvips version 8.6.1+ is required - see sharp.pixelplumbing.com/page/install
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 9))
#error "libvips version 8.9.1+ is required - please see https://sharp.pixelplumbing.com/install"
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
#error GCC version 4.6+ is required for C++11 features - see sharp.pixelplumbing.com/page/install#prerequisites
#error "GCC version 4.6+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
#endif
#if (defined(__clang__) && defined(__has_feature))
#if (!__has_feature(cxx_range_for))
#error clang version 3.0+ is required for C++11 features - see sharp.pixelplumbing.com/page/install#prerequisites
#error "clang version 3.0+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
#endif
#endif
@@ -43,55 +42,57 @@ using vips::VImage;
namespace sharp {
struct InputDescriptor {
struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
std::string name;
std::string file;
char *buffer;
bool failOnError;
int limitInputPixels;
VipsAccess access;
size_t bufferLength;
int density;
bool isBuffer;
double density;
int rawChannels;
int rawWidth;
int rawHeight;
int pages;
int page;
int createChannels;
int createWidth;
int createHeight;
double createBackground[4];
std::vector<double> createBackground;
InputDescriptor():
buffer(nullptr),
failOnError(FALSE),
failOnError(TRUE),
limitInputPixels(0x3FFF * 0x3FFF),
access(VIPS_ACCESS_RANDOM),
bufferLength(0),
density(72),
isBuffer(FALSE),
density(72.0),
rawChannels(0),
rawWidth(0),
rawHeight(0),
pages(1),
page(0),
createChannels(0),
createWidth(0),
createHeight(0) {
createBackground[0] = 0.0;
createBackground[1] = 0.0;
createBackground[2] = 0.0;
createBackground[3] = 255.0;
}
createHeight(0),
createBackground{ 0.0, 0.0, 0.0, 255.0 } {}
};
// Convenience methods to access the attributes of a v8::Object
bool HasAttr(v8::Handle<v8::Object> obj, std::string attr);
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr);
template<typename T> v8::Local<T> AttrAs(v8::Handle<v8::Object> obj, std::string attr) {
return Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked().As<T>();
}
template<typename T> T AttrTo(v8::Handle<v8::Object> obj, std::string attr) {
return Nan::To<T>(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked()).FromJust();
}
template<typename T> T AttrTo(v8::Handle<v8::Object> obj, int attr) {
return Nan::To<T>(Nan::Get(obj, attr).ToLocalChecked()).FromJust();
}
// Convenience methods to access the attributes of a Napi::Object
bool HasAttr(Napi::Object obj, std::string attr);
std::string AttrAsStr(Napi::Object obj, std::string attr);
uint32_t AttrAsUint32(Napi::Object obj, std::string attr);
int32_t AttrAsInt32(Napi::Object obj, std::string attr);
double AttrAsDouble(Napi::Object obj, std::string attr);
double AttrAsDouble(Napi::Object obj, unsigned int const attr);
bool AttrAsBool(Napi::Object obj, std::string attr);
std::vector<double> AttrAsRgba(Napi::Object obj, std::string attr);
// Create an InputDescriptor instance from a v8::Object describing an input image
InputDescriptor* CreateInputDescriptor(
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist);
// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input);
enum class ImageType {
JPEG,
@@ -100,6 +101,7 @@ namespace sharp {
TIFF,
GIF,
SVG,
HEIF,
PDF,
MAGICK,
OPENSLIDE,
@@ -107,7 +109,8 @@ namespace sharp {
FITS,
VIPS,
RAW,
UNKNOWN
UNKNOWN,
MISSING
};
// How many tasks are in the queue?
@@ -121,6 +124,9 @@ namespace sharp {
bool IsPng(std::string const &str);
bool IsWebp(std::string const &str);
bool IsTiff(std::string const &str);
bool IsHeic(std::string const &str);
bool IsHeif(std::string const &str);
bool IsAvif(std::string const &str);
bool IsDz(std::string const &str);
bool IsDzZip(std::string const &str);
bool IsV(std::string const &str);
@@ -140,10 +146,15 @@ namespace sharp {
*/
ImageType DetermineImageType(char const *file);
/*
Does this image type support multiple pages?
*/
bool ImageTypeSupportsPage(ImageType imageType);
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod);
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor);
/*
Does this image have an embedded profile?
@@ -164,12 +175,12 @@ namespace sharp {
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VImage image, int const orientation);
VImage SetExifOrientation(VImage image, int const orientation);
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image);
VImage RemoveExifOrientation(VImage image);
/*
Does this image have a non-default density?
@@ -184,7 +195,7 @@ namespace sharp {
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const int density);
VImage SetDensity(VImage image, const double density);
/*
Check the proposed format supports the current dimensions.
@@ -194,7 +205,7 @@ namespace sharp {
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
void FreeCallback(char* data, void* hint);
extern std::function<void(void*, char*)> FreeCallback;
/*
Called with warnings from the glib-registered "VIPS" domain
@@ -253,6 +264,11 @@ namespace sharp {
*/
std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba, VipsInterpretation const interpretation);
/*
Apply the alpha channel to a given colour
*/
std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour);
} // namespace sharp
#endif // SRC_COMMON_H_

View File

@@ -0,0 +1,178 @@
/* Object part of the VSource and VTarget class
*/
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>
#include <vips/debug.h>
/*
#define VIPS_DEBUG
#define VIPS_DEBUG_VERBOSE
*/
VIPS_NAMESPACE_START
VSource
VSource::new_from_descriptor( int descriptor )
{
VipsSource *input;
if( !(input = vips_source_new_from_descriptor( descriptor )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_file( const char *filename )
{
VipsSource *input;
if( !(input = vips_source_new_from_file( filename )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_blob( VipsBlob *blob )
{
VipsSource *input;
if( !(input = vips_source_new_from_blob( blob )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_memory( const void *data,
size_t size )
{
VipsSource *input;
if( !(input = vips_source_new_from_memory( data, size )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_options( const char *options )
{
VipsSource *input;
if( !(input = vips_source_new_from_options( options )) )
throw VError();
VSource out( input );
return( out );
}
VOption *
VOption::set( const char *name, const VSource value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_SOURCE );
g_value_set_object( &pair->value, value.get_source() );
options.push_back( pair );
return( this );
}
VTarget
VTarget::new_to_descriptor( int descriptor )
{
VipsTarget *output;
if( !(output = vips_target_new_to_descriptor( descriptor )) )
throw VError();
VTarget out( output );
return( out );
}
VTarget
VTarget::new_to_file( const char *filename )
{
VipsTarget *output;
if( !(output = vips_target_new_to_file( filename )) )
throw VError();
VTarget out( output );
return( out );
}
VTarget
VTarget::new_to_memory()
{
VipsTarget *output;
if( !(output = vips_target_new_to_memory()) )
throw VError();
VTarget out( output );
return( out );
}
VOption *
VOption::set( const char *name, const VTarget value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_TARGET );
g_value_set_object( &pair->value, value.get_target() );
options.push_back( pair );
return( this );
}
VIPS_NAMESPACE_END

View File

@@ -32,8 +32,6 @@
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <iostream>
#include <vips/vips8>
VIPS_NAMESPACE_START

View File

@@ -169,7 +169,7 @@ VOption::set( const char *name, const char *value )
// input image
VOption *
VOption::set( const char *name, VImage value )
VOption::set( const char *name, const VImage value )
{
Pair *pair = new Pair( name );
@@ -563,7 +563,7 @@ VImage::new_from_file( const char *name, VOption *options )
}
VImage
VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
VImage::new_from_buffer( const void *buf, size_t len, const char *option_string,
VOption *options )
{
const char *operation_name;
@@ -588,6 +588,36 @@ VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
return( out );
}
VImage
VImage::new_from_buffer( const std::string &buf, const char *option_string,
VOption *options )
{
return( new_from_buffer( buf.c_str(), buf.size(),
option_string, options ) );
}
VImage
VImage::new_from_source( VSource source, const char *option_string,
VOption *options )
{
const char *operation_name;
VImage out;
if( !(operation_name = vips_foreign_find_load_source(
source.get_source() )) ) {
delete options;
throw( VError() );
}
options = (options ? options : VImage::option())->
set( "source", source )->
set( "out", &out );
call_option_string( operation_name, option_string, options );
return( out );
}
VImage
VImage::new_matrix( int width, int height )
{
@@ -613,7 +643,7 @@ VImage::new_matrixv( int width, int height, ... )
}
VImage
VImage::write( VImage out )
VImage::write( VImage out ) const
{
if( vips_image_write( this->get_image(), out.get_image() ) )
throw VError();
@@ -622,7 +652,7 @@ VImage::write( VImage out )
}
void
VImage::write_to_file( const char *name, VOption *options )
VImage::write_to_file( const char *name, VOption *options ) const
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
@@ -642,7 +672,7 @@ VImage::write_to_file( const char *name, VOption *options )
void
VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
VOption *options )
VOption *options ) const
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
@@ -672,10 +702,30 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
}
}
void
VImage::write_to_target( const char *suffix, VTarget target,
VOption *options ) const
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
const char *operation_name;
vips__filename_split8( suffix, filename, option_string );
if( !(operation_name = vips_foreign_find_save_target( filename )) ) {
delete options;
throw VError();
}
call_option_string( operation_name, option_string,
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
#include "vips-operators.cpp"
std::vector<VImage>
VImage::bandsplit( VOption *options )
VImage::bandsplit( VOption *options ) const
{
std::vector<VImage> b;
@@ -686,7 +736,7 @@ VImage::bandsplit( VOption *options )
}
VImage
VImage::bandjoin( VImage other, VOption *options )
VImage::bandjoin( VImage other, VOption *options ) const
{
VImage v[2] = { *this, other };
std::vector<VImage> vec( v, v + VIPS_NUMBER( v ) );
@@ -695,7 +745,7 @@ VImage::bandjoin( VImage other, VOption *options )
}
VImage
VImage::composite( VImage other, VipsBlendMode mode, VOption *options )
VImage::composite( VImage other, VipsBlendMode mode, VOption *options ) const
{
VImage v[2] = { *this, other };
std::vector<VImage> ivec( v, v + VIPS_NUMBER( v ) );
@@ -706,7 +756,7 @@ VImage::composite( VImage other, VipsBlendMode mode, VOption *options )
}
std::complex<double>
VImage::minpos( VOption *options )
VImage::minpos( VOption *options ) const
{
double x, y;
@@ -719,7 +769,7 @@ VImage::minpos( VOption *options )
}
std::complex<double>
VImage::maxpos( VOption *options )
VImage::maxpos( VOption *options ) const
{
double x, y;
@@ -734,43 +784,43 @@ VImage::maxpos( VOption *options )
// Operator overloads
VImage
VImage::operator[]( int index )
VImage::operator[]( int index ) const
{
return( this->extract_band( index ) );
}
std::vector<double>
VImage::operator()( int x, int y )
VImage::operator()( int x, int y ) const
{
return( this->getpoint( x, y ) );
}
VImage
operator+( VImage a, VImage b )
operator+( const VImage a, const VImage b )
{
return( a.add( b ) );
}
VImage
operator+( double a, VImage b )
operator+( double a, const VImage b )
{
return( b.linear( 1.0, a ) );
}
VImage
operator+( VImage a, double b )
operator+( const VImage a, double b )
{
return( a.linear( 1.0, b ) );
}
VImage
operator+( std::vector<double> a, VImage b )
operator+( const std::vector<double> a, const VImage b )
{
return( b.linear( 1.0, a ) );
}
VImage
operator+( VImage a, std::vector<double> b )
operator+( const VImage a, const std::vector<double> b )
{
return( a.linear( 1.0, b ) );
}
@@ -788,37 +838,37 @@ operator+=( VImage &a, const double b )
}
VImage &
operator+=( VImage &a, std::vector<double> b )
operator+=( VImage &a, const std::vector<double> b )
{
return( a = a + b );
}
VImage
operator-( VImage a, VImage b )
operator-( const VImage a, const VImage b )
{
return( a.subtract( b ) );
}
VImage
operator-( double a, VImage b )
operator-( double a, const VImage b )
{
return( b.linear( -1.0, a ) );
}
VImage
operator-( VImage a, double b )
operator-( const VImage a, double b )
{
return( a.linear( 1.0, -b ) );
}
VImage
operator-( std::vector<double> a, VImage b )
operator-( const std::vector<double> a, const VImage b )
{
return( b.linear( -1.0, a ) );
}
VImage
operator-( VImage a, std::vector<double> b )
operator-( const VImage a, const std::vector<double> b )
{
return( a.linear( 1.0, vips::negate( b ) ) );
}
@@ -836,43 +886,43 @@ operator-=( VImage &a, const double b )
}
VImage &
operator-=( VImage &a, std::vector<double> b )
operator-=( VImage &a, const std::vector<double> b )
{
return( a = a - b );
}
VImage
operator-( VImage a )
operator-( const VImage a )
{
return( a * -1 );
}
VImage
operator*( VImage a, VImage b )
operator*( const VImage a, const VImage b )
{
return( a.multiply( b ) );
}
VImage
operator*( double a, VImage b )
operator*( double a, const VImage b )
{
return( b.linear( a, 0.0 ) );
}
VImage
operator*( VImage a, double b )
operator*( const VImage a, double b )
{
return( a.linear( b, 0.0 ) );
}
VImage
operator*( std::vector<double> a, VImage b )
operator*( const std::vector<double> a, const VImage b )
{
return( b.linear( a, 0.0 ) );
}
VImage
operator*( VImage a, std::vector<double> b )
operator*( const VImage a, const std::vector<double> b )
{
return( a.linear( b, 0.0 ) );
}
@@ -890,37 +940,37 @@ operator*=( VImage &a, const double b )
}
VImage &
operator*=( VImage &a, std::vector<double> b )
operator*=( VImage &a, const std::vector<double> b )
{
return( a = a * b );
}
VImage
operator/( VImage a, VImage b )
operator/( const VImage a, const VImage b )
{
return( a.divide( b ) );
}
VImage
operator/( double a, VImage b )
operator/( double a, const VImage b )
{
return( b.pow( -1.0 ).linear( a, 0.0 ) );
}
VImage
operator/( VImage a, double b )
operator/( const VImage a, double b )
{
return( a.linear( 1.0 / b, 0.0 ) );
}
VImage
operator/( std::vector<double> a, VImage b )
operator/( const std::vector<double> a, const VImage b )
{
return( b.pow( -1.0 ).linear( a, 0.0 ) );
}
VImage
operator/( VImage a, std::vector<double> b )
operator/( const VImage a, const std::vector<double> b )
{
return( a.linear( vips::invert( b ), 0.0 ) );
}
@@ -938,25 +988,25 @@ operator/=( VImage &a, const double b )
}
VImage &
operator/=( VImage &a, std::vector<double> b )
operator/=( VImage &a, const std::vector<double> b )
{
return( a = a / b );
}
VImage
operator%( VImage a, VImage b )
operator%( const VImage a, const VImage b )
{
return( a.remainder( b ) );
}
VImage
operator%( VImage a, double b )
operator%( const VImage a, const double b )
{
return( a.remainder_const( to_vector( b ) ) );
}
VImage
operator%( VImage a, std::vector<double> b )
operator%( const VImage a, const std::vector<double> b )
{
return( a.remainder_const( b ) );
}
@@ -974,243 +1024,243 @@ operator%=( VImage &a, const double b )
}
VImage &
operator%=( VImage &a, std::vector<double> b )
operator%=( VImage &a, const std::vector<double> b )
{
return( a = a % b );
}
VImage
operator<( VImage a, VImage b )
operator<( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator<( double a, VImage b )
operator<( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
to_vector( a ) ) );
}
VImage
operator<( VImage a, double b )
operator<( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
to_vector( b ) ) );
}
VImage
operator<( std::vector<double> a, VImage b )
operator<( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
a ) );
}
VImage
operator<( VImage a, std::vector<double> b )
operator<( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
b ) );
}
VImage
operator<=( VImage a, VImage b )
operator<=( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator<=( double a, VImage b )
operator<=( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
to_vector( a ) ) );
}
VImage
operator<=( VImage a, double b )
operator<=( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
to_vector( b ) ) );
}
VImage
operator<=( std::vector<double> a, VImage b )
operator<=( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
a ) );
}
VImage
operator<=( VImage a, std::vector<double> b )
operator<=( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
b ) );
}
VImage
operator>( VImage a, VImage b )
operator>( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator>( double a, VImage b )
operator>( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
to_vector( a ) ) );
}
VImage
operator>( VImage a, double b )
operator>( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
to_vector( b ) ) );
}
VImage
operator>( std::vector<double> a, VImage b )
operator>( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
a ) );
}
VImage
operator>( VImage a, std::vector<double> b )
operator>( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
b ) );
}
VImage
operator>=( VImage a, VImage b )
operator>=( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator>=( double a, VImage b )
operator>=( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
to_vector( a ) ) );
}
VImage
operator>=( VImage a, double b )
operator>=( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
to_vector( b ) ) );
}
VImage
operator>=( std::vector<double> a, VImage b )
operator>=( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
a ) );
}
VImage
operator>=( VImage a, std::vector<double> b )
operator>=( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
b ) );
}
VImage
operator==( VImage a, VImage b )
operator==( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator==( double a, VImage b )
operator==( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
to_vector( a ) ) );
}
VImage
operator==( VImage a, double b )
operator==( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
to_vector( b ) ) );
}
VImage
operator==( std::vector<double> a, VImage b )
operator==( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
a ) );
}
VImage
operator==( VImage a, std::vector<double> b )
operator==( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
b ) );
}
VImage
operator!=( VImage a, VImage b )
operator!=( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator!=( double a, VImage b )
operator!=( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
to_vector( a ) ) );
}
VImage
operator!=( VImage a, double b )
operator!=( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
to_vector( b ) ) );
}
VImage
operator!=( std::vector<double> a, VImage b )
operator!=( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
a ) );
}
VImage
operator!=( VImage a, std::vector<double> b )
operator!=( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
b ) );
}
VImage
operator&( VImage a, VImage b )
operator&( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator&( double a, VImage b )
operator&( const double a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_AND,
to_vector( a ) ) );
}
VImage
operator&( VImage a, double b )
operator&( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_AND,
to_vector( b ) ) );
}
VImage
operator&( std::vector<double> a, VImage b )
operator&( const std::vector<double> a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_AND, a ) );
}
VImage
operator&( VImage a, std::vector<double> b )
operator&( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_AND, b ) );
}
@@ -1228,40 +1278,40 @@ operator&=( VImage &a, const double b )
}
VImage &
operator&=( VImage &a, std::vector<double> b )
operator&=( VImage &a, const std::vector<double> b )
{
return( a = a & b );
}
VImage
operator|( VImage a, VImage b )
operator|( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator|( double a, VImage b )
operator|( const double a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
to_vector( a ) ) );
}
VImage
operator|( VImage a, double b )
operator|( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
to_vector( b ) ) );
}
VImage
operator|( std::vector<double> a, VImage b )
operator|( const std::vector<double> a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
a ) );
}
VImage
operator|( VImage a, std::vector<double> b )
operator|( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
b ) );
@@ -1280,40 +1330,40 @@ operator|=( VImage &a, const double b )
}
VImage &
operator|=( VImage &a, std::vector<double> b )
operator|=( VImage &a, const std::vector<double> b )
{
return( a = a | b );
}
VImage
operator^( VImage a, VImage b )
operator^( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator^( double a, VImage b )
operator^( const double a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
to_vector( a ) ) );
}
VImage
operator^( VImage a, double b )
operator^( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
to_vector( b ) ) );
}
VImage
operator^( std::vector<double> a, VImage b )
operator^( const std::vector<double> a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
a ) );
}
VImage
operator^( VImage a, std::vector<double> b )
operator^( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
b ) );
@@ -1332,26 +1382,26 @@ operator^=( VImage &a, const double b )
}
VImage &
operator^=( VImage &a, std::vector<double> b )
operator^=( VImage &a, const std::vector<double> b )
{
return( a = a ^ b );
}
VImage
operator<<( VImage a, VImage b )
operator<<( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_LSHIFT ) );
}
VImage
operator<<( VImage a, double b )
operator<<( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_LSHIFT,
to_vector( b ) ) );
}
VImage
operator<<( VImage a, std::vector<double> b )
operator<<( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_LSHIFT,
b ) );
@@ -1370,26 +1420,26 @@ operator<<=( VImage &a, const double b )
}
VImage &
operator<<=( VImage &a, std::vector<double> b )
operator<<=( VImage &a, const std::vector<double> b )
{
return( a = a << b );
}
VImage
operator>>( VImage a, VImage b )
operator>>( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_RSHIFT ) );
}
VImage
operator>>( VImage a, double b )
operator>>( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_RSHIFT,
to_vector( b ) ) );
}
VImage
operator>>( VImage a, std::vector<double> b )
operator>>( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_RSHIFT,
b ) );
@@ -1408,7 +1458,7 @@ operator>>=( VImage &a, const double b )
}
VImage &
operator>>=( VImage &a, std::vector<double> b )
operator>>=( VImage &a, const std::vector<double> b )
{
return( a = a << b );
}

View File

@@ -61,7 +61,7 @@ VInterpolate::new_from_name( const char *name, VOption *options )
}
VOption *
VOption::set( const char *name, VInterpolate value )
VOption::set( const char *name, const VInterpolate value )
{
Pair *pair = new Pair( name );

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -15,27 +15,16 @@
#include <numeric>
#include <vector>
#include <node.h>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
#include "common.h"
#include "metadata.h"
class MetadataWorker : public Nan::AsyncWorker {
class MetadataWorker : public Napi::AsyncWorker {
public:
MetadataWorker(
Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *debuglog,
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog),
buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer);
return index + 1;
});
}
MetadataWorker(Napi::Function callback, MetadataBaton *baton, Napi::Function debuglog) :
Napi::AsyncWorker(callback), baton(baton), debuglog(Napi::Persistent(debuglog)) {}
~MetadataWorker() {}
void Execute() {
@@ -45,7 +34,7 @@ class MetadataWorker : public Nan::AsyncWorker {
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, VIPS_ACCESS_SEQUENTIAL);
std::tie(image, imageType) = OpenInput(baton->input);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
@@ -61,6 +50,30 @@ class MetadataWorker : public Nan::AsyncWorker {
if (sharp::HasDensity(image)) {
baton->density = sharp::GetDensity(image);
}
if (image.get_typeof("jpeg-chroma-subsample") == VIPS_TYPE_REF_STRING) {
baton->chromaSubsampling = image.get_string("jpeg-chroma-subsample");
}
if (image.get_typeof("interlaced") == G_TYPE_INT) {
baton->isProgressive = image.get_int("interlaced") == 1;
}
if (image.get_typeof("palette-bit-depth") == G_TYPE_INT) {
baton->paletteBitDepth = image.get_int("palette-bit-depth");
}
if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT) {
baton->pages = image.get_int(VIPS_META_N_PAGES);
}
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
}
if (image.get_typeof("loop") == G_TYPE_INT) {
baton->loop = image.get_int("loop");
}
if (image.get_typeof("delay") == VIPS_TYPE_ARRAY_INT) {
baton->delay = image.get_array_int("delay");
}
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
baton->pagePrimary = image.get_int("heif-primary");
}
baton->hasProfile = sharp::HasProfile(image);
// Derived attributes
baton->hasAlpha = sharp::HasAlpha(image);
@@ -82,9 +95,9 @@ class MetadataWorker : public Nan::AsyncWorker {
baton->iccLength = iccLength;
}
// IPTC
if (image.get_typeof(VIPS_META_IPCT_NAME) == VIPS_TYPE_BLOB) {
if (image.get_typeof(VIPS_META_IPTC_NAME) == VIPS_TYPE_BLOB) {
size_t iptcLength;
void const *iptc = image.get_blob(VIPS_META_IPCT_NAME, &iptcLength);
void const *iptc = image.get_blob(VIPS_META_IPTC_NAME, &iptcLength);
baton->iptc = static_cast<char *>(g_malloc(iptcLength));
memcpy(baton->iptc, iptc, iptcLength);
baton->iptcLength = iptcLength;
@@ -97,6 +110,14 @@ class MetadataWorker : public Nan::AsyncWorker {
memcpy(baton->xmp, xmp, xmpLength);
baton->xmpLength = xmpLength;
}
// TIFFTAG_PHOTOSHOP
if (image.get_typeof(VIPS_META_PHOTOSHOP_NAME) == VIPS_TYPE_BLOB) {
size_t tifftagPhotoshopLength;
void const *tifftagPhotoshop = image.get_blob(VIPS_META_PHOTOSHOP_NAME, &tifftagPhotoshopLength);
baton->tifftagPhotoshop = static_cast<char *>(g_malloc(tifftagPhotoshopLength));
memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
}
}
// Clean up
@@ -104,102 +125,115 @@ class MetadataWorker : public Nan::AsyncWorker {
vips_thread_shutdown();
}
void HandleOKCallback() {
using Nan::New;
using Nan::Set;
Nan::HandleScope();
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
argv[0] = Nan::Error(baton->err.data());
} else {
// Metadata Object
v8::Local<v8::Object> info = New<v8::Object>();
Set(info, New("format").ToLocalChecked(), New<v8::String>(baton->format).ToLocalChecked());
Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(baton->width));
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(baton->height));
Set(info, New("space").ToLocalChecked(), New<v8::String>(baton->space).ToLocalChecked());
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(baton->channels));
Set(info, New("depth").ToLocalChecked(), New<v8::String>(baton->depth).ToLocalChecked());
if (baton->density > 0) {
Set(info, New("density").ToLocalChecked(), New<v8::Uint32>(baton->density));
}
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
if (baton->orientation > 0) {
Set(info, New("orientation").ToLocalChecked(), New<v8::Uint32>(baton->orientation));
}
if (baton->exifLength > 0) {
Set(info,
New("exif").ToLocalChecked(),
Nan::NewBuffer(baton->exif, baton->exifLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->iccLength > 0) {
Set(info,
New("icc").ToLocalChecked(),
Nan::NewBuffer(baton->icc, baton->iccLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->iptcLength > 0) {
Set(info,
New("iptc").ToLocalChecked(),
Nan::NewBuffer(baton->iptc, baton->iptcLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->xmpLength > 0) {
Set(info,
New("xmp").ToLocalChecked(),
Nan::NewBuffer(baton->xmp, baton->xmpLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
argv[1] = info;
}
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index);
return index + 1;
});
delete baton->input;
delete baton;
void OnOK() {
Napi::Env env = Env();
Napi::HandleScope scope(env);
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message);
debuglog.Call({ Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop();
}
// Return to JavaScript
callback->Call(2, argv);
if (baton->err.empty()) {
Napi::Object info = Napi::Object::New(env);
info.Set("format", baton->format);
if (baton->input->bufferLength > 0) {
info.Set("size", baton->input->bufferLength);
}
info.Set("width", baton->width);
info.Set("height", baton->height);
info.Set("space", baton->space);
info.Set("channels", baton->channels);
info.Set("depth", baton->depth);
if (baton->density > 0) {
info.Set("density", baton->density);
}
if (!baton->chromaSubsampling.empty()) {
info.Set("chromaSubsampling", baton->chromaSubsampling);
}
info.Set("isProgressive", baton->isProgressive);
if (baton->paletteBitDepth > 0) {
info.Set("paletteBitDepth", baton->paletteBitDepth);
}
if (baton->pages > 0) {
info.Set("pages", baton->pages);
}
if (baton->pageHeight > 0) {
info.Set("pageHeight", baton->pageHeight);
}
if (baton->loop >= 0) {
info.Set("loop", baton->loop);
}
if (!baton->delay.empty()) {
int i = 0;
Napi::Array delay = Napi::Array::New(env, static_cast<size_t>(baton->delay.size()));
for (int const d : baton->delay) {
delay.Set(i++, d);
}
info.Set("delay", delay);
}
if (baton->pagePrimary > -1) {
info.Set("pagePrimary", baton->pagePrimary);
}
info.Set("hasProfile", baton->hasProfile);
info.Set("hasAlpha", baton->hasAlpha);
if (baton->orientation > 0) {
info.Set("orientation", baton->orientation);
}
if (baton->exifLength > 0) {
info.Set("exif", Napi::Buffer<char>::New(env, baton->exif, baton->exifLength, sharp::FreeCallback));
}
if (baton->iccLength > 0) {
info.Set("icc", Napi::Buffer<char>::New(env, baton->icc, baton->iccLength, sharp::FreeCallback));
}
if (baton->iptcLength > 0) {
info.Set("iptc", Napi::Buffer<char>::New(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
}
if (baton->xmpLength > 0) {
info.Set("xmp", Napi::Buffer<char>::New(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
}
if (baton->tifftagPhotoshopLength > 0) {
info.Set("tifftagPhotoshop",
Napi::Buffer<char>::New(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback));
}
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
} else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
}
delete baton->input;
delete baton;
}
private:
MetadataBaton* baton;
Nan::Callback *debuglog;
std::vector<v8::Local<v8::Object>> buffersToPersist;
Napi::FunctionReference debuglog;
};
/*
metadata(options, callback)
*/
NAN_METHOD(metadata) {
// Input Buffers must not undergo GC compaction during processing
std::vector<v8::Local<v8::Object>> buffersToPersist;
Napi::Value metadata(const Napi::CallbackInfo& info) {
// V8 objects are converted to non-V8 types held in the baton struct
MetadataBaton *baton = new MetadataBaton;
v8::Local<v8::Object> options = info[0].As<v8::Object>();
Napi::Object options = info[0].As<Napi::Object>();
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
// Join queue for worker thread
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, debuglog, buffersToPersist));
Napi::Function callback = info[1].As<Napi::Function>();
MetadataWorker *worker = new MetadataWorker(callback, baton, debuglog);
worker->Receiver().Set("options", options);
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
return info.Env().Undefined();
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -16,7 +16,7 @@
#define SRC_METADATA_H_
#include <string>
#include <nan.h>
#include <napi.h>
#include "./common.h"
@@ -31,6 +31,14 @@ struct MetadataBaton {
int channels;
std::string depth;
int density;
std::string chromaSubsampling;
bool isProgressive;
int paletteBitDepth;
int pages;
int pageHeight;
int loop;
std::vector<int> delay;
int pagePrimary;
bool hasProfile;
bool hasAlpha;
int orientation;
@@ -42,6 +50,8 @@ struct MetadataBaton {
size_t iptcLength;
char *xmp;
size_t xmpLength;
char *tifftagPhotoshop;
size_t tifftagPhotoshopLength;
std::string err;
MetadataBaton():
@@ -50,6 +60,12 @@ struct MetadataBaton {
height(0),
channels(0),
density(0),
isProgressive(false),
paletteBitDepth(0),
pages(0),
pageHeight(0),
loop(-1),
pagePrimary(-1),
hasProfile(false),
hasAlpha(false),
orientation(0),
@@ -60,9 +76,11 @@ struct MetadataBaton {
iptc(nullptr),
iptcLength(0),
xmp(nullptr),
xmpLength(0) {}
xmpLength(0),
tifftagPhotoshop(nullptr),
tifftagPhotoshopLength(0) {}
};
NAN_METHOD(metadata);
Napi::Value metadata(const Napi::CallbackInfo& info);
#endif // SRC_METADATA_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -29,127 +29,52 @@ using vips::VError;
namespace sharp {
/*
Composite overlayImage over image at given position
Assumes alpha channels are already premultiplied and will be unpremultiplied after
*/
VImage Composite(VImage image, VImage overlayImage, int const left, int const top) {
if (HasAlpha(overlayImage)) {
// Alpha composite
if (overlayImage.width() < image.width() || overlayImage.height() < image.height()) {
// Enlarge overlay
std::vector<double> const background { 0.0, 0.0, 0.0, 0.0 };
overlayImage = overlayImage.embed(left, top, image.width(), image.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background));
}
return AlphaComposite(image, overlayImage);
} else {
if (HasAlpha(image)) {
// Add alpha channel to overlayImage so channels match
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
overlayImage = overlayImage.bandjoin(
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
}
return image.insert(overlayImage, left, top);
Removes alpha channel, if any.
*/
VImage RemoveAlpha(VImage image) {
if (HasAlpha(image)) {
image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
}
}
VImage AlphaComposite(VImage dst, VImage src) {
// Split src into non-alpha and alpha channels
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
// Split dst into non-alpha and alpha channels
VImage dstWithoutAlpha = dst.extract_band(0, VImage::option()->set("n", dst.bands() - 1));
VImage dstAlpha = dst[dst.bands() - 1] * (1.0 / 255.0);
//
// Compute normalized output alpha channel:
//
// References:
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
// - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826
//
// out_a = src_a + dst_a * (1 - src_a)
// ^^^^^^^^^^^
// t0
VImage t0 = srcAlpha.linear(-1.0, 1.0);
VImage outAlphaNormalized = srcAlpha + dstAlpha * t0;
//
// Compute output RGB channels:
//
// Wikipedia:
// out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a
// ^^^^^^^^^^^
// t0
//
// Omit division by `out_a` since `Compose` is supposed to output a
// premultiplied RGBA image as reversal of premultiplication is handled
// externally.
//
VImage outRGBPremultiplied = srcWithoutAlpha + dstWithoutAlpha * t0;
// Combine RGB and alpha channel into output image:
return outRGBPremultiplied.bandjoin(outAlphaNormalized * 255.0);
return image;
}
/*
Cutout src over dst with given gravity.
Ensures alpha channel, if missing.
*/
VImage EnsureAlpha(VImage image) {
if (!HasAlpha(image)) {
std::vector<double> alpha;
alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
image = image.bandjoin_const(alpha);
}
return image;
}
/*
* Tint an image using the specified chroma, preserving the original image luminance
*/
VImage Cutout(VImage mask, VImage dst, const int gravity) {
using sharp::CalculateCrop;
using sharp::HasAlpha;
using sharp::MaximumImageAlpha;
bool maskHasAlpha = HasAlpha(mask);
if (!maskHasAlpha && mask.bands() > 1) {
throw VError("Overlay image must have an alpha channel or one band");
VImage Tint(VImage image, double const a, double const b) {
// Get original colourspace
VipsInterpretation typeBeforeTint = image.interpretation();
if (typeBeforeTint == VIPS_INTERPRETATION_RGB) {
typeBeforeTint = VIPS_INTERPRETATION_sRGB;
}
if (!HasAlpha(dst)) {
throw VError("Image to be overlaid must have an alpha channel");
// Extract luminance
VImage luminance = image.colourspace(VIPS_INTERPRETATION_LAB)[0];
// Create the tinted version by combining the L from the original and the chroma from the tint
std::vector<double> chroma {a, b};
VImage tinted = luminance
.bandjoin(chroma)
.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_LAB))
.colourspace(typeBeforeTint);
// Attach original alpha channel, if any
if (HasAlpha(image)) {
// Extract original alpha channel
VImage alpha = image[image.bands() - 1];
// Join alpha channel to normalised image
tinted = tinted.bandjoin(alpha);
}
if (mask.width() > dst.width() || mask.height() > dst.height()) {
throw VError("Overlay image must have same dimensions or smaller");
}
// Enlarge overlay mask, if required
if (mask.width() < dst.width() || mask.height() < dst.height()) {
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
int left;
int top;
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), mask.width(), mask.height(), gravity);
// Embed onto transparent background
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
mask = mask.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background));
}
// we use the mask alpha if it has alpha
if (maskHasAlpha) {
mask = mask.extract_band(mask.bands() - 1, VImage::option()->set("n", 1));;
}
// Split dst into an optional alpha
VImage dstAlpha = dst.extract_band(dst.bands() - 1, VImage::option()->set("n", 1));
// we use the dst non-alpha
dst = dst.extract_band(0, VImage::option()->set("n", dst.bands() - 1));
// the range of the mask and the image need to match .. one could be
// 16-bit, one 8-bit
double const dstMax = MaximumImageAlpha(dst.interpretation());
double const maskMax = MaximumImageAlpha(mask.interpretation());
// combine the new mask and the existing alpha ... there are
// many ways of doing this, mult is the simplest
mask = dstMax * ((mask / maskMax) * (dstAlpha / dstMax));
// append the mask to the image data ... the mask might be float now,
// we must cast the format down to match the image data
return dst.bandjoin(mask.cast(dst.format()));
return tinted;
}
/*
@@ -196,10 +121,8 @@ namespace sharp {
VImage Gamma(VImage image, double const exponent) {
if (HasAlpha(image)) {
// Separate alpha channel
VImage imageWithoutAlpha = image.extract_band(0,
VImage::option()->set("n", image.bands() - 1));
VImage alpha = image[image.bands() - 1];
return imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
} else {
return image.gamma(VImage::option()->set("exponent", exponent));
}
@@ -243,6 +166,48 @@ namespace sharp {
return image.conv(kernel);
}
/*
* Recomb with a Matrix of the given bands/channel size.
* Eg. RGB will be a 3x3 matrix.
*/
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
double *m = matrix.get();
return image
.colourspace(VIPS_INTERPRETATION_sRGB)
.recomb(image.bands() == 3
? VImage::new_from_memory(
m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE
)
: VImage::new_matrixv(4, 4,
m[0], m[1], m[2], 0.0,
m[3], m[4], m[5], 0.0,
m[6], m[7], m[8], 0.0,
0.0, 0.0, 0.0, 1.0));
}
VImage Modulate(VImage image, double const brightness, double const saturation, int const hue) {
if (HasAlpha(image)) {
// Separate alpha channel
VImage alpha = image[image.bands() - 1];
return RemoveAlpha(image)
.colourspace(VIPS_INTERPRETATION_LCH)
.linear(
{ brightness, saturation, 1},
{ 0.0, 0.0, static_cast<double>(hue) }
)
.colourspace(VIPS_INTERPRETATION_sRGB)
.bandjoin(alpha);
} else {
return image
.colourspace(VIPS_INTERPRETATION_LCH)
.linear(
{ brightness, saturation, 1 },
{ 0.0, 0.0, static_cast<double>(hue) }
)
.colourspace(VIPS_INTERPRETATION_sRGB);
}
}
/*
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
@@ -289,56 +254,48 @@ namespace sharp {
return image.boolean(imageR, boolean);
}
VImage Trim(VImage image, int const tolerance) {
using sharp::MaximumImageAlpha;
// An equivalent of ImageMagick's -trim in C++ ... automatically remove
// "boring" image edges.
// We use .project to sum the rows and columns of a 0/255 mask image, the first
// non-zero row or column is the object edge. We make the mask image with an
// amount-different-from-background image plus a threshold.
// find the value of the pixel at (0, 0) ... we will search for all pixels
// significantly different from this
std::vector<double> background = image(0, 0);
double const max = MaximumImageAlpha(image.interpretation());
// we need to smooth the image, subtract the background from every pixel, take
// the absolute value of the difference, then threshold
VImage mask = (image.median(3) - background).abs() > (max * tolerance / 100);
// sum mask rows and columns, then search for the first non-zero sum in each
// direction
VImage rows;
VImage columns = mask.project(&rows);
VImage profileLeftV;
VImage profileLeftH = columns.profile(&profileLeftV);
VImage profileRightV;
VImage profileRightH = columns.fliphor().profile(&profileRightV);
VImage profileTopV;
VImage profileTopH = rows.profile(&profileTopV);
VImage profileBottomV;
VImage profileBottomH = rows.flipver().profile(&profileBottomV);
int left = static_cast<int>(floor(profileLeftV.min()));
int right = columns.width() - static_cast<int>(floor(profileRightV.min()));
int top = static_cast<int>(floor(profileTopH.min()));
int bottom = rows.height() - static_cast<int>(floor(profileBottomH.min()));
int width = right - left;
int height = bottom - top;
if (width <= 0 || height <= 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance");
/*
Trim an image
*/
VImage Trim(VImage image, double const 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();
}
int left, top, width, height;
left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background(0, 0))
->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");
}
}
// and now crop the original image
return image.extract_area(left, top, width, height);
}
/*
* Calculate (a * in + b)
*/
VImage Linear(VImage image, double const a, double const b) {
if (HasAlpha(image)) {
// Separate alpha channel
VImage alpha = image[image.bands() - 1];
return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
} else {
return image.linear(a, b);
}
}
} // namespace sharp

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -26,25 +26,19 @@ using vips::VImage;
namespace sharp {
/*
Alpha composite src over dst with given gravity.
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
*/
VImage Composite(VImage src, VImage dst, const int gravity);
/*
Composite overlayImage over image at given position
*/
VImage Composite(VImage image, VImage overlayImage, int const x, int const y);
/*
Alpha composite overlayImage over image, assumes matching dimensions
Removes alpha channel, if any.
*/
VImage AlphaComposite(VImage image, VImage overlayImage);
VImage RemoveAlpha(VImage image);
/*
Cutout src over dst with given gravity.
Ensures alpha channel, if missing.
*/
VImage Cutout(VImage src, VImage dst, const int gravity);
VImage EnsureAlpha(VImage image);
/*
* Tint an image using the specified chroma, preserving the original image luminance
*/
VImage Tint(VImage image, double const a, double const b);
/*
* Stretch luminance to cover full dynamic range.
@@ -90,7 +84,23 @@ namespace sharp {
/*
Trim an image
*/
VImage Trim(VImage image, int const tolerance);
VImage Trim(VImage image, double const threshold);
/*
* Linear adjustment (a * in + b)
*/
VImage Linear(VImage image, double const a, double const b);
/*
* Recomb with a Matrix of the given bands/channel size.
* Eg. RGB will be a 3x3 matrix.
*/
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
/*
* Modulate brightness, saturation and hue
*/
VImage Modulate(VImage image, double const brightness, double const saturation, int const hue);
} // namespace sharp

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -19,12 +19,12 @@
#include <string>
#include <vector>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
#include "./common.h"
NAN_METHOD(pipeline);
Napi::Value pipeline(const Napi::CallbackInfo& info);
enum class Canvas {
CROP,
@@ -34,20 +34,32 @@ enum class Canvas {
IGNORE_ASPECT
};
struct Composite {
sharp::InputDescriptor *input;
VipsBlendMode mode;
int gravity;
int left;
int top;
bool tile;
bool premultiplied;
Composite():
input(nullptr),
mode(VIPS_BLEND_MODE_OVER),
gravity(0),
left(-1),
top(-1),
tile(false),
premultiplied(false) {}
};
struct PipelineBaton {
sharp::InputDescriptor *input;
std::string iccProfilePath;
int limitInputPixels;
std::string formatOut;
std::string fileOut;
void *bufferOut;
size_t bufferOutLength;
sharp::InputDescriptor *overlay;
int overlayGravity;
int overlayXOffset;
int overlayYOffset;
bool overlayTile;
bool overlayCutout;
std::vector<Composite *> composite;
std::vector<sharp::InputDescriptor *> joinChannelIn;
int topOffsetPre;
int leftOffsetPre;
@@ -61,29 +73,42 @@ struct PipelineBaton {
int height;
int channels;
Canvas canvas;
int crop;
int embed;
int position;
std::vector<double> resizeBackground;
bool hasCropOffset;
int cropOffsetLeft;
int cropOffsetTop;
bool premultiplied;
std::string kernel;
bool fastShrinkOnLoad;
double background[4];
double tintA;
double tintB;
bool flatten;
std::vector<double> flattenBackground;
bool negate;
double blurSigma;
double brightness;
double saturation;
int hue;
int medianSize;
double sharpenSigma;
double sharpenFlat;
double sharpenJagged;
int threshold;
bool thresholdGrayscale;
int trimTolerance;
double trimThreshold;
int trimOffsetLeft;
int trimOffsetTop;
double linearA;
double linearB;
double gamma;
double gammaOut;
bool greyscale;
bool normalise;
bool useExifOrientation;
int angle;
double rotationAngle;
std::vector<double> rotationBackground;
bool rotateBeforePreExtract;
bool flip;
bool flop;
@@ -91,27 +116,42 @@ struct PipelineBaton {
int extendBottom;
int extendLeft;
int extendRight;
std::vector<double> extendBackground;
bool withoutEnlargement;
VipsAccess accessMethod;
int jpegQuality;
bool jpegProgressive;
std::string jpegChromaSubsampling;
bool jpegTrellisQuantisation;
int jpegQuantisationTable;
bool jpegOvershootDeringing;
bool jpegOptimiseScans;
bool jpegOptimiseCoding;
bool pngProgressive;
int pngCompressionLevel;
bool pngAdaptiveFiltering;
bool pngPalette;
int pngQuality;
int pngColours;
double pngDither;
int webpQuality;
int webpAlphaQuality;
bool webpNearLossless;
bool webpLossless;
bool webpSmartSubsample;
int webpReductionEffort;
int tiffQuality;
VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor;
bool tiffPyramid;
bool tiffSquash;
bool tiffTile;
int tiffTileHeight;
int tiffTileWidth;
double tiffXres;
double tiffYres;
int heifQuality;
VipsForeignHeifCompression heifCompression;
bool heifLossless;
std::string err;
bool withMetadata;
int withMetadataOrientation;
@@ -124,70 +164,102 @@ struct PipelineBaton {
VipsOperationBoolean booleanOp;
VipsOperationBoolean bandBoolOp;
int extractChannel;
bool removeAlpha;
bool ensureAlpha;
VipsInterpretation colourspace;
int tileSize;
int tileOverlap;
VipsForeignDzContainer tileContainer;
VipsForeignDzLayout tileLayout;
std::string tileFormat;
int tileAngle;
std::vector<double> tileBackground;
int tileSkipBlanks;
VipsForeignDzDepth tileDepth;
std::unique_ptr<double[]> recombMatrix;
PipelineBaton():
input(nullptr),
limitInputPixels(0),
bufferOutLength(0),
overlay(nullptr),
overlayGravity(0),
overlayXOffset(-1),
overlayYOffset(-1),
overlayTile(false),
overlayCutout(false),
topOffsetPre(-1),
topOffsetPost(-1),
channels(0),
canvas(Canvas::CROP),
crop(0),
embed(0),
position(0),
resizeBackground{ 0.0, 0.0, 0.0, 255.0 },
hasCropOffset(false),
cropOffsetLeft(0),
cropOffsetTop(0),
premultiplied(false),
tintA(128.0),
tintB(128.0),
flatten(false),
flattenBackground{ 0.0, 0.0, 0.0 },
negate(false),
blurSigma(0.0),
brightness(1.0),
saturation(1.0),
hue(0),
medianSize(0),
sharpenSigma(0.0),
sharpenFlat(1.0),
sharpenJagged(2.0),
threshold(0),
thresholdGrayscale(true),
trimTolerance(0),
trimThreshold(0.0),
trimOffsetLeft(0),
trimOffsetTop(0),
linearA(1.0),
linearB(0.0),
gamma(0.0),
greyscale(false),
normalise(false),
useExifOrientation(false),
angle(0),
rotationAngle(0.0),
rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
flip(false),
flop(false),
extendTop(0),
extendBottom(0),
extendLeft(0),
extendRight(0),
extendBackground{ 0.0, 0.0, 0.0, 255.0 },
withoutEnlargement(false),
jpegQuality(80),
jpegProgressive(false),
jpegChromaSubsampling("4:2:0"),
jpegTrellisQuantisation(false),
jpegQuantisationTable(0),
jpegOvershootDeringing(false),
jpegOptimiseScans(false),
jpegOptimiseCoding(true),
pngProgressive(false),
pngCompressionLevel(9),
pngAdaptiveFiltering(false),
pngPalette(false),
pngQuality(100),
pngColours(256),
pngDither(1.0),
webpQuality(80),
webpAlphaQuality(100),
webpNearLossless(false),
webpLossless(false),
webpSmartSubsample(false),
webpReductionEffort(4),
tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
tiffPyramid(false),
tiffSquash(false),
tiffTile(false),
tiffTileHeight(256),
tiffTileWidth(256),
tiffXres(1.0),
tiffYres(1.0),
heifQuality(80),
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_HEVC),
heifLossless(false),
withMetadata(false),
withMetadataOrientation(-1),
convKernelWidth(0),
@@ -198,16 +270,17 @@ struct PipelineBaton {
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
extractChannel(-1),
removeAlpha(false),
ensureAlpha(false),
colourspace(VIPS_INTERPRETATION_LAST),
tileSize(256),
tileOverlap(0),
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ) {
background[0] = 0.0;
background[1] = 0.0;
background[2] = 0.0;
background[3] = 255.0;
}
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
tileAngle(0),
tileBackground{ 255.0, 255.0, 255.0, 255.0 },
tileSkipBlanks(-1),
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST) {}
};
#endif // SRC_PIPELINE_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <node.h>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
#include "common.h"
@@ -22,33 +21,30 @@
#include "utilities.h"
#include "stats.h"
NAN_MODULE_INIT(init) {
static void* sharp_vips_init(void*) {
vips_init("sharp");
return nullptr;
}
Napi::Object init(Napi::Env env, Napi::Object exports) {
static GOnce sharp_vips_init_once = G_ONCE_INIT;
g_once(&sharp_vips_init_once, static_cast<GThreadFunc>(sharp_vips_init), nullptr);
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
// Methods available to JavaScript
Nan::Set(target, Nan::New("metadata").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked());
Nan::Set(target, Nan::New("pipeline").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(pipeline)).ToLocalChecked());
Nan::Set(target, Nan::New("cache").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(cache)).ToLocalChecked());
Nan::Set(target, Nan::New("concurrency").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(concurrency)).ToLocalChecked());
Nan::Set(target, Nan::New("counters").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(counters)).ToLocalChecked());
Nan::Set(target, Nan::New("simd").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(simd)).ToLocalChecked());
Nan::Set(target, Nan::New("libvipsVersion").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(libvipsVersion)).ToLocalChecked());
Nan::Set(target, Nan::New("format").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(format)).ToLocalChecked());
Nan::Set(target, Nan::New("_maxColourDistance").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(_maxColourDistance)).ToLocalChecked());
Nan::Set(target, Nan::New("stats").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(stats)).ToLocalChecked());
exports.Set("metadata", Napi::Function::New(env, metadata));
exports.Set("pipeline", Napi::Function::New(env, pipeline));
exports.Set("cache", Napi::Function::New(env, cache));
exports.Set("concurrency", Napi::Function::New(env, concurrency));
exports.Set("counters", Napi::Function::New(env, counters));
exports.Set("simd", Napi::Function::New(env, simd));
exports.Set("libvipsVersion", Napi::Function::New(env, libvipsVersion));
exports.Set("format", Napi::Function::New(env, format));
exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
exports.Set("stats", Napi::Function::New(env, stats));
return exports;
}
NODE_MODULE(sharp, init)
NODE_API_MODULE(sharp, init)

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -16,27 +16,16 @@
#include <vector>
#include <iostream>
#include <node.h>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
#include "common.h"
#include "stats.h"
class StatsWorker : public Nan::AsyncWorker {
class StatsWorker : public Napi::AsyncWorker {
public:
StatsWorker(
Nan::Callback *callback, StatsBaton *baton, Nan::Callback *debuglog,
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog),
buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer);
return index + 1;
});
}
StatsWorker(Napi::Function callback, StatsBaton *baton, Napi::Function debuglog) :
Napi::AsyncWorker(callback), baton(baton), debuglog(Napi::Persistent(debuglog)) {}
~StatsWorker() {}
const int STAT_MIN_INDEX = 0;
@@ -53,40 +42,41 @@ class StatsWorker : public Nan::AsyncWorker {
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
using Nan::New;
using Nan::Set;
using sharp::MaximumImageAlpha;
vips::VImage image;
vips::VImage stats;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, baton->accessMethod);
std::tie(image, imageType) = OpenInput(baton->input);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
if (imageType != sharp::ImageType::UNKNOWN) {
try {
stats = image.stats();
int bands = image.bands();
double const max = MaximumImageAlpha(image.interpretation());
vips::VImage stats = image.stats();
int const bands = image.bands();
for (int b = 1; b <= bands; b++) {
ChannelStats cStats(static_cast<int>(stats.getpoint(STAT_MIN_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAX_INDEX, b).front()),
stats.getpoint(STAT_SUM_INDEX, b).front(), stats.getpoint(STAT_SQ_SUM_INDEX, b).front(),
stats.getpoint(STAT_MEAN_INDEX, b).front(), stats.getpoint(STAT_STDEV_INDEX, b).front(),
static_cast<int>(stats.getpoint(STAT_MINX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MINY_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXY_INDEX, b).front()));
ChannelStats cStats(
static_cast<int>(stats.getpoint(STAT_MIN_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAX_INDEX, b).front()),
stats.getpoint(STAT_SUM_INDEX, b).front(),
stats.getpoint(STAT_SQ_SUM_INDEX, b).front(),
stats.getpoint(STAT_MEAN_INDEX, b).front(),
stats.getpoint(STAT_STDEV_INDEX, b).front(),
static_cast<int>(stats.getpoint(STAT_MINX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MINY_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXY_INDEX, b).front()));
baton->channelStats.push_back(cStats);
}
// alpha layer is there and the last band i.e. alpha has its max value greater than 0)
if (sharp::HasAlpha(image) && stats.getpoint(STAT_MIN_INDEX, bands).front() != max) {
baton->isOpaque = false;
// Image is not opaque when alpha layer is present and contains a non-mamixa value
if (sharp::HasAlpha(image)) {
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
if (minAlpha != sharp::MaximumImageAlpha(image.interpretation())) {
baton->isOpaque = false;
}
}
// Estimate entropy via histogram of greyscale value frequency
baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
@@ -97,92 +87,78 @@ class StatsWorker : public Nan::AsyncWorker {
vips_thread_shutdown();
}
void HandleOKCallback() {
using Nan::New;
using Nan::Set;
Nan::HandleScope();
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
argv[0] = Nan::Error(baton->err.data());
} else {
// Stats Object
v8::Local<v8::Object> info = New<v8::Object>();
v8::Local<v8::Array> channels = New<v8::Array>();
std::vector<ChannelStats>::iterator it;
int i = 0;
for (it=baton->channelStats.begin() ; it < baton->channelStats.end(); it++, i++) {
v8::Local<v8::Object> channelStat = New<v8::Object>();
Set(channelStat, New("min").ToLocalChecked(), New<v8::Number>(it->min));
Set(channelStat, New("max").ToLocalChecked(), New<v8::Number>(it->max));
Set(channelStat, New("sum").ToLocalChecked(), New<v8::Number>(it->sum));
Set(channelStat, New("squaresSum").ToLocalChecked(), New<v8::Number>(it->squaresSum));
Set(channelStat, New("mean").ToLocalChecked(), New<v8::Number>(it->mean));
Set(channelStat, New("stdev").ToLocalChecked(), New<v8::Number>(it->stdev));
Set(channelStat, New("minX").ToLocalChecked(), New<v8::Number>(it->minX));
Set(channelStat, New("minY").ToLocalChecked(), New<v8::Number>(it->minY));
Set(channelStat, New("maxX").ToLocalChecked(), New<v8::Number>(it->maxX));
Set(channelStat, New("maxY").ToLocalChecked(), New<v8::Number>(it->maxY));
channels->Set(i, channelStat);
}
Set(info, New("channels").ToLocalChecked(), channels);
Set(info, New("isOpaque").ToLocalChecked(), New<v8::Boolean>(baton->isOpaque));
argv[1] = info;
}
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index);
return index + 1;
});
delete baton->input;
delete baton;
void OnOK() {
Napi::Env env = Env();
Napi::HandleScope scope(env);
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message);
debuglog.Call({ Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop();
}
// Return to JavaScript
callback->Call(2, argv);
if (baton->err.empty()) {
// Stats Object
Napi::Object info = Napi::Object::New(env);
Napi::Array channels = Napi::Array::New(env);
std::vector<ChannelStats>::iterator it;
int i = 0;
for (it = baton->channelStats.begin(); it < baton->channelStats.end(); it++, i++) {
Napi::Object channelStat = Napi::Object::New(env);
channelStat.Set("min", it->min);
channelStat.Set("max", it->max);
channelStat.Set("sum", it->sum);
channelStat.Set("squaresSum", it->squaresSum);
channelStat.Set("mean", it->mean);
channelStat.Set("stdev", it->stdev);
channelStat.Set("minX", it->minX);
channelStat.Set("minY", it->minY);
channelStat.Set("maxX", it->maxX);
channelStat.Set("maxY", it->maxY);
channels.Set(i, channelStat);
}
info.Set("channels", channels);
info.Set("isOpaque", baton->isOpaque);
info.Set("entropy", baton->entropy);
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
} else {
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
}
delete baton->input;
delete baton;
}
private:
StatsBaton* baton;
Nan::Callback *debuglog;
std::vector<v8::Local<v8::Object>> buffersToPersist;
Napi::FunctionReference debuglog;
};
/*
stats(options, callback)
*/
NAN_METHOD(stats) {
using sharp::AttrTo;
// Input Buffers must not undergo GC compaction during processing
std::vector<v8::Local<v8::Object>> buffersToPersist;
Napi::Value stats(const Napi::CallbackInfo& info) {
// V8 objects are converted to non-V8 types held in the baton struct
StatsBaton *baton = new StatsBaton;
v8::Local<v8::Object> options = info[0].As<v8::Object>();
Napi::Object options = info[0].As<Napi::Object>();
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
// Join queue for worker thread
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new StatsWorker(callback, baton, debuglog, buffersToPersist));
Napi::Function callback = info[1].As<Napi::Function>();
StatsWorker *worker = new StatsWorker(callback, baton, debuglog);
worker->Receiver().Set("options", options);
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
return info.Env().Undefined();
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -16,7 +16,7 @@
#define SRC_STATS_H_
#include <string>
#include <nan.h>
#include <napi.h>
#include "./common.h"
@@ -33,12 +33,8 @@ struct ChannelStats {
int maxX;
int maxY;
ChannelStats():
min(0), max(0), sum(0), squaresSum(0), mean(0), stdev(0)
, minX(0), minY(0), maxX(0), maxY(0) {}
ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal,
double meanVal, double stdevVal, int minXVal, int minYVal, int maxXVal, int maxYVal):
ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal,
double meanVal, double stdevVal, int minXVal, int minYVal, int maxXVal, int maxYVal):
min(minVal), max(maxVal), sum(sumVal), squaresSum(squaresSumVal),
mean(meanVal), stdev(stdevVal), minX(minXVal), minY(minYVal), maxX(maxXVal), maxY(maxYVal) {}
};
@@ -46,20 +42,21 @@ struct ChannelStats {
struct StatsBaton {
// Input
sharp::InputDescriptor *input;
VipsAccess accessMethod;
// Output
std::vector<ChannelStats> channelStats;
bool isOpaque;
double entropy;
std::string err;
StatsBaton():
input(nullptr),
isOpaque(true)
isOpaque(true),
entropy(0.0)
{}
};
NAN_METHOD(stats);
Napi::Value stats(const Napi::CallbackInfo& info);
#endif // SRC_STATS_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -15,8 +15,7 @@
#include <cmath>
#include <string>
#include <node.h>
#include <nan.h>
#include <napi.h>
#include <vips/vips8>
#include <vips/vector.h>
@@ -24,182 +23,145 @@
#include "operations.h"
#include "utilities.h"
using v8::Boolean;
using v8::Integer;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using Nan::HandleScope;
using Nan::New;
using Nan::Set;
using Nan::ThrowError;
using Nan::To;
using Nan::Utf8String;
/*
Get and set cache limits
*/
NAN_METHOD(cache) {
HandleScope();
Napi::Value cache(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Set memory limit
if (info[0]->IsInt32()) {
vips_cache_set_max_mem(To<int32_t>(info[0]).FromJust() * 1048576);
if (info[0].IsNumber()) {
vips_cache_set_max_mem(info[0].As<Napi::Number>().Int32Value() * 1048576);
}
// Set file limit
if (info[1]->IsInt32()) {
vips_cache_set_max_files(To<int32_t>(info[1]).FromJust());
if (info[1].IsNumber()) {
vips_cache_set_max_files(info[1].As<Napi::Number>().Int32Value());
}
// Set items limit
if (info[2]->IsInt32()) {
vips_cache_set_max(To<int32_t>(info[2]).FromJust());
if (info[2].IsNumber()) {
vips_cache_set_max(info[2].As<Napi::Number>().Int32Value());
}
// Get memory stats
Local<Object> memory = New<Object>();
Set(memory, New("current").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576))));
Set(memory, New("high").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576))));
Set(memory, New("max").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576))));
Napi::Object memory = Napi::Object::New(env);
memory.Set("current", round(vips_tracked_get_mem() / 1048576));
memory.Set("high", round(vips_tracked_get_mem_highwater() / 1048576));
memory.Set("max", round(vips_cache_get_max_mem() / 1048576));
// Get file stats
Local<Object> files = New<Object>();
Set(files, New("current").ToLocalChecked(), New<Integer>(vips_tracked_get_files()));
Set(files, New("max").ToLocalChecked(), New<Integer>(vips_cache_get_max_files()));
Napi::Object files = Napi::Object::New(env);
files.Set("current", vips_tracked_get_files());
files.Set("max", vips_cache_get_max_files());
// Get item stats
Local<Object> items = New<Object>();
Set(items, New("current").ToLocalChecked(), New<Integer>(vips_cache_get_size()));
Set(items, New("max").ToLocalChecked(), New<Integer>(vips_cache_get_max()));
Napi::Object items = Napi::Object::New(env);
items.Set("current", vips_cache_get_size());
items.Set("max", vips_cache_get_max());
Local<Object> cache = New<Object>();
Set(cache, New("memory").ToLocalChecked(), memory);
Set(cache, New("files").ToLocalChecked(), files);
Set(cache, New("items").ToLocalChecked(), items);
info.GetReturnValue().Set(cache);
Napi::Object cache = Napi::Object::New(env);
cache.Set("memory", memory);
cache.Set("files", files);
cache.Set("items", items);
return cache;
}
/*
Get and set size of thread pool
*/
NAN_METHOD(concurrency) {
HandleScope();
Napi::Value concurrency(const Napi::CallbackInfo& info) {
// Set concurrency
if (info[0]->IsInt32()) {
vips_concurrency_set(To<int32_t>(info[0]).FromJust());
if (info[0].IsNumber()) {
vips_concurrency_set(info[0].As<Napi::Number>().Int32Value());
}
// Get concurrency
info.GetReturnValue().Set(New<Integer>(vips_concurrency_get()));
return Napi::Number::New(info.Env(), vips_concurrency_get());
}
/*
Get internal counters (queued tasks, processing tasks)
*/
NAN_METHOD(counters) {
using sharp::counterProcess;
using sharp::counterQueue;
HandleScope();
Local<Object> counters = New<Object>();
Set(counters, New("queue").ToLocalChecked(), New<Integer>(counterQueue));
Set(counters, New("process").ToLocalChecked(), New<Integer>(counterProcess));
info.GetReturnValue().Set(counters);
Napi::Value counters(const Napi::CallbackInfo& info) {
Napi::Object counters = Napi::Object::New(info.Env());
counters.Set("queue", sharp::counterQueue);
counters.Set("process", sharp::counterProcess);
return counters;
}
/*
Get and set use of SIMD vector unit instructions
*/
NAN_METHOD(simd) {
HandleScope();
Napi::Value simd(const Napi::CallbackInfo& info) {
// Set state
if (info[0]->IsBoolean()) {
vips_vector_set_enabled(To<bool>(info[0]).FromJust());
if (info[0].IsBoolean()) {
vips_vector_set_enabled(info[0].As<Napi::Boolean>().Value());
}
// Get state
info.GetReturnValue().Set(New<Boolean>(vips_vector_isenabled()));
return Napi::Boolean::New(info.Env(), vips_vector_isenabled());
}
/*
Get libvips version
*/
NAN_METHOD(libvipsVersion) {
HandleScope();
Napi::Value libvipsVersion(const Napi::CallbackInfo& info) {
char version[9];
g_snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
info.GetReturnValue().Set(New(version).ToLocalChecked());
return Napi::String::New(info.Env(), version);
}
/*
Get available input/output file/buffer/stream formats
*/
NAN_METHOD(format) {
HandleScope();
// Attribute names
Local<String> attrId = New("id").ToLocalChecked();
Local<String> attrInput = New("input").ToLocalChecked();
Local<String> attrOutput = New("output").ToLocalChecked();
Local<String> attrFile = New("file").ToLocalChecked();
Local<String> attrBuffer = New("buffer").ToLocalChecked();
Local<String> attrStream = New("stream").ToLocalChecked();
// Which load/save operations are available for each compressed format?
Local<Object> format = New<Object>();
for (std::string f : {
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "pdf", "v"
Napi::Value format(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
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"
}) {
// Input
Local<Boolean> hasInputFile =
New<Boolean>(vips_type_find("VipsOperation", (f + "load").c_str()));
Local<Boolean> hasInputBuffer =
New<Boolean>(vips_type_find("VipsOperation", (f + "load_buffer").c_str()));
Local<Object> input = New<Object>();
Set(input, attrFile, hasInputFile);
Set(input, attrBuffer, hasInputBuffer);
Set(input, attrStream, hasInputBuffer);
Napi::Boolean hasInputFile =
Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "load").c_str()));
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);
// Output
Local<Boolean> hasOutputFile =
New<Boolean>(vips_type_find("VipsOperation", (f + "save").c_str()));
Local<Boolean> hasOutputBuffer =
New<Boolean>(vips_type_find("VipsOperation", (f + "save_buffer").c_str()));
Local<Object> output = New<Object>();
Set(output, attrFile, hasOutputFile);
Set(output, attrBuffer, hasOutputBuffer);
Set(output, attrStream, hasOutputBuffer);
Napi::Boolean hasOutputFile =
Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "save").c_str()));
Napi::Boolean hasOutputBuffer =
Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "save_buffer").c_str()));
Napi::Object output = Napi::Object::New(env);
output.Set("file", hasOutputFile);
output.Set("buffer", hasOutputBuffer);
output.Set("stream", hasOutputBuffer);
// Other attributes
Local<Object> container = New<Object>();
Local<String> formatId = New(f).ToLocalChecked();
Set(container, attrId, formatId);
Set(container, attrInput, input);
Set(container, attrOutput, output);
Napi::Object container = Napi::Object::New(env);
container.Set("id", f);
container.Set("input", input);
container.Set("output", output);
// Add to set of formats
Set(format, formatId, container);
format.Set(f, container);
}
// Raw, uncompressed data
Local<Object> raw = New<Object>();
Local<String> rawId = New("raw").ToLocalChecked();
Set(raw, attrId, rawId);
Set(format, rawId, raw);
Local<Boolean> supported = New<Boolean>(true);
Local<Boolean> unsupported = New<Boolean>(false);
Local<Object> rawInput = New<Object>();
Set(rawInput, attrFile, unsupported);
Set(rawInput, attrBuffer, supported);
Set(rawInput, attrStream, supported);
Set(raw, attrInput, rawInput);
Local<Object> rawOutput = New<Object>();
Set(rawOutput, attrFile, unsupported);
Set(rawOutput, attrBuffer, supported);
Set(rawOutput, attrStream, supported);
Set(raw, attrOutput, rawOutput);
Napi::Boolean supported = Napi::Boolean::New(env, true);
Napi::Boolean unsupported = Napi::Boolean::New(env, false);
Napi::Object rawInput = Napi::Object::New(env);
rawInput.Set("file", unsupported);
rawInput.Set("buffer", supported);
rawInput.Set("stream", supported);
Napi::Object rawOutput = Napi::Object::New(env);
rawOutput.Set("file", unsupported);
rawOutput.Set("buffer", supported);
rawOutput.Set("stream", supported);
Napi::Object raw = Napi::Object::New(env);
raw.Set("id", "raw");
raw.Set("input", rawInput);
raw.Set("output", rawOutput);
format.Set("raw", raw);
info.GetReturnValue().Set(format);
return format;
}
/*
@@ -207,65 +169,59 @@ NAN_METHOD(format) {
Calculates the maximum colour distance using the DE2000 algorithm
between two images of the same dimensions and number of channels.
*/
NAN_METHOD(_maxColourDistance) {
using vips::VImage;
using vips::VError;
using sharp::DetermineImageType;
using sharp::ImageType;
using sharp::HasAlpha;
HandleScope();
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Open input files
VImage image1;
ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
if (imageType1 != ImageType::UNKNOWN) {
sharp::ImageType imageType1 = sharp::DetermineImageType(info[0].As<Napi::String>().Utf8Value().data());
if (imageType1 != sharp::ImageType::UNKNOWN) {
try {
image1 = VImage::new_from_file(*Utf8String(info[0]));
image1 = VImage::new_from_file(info[0].As<Napi::String>().Utf8Value().c_str());
} catch (...) {
return ThrowError("Input file 1 has corrupt header");
throw Napi::Error::New(env, "Input file 1 has corrupt header");
}
} else {
return ThrowError("Input file 1 is of an unsupported image format");
throw Napi::Error::New(env, "Input file 1 is of an unsupported image format");
}
VImage image2;
ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
if (imageType2 != ImageType::UNKNOWN) {
sharp::ImageType imageType2 = sharp::DetermineImageType(info[1].As<Napi::String>().Utf8Value().data());
if (imageType2 != sharp::ImageType::UNKNOWN) {
try {
image2 = VImage::new_from_file(*Utf8String(info[1]));
image2 = VImage::new_from_file(info[1].As<Napi::String>().Utf8Value().c_str());
} catch (...) {
return ThrowError("Input file 2 has corrupt header");
throw Napi::Error::New(env, "Input file 2 has corrupt header");
}
} else {
return ThrowError("Input file 2 is of an unsupported image format");
throw Napi::Error::New(env, "Input file 2 is of an unsupported image format");
}
// Ensure same number of channels
if (image1.bands() != image2.bands()) {
return ThrowError("mismatchedBands");
throw Napi::Error::New(env, "mismatchedBands");
}
// Ensure same dimensions
if (image1.width() != image2.width() || image1.height() != image2.height()) {
return ThrowError("mismatchedDimensions");
throw Napi::Error::New(env, "mismatchedDimensions");
}
double maxColourDistance;
try {
// Premultiply and remove alpha
if (HasAlpha(image1)) {
if (sharp::HasAlpha(image1)) {
image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
}
if (HasAlpha(image2)) {
if (sharp::HasAlpha(image2)) {
image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
}
// Calculate colour distance
maxColourDistance = image1.dE00(image2).max();
} catch (VError err) {
return ThrowError(err.what());
} catch (vips::VError const &err) {
throw Napi::Error::New(env, err.what());
}
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown();
info.GetReturnValue().Set(New<Number>(maxColourDistance));
return Napi::Number::New(env, maxColourDistance);
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
// 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.
@@ -15,14 +15,14 @@
#ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_
#include <nan.h>
#include <napi.h>
NAN_METHOD(cache);
NAN_METHOD(concurrency);
NAN_METHOD(counters);
NAN_METHOD(simd);
NAN_METHOD(libvipsVersion);
NAN_METHOD(format);
NAN_METHOD(_maxColourDistance);
Napi::Value cache(const Napi::CallbackInfo& info);
Napi::Value concurrency(const Napi::CallbackInfo& info);
Napi::Value counters(const Napi::CallbackInfo& info);
Napi::Value simd(const Napi::CallbackInfo& info);
Napi::Value libvipsVersion(const Napi::CallbackInfo& info);
Napi::Value format(const Napi::CallbackInfo& info);
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
#endif // SRC_UTILITIES_H_

View File

@@ -8,19 +8,16 @@
"test": "node perf && node random && node parallel"
},
"devDependencies": {
"async": "^2.6.0",
"async": "^3.1.0",
"benchmark": "^2.1.4",
"gm": "^1.23.1",
"imagemagick": "^0.1.3",
"imagemagick-native": "^1.9.3",
"images": "^3.0.1",
"jimp": "^0.2.28",
"mapnik": "^3.6.2",
"pajk-lwip": "^0.2.0",
"semver": "^5.4.1"
"jimp": "^0.9.3",
"mapnik": "^4.3.1",
"semver": "^7.1.1"
},
"license": "Apache-2.0",
"engines": {
"node": ">=4"
"node": ">=8.5.0"
}
}

View File

@@ -12,24 +12,6 @@ const gm = require('gm');
const imagemagick = require('imagemagick');
const mapnik = require('mapnik');
const jimp = require('jimp');
let images;
try {
images = require('images');
} catch (err) {
console.log('Excluding node-images');
}
let imagemagickNative;
try {
imagemagickNative = require('imagemagick-native');
} catch (err) {
console.log('Excluding imagemagick-native');
}
let lwip;
try {
lwip = require('pajk-lwip');
} catch (err) {
console.log('Excluding lwip');
}
const fixtures = require('../fixtures');
@@ -38,11 +20,9 @@ const height = 588;
// Disable libvips cache to ensure tests are as fair as they can be
sharp.cache(false);
// Enable use of SIMD
sharp.simd(true);
async.series({
'jpeg': function (callback) {
jpeg: function (callback) {
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
const jpegSuite = new Benchmark.Suite('jpeg');
// jimp
@@ -54,7 +34,7 @@ async.series({
throw err;
} else {
image
.resize(width, height)
.resize(width, height, jimp.RESIZE_BICUBIC)
.quality(80)
.getBuffer(jimp.MIME_JPEG, function (err) {
if (err) {
@@ -74,7 +54,7 @@ async.series({
throw err;
} else {
image
.resize(width, height)
.resize(width, height, jimp.RESIZE_BICUBIC)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
@@ -87,51 +67,6 @@ async.series({
});
}
});
// lwip
if (typeof lwip !== 'undefined') {
jpegSuite.add('lwip-file-file', {
defer: true,
fn: function (deferred) {
lwip.open(fixtures.inputJpg, function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.writeFile(fixtures.outputJpg, {quality: 80}, function (err) {
if (err) {
throw err;
}
deferred.resolve();
});
});
});
}
}).add('lwip-buffer-buffer', {
defer: true,
fn: function (deferred) {
lwip.open(inputJpgBuffer, 'jpg', function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.toBuffer('jpg', {quality: 80}, function (err, buffer) {
if (err) {
throw err;
}
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
});
});
}
});
}
// mapnik
jpegSuite.add('mapnik-file-file', {
defer: true,
@@ -185,29 +120,6 @@ async.series({
});
}
});
// imagemagick-native
if (typeof imagemagickNative !== 'undefined') {
jpegSuite.add('imagemagick-native-buffer-buffer', {
defer: true,
fn: function (deferred) {
imagemagickNative.convert({
srcData: inputJpgBuffer,
quality: 80,
width: width,
height: height,
format: 'JPEG',
filter: 'Lanczos'
}, function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
}
// gm
jpegSuite.add('gm-buffer-file', {
defer: true,
@@ -272,14 +184,6 @@ async.series({
});
}
});
// images
if (typeof images !== 'undefined') {
jpegSuite.add('images-file-file', function () {
images(fixtures.inputJpg)
.resize(width, height)
.save(fixtures.outputJpg, { quality: 80 });
});
}
// sharp
jpegSuite.add('sharp-buffer-file', {
defer: true,
@@ -356,6 +260,9 @@ async.series({
.then(function (buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
})
.catch(function (err) {
throw err;
});
}
}).on('cycle', function (event) {
@@ -553,8 +460,7 @@ async.series({
}).add('sharp-sequentialRead', {
defer: true,
fn: function (deferred) {
sharp(inputJpgBuffer)
.sequentialRead()
sharp(inputJpgBuffer, { sequentialRead: true })
.resize(width, height)
.toBuffer(function (err, buffer) {
if (err) {
@@ -569,8 +475,10 @@ async.series({
defer: true,
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.crop(sharp.strategy.entropy)
.resize(width, height, {
fit: 'cover',
position: sharp.strategy.entropy
})
.toBuffer(function (err, buffer) {
if (err) {
throw err;
@@ -584,8 +492,10 @@ async.series({
defer: true,
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.crop(sharp.strategy.attention)
.resize(width, height, {
fit: 'cover',
position: sharp.strategy.attention
})
.toBuffer(function (err, buffer) {
if (err) {
throw err;
@@ -696,31 +606,6 @@ async.series({
});
}
});
// lwip
if (typeof lwip !== 'undefined') {
pngSuite.add('lwip-buffer-buffer', {
defer: true,
fn: function (deferred) {
lwip.open(inputPngBuffer, 'png', function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.toBuffer('png', function (err, buffer) {
if (err) {
throw err;
}
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
});
});
}
});
}
// mapnik
pngSuite.add('mapnik-file-file', {
defer: true,
@@ -786,22 +671,6 @@ async.series({
});
}
});
// imagemagick-native
if (typeof imagemagickNative !== 'undefined') {
pngSuite.add('imagemagick-native-buffer-buffer', {
defer: true,
fn: function (deferred) {
imagemagickNative.convert({
srcData: inputPngBuffer,
width: width,
height: height,
format: 'PNG',
filter: 'Lanczos'
});
deferred.resolve();
}
});
}
// gm
pngSuite.add('gm-file-file', {
defer: true,
@@ -833,14 +702,6 @@ async.series({
});
}
});
// images
if (typeof images !== 'undefined') {
pngSuite.add('images-file-file', function () {
images(fixtures.inputPng)
.resize(width, height)
.save(fixtures.outputPng);
});
}
// sharp
pngSuite.add('sharp-buffer-file', {
defer: true,

BIN
test/fixtures/16-bit-grey-alpha.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

BIN
test/fixtures/G31D_MULTI.TIF vendored Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

BIN
test/fixtures/animated-loop-3.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

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