Compare commits

...

468 Commits

Author SHA1 Message Date
Lovell Fuller
fcf853712c Release v0.17.3 2017-04-01 10:20:44 +01:00
Lovell Fuller
088d36b47b Add support for TIFF float predictor 2017-04-01 10:08:47 +01:00
Lovell Fuller
27fb864ac4 Update dev deps, deconstify all the functions, API doc refresh 2017-03-31 21:42:23 +01:00
Lovell Fuller
4001c4a48a Add changelog and credit for #738 2017-03-31 21:17:19 +01:00
Sagiv Frankel
f64c18ef15 Docs: Add download info to Heroku section (#748) 2017-03-30 12:55:33 +01:00
Kristo Jorgenson
f8e72f443d Expose TIFF compression and predictor options (#738) 2017-03-29 12:12:04 +01:00
Lovell Fuller
5e015cc3ca Docs: use NODE_MODULES_CACHE=false for Heroku+yarn #722 2017-03-27 20:31:50 +01:00
Andreas Lind
9707f8c5d2 Add support for passing the crop strategy as a string (#735) 2017-03-16 14:27:09 +00:00
Lovell Fuller
6b1d698448 Add credit and changelog for #732 2017-03-16 07:37:05 +00:00
Alice Monday
72f69dda30 Add support for the "nearest" kernel for image reductions (#732) 2017-03-14 10:29:23 +00:00
Lovell Fuller
8b5d8a0577 Switch from seq to random access for normalise and 'smart' crop 2017-03-11 19:56:55 +00:00
Lovell Fuller
1aa053ce6f Create blank image (width, height, channels, background) #470 2017-03-11 11:46:01 +00:00
Lovell Fuller
701b1c4216 Document overlayWith image density parameter #729 2017-03-10 22:59:46 +00:00
Lovell Fuller
f1c4cef781 Version bump of dev dependencies 2017-03-04 22:28:01 +00:00
Lovell Fuller
6fe5b307b1 Allow toBuffer to resolve Promise with info+data #143 2017-03-04 22:15:31 +00:00
Lovell Fuller
679ce08998 Small doc updates 2017-03-04 18:37:23 +00:00
Lovell Fuller
eeb923eb5b Update docs domain name 2017-03-04 18:36:18 +00:00
Lovell Fuller
142c431745 Release v0.17.2 2017-02-11 10:35:34 +00:00
Lovell Fuller
81f5589411 Add use of 'cc' to improve C++ code style linting 2017-02-11 09:59:23 +00:00
Mark van Seventer
04f5c884a4 Add CLI tools section to installation guide (#691) 2017-01-25 21:34:23 +00:00
Lovell Fuller
d8df503404 Ensure Readable can start flowing after Writable finish #671 2017-01-22 14:03:06 +00:00
Lovell Fuller
d241efcdbe Add changelog entry and credit for #685 2017-01-22 13:58:11 +00:00
Rahul Nanwani
a1b8efe721 Expose WebP alpha quality, lossless and near-lossless output options (#685) 2017-01-19 13:45:32 +00:00
Lovell Fuller
815d076b35 Release v0.17.1 2017-01-15 15:23:49 +00:00
Lovell Fuller
86b4816b3f Allow HTTP-over-HTTPS proxy when d/l pre-compiled deps #679 2017-01-14 11:23:13 +00:00
Lovell Fuller
473055468a Docs: ensure alpha attribute is used for transparency 2017-01-13 21:24:57 +00:00
Brandon Aaron
c6a28db8b1 Packaging: bump zlib to 1.2.10 and png16 to 1.6.28 (#676) 2017-01-10 18:19:23 +00:00
子龙山人
971f567571 Docs: correct alpha attribute in extend background example (#675) 2017-01-09 10:47:02 +00:00
Lovell Fuller
7e2eca3d1e Credit recent new contributors, thank you! 2017-01-05 22:27:30 +00:00
Lovell Fuller
86b0053bf0 Removed preinstall.sh script 2017-01-05 22:21:41 +00:00
Lovell Fuller
cfc4b282f0 Doc refresh for d9b667e 2017-01-05 22:20:33 +00:00
Lovell Fuller
b85d2aa565 Merge branch 'master' of https://github.com/lovell/sharp 2017-01-05 22:17:16 +00:00
Lovell Fuller
70a3d4fb5e Improve error messages for invalid resize parameters
Dependency version bumps and doc refresh
2017-01-05 22:17:04 +00:00
Adam Coyne
d9b667e346 Docs: remove parentheses from sharp.format (#663) 2016-12-31 19:32:18 +00:00
Jérémy Lal
3a1db53d5a Simpler expression for finding vips-cpp libdir (#656)
Fixes: #655
2016-12-18 08:57:50 +00:00
Lovell Fuller
4858ebe051 Release v0.17.0 2016-12-11 19:22:14 +00:00
Lovell Fuller
d2455267a8 Allow non-RGB input to embed/extend onto bg with alpha #646 2016-12-11 16:01:21 +00:00
Lovell Fuller
61721bb086 Remove preinstall ref from docs ahead of script removal 2016-12-11 15:21:30 +00:00
Lovell Fuller
3e76ee25e3 Update semistandard dependency plus linting improvements 2016-12-05 21:22:40 +00:00
Lovell Fuller
a71e562ff7 Upgrade to latest v1.x.x major version of the color dependency.
Uses 'alpha' instead of 'a' to represent transparency values.
2016-12-04 21:04:18 +00:00
Lovell Fuller
850fc9adf9 Increase threshold for OS X gamma test 2016-12-04 19:38:05 +00:00
Lovell Fuller
d3c78f825c Ensure premultiply op occurs before box shrink #605 2016-12-04 18:25:44 +00:00
Lovell Fuller
7231d92d1f Autoconvert GIF+SVG input to PNG output if no format specified 2016-11-30 22:46:04 +00:00
Lovell Fuller
93e14484da Update dev dependencies, regenerate API docs 2016-11-27 22:29:57 +00:00
Lovell Fuller
d7d03b1ca2 Update benchmark target image height for fairer tests #624 2016-11-27 22:17:09 +00:00
Lovell Fuller
dfd6d95209 Remove slightly-too-experimental TypeScript definitions.
To be revisited, perhaps during 2017, when tooling has improved.
2016-11-21 22:29:30 +00:00
Lovell Fuller
e4e7384f99 Move lib/types.d.ts generation to separate script.
Fixes Promise<T>, ensures module prefix is declared.
2016-11-20 11:24:12 +00:00
Lovell Fuller
effa77afee Docs/types: add Promise<T> and Array<T> #472
Changelog updates and version bump of devDeps
2016-11-17 21:29:41 +00:00
Patrick Paskaris
6ccccf8c39 Allow use of extend with greyscale input (#623) 2016-11-14 22:09:43 +00:00
Lovell Fuller
dd9d66ef20 Update output docs to include tile image format.
Correct some of the JPEG output option type defs.
2016-11-13 21:03:18 +00:00
Patrick Paskaris
bc84d1e47a Allow PNG and WebP tile-based output in addition to JPEG (#622) 2016-11-13 20:36:43 +00:00
Lovell Fuller
6b426014ad Provide experimental, automated TypeScript declaration #472 2016-11-08 14:21:38 +00:00
Lovell Fuller
c6f12fe033 Small doc update and dep bumps ahead of v0.17.0 2016-11-08 12:03:55 +00:00
Lovell Fuller
bb096ac617 Final update to preinstall.sh before it goes away 2016-11-08 11:55:46 +00:00
Lovell Fuller
734df539dd Patch libtiff v4.0.6 with latest security fixes 2016-11-08 11:54:41 +00:00
Lovell Fuller
27b9481452 Update benchmarks ahead of sharp v0.17.0 with libvips v8.4.2.
Ubuntu provides newer *magick than Amazon, so this is fairer on it.
Add note about the serious security vulnerabilities in lwip.
2016-11-08 11:21:32 +00:00
Lovell Fuller
945706c2a4 Remove use of deprecated API from benchmark
Add mapnik as possible contender
2016-11-07 18:53:54 +00:00
Lovell Fuller
a7b024d4fa Tile-based output filename may not exist, check g_stat return value 2016-11-06 14:31:32 +00:00
Lovell Fuller
9911863441 Expose libvips centre option, mimics *magick +0.5px convention 2016-11-04 18:17:33 +00:00
Lovell Fuller
deb978bf57 Remove use of deprecated functions from test code 2016-11-04 10:18:29 +00:00
Lovell Fuller
55998707a5 Regenerate docs 2016-11-02 18:04:00 +00:00
Lovell Fuller
4af702ee11 Docs: change Deep Zoom file extension for libvips v8.4.2+ 2016-11-02 17:25:18 +00:00
Lovell Fuller
8717ecc429 Modularise JS source in 'lib' subdirectory.
Generate public API documention via jsdoc comments.
2016-11-02 09:25:20 +00:00
Lovell Fuller
552cfd6ff1 Store pre-compiled binary dependencies in 'vendor' directory.
This frees up 'lib' directory to allow for more modular JavaScript.
2016-11-02 09:25:20 +00:00
Lovell Fuller
928edfd1dd Ensure jsdoc comments/types exist for all public methods.
This is a precursor to fully-automated docs and typings.
2016-11-02 09:25:20 +00:00
Lovell Fuller
98fb2e73f9 Remove trailing space from pkg-config libs output
Workaround for bug in pkg-config prior to v0.29
2016-11-02 09:25:20 +00:00
Lovell Fuller
de09577342 Put back overzealous code removal from 2470e5c 2016-11-02 09:25:20 +00:00
Lovell Fuller
cbdbbe535a Update tests to meet semistandard code standards
Switch to const/let instead of var
2016-11-02 09:25:20 +00:00
Lovell Fuller
36e636dca1 Drop support for versions of Node prior to v4.
Reduce production (sub)depedency count from 93 to 50.
Modernise dev tooling, e.g. use nyc, replace jshint with semistandard.
Make 'npm test' command consistent across platforms.
2016-11-02 09:25:20 +00:00
Lovell Fuller
3f5e38bb62 Deprecate output format option functions.
Access is now via options of existing output format functions.

e.g. use .jpeg({quality: n}) instead of .jpeg().quality(n)
2016-11-02 09:25:20 +00:00
Lovell Fuller
eb30f6ceff Upgrade libvips to v8.4.2
Improved EXIF orientation and GIF alpha channel support
2016-11-02 09:25:20 +00:00
Lovell Fuller
1051fcd278 Release v0.16.2 2016-10-22 18:39:33 +01:00
Lovell Fuller
1a0030e086 Restrict readelf usage to Linux only #602 2016-10-14 16:07:17 +01:00
Lovell Fuller
114ce370ed Provide a default lib location when detecting C++11 ABI
Useful for pkgconfig without -L, e.g. Alpine Linux
See also commit 07d66da
2016-10-13 12:38:53 +01:00
Lovell Fuller
207dcbeaa4 Release v0.16.1 2016-10-13 10:53:38 +01:00
Lovell Fuller
d4a1722863 The long-awaited return of code examples to README 2016-10-12 19:41:49 +01:00
Lovell Fuller
18b9991fe7 Add experimental 'attention' crop strategy 2016-10-12 11:18:58 +01:00
Lovell Fuller
739178dd74 Include '.node' ext for Meteor's require() implementation #537 2016-10-05 10:50:13 +01:00
Taka Kojima
dcd1392a85 Allow platform, arch and arm_version to be overridden (#581)
Aids cross-compilation
2016-10-01 12:54:14 +01:00
Lovell Fuller
07d66da57b Auto-detect C++11 ABI version, remove --sharp-cxx11 flag 2016-09-28 21:40:30 +01:00
Lovell Fuller
28ce33feb3 Fix y-axis calc when overlaying at fixed point #566 2016-09-16 11:20:08 +01:00
Brandon Aaron
86039a3f2b Bumping png16 to 1.6.25 (#570) 2016-09-12 19:43:16 +01:00
Lovell Fuller
af9d09f8ae Ensure conv kernel scale is clamped to min val of 1 #561 2016-09-03 20:06:49 +01:00
Lovell Fuller
7c06a48ec0 Release v0.16.0 2016-08-18 09:00:04 +01:00
Lovell Fuller
7ada9dbd0d Changelog update, fix for small leak introduced in 5c5d74a 2016-08-17 20:56:53 +01:00
Matt Hirsch
5c5d74a903 Add joinChannel and toColourspace/toColorspace operations (#513) 2016-08-17 15:42:05 +01:00
Lovell Fuller
72354d55a8 Doc and changelog updates #519 #540 2016-08-13 17:24:06 +01:00
cmtt
fc2002fbd0 Add alpha channels, if missing, to overlayWith images (#540) 2016-08-13 17:19:15 +01:00
Matt Hirsch
82ec2715f1 Prevent bandbool creating a single channel sRGB image (#519) 2016-08-13 14:55:15 +01:00
Lovell Fuller
ef6e90fb3c Correct dist name logging in packaging test script 2016-08-13 11:43:13 +01:00
Lovell Fuller
475f0bf120 Refactor packaging scripts, add ARMv7/v8 binaries 2016-08-12 13:40:44 +01:00
Lovell Fuller
e68a14c94c Dependency version bumps 2016-08-01 20:23:45 +01:00
Lovell Fuller
da0dc28bc4 Remove unescaped module_root_dir as it can contain spaces 2016-08-01 13:44:46 +01:00
Lovell Fuller
e6bfa52b0b Add raw pixel data support to boolean and withOverlay ops
The previously-scattered image opening logic has been refactored to a
single ImageDescriptor struct/Object available to both JS and C++ code

This removed about 150 LOC but more importantly reduces the complexity
of adding/exposing new operations that require an input image.
2016-07-26 23:07:25 +01:00
Lovell Fuller
36bfbdee0d Add support for using pre-compiled binaries with OSX 2016-07-25 16:32:42 +01:00
Lovell Fuller
7a9a4127a0 Remove deprecated interpolateWith method
Version bump dependencies
2016-07-25 16:11:53 +01:00
Lovell Fuller
4f1472d4ff Upgrade to libvips v8.3.2 2016-07-25 15:30:14 +01:00
Lovell Fuller
032bb7e96b Ensure ICC profiles are removed from PNG output #521 2016-07-21 16:49:27 +01:00
Lovell Fuller
9ddc817a09 Add WebP availability check to test added in a5bd68e 2016-07-21 15:55:34 +01:00
Lovell Fuller
a5bd68ef8c Recalc after WebP shrink-on-load to avoid rounding errors #508 2016-07-21 15:18:14 +01:00
Lovell Fuller
a2ec3642bf Alpine now provides vips in its testing repo
Resize+sharpen+alpha seems to stack-smash, ignore for now
2016-07-20 20:05:43 +01:00
Lovell Fuller
9647fe1b9f Reduce size of pre-built binaries by ~5% 2016-07-20 20:03:41 +01:00
Lovell Fuller
762cda75a9 Update libxml2 dependency CVE-2016-4448 #515
Also updates:
* libpng as previous version is now unavailable (?)
* libjpeg as previous version was pre-release
2016-07-18 12:07:28 +01:00
Matt Hirsch
c39a9b8de9 Prevent boolean errors during extract operation (#509) (#511) 2016-07-16 10:56:15 +01:00
Matt Hirsch
15a577863a Ensure boolean, bandbool, extractChannel ops occur before sRGB conversion (#504) 2016-07-13 19:20:50 +01:00
Lovell Fuller
2d500554c1 Release v0.15.1 2016-07-12 16:06:38 +01:00
Lovell Fuller
c42fb97419 Refactor pipeline to use common 16-bit detection methods 2016-07-11 23:03:45 +01:00
Lovell Fuller
d1d6155fd1 Increase unit test coverage for recently added operations
Switch param validation to use internal functions
2016-07-11 22:23:15 +01:00
Lovell Fuller
ff8c42e894 Changelog/doc/test updates for various new operations
Dependency version bumps
2016-07-11 15:52:30 +01:00
Lovell Fuller
e10aeb29eb Add missing include introduced by fee3d88 2016-07-11 14:05:26 +01:00
Lovell Fuller
fee3d882c7 Prevent GC of Buffer object vector via accumulate/lambda.
Removes need for naming Buffers and separate container struct.
2016-07-11 13:07:32 +01:00
Matt Hirsch
d17e8d3450 Add boolean feature for bitwise image operations (#501) 2016-07-11 09:51:43 +01:00
Lovell Fuller
99f960bf56 Docs: Clarify trimming of existing cache entries #487 2016-07-11 09:42:19 +01:00
Matt Hirsch
83d8847f57 Add extractChannel operation to extract a channel from an image (#497) 2016-07-09 16:48:30 +01:00
Matt Hirsch
f672f86b53 Add ability to read and write native vips .v files (#500) 2016-07-09 16:21:16 +01:00
Kleis Auke Wolthuizen
b69627891d Add trim operation to remove "boring" edges (#491 #492) 2016-07-08 22:19:10 +01:00
Matt Hirsch
673d8278b5 Add a unit test for extract area out of bounds (#498) 2016-07-08 20:52:33 +01:00
Lovell Fuller
8dd554b935 Use "previous" AppVeyor env as temp workaround for nodejs/node-gyp#952 2016-07-08 13:15:24 +01:00
Matt Hirsch
65b7f7d7d5 Add bandbool feature for channel-wise boolean operations (#496) 2016-07-07 21:03:49 +01:00
Matt Hirsch
a982cfdb20 Update docs to better reflect output of RAW data (#499) 2016-07-07 20:50:59 +01:00
Lovell Fuller
7689fbe54d Ensure --sharp-cxx11 flag is passed through #456 #494
node-gyp replaces dashes with underscores
2016-07-06 10:40:53 +01:00
Lovell Fuller
c9d32e22d3 Docs, changelog for top/left overlayWith offset #473 2016-07-05 11:17:41 +01:00
Rahul Nanwani
278273b5c3 Add top/left offset support to overlayWith operation (#473) 2016-07-05 10:12:02 +01:00
Lovell Fuller
a5d85b8a54 Changelog plus tidy of code/docs for convolve operation 2016-07-04 22:13:47 +01:00
Lovell Fuller
4c172d25f6 Allow images with alpha channel to use LAB sharpen #490 2016-07-04 21:33:44 +01:00
Matt Hirsch
b70a7d9a3b Add convolve operation for kernel-based convolution (#479) 2016-07-04 20:48:00 +01:00
Lovell Fuller
ba5a8b44ed Changelog, credit and doc tidy for improved threshold operation 2016-07-04 10:41:13 +01:00
Lovell Fuller
91e1ed1314 Ensure ICC profiles are licenced for distribution #486 2016-07-04 10:13:16 +01:00
Matt Hirsch
85f20c6e1b Add greyscale option to threshold operation (#480) 2016-07-03 19:32:07 +01:00
Lovell Fuller
4b98dbb454 Docs: reduce ambiguity of tile-based ZIP file output #474 2016-06-26 13:58:49 +01:00
Lovell Fuller
c3ad4fbdaa Document new cutout option of overlayWith feature #435 2016-06-26 13:53:20 +01:00
Kleis Auke Wolthuizen
2e9cd83ed2 Add support for clipping/cutting out (#435) (#448)
USAGE: overlayWith('overlayimage.png', { cutout: true } )
2016-06-25 16:48:01 +01:00
Lovell Fuller
f1ead06645 Update test expectations/thresholds previously failing on OS X.
Remove Node v5 from CI builds.
2016-06-14 22:25:27 +01:00
Teoh Han Hui
d486eaad03 Document breaking change in extract API in v0.14.0 (#465)
Deprecated style of calling extract was removed in 2034efc
2016-06-14 10:46:19 +01:00
Lovell Fuller
7d261a147d Ensure scaling factors are calculated independently #452
Fixes bug introduced in v0.15.0 where, if the shrink operation
rounded up along one dimension, it could then also round up the
reduce operation on the same axis, creating a small stretch effect.
2016-06-13 23:03:45 +01:00
Lovell Fuller
61038888c4 Document --sharp-cxx11 install flag #456 2016-06-11 08:28:27 +01:00
Jérémy Lal
39040fb9a0 Allow node-gyp configure --sharp-cxx11=1 (#456)
Can also be set in .npmrc.
Closes: #442
2016-06-08 17:42:46 +01:00
Lovell Fuller
4f3262c328 List licence details for all library dependencies 2016-05-27 23:17:06 +01:00
Lovell Fuller
69126a7c5f Add docs and credits for #439 and #443 2016-05-27 21:33:07 +01:00
lemnisk8
62554b766f Add support for repeated/tiled overlay image (#443)
USAGE: overlayWith('overlayimage.png', { tile: true, gravity: northwest} )
When using the tile option, the gravity option is applied to the extracted part of the tiled overlay image.
2016-05-26 16:42:17 +01:00
frulo
e699e36270 Add alpha channel, if required, before extend operation (#439) 2016-05-26 09:46:14 +01:00
Lovell Fuller
331926dc3c Concat Stream-based input in single operation for ~+3% perf and less GC #429 2016-05-24 21:04:18 +01:00
Lovell Fuller
8a3b660bbc Release v0.15.0 2016-05-21 15:33:56 +01:00
Lovell Fuller
933989c87d Update benchmark results ahead of v0.15.0, ~20% improvement 2016-05-21 10:21:42 +01:00
Lovell Fuller
e3cbcb98c0 Add hints about compiling with _GLIBCXX_USE_CXX11_ABI #432
Increase deprecatedness of preinstall script
Additional valgrind suppressions for libwebp
2016-05-21 09:03:09 +01:00
Lovell Fuller
32a2787254 Thank you to all the new contributors 2016-05-18 20:40:31 +01:00
Lovell Fuller
fccfc27de0 Update ARM packaging to libvips v8.3.1 2016-05-18 19:58:30 +01:00
Lovell Fuller
cdb2894bd9 Use libvips' new lanczos3 kernel as default for image reduce
Deprecate interpolateWith method, now provided as an option
2016-05-18 19:57:22 +01:00
Lovell Fuller
051d022fc2 Upgrade to libvips v8.3.1
Remove packaging tests and therefore support for Centos 6
2016-05-08 22:15:08 +01:00
Lovell Fuller
7388d97502 Allow keyword macros for glib support on MSVC 2016-05-08 13:30:42 +01:00
Lovell Fuller
1bece3a792 Add 2 channel (grey+alpha) GIF test case #375 2016-05-07 20:04:17 +01:00
Lovell Fuller
1de0038516 Upgrade to libvips 8.3.x
Add support for libvips' new native loaders, including GIF and SVG
Pre-built binaries now include giflib and librsvg, exclude *magick
2016-05-07 20:04:17 +01:00
Lovell Fuller
b7a098fb28 Break existing sharpen API to accept sigma and improve precision 2016-05-07 20:04:17 +01:00
Lovell Fuller
ee21d2991c Use shrink-on-load for WebP input 2016-05-07 20:04:17 +01:00
Lovell Fuller
f8eab49962 Add Node v6 to CI builds 2016-05-07 19:50:15 +01:00
Lovell Fuller
c9b3847a69 Docs: basic security considerations for installation #424 2016-05-07 19:48:06 +01:00
Felix Bünemann
dce3840537 Update Lambda instructions for Node.js 4.3 (#419)
Amazon introduced Node.js 4.3 support for Lambda, which is now the
recommended runtime instead of the old Node.js 0.10. This commit revises
the Lambda docs to build Node.js 4.3 compatible binaries using the
latest stable Node.js 4.x packages from Nodesource.
2016-04-27 19:39:30 +01:00
Lovell Fuller
b6030c161b Update expected test fixtures for libvips 8.3 2016-04-23 20:07:55 +01:00
Lovell Fuller
c920180cb3 Remove (un)premultiply ops when not resizing/compositing #413 2016-04-23 19:50:00 +01:00
Lovell Fuller
531a0402f7 Changelog updates ahead of v0.14.1
Note effect of C++11 ABI changes on upgrades
2016-04-16 21:16:37 +01:00
Lovell Fuller
cb10f9a9c8 Support gyp-based (re)build without npm env vars #412 2016-04-16 20:35:28 +01:00
Lovell Fuller
c808139b02 Changelog additions and version bumps ahead of v0.14.1 2016-04-14 21:57:44 +01:00
Lovell Fuller
e0d58266be Allow use of embed with 1 and 2 channel images #411 2016-04-14 21:39:17 +01:00
Felix Bünemann
1b7c5816fc Speed up slow pixel limit tests (#404)
This replaces the single color 20000x14000 PNG with a 20000x14000
Grayscale JPG. On a Broadwell Core i7 @ 3.1 GHz this speeds up the
"Disabling limit works" test from ~6.2s to ~0.15s because it can use
jpeg resize-on-load optimizations.

This should fix occasional timeouts on Travis CI.
2016-04-08 22:21:28 +01:00
Felix Bünemann
b224874332 Add support for writing dz format to zip container (#402)
To enable this you can either use the `.zip` or `.szi` file extensions
or use `.tile({container: 'zip'})` with the `.dzi` extension.
2016-04-08 19:58:13 +01:00
Samy Al Zahrani
ef61da3051 Ensure dimensions of final output image are provided (#399)
Add a failing test for vips::VError exception
* fix buffer size mismatch
* Loosen error message assertion
* Update image
2016-04-08 08:58:51 +01:00
Samy Al Zahrani
f214269aa1 Enable RTTI for clang-based builds
This allows for a more verbose error message on mac os x

```

libc++abi.dylib: terminating with uncaught exception of type vips::VError

```

becomes

```

libc++abi.dylib: terminating with uncaught exception of type vips::VError: VipsImage: memory area too small --- should be 1191960 bytes, you passed 1189440

```
2016-04-06 15:23:17 +01:00
Lovell Fuller
6bc2ea8dc7 No longer publish deprecated preinstall script to npm 2016-04-04 12:49:48 +01:00
Lovell Fuller
71fb839e2b Speed up limitInputPixels test case
Update changelog
2016-04-04 12:48:53 +01:00
kentongray
8c9c070caf Ability to disable limitInputPixels #250
Update docs
Added a giant image for testing
Adding myself to contributors
Added tests to verify giant image can be opened
Extend test-win time limit (because of large images)
2016-04-04 08:35:11 +01:00
Lovell Fuller
b2d7d4c4a9 Release v0.14.0 2016-04-02 13:21:44 +01:00
Lovell Fuller
0ac7fbfc07 Changelog entry for #382 #385 2016-04-02 12:19:58 +01:00
John Tobin
ebfc897bcf Fix for orientation values 1-8 2016-04-02 11:59:26 +01:00
Lovell Fuller
c66495b66c Tighten C++ linting rules
Bump benchmark dependencies
Update leak test suppressions
Update future branch details
2016-03-31 20:30:40 +01:00
Lovell Fuller
24fb0c33c2 Add further test case for #387, which builds on 25b63a2 2016-03-30 19:26:19 +01:00
Lovell Fuller
25b63a2fb4 Ensure ratios are not swapped when rotating 90/270 and ignoring aspect 2016-03-28 22:40:37 +01:00
Lovell Fuller
e576165cf1 Use Travis CI's multi-OS feature 2016-03-25 19:12:25 +00:00
Lovell Fuller
fe2eccef39 Merge branch 'needle' 2016-03-22 09:52:25 +00:00
Lovell Fuller
0e0e746a0d Update preinstall to libvips v8.2.3 for Centos 6 2016-03-22 09:44:08 +00:00
Lovell Fuller
5b4f4b0672 Upgrade to libvips v8.2.3 ahead of sharp v0.14.0 2016-03-22 09:37:52 +00:00
Lovell Fuller
185fcfe635 Improve entropy-based crop docs based on feedback.
Fix includes to keep MSVC compiler happy.
Additional memory leak suppressions for latest V8.
2016-03-05 18:30:38 +00:00
Lovell Fuller
2034efcf55 Add experimental, entropy-based auto-crop
Remove deprecated extract API
2016-03-05 12:29:16 +00:00
Lovell Fuller
38ddb3b866 Add support for Zoomify and Google tile layouts
Breaks existing tile API
2016-03-03 20:39:38 +00:00
Lovell Fuller
f950294f70 Add ability to extend (pad) the edges of an image 2016-03-03 09:18:11 +00:00
Lovell Fuller
86815bc9c4 Emit post-processing 'info' event for Stream-based output 2016-03-01 20:08:05 +00:00
Lovell Fuller
bb37dc1ea6 Expose density metadata; set density of images from vector input 2016-03-01 19:33:54 +00:00
Lovell Fuller
d92ea31858 overlayWith improvements: diff sizes/formats, gravity, buffer input 2016-02-29 15:15:27 +00:00
Lovell Fuller
55f204c6f9 Merge pull request #368 from felixbuenemann/improve-linux-lambda-docs
[ci skip] Improved Linux docs, Lambda instructions
2016-02-28 09:15:02 +00:00
Felix Bünemann
e97909f776 [ci skip] Improved Linux docs, Lambda instructions
* Add a more detailed explanation of how sharp discovers libvips
* Add a section on packaging sharp for running on AWS Lambda
2016-02-27 20:25:32 +01:00
Lovell Fuller
c210ac73cc Release v0.13.1 2016-02-27 16:17:58 +00:00
Lovell Fuller
962c91daf0 OpenSUSE package name for npm has changed 2016-02-25 22:19:08 +00:00
Lovell Fuller
df33c3024a Fix embedding onto transparent backgrounds #366
Fully automate embed tests to prevent regression
2016-02-25 18:36:00 +00:00
Lovell Fuller
62e04f7784 Merge pull request #361 from jardakotesovec/clone-clean-up
Remove left over, non-functional code from clone feature.
2016-02-17 11:38:34 +00:00
Jarda Kotesovec
32fcb771ca clone clean up 2016-02-17 10:25:00 +01:00
Lovell Fuller
a21760b374 Release v0.13.0 2016-02-15 19:12:23 +00:00
Lovell Fuller
cd05c7814a Merge pull request #359 from wjordan/alpine-packaging-test
Use libvips-dev apk for alpine-linux packaging test
2016-02-13 20:37:21 +00:00
Will Jordan
7b12f091e8 use alpine:edge image for packaging test
vips requires main/gettext-0.19.7r1 for aports commit 4ef70b432b05b720f7f144c2060550749d378205 to link correctly.
2016-02-13 13:48:46 -05:00
Will Jordan
e149e60c7a Use libvips-dev apk for alpine-linux packaging test 2016-02-13 16:07:56 +00:00
Lovell Fuller
bdac84059d Update perf results for forthcoming v0.13.0
Install runtime rather than dev pkgs on Alpine Linux
2016-02-12 19:33:21 +00:00
Lovell Fuller
2d05804fc3 Add cache recommendations for use with Alpine/musl #354
Prevent Windows EBUSY errors during tests
2016-02-11 20:33:33 +00:00
Lovell Fuller
2a56de69cc Add Alpine Linux packaging test #354
Requires libvips cache to be disabled for tests
Skip tiff/magick tests when format unavailable
2016-02-11 18:30:50 +00:00
Lovell Fuller
6ca2a4a9cd Ensure sharp.format lists support for raw input #220 2016-02-11 18:12:51 +00:00
Lovell Fuller
a9eb65c462 Most Linux systems no longer require the preinstall script 2016-02-09 20:18:00 +00:00
Lovell Fuller
afb30b3695 Ensure VipsArea is unreferenced after Buffer-based output
Prevents the leak of a ~1KB GMutex per output image
2016-02-09 19:28:17 +00:00
Lovell Fuller
09b019ed13 Expand glibc check to include 'gnu libc' identifier 2016-02-08 20:45:24 +00:00
Lovell Fuller
d46ac3a478 Check for glibc before downloading pre-compiled binaries #354 2016-02-08 20:06:01 +00:00
Lovell Fuller
677b2b9089 Selected output format > unknown file extension #344 2016-02-07 20:13:13 +00:00
Lovell Fuller
5c1067c63f Remove the no-longer-maintained gulp-sharp #353 2016-02-07 15:35:41 +00:00
Lovell Fuller
736c04a7a4 Only set density option when using magick loader
to reduce number of warnings from libvips #352
2016-02-04 19:16:54 +00:00
Lovell Fuller
0e29c55d13 Merge branch 'master' of https://github.com/lovell/sharp 2016-02-04 19:11:51 +00:00
Lovell Fuller
ca49e6079c Increase threshold for gamma=0 test, due to either
the version of libjpeg or a cumulative rounding error.
2016-02-04 19:10:58 +00:00
Lovell Fuller
320a7464c7 Merge pull request #351 from joelmukuthu/master
Fix: default crop gravity to sharp.gravity.center
2016-02-04 10:11:48 +00:00
Joel Mukuthu
da74cd078f Fix: default crop gravity to sharp.gravity.center
Closes https://github.com/lovell/sharp/issues/350
2016-02-04 10:38:58 +01:00
Lovell Fuller
322aa60891 Ensure 16-bit input images can be normalised 2016-02-03 19:33:34 +00:00
Lovell Fuller
e380576da2 Add support for raw, uncompressed pixel Buffer/Stream input 2016-02-03 19:21:37 +00:00
Lovell Fuller
cf7664a854 Improve SVG support by allowing control of density/DPI
Switch pre-built libs from Imagemagick to Graphicsmagick
2016-02-03 17:48:22 +00:00
Lovell Fuller
56508e8d79 Add support for libvips' PPM and FITS loaders #347 2016-02-03 17:48:22 +00:00
Lovell Fuller
2656c69d99 Upgrade to libvips v8.2.2 2016-02-03 17:48:22 +00:00
Lovell Fuller
57c1e3ae26 Slightly simplify marshalling of data from V8 Objects 2016-02-03 17:48:22 +00:00
Lovell Fuller
2675b2265b Ensure 16-bit input images embed onto alpha background
Support gamma correction of images with alpha channel
Favour shrink over affine when reducing by integral factor
2016-02-03 17:48:22 +00:00
Lovell Fuller
41e50770d1 Update benchmark test dependencies 2016-02-03 17:48:22 +00:00
Lovell Fuller
b3d6e94984 Optimisation for integral factors: favour shrink over affine 2016-02-03 17:48:22 +00:00
Lovell Fuller
5c9c17f1f6 Switch from libvips' C to C++ binding
Requires upgrade to libvips 8.2.1
2016-02-03 17:48:22 +00:00
Lovell Fuller
11329d5e09 Expose control of the number of open files in libvips' cache.
Breaks API of existing cache method.
Disable libvips cache for I/O tests.
2016-02-03 17:48:22 +00:00
Lovell Fuller
8843211e12 Upgrade libvips to v8.2.2 2016-01-31 11:54:19 +00:00
Lovell Fuller
20e75dc50b v0.12.2 2016-01-16 11:29:23 +00:00
Lovell Fuller
d2e5441d6e Dependency version bumps 2016-01-14 18:39:29 +00:00
Lovell Fuller
0ffa1e72d0 Attempt to remove temp file after install #331 2016-01-06 16:31:01 +00:00
Lovell Fuller
a0e034a9e9 Add support for pre-compiled libvips on ARM CPUs
Uses a HypriotOS-managed docker container for this
2016-01-06 15:50:36 +00:00
Lovell Fuller
3c7cbf8685 Update libvips to v8.2.0
Plus version bumps of other dependencies
2016-01-02 14:19:46 +00:00
Lovell Fuller
7541dfcab2 Document support for FreeBSD/gmake #326 2016-01-01 16:34:18 +00:00
Lovell Fuller
dc2b79ac9a Use array context for include/library paths in gyp config
Remove -I prefix from include paths
Should allow for compilation on FreeBSD #326
2016-01-01 14:04:52 +00:00
Lovell Fuller
6d62051877 Bump minimum version of libvips to 8.1.1 #324
Debian-based systems must compile from source
2015-12-23 20:54:37 +00:00
Lovell Fuller
61b86744d7 Ensure 16-bit input images work with embed option #325 2015-12-23 20:46:49 +00:00
Lovell Fuller
fd5b4a131f v0.12.1 2015-12-12 10:21:36 +00:00
Lovell Fuller
32c4b9eff1 Allow SIMD vector unit to be toggled on/off #172
Currently defaults to off but future versions may default to on
2015-12-12 09:11:50 +00:00
Lovell Fuller
95cf35efc5 Add changelog for v0.12.1 2015-12-06 20:40:05 +00:00
Lovell Fuller
58e6368525 Ensure embedded ICC profiles output with perceptual intent #321 2015-12-06 20:24:17 +00:00
Lovell Fuller
16e0d54b15 Use the NPM-configured HTTPS proxy, if present 2015-12-03 21:30:47 +00:00
Lovell Fuller
be381e4440 Add release date for v0.12.0 2015-11-23 14:06:49 +00:00
Lovell Fuller
9982182926 Update perf test results ahead of v0.12.0 2015-11-23 13:09:06 +00:00
Lovell Fuller
607d157b76 Benchmark test updates ahead of v0.12.0 2015-11-23 10:53:52 +00:00
Lovell Fuller
e21277ceba Version bump of libpng Windows dependency 2015-11-22 21:11:29 +00:00
Lovell Fuller
8012733a52 Expose libvips+deps versions attribute
Add versions.json for Linux packaging

Bump vips-dev Windows version for latest libpng
2015-11-22 20:58:38 +00:00
Lovell Fuller
01a1377972 Include cmath header for clang #301 2015-11-22 09:39:42 +00:00
Lovell Fuller
37e4b9b5ba Update changelog ahead of v0.12.0
Highlight features in readme+docs

Add link to Docker-based Linux CI build status
2015-11-22 09:17:51 +00:00
Lovell Fuller
8a3098604c Remove experimental code that could prevent anti-alias filter 2015-11-21 23:37:44 +00:00
Lovell Fuller
5febce7a59 Remove executable bit from test/* file permissions 2015-11-21 23:05:48 +00:00
Lovell Fuller
3dbedf1fb6 Match g_malloc/g_free for make benefit glorious nation of Windows
Remove C++ standard lib from Windows packaging
2015-11-21 22:51:32 +00:00
Lovell Fuller
0ae619dfc5 Remove stray win32 library that was causing segfaults
Explicit int cast to prevent 'loss of precision' warnings

Remove unnecessary semver checking from I/O tests
2015-11-21 22:18:39 +00:00
Lovell Fuller
05dd191e17 Ensure 16-bit+alpha input images work with vips_premultiply #301
Improves SVG support as *magick serves these as 16-bit

Add automated tests for SVG and 16-bit+alpha PNG inputs
2015-11-21 20:21:34 +00:00
Lovell Fuller
c9ecc7a517 Document Debian 7 support
Add liborc to fix openSUSE support

Switch recommended *magick to ImageMagick for OS X

Increase test timeouts (for Circle CI)
2015-11-19 22:47:34 +00:00
Lovell Fuller
434a433a09 Make output of packaging tests easier to understand
Slight simplification of Linux packaging script
2015-11-19 21:36:16 +00:00
Lovell Fuller
3de54d897c Merge pull request #309 from papandreou/feature/extractWithOptionsObject
Add an options object to the extract operation, deprecate existing behaviour.
2015-11-18 11:48:11 +00:00
Andreas Lind
530c2a9fcf Stop using the legacy .extract(top,left,width,height) in the documentation. 2015-11-18 12:06:10 +01:00
Andreas Lind
60b8b92630 Add support for .extract({left:...,top:...,width:...,height:...}). 2015-11-18 12:06:10 +01:00
Lovell Fuller
5842da22d8 Merge pull request #306 from dacarley/negate
Add negate operation to invert all pixel values.
2015-11-17 20:45:09 +00:00
Lovell Fuller
9850e3dae0 Merge pull request #303 from dacarley/threshold
Add threshold operation.
Converts to greyscale then splits into black/white based on given value.
2015-11-17 20:34:40 +00:00
David Carley
3af62446fc Implements greyscale thresholding 2015-11-17 12:15:34 -06:00
Lovell Fuller
1f71dade67 Merge pull request #307 from papandreou/wheezy
Linux Dockerfile: Use Debian Wheezy as the base image
2015-11-17 17:29:35 +00:00
Lovell Fuller
8be664b66f Merge pull request #305 from papandreou/fix/imagemagickUrl
Dockerfile: Update ImageMagick tarball url (they took -5 down and up -6)
2015-11-17 17:24:30 +00:00
Andreas Lind
c0be4f1307 Dial back the required libc version to 2.13 2015-11-17 17:37:06 +01:00
Andreas Lind
d9c754f5c1 Linux Dockerfile: Use a Debian Wheezy image instead of Ubuntu Precise. 2015-11-17 17:37:06 +01:00
David Carley
33a175eafb Implements negation. 2015-11-17 10:18:59 -06:00
Andreas Lind
7c990b3ab3 Dockerfile: Update ImageMagick tarball url (they took -5 down and up put -6). 2015-11-17 17:16:19 +01:00
Lovell Fuller
5dfeaa9fd1 Ensure gaussian blur is applied before lbb interpolator #289 2015-11-16 08:26:35 +00:00
Lovell Fuller
84fd1caa46 Switch default interpolator to bicubic #289
Only use gaussian blur for non-linear interpolators

Improves performance of bilinear by ~15%

Add liborc to the packaged build to improve bicubic perf

Add examples of the various interpolation methods

Add bilinear vs bicubic to perf tests
2015-11-15 22:04:31 +00:00
Lovell Fuller
2678d761ba Use FreeCallback to support mixed Windows runtime libs #152 2015-11-14 13:58:05 +00:00
Lovell Fuller
ede2ee9ce3 Use Persistent wrapper to prevent GC of input Buffer #152
Avoids memcpy of input and output Buffer objects

Improves Buffer and Stream performance by ~3%
2015-11-14 11:24:15 +00:00
Lovell Fuller
20f468991f Start to use libvips 8.1.0+ features #152
Use native (un)premultiply

Support normalise on Windows
2015-11-12 22:14:53 +00:00
Lovell Fuller
58d9e0fef7 Pre-extract rotate should not swap width/height #296 2015-11-11 22:34:30 +00:00
Lovell Fuller
d7278f022b Ensure latest npm on openSUSE test 2015-11-11 22:25:17 +00:00
Lovell Fuller
7383596f8c Allow tty-less docker-inside-docker usage 2015-11-11 21:37:04 +00:00
Lovell Fuller
f6831ab46b Increase packaging test logging 2015-11-10 23:30:08 +00:00
Lovell Fuller
7cf0f95ed1 Fix Circle CI config
CI providers should really provide config linters :)
2015-11-10 21:53:26 +00:00
Lovell Fuller
bf6b894480 Improve dependency-less documentation
Start to comment ever-growing GYP config

Add Circle CI config to run packaging tests
2015-11-10 21:49:45 +00:00
Lovell Fuller
ee8fcb6109 Update docs to reflect ease-of-installation 2015-11-10 00:04:06 +00:00
Lovell Fuller
05cec013fe Ensure support for more Linux flavours
Add docker-based packaging tests
2015-11-09 08:37:30 +00:00
Lovell Fuller
f4cbbd7b79 Ensure Windows CI uses x64 2015-11-07 20:22:17 +00:00
Lovell Fuller
2129adfcc3 Initial commit of local libvips binding/packaging
Copy Windows DLLs into release dir as no rpath equivalent
Use local libvips on Windows CI
2015-11-07 19:58:26 +00:00
Lovell Fuller
9f59a2aebf Version bumps and changelog for v0.11.4 2015-11-05 21:36:44 +00:00
Lovell Fuller
26fb75bf3f Merge pull request #291 from brandonaaron/corner-gravity
Add corner gravity support
2015-10-27 21:17:53 +00:00
Brandon Aaron
25e5f27785 add corner gavity support 2015-10-27 15:10:10 -04:00
Lovell Fuller
ef62daccf9 Upgrade CI and preinstall script to libvips 8.1.1 2015-10-16 23:43:55 +01:00
Lovell Fuller
9067c0a000 Merge pull request #288 from brandonaaron/exif_flop_2_and_4
EXIF Orientation tags 2 and 4 were flipping instead of flopping
2015-10-16 21:45:16 +01:00
Brandon Aaron
79470d2e07 EXIF Orientation tags 2 and 4 were flipping instead of flopping 2015-10-16 15:41:40 -04:00
Lovell Fuller
3cefa6f2bf Merge pull request #287 from vlapo/master
Add --runtime_link=static option to GYP binding for use with AWS Lambda
2015-10-14 19:22:09 +01:00
vlapo
75d954a6bc Add --runtime_link=static 2015-10-14 11:29:29 +02:00
Lovell Fuller
1b7e3746cc Merge pull request #286 from rayshan/patch-1
Replace `filename` with `path` to make consistent with Node's `fs` module
2015-10-13 21:03:32 +01:00
Ray Shan
29252d9dbb Clarify fileName argument by changing to path
I noticed we can do something like `.toFile("tmp/temp.jpg")`
2015-10-13 12:21:41 -07:00
Lovell Fuller
23e14861be Update perf test results to include jimp
Remove imagemagick-native until Node v4 support
2015-10-10 18:41:01 +01:00
Lovell Fuller
97960b5f91 Merge branch 'master' of https://github.com/lovell/sharp 2015-10-10 13:44:42 +01:00
Lovell Fuller
18c4ad9adf Version bumps of perf test candidates 2015-10-10 13:44:02 +01:00
Lovell Fuller
b240c53633 Merge pull request #282 from jabbrass/patch-1
Fix typo (docs/api)
2015-10-08 07:27:35 +01:00
J. Andrew Brassington
660f3d58be Fix typo (docs/api)
Line 330: "betweem" => "between"
2015-10-07 19:01:35 -07:00
Lovell Fuller
b6d75cda8e Revert Windows/Appveyor CI to libvips 8.0.2 2015-10-07 21:28:58 +01:00
Lovell Fuller
e07356c11c Update list of preinstall OS support
Upgrade to libvips 8.1.0
2015-10-07 20:49:47 +01:00
Lovell Fuller
82e215a42e Merge pull request #280 from roryrjb/master
(preinstall) add wily to 'supported' distros
2015-10-03 10:20:51 +01:00
Rory Bradford
cc1c36d891 (preinstall) add wily to 'supported' distros 2015-10-03 09:45:46 +01:00
Lovell Fuller
a1a2d7de5c Centos 7 provides LittleCMS via lcms2-devel #278 2015-09-30 17:35:35 +01:00
Lovell Fuller
6dce2deb82 Add jimp to perf tests #275 2015-09-27 09:38:47 +01:00
Lovell Fuller
cdad84edc6 Merge pull request #266 from roryrjb/master
(preinstall) add freya to 'supported' distros
2015-09-11 17:32:03 +01:00
Rory Bradford
de842a67d8 (preinstall) add freya to 'supported' distros 2015-09-11 17:10:25 +01:00
Lovell Fuller
918bbe88c6 Switch Travis to Ubuntu 14.04/GCE
Remove v0.10 and custom msvs flag from Appveyor
2015-09-09 22:18:22 +01:00
Lovell Fuller
7d3891989c Add Node.js v4 to CI builds 2015-09-08 20:50:33 +01:00
Lovell Fuller
168fe7c8d9 v0.11.3 2015-09-08 19:16:07 +01:00
Lovell Fuller
29ab8408fb Version bumps and changelog for v0.11.3 2015-09-08 15:13:28 +01:00
Lovell Fuller
692e2d4df4 Automate expected vs actual for blur/sharpen tests 2015-09-07 14:17:35 +01:00
Lovell Fuller
85d86dbede Merge pull request #263 from chrisriley/nan2-doubles
Convert blur and sharpen parameters to double instead of int.
This was broken during the nan v2 upgrade by commit b7e0a6f.
2015-09-06 19:29:06 +01:00
Chris Riley
ce2d7b8efc Update pipeline.cc
In the upgrade to nan v2 that was part of v0.11.2 the baton values for blurSigma, sharpenFlat, and sharpenJagged were being cast to int32_t causing blur(sigma) and sharpen(radius, flat, jagged) to fail with "parameters out of range". This patch casts these baton values to doubles.
2015-09-06 12:29:07 -04:00
Lovell Fuller
be00d72d82 Upgrade nan to fix clang linking problem #259 2015-08-28 10:48:20 +01:00
Lovell Fuller
5b376364f5 Add test case for require-time libc++ segfault 2015-08-27 23:59:48 +01:00
Lovell Fuller
409d15c624 Support EXIF Orientation removal in libvips v8.1.0
See #189 and jcupitt/libvips@fbe321e
2015-08-24 21:48:38 +01:00
Lovell Fuller
ce22388c3b Add io.js v3 to Appveyor CI test matrix 2015-08-24 19:31:11 +01:00
Lovell Fuller
30143cf509 Version bumps and changelog updates for v0.11.2 2015-08-24 19:03:05 +01:00
Lovell Fuller
78f31d2b0d Auto-exclude benchmark contenders that fail compilation 2015-08-24 18:55:59 +01:00
Lovell Fuller
4e67a5025a Use malloc for Nan::NewBuffer owned data
GC results in free() rather than delete[]

Add plenty of new valgrind suppressions
2015-08-24 16:14:49 +01:00
Lovell Fuller
b7e0a6f277 Upgrade to nan v2 #246
Provides support for io.js v3 and Node v4
2015-08-23 21:36:48 +01:00
Lovell Fuller
045680fba5 Document use of crop gravity as a String 2015-08-19 16:28:28 +01:00
Lovell Fuller
692347cc6b Merge pull request #255 from papandreou/feature/cropString
crop: Permit specifying the gravity as a string
2015-08-19 15:13:29 +01:00
Andreas Lind
faa515d969 crop: Permit specifying the gravity as a string.
Will be looked up in require('sharp').gravity.
2015-08-19 14:49:02 +02:00
Lovell Fuller
c4a278ec9c Update changelog for v0.11.1
Version bumps for benchmark test
2015-08-12 09:49:50 +01:00
Lovell Fuller
658a541f49 Add test case for enlarge+embed combo #243 2015-08-12 09:26:38 +01:00
Lovell Fuller
01435977de Blur and sharpen ops always convolve
Partial revert of 36ac882
2015-08-12 07:25:14 +01:00
Lovell Fuller
36ac8828f2 Gamma correction and premultiply do not mix
Skip premultiply for fast blur/sharpen

Automate gamma correction tests
2015-08-11 21:51:22 +01:00
Lovell Fuller
9c83d98bbb Update Appveyor CI to MSVC 2015 2015-07-31 12:11:18 +01:00
Lovell Fuller
dee9ca3ec2 Merge pull request #244 from TheThing/patch-1
Silence MSVC warning:
"C4530: C++ exception handler used,
but unwind semantics are not enabled."
2015-07-26 15:58:54 +01:00
Jonatan Nilsson
d375327d20 binding.gyp: Fix warnings during compiling
Fixes the following warnings while compiling:

```
D:\Forritun\Microsoft Visual Studio 14.0\VC\include\xlocale(341): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc (compiling source file ..\src\common.cc) [D:\sharp\build\sharp.vcxproj]
D:\Forritun\Microsoft Visual Studio 14.0\VC\include\xlocale(341): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc (compiling source file ..\src\operations.cc) [D:\sharp\build\sharp.vcxproj]
...etc...
```

Solution taken from here: https://github.com/TooTallNate/node-gyp/issues/26#issuecomment-7296389
2015-07-25 07:44:06 +00:00
Lovell Fuller
09244192e9 Add permalinks back to mkdocs
Erroneously removed in de333eb
2015-07-16 09:34:42 +01:00
Lovell Fuller
de333eb02d Add PPA details for Ubuntu LTS
Remove Mac OS from preinstall script - please use homebrew
2015-07-16 09:28:54 +01:00
Lovell Fuller
8b50f15a44 Version bumps ahead of v0.11.0 2015-07-15 10:55:06 +01:00
Lovell Fuller
f853fa3e23 Suppress known magick and glib leaks 2015-07-15 10:47:09 +01:00
Lovell Fuller
d26f6b3b89 Big documentation clean-up
Add structure via mkdocs (replaces ever-growing README)

Inline usage examples with the method they demonstrate

Add changelog
2015-07-15 10:17:55 +01:00
Lovell Fuller
022a2b1ade Ensure test coverage of overlayWith error paths 2015-07-15 09:37:25 +01:00
Lovell Fuller
4f1ac5717e Clarify removal of EXIF Orientation tag #189 2015-07-13 22:28:59 +01:00
Lovell Fuller
d303703dc5 Allow override of EXIF Orientation tag #189
Clear Orientation when rotate/flip/flop are used
2015-07-13 20:00:33 +01:00
Lovell Fuller
642e5687b6 Guard against unexpected overlay band format #97 2015-07-11 22:56:18 +01:00
Lovell Fuller
2ec845b083 Create normalize/blur/sharpen operations
Reduces pipeline by ~100 LOC
2015-07-11 21:44:39 +01:00
Lovell Fuller
c40cd1aa50 Switch Appveyor CI to x64 and iojs 2.x
Still install/build for 32-bit node/iojs exe
2015-07-11 19:56:23 +01:00
Lovell Fuller
804162c69a Increase AppVeyor CI timeout 2015-07-11 08:58:15 +01:00
Lovell Fuller
08b2a647d0 Update benchmark results for 0.11.0
Adds CImg-based 'lwip' module

Use AWS EC2 c4 instance type
2015-07-10 23:26:25 +01:00
Lovell Fuller
3a058c0c27 Add 'lwip' module to benchmark tests
Dependencies bump
2015-07-10 22:14:56 +01:00
Lovell Fuller
b8885c1faa Auto-width and height calcs now round instead of floor
I think this will better match people's expectations
2015-07-03 15:21:28 +01:00
Lovell Fuller
321e0f2bfe Add 'icc' raw profile data to metadata #129 2015-06-29 21:27:23 +01:00
Lovell Fuller
cff8b45420 Add OS X build status image
Almost-automated(TM)
2015-06-29 14:24:59 +01:00
Lovell Fuller
6ac47c1ef8 Add raw EXIF data to metadata response
Copy metadata input buffer to match pipeline

Prevents possible metadata segfault under load
2015-06-28 23:35:40 +01:00
Lovell Fuller
86490bedfb Add 'clone' method to snapshot an instance
Cloned instances share a common input
Allows multiple output Streams to use a single input Stream
2015-06-28 14:21:02 +01:00
Lovell Fuller
1091be374e Alpha compositing: support grey+alpha src and non-alpha dst 2015-06-02 14:51:08 +01:00
Lovell Fuller
36be0453dd Refactor internal 'resize' to more apt 'pipeline'
Refactor 'composite' C to C++ 'operations'
2015-06-01 16:33:26 +01:00
Lovell Fuller
e2c53b59ce Tighten constructor and quality param checks #221 2015-06-01 14:48:57 +01:00
Lovell Fuller
f19b6c48ca Skip normalise operation for images with one colour
It didn't play nicely with premultiplication
2015-06-01 14:21:02 +01:00
Lovell Fuller
d2a2654ace Replace use of 'finally' in failing tests 2015-06-01 14:21:02 +01:00
Lovell Fuller
8832ae0bf9 Add private maxColourDistance for functional tests
Switch MSE-based tests to use it

Remove experimental MSE-based compare API
2015-06-01 14:21:02 +01:00
Daniel Gasienica
ef8db1eebf Premultiply alpha channel to avoid dark artifacts during tranformation
Add `Sharp.compare(file1, file2, callback)` function for comparing images
using mean squared error (MSE). This is useful for unit tests.

See:
- https://github.com/jcupitt/libvips/issues/291
- http://entropymine.com/imageworsener/resizealpha/
2015-06-01 14:21:02 +01:00
Lovell Fuller
c792a047b1 Ensure libvips version requirement
Should improve debugging, e.g. #222
2015-06-01 14:21:01 +01:00
Daniel Gasienica
64f7f1d662 Add experimental overlayWith API
Composites an overlay image with alpha channel into the input image (which
must have alpha channel) using ‘over’ alpha compositing blend mode. This API
requires both images to have the same dimensions.

References:
- http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
- https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826

See #97.
2015-06-01 14:21:01 +01:00
Lovell Fuller
c886eaa6b0 Version bumps for v0.11.0 WIP 2015-06-01 14:21:01 +01:00
Lovell Fuller
b50fb53f27 Use image fingerprints in functional tests #122 2015-06-01 14:21:01 +01:00
Lovell Fuller
75d72cfded Version bumps ahead of v0.10.1 2015-06-01 09:50:49 +01:00
Lovell Fuller
21b0d8c7f7 Version bumps ahead of v0.10.1
Use SPDX format in licence field
2015-05-31 19:35:24 +01:00
Lovell Fuller
fa8f06f07d Include C standard library for 'atoi' #228
Xcode 6.3 appears to no longer do this
2015-05-31 19:05:18 +01:00
Lovell Fuller
e07a105b7c Test availability of __has_feature macro 2015-05-11 11:54:53 +01:00
Lovell Fuller
4f72dcbf54 Verify platform/compiler compatibility #178 #214 2015-05-11 10:46:47 +01:00
Lovell Fuller
b77877c83d Add Amazon Linux 2015.03
Update to libvips version 8.0.2
2015-05-07 15:50:14 +01:00
Lovell Fuller
8fd3520257 Correct use of Windows CI env variable 2015-05-05 11:15:32 +01:00
Lovell Fuller
f15e64039c Windows CI does not yet have io.js v2 2015-05-05 10:46:23 +01:00
Lovell Fuller
3ffe2ba17f Windows CI matrix and version bumps 2015-05-05 10:15:20 +01:00
Lovell Fuller
33782d3c83 Embed alpha image on non-transparent background #204 2015-04-29 20:14:45 +01:00
Lovell Fuller
783826aa26 Reverse Openslide if/else for Debian 8 #203 2015-04-27 18:44:53 +01:00
Lovell Fuller
c2ef16eac2 Don't publish AppVeyor config to npm 2015-04-27 18:44:11 +01:00
Lovell Fuller
e999fb6e30 Dependency version bumps ahead of v0.10.0 2015-04-23 14:29:14 +01:00
Lovell Fuller
d1fc0591a5 Silence Windows compiler warnings #19 2015-04-21 14:39:37 +01:00
Lovell Fuller
fb1c9cf3d3 Less strict assert for unordered events 2015-04-21 13:57:24 +01:00
Lovell Fuller
21ba1dfc26 Wrap flapping event test in nextTick 2015-04-21 13:22:29 +01:00
Lovell Fuller
dacd62428e Fix Windows CI binding config 2015-04-21 12:37:45 +01:00
Lovell Fuller
1e52c2dbe6 Windows compatibility #19
Hide WebP format and normalise option

Separate test runners for node and iojs
2015-04-21 12:13:19 +01:00
Lovell Fuller
8926ebc56c Add Appveyor config for Windows CI
Silence 'possible loss of data' warning
2015-04-20 19:00:22 +01:00
Lovell Fuller
9da87ce868 Fix typo in conditional introduced in 8ac33aa 2015-04-20 17:50:47 +01:00
Lovell Fuller
46cc45c186 Fail fast for unknown interpolator 2015-04-20 11:22:21 +01:00
Lovell Fuller
54f2243386 Merge pull request #198 from bkw/unknownInterpolator
Runtime guard against unknown interpolator class, avoids segfault.
2015-04-20 10:17:44 +01:00
Bernhard K. Weisshuhn
8ac33aad69 avoid segfault with unknown interpolator 2015-04-20 00:24:03 +02:00
Lovell Fuller
6fc62d39c9 Update thank you list
Add perf test for normalise
2015-04-19 21:09:19 +01:00
Lovell Fuller
a0655806de No need to remove dzi file extension
libvips handles this - ensures filenames containing . work
2015-04-19 21:08:15 +01:00
Lovell Fuller
3614d14f83 A few small fixes to the test scripts 2015-04-19 16:15:40 +01:00
Lovell Fuller
f6fd45cc90 Expose libjpeg extension param features
Trellis quantisation, overshoot deringing and scan optimisation
2015-04-19 16:15:40 +01:00
Lovell Fuller
be39297f3b Merge pull request #194 from bkw/normalize
Add normalize() to use full luminance range.
2015-04-19 16:05:20 +01:00
Bernhard K. Weisshuhn
dce36e0074 Add normalize() for simple histogram stretching
Available as normalize() or normalise().
Normalization takes place in LAB place and thus should not change any
colors.

Existing alpha channels are preserved untouched by normalization.
2015-04-18 12:55:04 +02:00
Lovell Fuller
ba034a8164 Add docs for new ignoreAspectRatio option 2015-04-16 18:28:30 +01:00
Lovell Fuller
3dfc7bea3a Merge pull request #192 from skedastik/judgement
Add support for ignoreAspectRatio option when resizing
2015-04-16 15:53:43 +01:00
Alaric Holloway
f72435c750 Support resize without preserving aspect ratio #118 2015-04-16 06:50:47 -07:00
Lovell Fuller
3810f642d3 Add small cache before convolution for seq access 2015-04-14 21:31:20 +01:00
Lovell Fuller
ae968142ee Soften limitInputPixels upper limit #146
Default limit of 14-bit dimensions remains
2015-04-12 14:23:36 +01:00
Lovell Fuller
ccb7887cb9 Use libjpeg-dev metapackage for Debian variants #190 2015-04-09 11:56:36 +01:00
Lovell Fuller
f1ad1216ca Add support for Windows #19
Requires VIPS_HOME environment variable
2015-04-05 22:42:14 +01:00
Lovell Fuller
ce6813329b Improve code portability ahead of Windows support 2015-04-05 21:57:53 +01:00
Lovell Fuller
7ad7193b1e Add test for progressive JPEG output 2015-03-30 11:36:14 +01:00
Lovell Fuller
bd96a49de6 Add usage example of Stream error handling #182 2015-03-24 10:35:05 +00:00
Lovell Fuller
81c710eaa3 Add EventEmitter for queue length changes
Remove unnecessary params from Error handler
2015-03-20 15:44:18 +00:00
Lovell Fuller
711f0fefb6 Disable unused static library and introspection components 2015-03-20 14:57:13 +00:00
Lovell Fuller
33ca86e4f2 Add example of Deep Zoom output
Clean up preinstall and prereqs docs
2015-03-12 17:33:06 +00:00
Lovell Fuller
9b5229f2dd Support old and new Magick loader class names
See jcupitt/libvips@99b4bcb
2015-03-12 17:12:06 +00:00
Lovell Fuller
5781a23a4d Combine new tile* API methods
Use v7.40.0+ libvips loader methods

Separate Openslide as input vs Deep Zoom as output

Split tile-based tests into new file

Added assertions for generated tile size
2015-03-12 15:39:27 +00:00
Victor Mateevitsi
2d1e6f2644 Added Deep Zoom support.
Added OpenSuse 13.1 and 13.2 support in preinstall.sh script.
Added OpenSlide support in preinstall script.
Added unit tests for Deep Zoom and OpenSlide.
2015-03-10 14:00:27 +00:00
Lovell Fuller
5240eeb518 Merge pull request #166 from mcuelenaere/no-custom-image-header-checking
Let libvips check whether we received a valid image or not
2015-03-01 15:41:37 +00:00
Maurus Cuelenaere
125ee836fe Let libvips check whether we received a valid image or not
This removes the custom image fingerprinting code and uses the libvips
is_a_buffer() infrastructure instead.
2015-03-01 11:53:17 +01:00
Lovell Fuller
3ca2f009f4 Remove confusing CSS equivs introduced in 77bbbb9 2015-02-27 15:04:11 +00:00
Lovell Fuller
a900c28f7c Version bumps 2015-02-27 14:46:03 +00:00
Lovell Fuller
77bbbb9715 Improve min/max docs, thanks @LinusU
Add requirement for C++11 compiler

Init scaling factor to silence compiler warning
2015-02-27 13:49:16 +00:00
Lovell Fuller
88753a6333 Merge pull request #175 from LinusU/feature-min
feature: min
2015-02-27 13:25:13 +00:00
Linus Unnebäck
bcd82f4893 feature: min 2015-02-27 13:50:52 +01:00
Lovell Fuller
749dc61f85 JSON and the agro-noughts 2015-02-26 19:49:54 +00:00
Lovell Fuller
c7ccf6801d Expose runtime format availability
Aids addition of new format/method combos

Dogfood this in the test code
2015-02-26 19:41:33 +00:00
Lovell Fuller
1565522ecc Add libgsf dependency ahead of Deep Zoom support 2015-02-26 19:36:42 +00:00
Lovell Fuller
a44df2f533 Merge pull request #173 from LinusU/patch-1
Install libgsf-1-dev on Debian 8
2015-02-26 14:00:51 +00:00
Linus Unnebäck
317510746f Install libgsf-1-dev on Debian 8
This was needed on my docker container running Debian Jessie. This is the error without it:

```text
Package libgsf-1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `libgsf-1.pc'
to the PKG_CONFIG_PATH environment variable
Package 'libgsf-1', required by 'vips', not found
gyp: Call to 'PKG_CONFIG_PATH=":$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig" pkg-config --libs vips' returned exit status 1. while trying to load binding.gyp
```
2015-02-26 13:44:00 +01:00
Lovell Fuller
ef54e327b7 Document GIF input via Buffer and Stream
Ensure @mcuelenaere is credited
2015-02-16 13:51:47 +00:00
Lovell Fuller
d8d0158774 Version bump of libvips to 7.42.3
Rename version vars to major, minor and patch
2015-02-16 13:49:22 +00:00
Lovell Fuller
4d75f27a25 Merge branch 'mcuelenaere-imagemagick-buffer' 2015-02-16 13:27:51 +00:00
Lovell Fuller
f89e9d726d Add support for libvips v8.0.0 2015-02-16 13:27:22 +00:00
Lovell Fuller
5194b37460 Merge branch 'imagemagick-buffer' of https://github.com/mcuelenaere/sharp into mcuelenaere-imagemagick-buffer 2015-02-16 12:02:27 +00:00
Lovell Fuller
55ea432711 Add assumeyes flag for Fedora #167 2015-02-16 11:18:42 +00:00
Maurus Cuelenaere
ab7408c96f Add support for loading images through ImageMagick as a buffer 2015-02-16 10:12:59 +01:00
Lovell Fuller
1f7e80e581 Add chroma subsampling options for JPEG output 2015-02-13 09:41:42 +00:00
Lovell Fuller
0e91ca90d6 Remove lingering NanAdjustExternalMemory
Should have been removed in fe34548b
2015-02-12 12:15:56 +00:00
Lovell Fuller
8f41fed9c2 Add toFormat convenience method #137 2015-02-12 11:37:56 +00:00
Lovell Fuller
96dd40cee1 Add Node.js 0.12 stable to CI build
Replaces 0.11 unstable
2015-02-09 17:09:37 +00:00
Lovell Fuller
62767d072b Version bumps 2015-02-09 09:52:27 +00:00
Lovell Fuller
33880ce19e Merge pull request #161 from ide/nan
Change nan dependency back to ^1.6.2
2015-02-06 21:32:27 +00:00
James Ide
988176846d Change nan dependency back to ^1.6.2
The issue with nan on io.js was fixed in https://github.com/rvagg/nan/pull/273. `npm install` on io.js 1.1.0 works now.
2015-02-06 12:30:15 -08:00
Lovell Fuller
657d436a0f Merge pull request #160 from jo/patch-1
Adjust comment in interpolation example
2015-02-06 14:28:59 +00:00
Johannes Jörg Schmidt
e5549e3063 Adjust comment in interpolation example 2015-02-06 15:17:10 +01:00
Lovell Fuller
0b2fb967b8 Add iojs to CI test matrix
Specific version of nan required
2015-02-06 09:36:45 +00:00
Lovell Fuller
f57478c1aa Update bench to latest imagemagick-native
Use 'Triangle' filter as bilinear equiv.
2015-02-02 16:16:45 +00:00
Lovell Fuller
e5a5e2ca7e Tighten 'extract' parameter validation #158 2015-01-29 22:46:04 +00:00
Lovell Fuller
797d503a99 Merge pull request #156 from jonathanong/patch-1
⬇️ nan@^1.5.1
2015-01-28 22:04:52 +00:00
Jonathan Ong
512a281986 ⬇️ nan@^1.5.1
1.6 breaks iojs. this lowers the minimum nan version so that developers can still do `nan@~1.5.1`.
2015-01-27 15:11:40 -08:00
Lovell Fuller
37c5ca7166 Skip SVG test when format is unavailable
It's possible to compile *magick without SVG support
2015-01-25 11:34:16 +00:00
Lovell Fuller
cda700ef73 Update libmagick references to the C library 2015-01-23 11:00:39 +00:00
Lovell Fuller
d32901da8d Dependency version bumps 2015-01-23 10:50:50 +00:00
Lovell Fuller
83ebe12061 Remove atexit handler as libvips defines this
New grunt-sharp build tool

Version bump for latest libvips
2015-01-22 14:17:20 +00:00
Lovell Fuller
fe34548bad Remove optional AdjustAmountOfExternalAllocatedMemory #151
Isolate not available when deleting the buffer
2015-01-21 20:13:43 +00:00
Lovell Fuller
855945bef2 Delete input buffer on postclose #151
Notify V8 GC of memory (de)allocation
2015-01-21 10:34:03 +00:00
Lovell Fuller
8421e3aa5f Add limitInputPixels option to reject input #115 2015-01-20 14:18:05 +00:00
Lovell Fuller
c93f79daa7 Guard against InitImage failure #150
Protects against truncated image headers
2015-01-20 10:38:44 +00:00
Lovell Fuller
35c53f78c8 Ensure bench/random test uses int dimensions 2015-01-16 22:31:46 +00:00
Lovell Fuller
c158d51f8b Explicit cast to uint32 required for nan 1.5.x
See rvagg/nan#229
2015-01-16 22:31:46 +00:00
Lovell Fuller
8e9a8dfede Remove cpplint namespace-related warnings 2015-01-16 22:31:46 +00:00
Lovell Fuller
67dc694cfb Link to new contributor guide
Remove now-duplicate content
2015-01-16 22:31:46 +00:00
Lovell Fuller
74704a132c Add a contribution guide to help those who help 2015-01-16 22:31:46 +00:00
Lovell Fuller
b86674f91f Add cpplint to test suite 2015-01-16 22:31:46 +00:00
Lovell Fuller
5dab3c8482 Allow rotate before pre-resize extraction #145 2015-01-16 22:30:57 +00:00
Lovell Fuller
a190ae6b08 Add raw, uncompressed image data output #136 2015-01-16 22:28:24 +00:00
Lovell Fuller
464fb1726d Keep output dimensions within WebP 14-bit range 2015-01-16 22:28:24 +00:00
Lovell Fuller
065ce6454b Explicit cast of size_t to 32 bit integer
Ensures compilation with nan 1.5.0+
2015-01-15 15:16:01 +00:00
Lovell Fuller
850c2ecdd6 Version bumps 2014-12-15 14:02:30 +00:00
Lovell Fuller
926c5603aa Improve documentation on concurrency/parallelism 2014-12-15 14:00:23 +00:00
Lovell Fuller
d3225fa193 Add 'size' attribute to callback's info Object #138 2014-12-15 13:54:19 +00:00
Lovell Fuller
f026a835fd Move unref of input Buffer to C++ #138 2014-12-14 10:31:25 +00:00
Lovell Fuller
47241db789 Let V8 garbage collect the Buffer earlier #138 2014-12-13 08:48:24 +00:00
Lovell Fuller
34a9970bd9 Remove useless re-definition of image #139 2014-12-12 22:04:55 +00:00
Lovell Fuller
57203f841a Copy input Buffer to avoid V8 heap compaction #138 2014-12-12 22:02:42 +00:00
Lovell Fuller
bd20bd1881 Version bumps 2014-12-11 13:32:52 +00:00
Lovell Fuller
60f1fda7ee Change interpretation to sRGB before transformation #133 2014-12-11 13:32:36 +00:00
Lovell Fuller
ea1013f6ec Add support for latest Amazon Linux 2014-12-08 10:52:59 +00:00
Lovell Fuller
247b607afd Add SVG and PSD fixtures and tests 2014-12-05 21:35:18 +00:00
Lovell Fuller
a56102a209 Ensure ICC transform of withMetadata output #133 2014-12-04 11:28:09 +00:00
Lovell Fuller
940b6f505f Add test for Promise rejection path 2014-12-04 10:48:45 +00:00
Lovell Fuller
e1b5574c4a Handle broken, embedded ICC profile #131 2014-12-03 10:23:35 +00:00
329 changed files with 19854 additions and 3884 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

13
.gitignore vendored
View File

@@ -2,8 +2,15 @@ build
node_modules
coverage
test/bench/node_modules
test/fixtures/output.*
test/fixtures/output*
test/leak/libvips.supp
# Mac OS X
test/saliency/report.json
test/saliency/Image*
test/saliency/[Uu]serData*
!test/saliency/userData.js
vendor
packaging/libvips*
packaging/*.log
!packaging/build
.DS_Store
.nyc_output

View File

@@ -1,3 +0,0 @@
node_modules
test/bench/node_modules
coverage

View File

@@ -1,8 +0,0 @@
{
"strict": true,
"node": true,
"globals": {
"describe": true,
"it": true
}
}

View File

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

View File

@@ -1,8 +1,21 @@
language: node_js
node_js:
- "0.10"
- "0.11"
- "4"
- "6"
- "7"
os:
- linux
- osx
sudo: false
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
osx_image: xcode8
before_install:
- curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

108
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,108 @@
# Contributing to sharp
Hello, thank you for your interest in helping!
## Submit a new bug report
Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem.
If you're having installation problems, please include the output of running `npm install --verbose sharp`.
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.
Implementation is usually straightforward if _libvips_ [already supports](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/ch03.html) the feature you need.
## Submit a Pull Request to fix a bug
Thank you! To prevent the problem occurring again, please add unit tests that would have failed.
Please select the `master` branch as the destination for your Pull Request so your fix can be included in the next minor release.
Please squash your changes into a single commit using a command like `git rebase -i upstream/master`.
To test C++ changes, you can compile the module using `npm install` and then run the tests using `npm test`.
## Submit a Pull Request with a new feature
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/master/test/unit) to cover your new feature.
A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
Where possible, the functional tests use gradient-based perceptual hashes
based on [dHash](http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html)
to compare expected vs actual images.
You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/master/package.json#L5).
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
| Release | WIP branch |
| ------: | :--------- |
| v0.18.0 | ridge |
| v0.19.0 | suit |
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_.
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()`.
Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to gather feedback on a potential API change.
### Remove an existing public method
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.
## Documentation
The public API is documented with [JSDoc](http://usejsdoc.org/) annotated comments.
These can be converted to Markdown by running:
```sh
npm run docs
```
Please include documentation updates in any Pull Request that modifies the public API.
## Run the tests
### Functional tests and static code analysis
```sh
npm test
```
### Memory leak tests
Requires [Valgrind](http://valgrind.org/).
```sh
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)
for private, paid consulting.

632
README.md Executable file → Normal file
View File

@@ -1,619 +1,85 @@
# sharp
* [Installation](https://github.com/lovell/sharp#installation)
* [Usage examples](https://github.com/lovell/sharp#usage-examples)
* [API](https://github.com/lovell/sharp#api)
* [Testing](https://github.com/lovell/sharp#testing)
* [Performance](https://github.com/lovell/sharp#performance)
* [Thanks](https://github.com/lovell/sharp#thanks)
* [Licence](https://github.com/lovell/sharp#licence)
```sh
npm install sharp
```
The typical use case for this high speed Node.js module is to convert large images of many formats to smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
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.
This module supports reading and writing JPEG, PNG and WebP images to and from Streams, Buffer objects and the filesystem.
It also supports reading images of many other types from the filesystem via libmagick++ or libgraphicsmagick++ if present.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
Lanczos resampling ensures quality is not sacrificed for speed.
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. Resizing an image is typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
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 can be disabled, which for diagrams and line art often produces the same result as [pngcrush](http://pmt.sourceforge.net/pngcrush/).
OS X, Windows (x64), Linux (x64, ARM) systems do not require
the installation of any external runtime dependencies.
Everything remains non-blocking thanks to _libuv_, no child processes are spawned and Promises/A+ are supported.
Anyone who has used the Node.js bindings for [GraphicsMagick](https://github.com/aheckmann/gm) will find the API similarly fluent.
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).
## Installation
npm install sharp
### Prerequisites
* Node.js v0.10+
* [libvips](https://github.com/jcupitt/libvips) v7.40.0+ (7.42.0+ recommended)
To install the most suitable version of libvips on the following Operating Systems:
* Mac OS
* Homebrew
* MacPorts
* Debian Linux
* Debian 7, 8
* Ubuntu 12.04, 14.04, 14.10, 15.04
* Mint 13, 17
* Red Hat Linux
* RHEL/Centos/Scientific 6, 7
* Fedora 21, 22
run the following as a user with `sudo` access:
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
or run the following as `root`:
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | bash -
The [preinstall.sh](https://github.com/lovell/sharp/blob/master/preinstall.sh) script requires `curl` and `pkg-config`.
### Mac OS tips
Manual install via homebrew:
brew install homebrew/science/vips --with-webp --with-graphicsmagick
A missing or incorrectly configured _Xcode Command Line Tools_ installation [can lead](https://github.com/lovell/sharp/issues/80) to a `library not found for -ljpeg` error. If so, please try:
xcode-select --install
The _gettext_ dependency of _libvips_ [can lead](https://github.com/lovell/sharp/issues/9) to a `library not found for -lintl` error. If so, please try:
brew link gettext --force
### Heroku
[Alessandro Tagliapietra](https://github.com/alex88) maintains an [Heroku buildpack for libvips](https://github.com/alex88/heroku-buildpack-vips) and its dependencies.
### Docker
[Marc Bachmann](https://github.com/marcbachmann) maintains a [Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
docker pull marcbachmann/libvips
### gulp.js
[Eugeny Vlasenko](https://github.com/mahnunchik) maintains [gulp-responsive](https://www.npmjs.org/package/gulp-responsive) and [Mohammad Prabowo](https://github.com/rizalp) maintains [gulp-sharp](https://www.npmjs.org/package/gulp-sharp).
## Usage examples
## Examples
```javascript
var sharp = require('sharp');
import sharp from 'sharp';
```
```javascript
sharp('input.jpg').resize(300, 200).toFile('output.jpg', function(err) {
if (err) {
throw err;
}
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
sharp(inputBuffer)
.resize(320, 240)
.toFile('output.webp', (err, info) => ... );
```
```javascript
var transformer = sharp().resize(300, 200).crop(sharp.gravity.north);
readableStream.pipe(transformer).pipe(writableStream);
// Read image data from readableStream, resize and write image data to writableStream
```
```javascript
var image = sharp(inputJpg);
image.metadata(function(err, metadata) {
image.resize(metadata.width / 2).webp().toBuffer(function(err, outputBuffer, info) {
// outputBuffer contains a WebP image half the width and height of the original JPEG
});
});
```
```javascript
var pipeline = sharp()
sharp('input.jpg')
.rotate()
.resize(null, 200)
.progressive()
.toBuffer(function(err, outputBuffer, info) {
if (err) {
throw err;
}
// outputBuffer contains 200px high progressive JPEG image data,
// auto-rotated using EXIF Orientation tag
// info.width and info.height contain the dimensions of the resized image
});
readableStream.pipe(pipeline);
```
```javascript
sharp('input.png')
.rotate(180)
.resize(300)
.flatten()
.background('#ff6600')
.sharpen()
.withMetadata()
.quality(90)
.webp()
.resize(200)
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, sharpened, with metadata, 90% quality WebP image
// data
});
.then( data => ... )
.catch( err => ... );
```
```javascript
http.createServer(function(request, response) {
response.writeHead(200, {'Content-Type': 'image/webp'});
sharp('input.jpg').rotate().resize(200).webp().pipe(response);
}).listen(8000);
// Create HTTP server that always returns auto-rotated 'input.jpg',
// resized to 200 pixels wide, in WebP format
const roundedCorners = new Buffer(
'<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 })
.png();
readableStream
.pipe(roundedCornerResizer)
.pipe(writableStream);
```
```javascript
sharp(input)
.extract(top, left, width, height)
.toFile(output);
// Extract a region of the input image, saving in the same format.
```
```javascript
sharp(input)
.extract(topOffsetPre, leftOffsetPre, widthPre, heightPre)
.resize(width, height)
.extract(topOffsetPost, leftOffsetPost, widthPost, heightPost)
.toFile(output);
// Extract a region, resize, then extract from the resized image
```
```javascript
sharp(inputBuffer)
.resize(200, 300)
.interpolateWith(sharp.interpolator.nohalo)
.background('white')
.embed()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a bicubic scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
```javascript
sharp('input.gif')
.resize(200, 300)
.background({r: 0, g: 0, b: 0, a: 0})
.embed()
.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
});
```
```javascript
sharp(inputBuffer)
.resize(200, 200)
.max()
.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
});
```
## API
### Input methods
#### sharp([input])
Constructor to which further methods are chained. `input`, if present, can be one of:
* Buffer containing JPEG, PNG, WebP or TIFF image data, or
* String containing the filename of an image, with most major formats supported.
The object returned implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
JPEG, PNG, WebP or TIFF format image data can be streamed into the object when `input` is not provided.
JPEG, PNG or WebP format image data can be streamed out from this object.
#### metadata([callback])
Fast access to image metadata without decoding any compressed image data.
`callback`, if present, gets the arguments `(err, metadata)` where `metadata` has the attributes:
* `format`: Name of decoder to be used to decompress image data e.g. `jpeg`, `png`, `webp` (for file-based input additionally `tiff` and `magick`)
* `width`: Number of pixels wide
* `height`: Number of pixels high
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522)
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* `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
A Promises/A+ promise is returned when `callback` is not provided.
#### 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.
### Image transformation options
#### resize(width, [height])
Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified.
`width` is the Number of pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
`height` is the Number of pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
#### extract(top, left, width, height)
Extract a region of the image. Can be used with or without a `resize` operation.
`top` and `left` are the offset, in pixels, from the top-left corner.
`width` and `height` are the dimensions of the extracted image.
Use `extract` before `resize` for pre-resize extraction. Use `extract` after `resize` for post-resize extraction. Use `extract` before and after for both.
#### crop([gravity])
Crop the resized image to the exact size specified, the default behaviour.
`gravity`, if present, is an attribute of the `sharp.gravity` Object e.g. `sharp.gravity.north`.
Possible values are `north`, `east`, `south`, `west`, `center` and `centre`. The default gravity is `center`/`centre`.
#### max()
Preserving aspect ratio, resize the image to the maximum `width` or `height` specified.
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
#### background(rgba)
Set the background for the `embed` and `flatten` operations.
`rgba` is parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
The alpha value is a float between `0` (transparent) and `1` (opaque).
The default background is `{r: 0, g: 0, b: 0, a: 1}`, black without transparency.
#### 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.
#### flatten()
Merge alpha transparency channel, if any, with `background`.
#### rotate([angle])
Rotate the output image by either an explicit angle or auto-orient based on the EXIF `Orientation` tag.
`angle`, if present, is a Number with a value of `0`, `90`, `180` or `270`.
Use this method without `angle` to determine the angle from EXIF data. Mirroring is supported and may infer the use of a `flip` operation.
#### flip()
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
#### flop()
Flop the image about the horizontal X axis. This always occurs after rotation, if any.
#### withoutEnlargement()
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".
#### blur([sigma])
When used without parameters, performs a fast, mild blur of the output image. This typically reduces performance by 10%.
When a `sigma` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 25%.
* `sigma`, if present, is a Number between 0.3 and 1000 representing the approximate blur radius in pixels.
#### sharpen([radius], [flat], [jagged])
When used without parameters, performs a fast, mild sharpen of the output image. This typically reduces performance by 10%.
When a `radius` 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. This typically reduces performance by 50%.
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels.
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
* `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0.
#### interpolateWith(interpolator)
Use the given interpolator for image resizing, where `interpolator` is an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.bicubic`.
Possible interpolators, in order of performance, are:
* `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation), suitable for image enlargement only.
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), the default and fastest image reduction interpolation.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation), which typically reduces performance by 5%.
* `vertexSplitQuadraticBasisSpline`: Use [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48), which prevents "staircasing" and typically reduces performance by 5%.
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" and typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance and typically reduces performance by a factor of 3.
#### gamma([gamma])
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` then increasing the encoding (brighten) post-resize at a factor of `gamma`.
`gamma`, if present, is a Number betweem 1 and 3. The default value is `2.2`, a suitable approximation for sRGB images.
This can improve the perceived brightness of a resized image in non-linear colour spaces.
JPEG input images will not take advantage of the shrink-on-load performance optimisation when applying a gamma correction.
#### grayscale() / greyscale()
Convert to 8-bit greyscale; 256 shades of grey.
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
The output image will still be web-friendly sRGB and contain three (identical) channels.
### Output options
#### jpeg()
Use JPEG format for the output image.
#### png()
Use PNG format for the output image.
#### webp()
Use WebP format for the output image.
#### quality(quality)
The output quality to use for lossy JPEG, WebP and TIFF output formats. The default quality is `80`.
`quality` is a Number between 1 and 100.
#### progressive()
Use progressive (interlace) scan for JPEG and PNG output. This typically reduces compression performance by 30% but results in an image that can be rendered sooner when decompressed.
#### withMetadata()
Include all metadata (ICC, EXIF, XMP) from the input image in the output image.
The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space.
#### compressionLevel(compressionLevel)
An advanced setting for the _zlib_ compression level of the lossless PNG output format. The default level is `6`.
`compressionLevel` is a Number between 0 and 9.
#### withoutAdaptiveFiltering()
_Requires libvips 7.42.0+_
An advanced setting to disable adaptive row filtering for the lossless PNG output format.
### Output methods
#### toFile(filename, [callback])
`filename` is a String containing the filename to write the image data to. The format is inferred from the extension, with JPEG, PNG, WebP and TIFF supported.
`callback`, if present, is called with two arguments `(err, info)` where:
* `err` contains an error message, if any.
* `info` contains the output image `format`, `width` and `height`.
A Promises/A+ promise is returned when `callback` is not provided.
#### toBuffer([callback])
Write image data to a Buffer, the format of which will match the input image by default. JPEG, PNG and WebP are supported.
`callback`, if present, gets three arguments `(err, buffer, info)` where:
* `err` is an error message, if any.
* `buffer` is the output image data.
* `info` contains the output image `format`, `width` and `height`.
A Promises/A+ promise is returned when `callback` is not provided.
### Utility methods
#### sharp.cache([memory], [items])
If `memory` or `items` are provided, set the limits of _libvips'_ operation cache.
* `memory` is the maximum memory in MB to use for this cache, with a default value of 100
* `items` is the maximum number of operations to cache, with a default value of 500
This method always returns cache statistics, useful for determining how much working memory is required for a particular task.
```javascript
var stats = sharp.cache(); // { current: 75, high: 99, memory: 100, items: 500 }
sharp.cache(200); // { current: 75, high: 99, memory: 200, items: 500 }
sharp.cache(50, 200); // { current: 49, high: 99, memory: 50, items: 200}
```
#### sharp.concurrency([threads])
`threads`, if provided, is the Number of threads _libvips'_ should create for image processing. The default value is the number of CPU cores. A value of `0` will reset to this default.
This method always returns the current concurrency.
```javascript
var threads = sharp.concurrency(); // 4
sharp.concurrency(2); // 2
sharp.concurrency(0); // 4
```
#### sharp.counters()
Provides access to internal task counters.
* `queue` is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
* `process` is the number of resize tasks currently being processed.
```javascript
var counters = sharp.counters(); // { queue: 2, process: 4 }
```
## Testing
### Functional tests
#### Coverage
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
#### Ubuntu 12.04
### Documentation
[![Ubuntu 12.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
Visit [sharp.dimens.io](http://sharp.dimens.io/) for complete
[installation instructions](http://sharp.dimens.io/page/install),
[API documentation](http://sharp.dimens.io/page/api),
[benchmark tests](http://sharp.dimens.io/page/performance) and
[changelog](http://sharp.dimens.io/page/changelog).
#### Centos 6.5
### Contributing
[![Centos 6.5 Build Status](https://snap-ci.com/lovell/sharp/branch/master/build_image)](https://snap-ci.com/lovell/sharp/branch/master)
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
#### It worked on my machine
### Licence
```
npm test
```
### Memory leak tests
```
cd sharp/test/leak
./leak.sh
```
Requires _valgrind_:
```
brew install valgrind
```
```
sudo apt-get install -qq valgrind
```
### Benchmark tests
```
cd sharp/test/bench
npm install
npm test
```
Requires both _ImageMagick_ and _GraphicsMagick_:
```
brew install imagemagick
brew install graphicsmagick
```
```
sudo apt-get install -qq imagemagick graphicsmagick libmagick++-dev
```
```
sudo yum install ImageMagick
sudo yum install -y http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
sudo yum install -y --enablerepo=epel GraphicsMagick
```
## Performance
### Test environment
* AWS EC2 [c3.xlarge](http://aws.amazon.com/ec2/instance-types/#Compute_Optimized)
* Ubuntu 14.04
* libvips 7.40.8
* liborc 0.4.22
### The contenders
* [imagemagick-native](https://github.com/mash/node-imagemagick-native) v1.2.2 - Supports Buffers only
* [imagemagick](https://github.com/yourdeveloper/node-imagemagick) v0.1.3 - Supports filesystem only and "has been unmaintained for a long time".
* [gm](https://github.com/aheckmann/gm) v1.16.0 - Fully featured wrapper around GraphicsMagick.
* sharp v0.6.2 - Caching within libvips disabled to ensure a fair comparison.
### The task
Decompress a 2725x2225 JPEG image, resize and crop to 720x480, then compress to JPEG.
### Results
| Module | Input | Output | Ops/sec | Speed-up |
| :-------------------- | :----- | :----- | ------: | -------: |
| imagemagick-native | buffer | buffer | 1.58 | 1 |
| imagemagick | file | file | 6.23 | 3.9 |
| gm | buffer | file | 5.32 | 3.4 |
| gm | buffer | buffer | 5.32 | 3.4 |
| gm | file | file | 5.36 | 3.4 |
| gm | file | buffer | 5.36 | 3.4 |
| sharp | buffer | file | 22.05 | 14.0 |
| sharp | buffer | buffer | 22.14 | 14.0 |
| sharp | file | file | 21.79 | 13.8 |
| sharp | file | buffer | 21.90 | 13.9 |
| sharp | stream | stream | 20.87 | 13.2 |
| sharp +promise | file | buffer | 21.89 | 13.9 |
| sharp +sharpen | file | buffer | 19.69 | 12.5 |
| sharp +progressive | file | buffer | 16.93 | 10.7 |
| sharp +sequentialRead | file | buffer | 21.60 | 13.7 |
You can expect greater performance with caching enabled (default) and using 8+ core machines.
## Thanks
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)
Thank you!
## Licence
Copyright 2013, 2014 Lovell Fuller and contributors.
Copyright 2013, 2014, 2015, 2016, 2017 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)
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,

15
appveyor.yml Normal file
View File

@@ -0,0 +1,15 @@
os: Visual Studio 2015
version: "{build}"
build: off
platform: x64
environment:
matrix:
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "7"
install:
- ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@latest
- npm install
test_script:
- npm test

270
binding.gyp Executable file → Normal file
View File

@@ -1,23 +1,193 @@
{
'targets': [{
'target_name': 'libvips-cpp',
'conditions': [
['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/VInterpolate.cpp',
'src/libvips/cplusplus/VImage.cpp'
],
'include_dirs': [
'vendor/include',
'vendor/include/glib-2.0',
'vendor/lib/glib-2.0/include'
],
'libraries': [
'../vendor/lib/libvips.lib',
'../vendor/lib/libglib-2.0.lib',
'../vendor/lib/libgobject-2.0.lib'
],
'configurations': {
'Release': {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'msvs_disabled_warnings': [
4275
]
}
}
}, {
# Ignore this target for non-Windows
'type': 'none'
}]
]
}, {
'target_name': 'sharp',
'dependencies': [
'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()")'
}, {
'use_global_vips': ''
}]
]
},
'sources': [
'src/common.cc',
'src/utilities.cc',
'src/metadata.cc',
'src/resize.cc',
'src/sharp.cc'
],
'variables': {
'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'
},
'libraries': [
'<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --libs vips)'
'src/operations.cc',
'src/pipeline.cc',
'src/sharp.cc',
'src/utilities.cc'
],
'include_dirs': [
'<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --cflags vips glib-2.0)',
'<!(node -e "require(\'nan\')")'
],
'conditions': [
['use_global_vips == "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': [
['runtime_link == "static"', {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
}, {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
}],
['OS == "linux"', {
'defines': [
# Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible.
'_GLIBCXX_USE_CXX11_ABI=<!(if readelf -Ws "$(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --variable libdir vips-cpp)/libvips-cpp.so" | c++filt | grep -qF __cxx11;then echo "1";else echo "0";fi)'
]
}]
]
}, {
# Attempt to download pre-built libvips and install locally within node_modules
'include_dirs': [
'vendor/include',
'vendor/include/glib-2.0',
'vendor/lib/glib-2.0/include'
],
'conditions': [
['OS == "win"', {
'defines': [
'_ALLOW_KEYWORD_MACROS'
],
'libraries': [
'../vendor/lib/libvips.lib',
'../vendor/lib/libglib-2.0.lib',
'../vendor/lib/libgobject-2.0.lib'
]
}],
['OS == "mac"', {
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'libraries': [
'../vendor/lib/libvips-cpp.42.dylib',
'../vendor/lib/libvips.42.dylib',
'../vendor/lib/libglib-2.0.0.dylib',
'../vendor/lib/libgobject-2.0.0.dylib',
# Ensure runtime linking is relative to sharp.node
'-rpath \'@loader_path/../../vendor/lib\''
]
}],
['OS == "linux"', {
'variables': {
'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")'
},
'defines': [
'_GLIBCXX_USE_CXX11_ABI=0'
],
'libraries': [
'../vendor/lib/libvips-cpp.so',
'../vendor/lib/libvips.so',
'../vendor/lib/libglib-2.0.so',
'../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/libffi.so',
'../vendor/lib/libfontconfig.so',
'../vendor/lib/libfreetype.so',
'../vendor/lib/libgdk_pixbuf-2.0.so',
'../vendor/lib/libgif.so',
'../vendor/lib/libgio-2.0.so',
'../vendor/lib/libgmodule-2.0.so',
'../vendor/lib/libgsf-1.so',
'../vendor/lib/libgthread-2.0.so',
'../vendor/lib/libharfbuzz.so',
'../vendor/lib/libjpeg.so',
'../vendor/lib/liblcms2.so',
'../vendor/lib/liborc-0.4.so',
'../vendor/lib/libpango-1.0.so',
'../vendor/lib/libpangocairo-1.0.so',
'../vendor/lib/libpangoft2-1.0.so',
'../vendor/lib/libpixman-1.so',
'../vendor/lib/libpng.so',
'../vendor/lib/librsvg-2.so',
'../vendor/lib/libtiff.so',
'../vendor/lib/libwebp.so',
'../vendor/lib/libxml2.so',
'../vendor/lib/libz.so',
# Ensure runtime linking is relative to sharp.node
'-Wl,--disable-new-dtags -Wl,-rpath=\'$${ORIGIN}/../../vendor/lib\''
]
}]
]
}]
],
'cflags_cc': [
'-std=c++0x',
'-fexceptions',
@@ -25,14 +195,84 @@
'-O3'
],
'xcode_settings': {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'GCC_ENABLE_CPP_RTTI': 'YES',
'OTHER_CPLUSPLUSFLAGS': [
'-std=c++11',
'-stdlib=libc++',
'-fexceptions',
'-Wall',
'-O3'
],
'MACOSX_DEPLOYMENT_TARGET': '10.7'
}
]
},
'configurations': {
'Release': {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'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/GNU.Gettext.dll',
'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/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/libiconv-2.dll',
'vendor/lib/libintl-8.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/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-6.dll',
'vendor/lib/libxml2-2.dll',
'vendor/lib/zlib1.dll'
]
}]
}]
]
}]
}

129
binding.js Normal file
View File

@@ -0,0 +1,129 @@
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const zlib = require('zlib');
const caw = require('caw');
const got = require('got');
const semver = require('semver');
const tar = require('tar');
const distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
// 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 platform = process.env.npm_config_platform || process.platform;
const arch = process.env.npm_config_arch || process.arch;
// -- Helpers
// Does this file exist?
const isFile = function (file) {
try {
return fs.statSync(file).isFile();
} catch (err) {}
};
const unpack = function (tarPath, done) {
const extractor = tar.Extract({ path: path.join(__dirname, 'vendor') });
if (done) {
extractor.on('end', done);
}
extractor.on('error', error);
fs.createReadStream(tarPath)
.on('error', error)
.pipe(zlib.Unzip())
.pipe(extractor);
};
const platformId = function () {
const platformId = [platform];
if (arch === 'arm' || arch === 'armhf' || arch === 'arch64') {
const armVersion = (arch === 'arch64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
platformId.push('armv' + armVersion);
} else {
platformId.push(arch);
}
return platformId.join('-');
};
// Error
const error = function (msg) {
if (msg instanceof Error) {
msg = msg.message;
}
process.stderr.write('ERROR: ' + msg + '\n');
process.exit(1);
};
// -- Binary downloaders
module.exports.download_vips = function () {
// Has vips been installed locally?
const vipsHeaderPath = path.join(__dirname, 'vendor', 'include', 'vips', 'vips.h');
if (!isFile(vipsHeaderPath)) {
// Ensure Intel 64-bit or ARM
if (arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
// Ensure glibc >= 2.15
const lddVersion = process.env.LDD_VERSION;
if (lddVersion) {
if (/(glibc|gnu libc)/i.test(lddVersion)) {
const glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) {
error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
} else {
error(lddVersion.split(/\n/)[0] + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
}
// Arch/platform-specific .tar.gz
const tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz';
const tarPathLocal = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPathLocal)) {
unpack(tarPathLocal);
} else {
// Download to per-process temporary file
const tarPathTemp = path.join(os.tmpdir(), process.pid + '-' + tarFilename);
const tmpFile = fs.createWriteStream(tarPathTemp).on('finish', function () {
unpack(tarPathTemp, function () {
// Attempt to remove temporary file
try {
fs.unlinkSync(tarPathTemp);
} catch (err) {}
});
});
const gotOpt = {
agent: caw(null, {
protocol: 'https'
})
};
const url = distBaseUrl + tarFilename;
got.stream(url, gotOpt).on('response', function (response) {
if (response.statusCode !== 200) {
error(url + ' status code ' + response.statusCode);
}
}).on('error', function (err) {
error('Download of ' + url + ' failed: ' + err.message);
}).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');
}
};

8
circle.yml Normal file
View File

@@ -0,0 +1,8 @@
machine:
node:
version: v4.6.1
services:
- docker
test:
override:
- ./packaging/test-linux-x64.sh

77
docs/api-channel.md Normal file
View File

@@ -0,0 +1,77 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [extractChannel](#extractchannel)
- [joinChannel](#joinchannel)
- [bandbool](#bandbool)
## extractChannel
Extract a single channel from a multi-channel image.
**Parameters**
- `channel` **([Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) \| [String](https://developer.mozilla.org/en-US/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.
**Examples**
```javascript
sharp(input)
.extractChannel('green')
.toFile('input_green.jpg', function(err, info) {
// info.channels === 1
// input_green.jpg contains the green channel of the input image
});
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid channel
Returns **Sharp**
## joinChannel
Join one or more channels to the image.
The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
Channel ordering follows vips convention:
- sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
- CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
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**
- `images` **([Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Buffer](https://nodejs.org/api/buffer.html))> | [String](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** image options, see `sharp()` constructor.
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## bandbool
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
**Parameters**
- `boolOp` **[String](https://developer.mozilla.org/en-US/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.
**Examples**
```javascript
sharp('3-channel-rgb-input.png')
.bandbool(sharp.bool.and)
.toFile('1-channel-output.png', function (err, info) {
// The output will be a single channel image where each pixel `P = R & G & B`.
// If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
// then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
});
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**

79
docs/api-colour.md Normal file
View File

@@ -0,0 +1,79 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [background](#background)
- [greyscale](#greyscale)
- [grayscale](#grayscale)
- [toColourspace](#tocolourspace)
- [toColorspace](#tocolorspace)
## background
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/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/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.
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameter
Returns **Sharp**
## greyscale
Convert to 8-bit greyscale; 256 shades of grey.
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
By default the output image will be web-friendly sRGB and contain three (identical) color channels.
This may be overridden by other sharp operations such as `toColourspace('b-w')`,
which will produce an output image containing one color channel.
An alpha channel may be present, and will be unchanged by the operation.
**Parameters**
- `greyscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## grayscale
Alternative spelling of `greyscale`.
**Parameters**
- `grayscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## toColourspace
Set the output colourspace.
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
**Parameters**
- `colourspace` **[String](https://developer.mozilla.org/en-US/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)
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## toColorspace
Alternative spelling of `toColourspace`.
**Parameters**
- `colorspace` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** output colorspace.
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**

57
docs/api-composite.md Normal file
View File

@@ -0,0 +1,57 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [overlayWith](#overlaywith)
## 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.
If both `top` and `left` options are provided, they take precedence over `gravity`.
**Parameters**
- `overlay` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.gravity` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** gravity at which to place the overlay. (optional, default `'centre'`)
- `options.top` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the top edge.
- `options.left` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the left edge.
- `options.tile` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes overlay when using raw pixel data.
- `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a blank overlay to be created.
- `options.create.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
- `options.create.background` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/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.
**Examples**
```javascript
sharp('input.png')
.rotate(180)
.resize(300)
.flatten()
.background('#ff6600')
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
.sharpen()
.withMetadata()
.quality(90)
.webp()
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**

108
docs/api-constructor.md Normal file
View File

@@ -0,0 +1,108 @@
<!-- 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**
- `input` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))?** 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.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined.
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** if present, is an Object with optional attributes.
- `options.density` **[Number](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 1-4
- `options.create` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a new image to be created.
- `options.create.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
- `options.create.background` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/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.
**Examples**
```javascript
sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg', function(err) {
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
```
```javascript
// Read image data from readableStream,
// resize to 300 pixels wide,
// emit an 'info' event with calculated dimensions
// and finally write image data to writableStream
var transformer = sharp()
.resize(300)
.on('info', function(info) {
console.log('Image height is ' + info.height);
});
readableStream.pipe(transformer).pipe(writableStream);
```
```javascript
// Create a blank 300x200 PNG image of semi-transluent red pixels
sharp(null, {
create: {
width: 300,
height: 200,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 }
}
})
.png()
.toBuffer()
.then( ... );
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **[Sharp](#sharp)**
### format
An Object containing nested boolean values representing the available input and output formats/methods.
**Examples**
```javascript
console.log(sharp.format());
```
Returns **[Object](https://developer.mozilla.org/en-US/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)');
});
```

93
docs/api-input.md Normal file
View File

@@ -0,0 +1,93 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [clone](#clone)
- [metadata](#metadata)
- [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 image metadata without decoding any compressed image data.
A Promises/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#L568)
- `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
- `density`: Number of pixels per inch (DPI), if present
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
- `orientation`: Number value of the EXIF Orientation header, if present
- `exif`: Buffer containing raw EXIF data, if present
- `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
**Parameters**
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called with the arguments `(err, metadata)`
**Examples**
```javascript
const image = sharp(inputJpg);
image
.metadata()
.then(function(metadata) {
return image
.resize(Math.round(metadata.width / 2))
.webp()
.toBuffer();
})
.then(function(data) {
// data contains a WebP image half the width and height of the original JPEG
});
```
Returns **([Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | Sharp)**
## limitInputPixels
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.
**Parameters**
- `limit` **([Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) \| [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** an integral Number of pixels, zero or false to remove limit, true to use default limit.
- Throws **[Error](https://developer.mozilla.org/en-US/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.
**Parameters**
- `sequentialRead` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**

320
docs/api-operation.md Normal file
View File

@@ -0,0 +1,320 @@
<!-- 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.
Use this method without angle to determine the angle from EXIF data.
Mirroring is supported and may infer the use of a flip operation.
The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
Method order is important when both rotating and extracting regions,
for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
**Parameters**
- `angle` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 0, 90, 180 or 270. (optional, default `auto`)
**Examples**
```javascript
const pipeline = sharp()
.rotate()
.resize(null, 200)
.toBuffer(function (err, outputBuffer, info) {
// outputBuffer contains 200px high JPEG image data,
// auto-rotated using EXIF Orientation tag
// info.width and info.height contain the dimensions of the resized image
});
readableStream.pipe(pipeline);
```
- Throws **[Error](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `options.left` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** zero-indexed offset from left edge
- `options.top` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** zero-indexed offset from top edge
- `options.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** dimension of extracted image
- `options.height` **[Number](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## flip
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**
- `flip` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## flop
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**
- `flop` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## sharpen
Sharpen the image.
When used without parameters, performs a fast, mild sharpen of the output image.
When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
Separate control over the level of sharpening in "flat" and "jagged" areas is available.
**Parameters**
- `sigma` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `flat` **[Number](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## blur
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**
- `sigma` **[Number](https://developer.mozilla.org/en-US/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`.
- Throws **[Error](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) \| [Object](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `extend.left` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `extend.bottom` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `extend.right` **[Number](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## flatten
Merge alpha transparency channel, if any, with `background`.
**Parameters**
- `flatten` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## gamma
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
then increasing the encoding (brighten) post-resize at a factor of `gamma`.
This can improve the perceived brightness of a resized image in non-linear colour spaces.
JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
when applying a gamma correction.
**Parameters**
- `gamma` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** value between 1.0 and 3.0. (optional, default `2.2`)
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## negate
Produce the "negative" of the image.
**Parameters**
- `negate` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## normalise
Enhance output image contrast by stretching its luminance to cover the full dynamic range.
**Parameters**
- `normalise` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## normalize
Alternative spelling of normalise.
**Parameters**
- `normalize` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**
## convolve
Convolve the image with the specified kernel.
**Parameters**
- `kernel` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `kernel.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels.
- `kernel.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels.
- `kernel.kernel` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the scale of the kernel in pixels. (optional, default `sum`)
- `kernel.offset` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the offset of the kernel in pixels. (optional, default `0`)
**Examples**
```javascript
sharp(input)
.convolve({
width: 3,
height: 3,
kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1]
})
.raw()
.toBuffer(function(err, data, info) {
// data contains the raw pixel data representing the convolution
// of the input image with the horizontal Sobel operator
});
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## threshold
Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
**Parameters**
- `threshold` **[Number](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.greyscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** alternative spelling for greyscale. (optional, default `true`)
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## boolean
Perform a bitwise boolean operation with operand image.
This operation creates an output image where each pixel is the result of
the selected bitwise boolean `operation` between the corresponding pixels of the input images.
**Parameters**
- `operand` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes operand when using raw pixel data.
- `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**

197
docs/api-output.md Normal file
View File

@@ -0,0 +1,197 @@
<!-- 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.
If an explicit output format is not selected, it will be inferred from the extension,
with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
Note that raw pixel data is only supported for buffer output.
A Promises/A+ promise is returned when `callback` is not provided.
**Parameters**
- `fileOut` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to write the image data to.
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called on completion with two arguments `(err, info)`.
`info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)>** when no callback is provided
## toBuffer
Write output to a Buffer.
JPEG, PNG, WebP, and RAW output are supported.
By default, the format will match the input image, except GIF and SVG input which become PNG output.
`callback`, if present, gets three arguments `(err, data, info)` where:
- `err` is an error, if any.
- `data` is the output image data.
- `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
A Promise is returned when `callback` is not provided.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.resolveWithObject` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Statements/function)?**
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Buffer](https://nodejs.org/api/buffer.html)>** 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**
- `withMetadata` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `withMetadata.orientation` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** value between 1 and 8, used to update the EXIF `Orientation` tag.
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**
## jpeg
Use these JPEG options for output image.
**Parameters**
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`)
- `options.progressive` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use progressive (interlace) scan (optional, default `false`)
- `options.chromaSubsampling` **[String](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** apply trellis quantisation, requires mozjpeg (optional, default `false`)
- `options.overshootDeringing` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** apply overshoot deringing, requires mozjpeg (optional, default `false`)
- `options.optimiseScans` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`)
- `options.optimizeScans` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** alternative spelling of optimiseScans (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.progressive` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** zlib compression level (optional, default `6`)
- `options.adaptiveFiltering` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use adaptive row filtering (optional, default `true`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`)
- `options.alphaQuality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality of alpha layer, integer 0-100 (optional, default `100`)
- `options.lossless` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use lossless compression mode (optional, default `false`)
- `options.nearLossless` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use near_lossless compression mode (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** compression options: lzw, deflate, jpeg (optional, default `'jpeg'`)
- `options.predictor` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** compression predictor options: none, horizontal, float (optional, default `'none'`)
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options
Returns **Sharp**
## raw
Force output to be raw, uncompressed uint8 pixel data.
Returns **Sharp**
## toFormat
Force output to a given format.
**Parameters**
- `format` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** as a String or an Object with an 'id' attribute
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** output options
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** unsupported format or options
Returns **Sharp**
## tile
Use tile-based deep zoom (image pyramid) output.
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
**Parameters**
- `tile` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `tile.size` **[Number](https://developer.mozilla.org/en-US/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/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
**Examples**
```javascript
sharp('input.tiff')
.png()
.tile({
size: 512
})
.toFile('output.dz', function(err, info) {
// output.dzi is the Deep Zoom XML definition
// output_files contains 512x512 tiles grouped by zoom level
});
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **Sharp**

177
docs/api-resize.md Normal file
View File

@@ -0,0 +1,177 @@
<!-- 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.
Possible reduction 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).
- `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).
Possible enlargement interpolators are:
- `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
- `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), faster than bicubic but with less smooth results.
- `vertexSplitQuadraticBasisSpline`: Use the smoother [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48) to prevent "staircasing" when enlarging.
- `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default).
- `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2.
- `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance but typically reduces performance by a factor of 3.
**Parameters**
- `width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels wide the resultant image should be, between 1 and 16383 (0x3FFF). Use `null` or `undefined` to auto-scale the width to match the height.
- `height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width.
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.kernel` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** the kernel to use for image reduction. (optional, default `'lanczos3'`)
- `options.interpolator` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** the interpolator to use for image enlargement. (optional, default `'bicubic'`)
- `options.centreSampling` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use \*magick centre sampling convention instead of corner sampling. (optional, default `false`)
- `options.centerSampling` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** alternative spelling of centreSampling. (optional, default `false`)
**Examples**
```javascript
sharp(inputBuffer)
.resize(200, 300, {
kernel: sharp.kernel.lanczos2,
interpolator: sharp.interpolator.nohalo
})
.background('white')
.embed()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a lanczos2/nohalo scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
- Throws **[Error](https://developer.mozilla.org/en-US/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`.
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.
**Parameters**
- `crop` **[String](https://developer.mozilla.org/en-US/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'`)
**Examples**
```javascript
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);
```
- Throws **[Error](https://developer.mozilla.org/en-US/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.
**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
});
```
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()
.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**
## 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`.
Returns **Sharp**
## ignoreAspectRatio
Ignoring the aspect ratio of the input, stretch the image to
the exact `width` and/or `height` provided via `resize`.
Returns **Sharp**
## withoutEnlargement
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_".
**Parameters**
- `withoutEnlargement` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`)
Returns **Sharp**

106
docs/api-utility.md Normal file
View File

@@ -0,0 +1,106 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [cache](#cache)
- [concurrency](#concurrency)
- [counters](#counters)
- [simd](#simd)
## cache
Gets, or when options are provided sets, the limits of _libvips'_ operation cache.
Existing entries in the cache will be trimmed after any change in limits.
This method always returns cache statistics,
useful for determining how much working memory is required for a particular task.
**Parameters**
- `options` **([Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) \| [Boolean](https://developer.mozilla.org/en-US/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.
- `options.memory` **[Number](https://developer.mozilla.org/en-US/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/en-US/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/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** is the maximum number of operations to cache (optional, default `100`)
**Examples**
```javascript
const stats = sharp.cache();
```
```javascript
sharp.cache( { items: 200 } );
sharp.cache( { files: 0 } );
sharp.cache(false);
```
Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
## concurrency
Gets, or when a concurrency is provided sets,
the number of threads _libvips'_ should create to process each image.
The default value is the number of CPU cores.
A value of `0` will reset to this default.
The maximum number of images that can be processed in parallel
is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
This method always returns the current concurrency.
**Parameters**
- `concurrency` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
**Examples**
```javascript
const threads = sharp.concurrency(); // 4
sharp.concurrency(2); // 2
sharp.concurrency(0); // 4
```
Returns **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** concurrency
## counters
Provides access to internal task counters.
- queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
- process is the number of resize tasks currently being processed.
**Examples**
```javascript
const counters = sharp.counters(); // { queue: 2, process: 4 }
```
Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
## simd
Get and set use of SIMD vector unit instructions.
Requires libvips to have been compiled with liborc support.
Improves the performance of `resize`, `blur` and `sharpen` operations
by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
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**
- `simd` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `false`)
**Examples**
```javascript
const simd = sharp.simd();
// simd is `true` if SIMD is currently enabled
```
```javascript
const simd = sharp.simd(true);
// attempts to enable the use of SIMD, returning true if available
```
Returns **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**

531
docs/changelog.md Normal file
View File

@@ -0,0 +1,531 @@
# Changelog
### v0.17 - "*quill*"
Requires libvips v8.4.2.
#### 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)
[@salzhrani](https://github.com/salzhrani)
* Create blank image of given width, height, channels and background.
[#470](https://github.com/lovell/sharp/issues/470)
[@pjarts](https://github.com/pjarts)
* Add support for the "nearest" kernel for image reductions.
[#732](https://github.com/lovell/sharp/pull/732)
[@alice0meta](https://github.com/alice0meta)
* Add support for TIFF compression and predictor options.
[#738](https://github.com/lovell/sharp/pull/738)
[@kristojorg](https://github.com/kristojorg)
#### 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)
[@danhaller](https://github.com/danhaller)
* Expose WebP alpha quality, lossless and near-lossless output options.
[#685](https://github.com/lovell/sharp/pull/685)
[@rnanwani](https://github.com/rnanwani)
#### v0.17.1 - 15<sup>th</sup> January 2017
* Improve error messages for invalid parameters.
[@spikeon](https://github.com/spikeon)
[#644](https://github.com/lovell/sharp/pull/644)
* Simplify expression for finding vips-cpp libdir.
[#656](https://github.com/lovell/sharp/pull/656)
* Allow HTTPS-over-HTTP proxy when downloading pre-compiled dependencies.
[@wangzhiwei1888](https://github.com/wangzhiwei1888)
[#679](https://github.com/lovell/sharp/issues/679)
#### v0.17.0 - 11<sup>th</sup> December 2016
* Drop support for versions of Node prior to v4.
* Deprecate the following output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
withoutChromaSubsampling, trellisQuantisation, trellisQuantization,
overshootDeringing, optimiseScans and optimizeScans.
Access to these is now via output format functions, for example `quality(n)`
is now `jpeg({quality: n})` and/or `webp({quality: n})`.
* Autoconvert GIF and SVG input to PNG output if no other format is specified.
* Expose libvips' "centre" resize option to mimic \*magick's +0.5px convention.
[#568](https://github.com/lovell/sharp/issues/568)
* Ensure support for embedded base64 PNG and JPEG images within an SVG.
[#601](https://github.com/lovell/sharp/issues/601)
[@dynamite-ready](https://github.com/dynamite-ready)
* Ensure premultiply operation occurs before box filter shrink.
[#605](https://github.com/lovell/sharp/issues/605)
[@CmdrShepardsPie](https://github.com/CmdrShepardsPie)
[@teroparvinen](https://github.com/teroparvinen)
* Add support for PNG and WebP tile-based output formats (in addition to JPEG).
[#622](https://github.com/lovell/sharp/pull/622)
[@ppaskaris](https://github.com/ppaskaris)
* Allow use of extend with greyscale input.
[#623](https://github.com/lovell/sharp/pull/623)
[@ppaskaris](https://github.com/ppaskaris)
* Allow non-RGB input to embed/extend onto background with an alpha channel.
[#646](https://github.com/lovell/sharp/issues/646)
[@DaGaMs](https://github.com/DaGaMs)
### v0.16 - "*pencil*"
Requires libvips v8.3.3
#### 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
* C++11 ABI version is now auto-detected, remove sharp-cxx11 installation flag.
* Add experimental 'attention' crop strategy.
[#295](https://github.com/lovell/sharp/issues/295)
* Include .node extension for Meteor's require() implementation.
[#537](https://github.com/lovell/sharp/issues/537)
[@isjackwild](https://github.com/isjackwild)
* Ensure convolution kernel scale is clamped to a minimum value of 1.
[#561](https://github.com/lovell/sharp/issues/561)
[@abagshaw](https://github.com/abagshaw)
* Correct calculation of y-axis placement when overlaying image at a fixed point.
[#566](https://github.com/lovell/sharp/issues/566)
[@Nateowami](https://github.com/Nateowami)
#### 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)
* Ensure boolean, bandbool, extractChannel ops occur before sRGB conversion.
[#504](https://github.com/lovell/sharp/pull/504)
[@mhirsch](https://github.com/mhirsch)
* Recalculate factors after WebP shrink-on-load to avoid round-to-zero errors.
[#508](https://github.com/lovell/sharp/issues/508)
[@asilvas](https://github.com/asilvas)
* Prevent boolean errors during extract operation.
[#511](https://github.com/lovell/sharp/pull/511)
[@mhirsch](https://github.com/mhirsch)
* Add joinChannel and toColourspace/toColorspace operations.
[#513](https://github.com/lovell/sharp/pull/513)
[@mhirsch](https://github.com/mhirsch)
* Add support for raw pixel data with boolean and withOverlay operations.
[#516](https://github.com/lovell/sharp/pull/516)
[@mhirsch](https://github.com/mhirsch)
* Prevent bandbool creating a single channel sRGB image.
[#519](https://github.com/lovell/sharp/pull/519)
[@mhirsch](https://github.com/mhirsch)
* Ensure ICC profiles are removed from PNG output unless withMetadata used.
[#521](https://github.com/lovell/sharp/issues/521)
[@ChrisPinewood](https://github.com/ChrisPinewood)
* Add alpha channels, if missing, to overlayWith images.
[#540](https://github.com/lovell/sharp/pull/540)
[@cmtt](https://github.com/cmtt)
* Remove deprecated interpolateWith method - use resize(w, h, { interpolator: ... })
[#310](https://github.com/lovell/sharp/issues/310)
### v0.15 - "*outfit*"
Requires libvips v8.3.1
#### 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)
[@papandreou](https://github.com/papandreou)
* Add alpha channel, if required, before extend operation.
[#439](https://github.com/lovell/sharp/pull/439)
[@frulo](https://github.com/frulo)
* Allow overlay image to be repeated across entire image via tile option.
[#443](https://github.com/lovell/sharp/pull/443)
[@lemnisk8](https://github.com/lemnisk8)
* Add cutout option to overlayWith feature, applies only the alpha channel of the overlay image.
[#448](https://github.com/lovell/sharp/pull/448)
[@kleisauke](https://github.com/kleisauke)
* Ensure scaling factors are calculated independently to prevent rounding errors.
[#452](https://github.com/lovell/sharp/issues/452)
[@puzrin](https://github.com/puzrin)
* Add --sharp-cxx11 flag to compile with gcc's new C++11 ABI.
[#456](https://github.com/lovell/sharp/pull/456)
[@kapouer](https://github.com/kapouer)
* Add top/left offset support to overlayWith operation.
[#473](https://github.com/lovell/sharp/pull/473)
[@rnanwani](https://github.com/rnanwani)
* Add convolve operation for kernel-based convolution.
[#479](https://github.com/lovell/sharp/pull/479)
[@mhirsch](https://github.com/mhirsch)
* Add greyscale option to threshold operation for colourspace conversion control.
[#480](https://github.com/lovell/sharp/pull/480)
[@mhirsch](https://github.com/mhirsch)
* Ensure ICC profiles are licenced for distribution.
[#486](https://github.com/lovell/sharp/issues/486)
[@kapouer](https://github.com/kapouer)
* Allow images with an alpha channel to work with LAB-colourspace based sharpen.
[#490](https://github.com/lovell/sharp/issues/490)
[@jwagner](https://github.com/jwagner)
* Add trim operation to remove "boring" edges.
[#492](https://github.com/lovell/sharp/pull/492)
[@kleisauke](https://github.com/kleisauke)
* Add bandbool feature for channel-wise boolean operations.
[#496](https://github.com/lovell/sharp/pull/496)
[@mhirsch](https://github.com/mhirsch)
* Add extractChannel operation to extract a channel from an image.
[#497](https://github.com/lovell/sharp/pull/497)
[@mhirsch](https://github.com/mhirsch)
* Add ability to read and write native libvips .v files.
[#500](https://github.com/lovell/sharp/pull/500)
[@mhirsch](https://github.com/mhirsch)
* Add boolean feature for bitwise image operations.
[#501](https://github.com/lovell/sharp/pull/501)
[@mhirsch](https://github.com/mhirsch)
#### 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.
[#310](https://github.com/lovell/sharp/issues/310)
[@jcupitt](https://github.com/jcupitt)
* Take advantage of libvips v8.3 features.
Add support for libvips' new GIF and SVG loaders.
Pre-built binaries now include giflib and librsvg, exclude *magick.
Use shrink-on-load for WebP input.
Break existing sharpen API to accept sigma and improve precision.
[#369](https://github.com/lovell/sharp/issues/369)
* Remove unnecessary (un)premultiply operations when not resizing/compositing.
[#413](https://github.com/lovell/sharp/issues/413)
[@jardakotesovec](https://github.com/jardakotesovec)
### v0.14 - "*needle*"
Requires libvips v8.2.3
#### 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)
[#316](https://github.com/lovell/sharp/pull/316)
[@anandthakker](https://github.com/anandthakker)
[@kentongray](https://github.com/kentongray)
* Use final output image for metadata passed to callback.
[#399](https://github.com/lovell/sharp/pull/399)
[@salzhrani](https://github.com/salzhrani)
* Add support for writing tiled images to a zip container.
[#402](https://github.com/lovell/sharp/pull/402)
[@felixbuenemann](https://github.com/felixbuenemann)
* Allow use of embed with 1 and 2 channel images.
[#411](https://github.com/lovell/sharp/issues/411)
[@janaz](https://github.com/janaz)
* Improve Electron compatibility by allowing node-gyp rebuilds without npm.
[#412](https://github.com/lovell/sharp/issues/412)
[@nouh](https://github.com/nouh)
#### 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)
[@blowsie](https://github.com/blowsie)
* Add support for Zoomify and Google tile layouts. Breaks existing tile API.
[#223](https://github.com/lovell/sharp/issues/223)
[@bdunnette](https://github.com/bdunnette)
* Improvements to overlayWith: differing sizes/formats, gravity, buffer input.
[#239](https://github.com/lovell/sharp/issues/239)
[@chrisriley](https://github.com/chrisriley)
* Add entropy-based crop strategy to remove least interesting edges.
[#295](https://github.com/lovell/sharp/issues/295)
[@rightaway](https://github.com/rightaway)
* Expose density metadata; set density of images from vector input.
[#338](https://github.com/lovell/sharp/issues/338)
[@lookfirst](https://github.com/lookfirst)
* Emit post-processing 'info' event for Stream output.
[#367](https://github.com/lovell/sharp/issues/367)
[@salzhrani](https://github.com/salzhrani)
* Ensure output image EXIF Orientation values are within 1-8 range.
[#385](https://github.com/lovell/sharp/pull/385)
[@jtobinisaniceguy](https://github.com/jtobinisaniceguy)
* Ensure ratios are not swapped when rotating 90/270 and ignoring aspect.
[#387](https://github.com/lovell/sharp/issues/387)
[@kleisauke](https://github.com/kleisauke)
* Remove deprecated style of calling extract API. Breaks calls using positional arguments.
[#276](https://github.com/lovell/sharp/issues/276)
### v0.13 - "*mind*"
Requires libvips v8.2.2
#### 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
* Improve vector image support by allowing control of density/DPI.
Switch pre-built libs from Imagemagick to Graphicsmagick.
[#110](https://github.com/lovell/sharp/issues/110)
[@bradisbell](https://github.com/bradisbell)
* Add support for raw, uncompressed pixel Buffer/Stream input.
[#220](https://github.com/lovell/sharp/issues/220)
[@mikemorris](https://github.com/mikemorris)
* Switch from libvips' C to C++ bindings, requires upgrade to v8.2.2.
[#299](https://github.com/lovell/sharp/issues/299)
* Control number of open files in libvips' cache; breaks existing `cache` behaviour.
[#315](https://github.com/lovell/sharp/issues/315)
[@impomezia](https://github.com/impomezia)
* Ensure 16-bit input images can be normalised and embedded onto transparent backgrounds.
[#339](https://github.com/lovell/sharp/issues/339)
[#340](https://github.com/lovell/sharp/issues/340)
[@janaz](https://github.com/janaz)
* Ensure selected format takes precedence over any unknown output filename extension.
[#344](https://github.com/lovell/sharp/issues/344)
[@ubaltaci](https://github.com/ubaltaci)
* Add support for libvips' PBM, PGM, PPM and FITS image format loaders.
[#347](https://github.com/lovell/sharp/issues/347)
[@oaleynik](https://github.com/oaleynik)
* Ensure default crop gravity is center/centre.
[#351](https://github.com/lovell/sharp/pull/351)
[@joelmukuthu](https://github.com/joelmukuthu)
* Improve support for musl libc systems e.g. Alpine Linux.
[#354](https://github.com/lovell/sharp/issues/354)
[#359](https://github.com/lovell/sharp/pull/359)
[@download13](https://github.com/download13)
[@wjordan](https://github.com/wjordan)
* Small optimisation when reducing by an integral factor to favour shrink over affine.
* Add support for gamma correction of images with an alpha channel.
### v0.12 - "*look*"
Requires libvips v8.2.0
#### v0.12.2 - 16<sup>th</sup> January 2016
* Upgrade libvips to v8.2.0 for improved vips_shrink.
* Add pre-compiled libvips for ARMv6+ CPUs.
* Ensure 16-bit input images work with embed option.
[#325](https://github.com/lovell/sharp/issues/325)
[@janaz](https://github.com/janaz)
* Allow compilation with gmake to provide FreeBSD support.
[#326](https://github.com/lovell/sharp/issues/326)
[@c0decafe](https://github.com/c0decafe)
* Attempt to remove temporary file after installation.
[#331](https://github.com/lovell/sharp/issues/331)
[@dtoubelis](https://github.com/dtoubelis)
#### 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)
[@bkw](https://github.com/bkw)
[@puzrin](https://github.com/puzrin)
* Ensure embedded ICC profiles output with perceptual intent.
[#321](https://github.com/lovell/sharp/issues/321)
[@vlapo](https://github.com/vlapo)
* Use the NPM-configured HTTPS proxy, if any, for binary downloads.
#### 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)
* Take advantage of libvips v8.1.0+ features.
[#152](https://github.com/lovell/sharp/issues/152)
* Add support for 64-bit Windows. Drop support for 32-bit Windows.
[#224](https://github.com/lovell/sharp/issues/224)
[@sabrehagen](https://github.com/sabrehagen)
* Switch default interpolator to bicubic.
[#289](https://github.com/lovell/sharp/issues/289)
[@mahnunchik](https://github.com/mahnunchik)
* Pre-extract rotatation should not swap width/height.
[#296](https://github.com/lovell/sharp/issues/296)
[@asilvas](https://github.com/asilvas)
* Ensure 16-bit+alpha input images are (un)premultiplied correctly.
[#301](https://github.com/lovell/sharp/issues/301)
[@izaakschroeder](https://github.com/izaakschroeder)
* Add `threshold` operation.
[#303](https://github.com/lovell/sharp/pull/303)
[@dacarley](https://github.com/dacarley)
* Add `negate` operation.
[#306](https://github.com/lovell/sharp/pull/306)
[@dacarley](https://github.com/dacarley)
* Support `options` Object with existing `extract` operation.
[#309](https://github.com/lovell/sharp/pull/309)
[@papandreou](https://github.com/papandreou)
### v0.11 - "*knife*"
#### 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)
[@brandonaaron](https://github.com/brandonaaron)
* Ensure correct auto-rotation for EXIF Orientation values 2 and 4.
[#288](https://github.com/lovell/sharp/pull/288)
[@brandonaaron](https://github.com/brandonaaron)
* Make static linking possible via `--runtime_link` install option.
[#287](https://github.com/lovell/sharp/pull/287)
[@vlapo](https://github.com/vlapo)
#### 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
* Allow crop gravity to be provided as a String.
[#255](https://github.com/lovell/sharp/pull/255)
[@papandreou](https://github.com/papandreou)
* 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
* Silence MSVC warning: "C4530: C++ exception handler used, but unwind semantics are not enabled".
[#244](https://github.com/lovell/sharp/pull/244)
[@TheThing](https://github.com/TheThing)
* Suppress gamma correction for input image with alpha transparency.
[#249](https://github.com/lovell/sharp/issues/249)
[@compeak](https://github.com/compeak)
#### 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)
[@gasi](https://github.com/gasi)
* Expose raw ICC profile data as a Buffer when using `metadata`.
[#129](https://github.com/lovell/sharp/issues/129)
[@homerjam](https://github.com/homerjam)
* Allow image header updates via a parameter passed to existing `withMetadata` method.
Provide initial support for EXIF `Orientation` tag,
which if present is now removed when using `rotate`, `flip` or `flop`.
[#189](https://github.com/lovell/sharp/issues/189)
[@h2non](https://github.com/h2non)
* Tighten constructor parameter checks.
[#221](https://github.com/lovell/sharp/issues/221)
[@mikemorris](https://github.com/mikemorris)
* Allow one input Stream to be shared with two or more output Streams via new `clone` method.
[#235](https://github.com/lovell/sharp/issues/235)
[@jaubourg](https://github.com/jaubourg)
* Use `round` instead of `floor` when auto-scaling dimensions to avoid floating-point rounding errors.
[#238](https://github.com/lovell/sharp/issues/238)
[@richardadjogah](https://github.com/richardadjogah)
### v0.10 - "*judgment*"
#### 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)
[@mikemliu](https://github.com/mikemliu)
* Include C standard library for `atoi` as Xcode 6.3 appears to no longer do this.
[#228](https://github.com/lovell/sharp/issues/228)
[@doggan](https://github.com/doggan)
#### v0.10.0 - 23<sup>rd</sup> April 2015
* Add support for Windows (x86).
[#19](https://github.com/lovell/sharp/issues/19)
[@DullReferenceException](https://github.com/DullReferenceException)
[@itsananderson](https://github.com/itsananderson)
* Add support for Openslide input and DeepZoom output.
[#146](https://github.com/lovell/sharp/issues/146)
[@mvictoras](https://github.com/mvictoras)
* Allow arbitrary aspect ratios when resizing images via new `ignoreAspectRatio` method.
[#192](https://github.com/lovell/sharp/issues/192)
[@skedastik](https://github.com/skedastik)
* Enhance output image contrast by stretching its luminance to cover the full dynamic range via new `normalize` method.
[#194](https://github.com/lovell/sharp/issues/194)
[@bkw](https://github.com/bkw)
[@codingforce](https://github.com/codingforce)

118
docs/index.md Normal file
View File

@@ -0,0 +1,118 @@
# 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 and WebP 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 can be disabled,
which for diagrams and line art often produces the same result
as [pngcrush](http://pmt.sourceforge.net/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)
Thank you!
### Licence
Copyright 2013, 2014, 2015, 2016, 2017 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.

221
docs/install.md Normal file
View File

@@ -0,0 +1,221 @@
# Installation
```sh
npm install sharp
```
```sh
yarn add sharp
```
### Prerequisites
* Node v4+
* 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)
### Linux
[![Ubuntu 14.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
[![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/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 6.5MB.
Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.:
* Debian 7, 8
* Ubuntu 12.04, 14.04, 16.04
* Centos 7
* Fedora
* openSUSE 13.2
* Archlinux
* Raspbian Jessie
* Amazon Linux 2016.03, 2016.09
To use a 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.
This allows the use of newer versions of libvips with older versions of sharp.
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:
https://github.com/jcupitt/libvips#building-libvips-from-a-source-tarball
For Linux-based operating systems such as Alpine that use musl libc,
the smaller stack size means libvips' cache should be disabled
via `sharp.cache(false)` to avoid a stack overflow.
### Mac OS
[![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/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 6.3MB.
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 9MB.
Only 64-bit (x64) `node.exe` is supported.
### FreeBSD
libvips must be installed before `npm install` is run.
This can be achieved via [FreshPorts](https://www.freshports.org/graphics/vips/):
```sh
cd /usr/ports/graphics/vips/ && make install clean
```
### Heroku
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 6.5MB.
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager.
### Docker
[Marc Bachmann](https://github.com/marcbachmann) maintains an
[Ubuntu-based Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
```sh
docker pull marcbachmann/libvips
```
[Will Jordan](https://github.com/wjordan) maintains an
[Alpine-based Dockerfile for libvips](https://github.com/wjordan/dockerfile-libvips).
```sh
docker pull wjordan/libvips
```
### AWS Lambda
In order to use sharp on AWS Lambda, you need to [create a deployment package](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html). Because sharp
downloads and links libraries for the current platform during `npm install` you have to
do this on a system similar to the [Lambda Execution Environment](http://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html). The easiest ways to do this, is to setup a
small t2.micro instance using the AMI ID listed in the previous link, ssh into it as ec2-user
and follow the instructions below.
Install dependencies:
```sh
curl -s https://rpm.nodesource.com/setup_4.x | sudo bash -
sudo yum install -y gcc-c++ nodejs
```
Copy your code and package.json to the instance using `scp` and create a deployment package:
```sh
cd sharp-lambda-example
npm install
zip -ur9 ../sharp-lambda-example.zip index.js node_modules
```
You can now download your deployment ZIP using `scp` and upload it to Lambda. Be sure to set your Lambda runtime to Node.js 4.3.
**Performance Tip:** To get the best performance on Lambda choose the largest memory available because this also gives you the most cpu time (a 1536 MB function is 12x faster 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)
### 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.
### Licences
If a global installation of libvips that meets the
minimum version requirement cannot be found,
this module will 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://dl.bintray.com/lovell/sharp/
This module is licensed under the terms of the
[Apache 2.0 Licence](https://github.com/lovell/sharp/blob/master/LICENSE).
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.
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 |
| 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) |

73
docs/performance.md Normal file
View File

@@ -0,0 +1,73 @@
# Performance
### Test environment
* AWS EC2 eu-central-1 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @ 2.90GHz)
* Ubuntu 16.04.1 LTS (HVM, SSD, 20161115, ami-82cf0aed)
* Node.js v6.9.1
### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.27 - Image processing in pure JavaScript. Bilinear interpolation only.
* [lwip](https://www.npmjs.com/package/lwip) v0.0.9 - Wrapper around CImg. Compiles outdated, insecure dependencies from source.
* [mapnik](https://www.npmjs.org/package/mapnik) v3.5.14 - 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.
* [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.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.17.0 / libvips v8.4.2 - Caching within libvips disabled to ensure a fair comparison.
### The task
Decompress a 2725x2225 JPEG image,
resize to 720x588 using Lanczos 3 resampling (where available),
then compress to JPEG at a "quality" setting of 80.
### Results
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp (bilinear) | buffer | buffer | 1.06 | 1.0 |
| lwip | buffer | buffer | 1.87 | 1.8 |
| mapnik | buffer | buffer | 2.91 | 2.7 |
| imagemagick-native | buffer | buffer | 4.03 | 3.8 |
| imagemagick | file | file | 7.10 | 6.7 |
| gm | buffer | buffer | 7.08 | 6.7 |
| gm | file | file | 7.10 | 6.7 |
| sharp | stream | stream | 27.61 | 26.0 |
| sharp | file | file | 28.41 | 26.8 |
| sharp | buffer | file | 28.71 | 27.1 |
| sharp | file | buffer | 28.60 | 27.0 |
| sharp | buffer | buffer | 29.08 | 27.4 |
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
Requires both _ImageMagick_ and _GraphicsMagick_:
```sh
brew install imagemagick
brew install graphicsmagick
```
```sh
sudo apt-get install imagemagick libmagick++-dev graphicsmagick
```
```sh
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick
```
### Running the benchmark test
```sh
git clone https://github.com/lovell/sharp.git
cd sharp
npm install
cd test/bench
npm install
npm test
```

Binary file not shown.

588
index.js
View File

@@ -1,588 +0,0 @@
'use strict';
var path = require('path');
var util = require('util');
var stream = require('stream');
var semver = require('semver');
var color = require('color');
var BluebirdPromise = require('bluebird');
var sharp = require('./build/Release/sharp');
var libvipsVersion = sharp.libvipsVersion();
var Sharp = function(input) {
if (!(this instanceof Sharp)) {
return new Sharp(input);
}
stream.Duplex.call(this);
this.options = {
// input options
streamIn: false,
sequentialRead: false,
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
widthPre: -1,
heightPre: -1,
topOffsetPost: -1,
leftOffsetPost: -1,
widthPost: -1,
heightPost: -1,
width: -1,
height: -1,
canvas: 'c',
gravity: 0,
angle: 0,
flip: false,
flop: false,
withoutEnlargement: false,
interpolator: 'bilinear',
// operations
background: [0, 0, 0, 255],
flatten: false,
blurSigma: 0,
sharpenRadius: 0,
sharpenFlat: 1,
sharpenJagged: 2,
gamma: 0,
greyscale: false,
// output options
output: '__input',
progressive: false,
quality: 80,
compressionLevel: 6,
withoutAdaptiveFiltering: false,
streamOut: false,
withMetadata: false
};
if (typeof input === 'string') {
// input=file
this.options.fileIn = input;
} else if (typeof input === 'object' && input instanceof Buffer) {
// input=buffer
if (
(input.length > 3) &&
// JPEG
(input[0] === 0xFF && input[1] === 0xD8) ||
// PNG
(input[0] === 0x89 && input[1] === 0x50) ||
// WebP
(input[0] === 0x52 && input[1] === 0x49) ||
// TIFF
(input[0] === 0x4D && input[1] === 0x4D && input[2] === 0x00 && (input[3] === 0x2A || input[3] === 0x2B)) ||
(input[0] === 0x49 && input[1] === 0x49 && (input[2] === 0x2A || input[2] === 0x2B) && input[3] === 0x00)
) {
this.options.bufferIn = input;
} else {
throw new Error('Buffer contains an unsupported image format. JPEG, PNG, WebP and TIFF are currently supported.');
}
} else {
// input=stream
this.options.streamIn = true;
}
return this;
};
module.exports = Sharp;
util.inherits(Sharp, stream.Duplex);
/*
Handle incoming chunk on Writable Stream
*/
Sharp.prototype._write = function(chunk, encoding, callback) {
/*jslint unused: false */
if (this.options.streamIn) {
if (typeof chunk === 'object' && chunk instanceof Buffer) {
if (typeof this.options.bufferIn === 'undefined') {
// Create new Buffer
this.options.bufferIn = new Buffer(chunk.length);
chunk.copy(this.options.bufferIn);
} else {
// Append to existing Buffer
this.options.bufferIn = Buffer.concat(
[this.options.bufferIn, chunk],
this.options.bufferIn.length + chunk.length
);
}
callback();
} else {
callback(new Error('Non-Buffer data on Writable Stream'));
}
} else {
callback(new Error('Unexpected data on Writable Stream'));
}
};
// Crop this part of the resized image (Center/Centre, North, East, South, West)
module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'south': 3, 'west': 4};
Sharp.prototype.crop = function(gravity) {
this.options.canvas = 'c';
if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 4) {
this.options.gravity = gravity;
} else {
throw new Error('Unsupported crop gravity ' + gravity);
}
return this;
};
Sharp.prototype.extract = function(topOffset, leftOffset, width, height) {
/*jslint unused: false */
var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
var values = arguments;
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) {
this.options[name + suffix] = values[index];
}.bind(this));
return this;
};
/*
Set the background colour for embed and flatten operations.
Delegates to the 'Color' module, which can throw an Error
but is liberal in what it accepts, clamping values to sensible min/max.
*/
Sharp.prototype.background = function(rgba) {
var colour = color(rgba);
this.options.background = colour.rgbArray();
this.options.background.push(colour.alpha() * 255);
return this;
};
Sharp.prototype.embed = function() {
this.options.canvas = 'e';
return this;
};
Sharp.prototype.max = function() {
this.options.canvas = 'm';
return this;
};
Sharp.prototype.flatten = function(flatten) {
this.options.flatten = (typeof flatten === 'boolean') ? flatten : true;
return this;
};
/*
Rotate output image by 0, 90, 180 or 270 degrees
Auto-rotation based on the EXIF Orientation tag is represented by an angle of -1
*/
Sharp.prototype.rotate = function(angle) {
if (typeof angle === 'undefined') {
this.options.angle = -1;
} else if (!Number.isNaN(angle) && [0, 90, 180, 270].indexOf(angle) !== -1) {
this.options.angle = angle;
} else {
throw new Error('Unsupported angle (0, 90, 180, 270) ' + angle);
}
return this;
};
/*
Flip the image vertically, about the Y axis
*/
Sharp.prototype.flip = function(flip) {
this.options.flip = (typeof flip === 'boolean') ? flip : true;
return this;
};
/*
Flop the image horizontally, about the X axis
*/
Sharp.prototype.flop = function(flop) {
this.options.flop = (typeof flop === 'boolean') ? flop : true;
return this;
};
/*
Do not enlarge the output if the input 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"
*/
Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
this.options.withoutEnlargement = (typeof withoutEnlargement === 'boolean') ? withoutEnlargement : true;
return this;
};
/*
Blur the output image.
Call without a sigma to use a fast, mild blur.
Call with a sigma to use a slower, more accurate Gaussian blur.
*/
Sharp.prototype.blur = function(sigma) {
if (typeof sigma === 'undefined') {
// No arguments: default to mild blur
this.options.blurSigma = -1;
} else if (typeof sigma === 'boolean') {
// Boolean argument: apply mild blur?
this.options.blurSigma = sigma ? -1 : 0;
} else if (typeof sigma === 'number' && !Number.isNaN(sigma) && sigma >= 0.3 && sigma <= 1000) {
// Numeric argument: specific sigma
this.options.blurSigma = sigma;
} else {
throw new Error('Invalid blur sigma (0.3 to 1000.0) ' + sigma);
}
return this;
};
/*
Sharpen the output image.
Call without a radius to use a fast, mild sharpen.
Call with a radius to use a slow, accurate sharpen using the L of LAB colour space.
radius - size of mask in pixels, must be integer
flat - level of "flat" area sharpen, default 1
jagged - level of "jagged" area sharpen, default 2
*/
Sharp.prototype.sharpen = function(radius, flat, jagged) {
if (typeof radius === 'undefined') {
// No arguments: default to mild sharpen
this.options.sharpenRadius = -1;
} else if (typeof radius === 'boolean') {
// Boolean argument: apply mild sharpen?
this.options.sharpenRadius = radius ? -1 : 0;
} else if (typeof radius === 'number' && !Number.isNaN(radius) && (radius % 1 === 0) && radius >= 1) {
// Numeric argument: specific radius
this.options.sharpenRadius = radius;
// Control over flat areas
if (typeof flat !== 'undefined' && flat !== null) {
if (typeof flat === 'number' && !Number.isNaN(flat) && flat >= 0) {
this.options.sharpenFlat = flat;
} else {
throw new Error('Invalid sharpen level for flat areas ' + flat + ' (expected >= 0)');
}
}
// Control over jagged areas
if (typeof jagged !== 'undefined' && jagged !== null) {
if (typeof jagged === 'number' && !Number.isNaN(jagged) && jagged >= 0) {
this.options.sharpenJagged = jagged;
} else {
throw new Error('Invalid sharpen level for jagged areas ' + jagged + ' (expected >= 0)');
}
}
} else {
throw new Error('Invalid sharpen radius ' + radius + ' (expected integer >= 1)');
}
return this;
};
/*
Set the interpolator to use for the affine transformation
*/
module.exports.interpolator = {
nearest: 'nearest',
bilinear: 'bilinear',
bicubic: 'bicubic',
nohalo: 'nohalo',
locallyBoundedBicubic: 'lbb',
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
Sharp.prototype.interpolateWith = function(interpolator) {
this.options.interpolator = interpolator;
return this;
};
/*
Darken image pre-resize (1/gamma) and brighten post-resize (gamma).
Improves brightness of resized image in non-linear colour spaces.
*/
Sharp.prototype.gamma = function(gamma) {
if (typeof gamma === 'undefined') {
// Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2;
} else if (!Number.isNaN(gamma) && gamma >= 1 && gamma <= 3) {
this.options.gamma = gamma;
} else {
throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
}
return this;
};
/*
Convert to greyscale
*/
Sharp.prototype.greyscale = function(greyscale) {
this.options.greyscale = (typeof greyscale === 'boolean') ? greyscale : true;
return this;
};
Sharp.prototype.grayscale = Sharp.prototype.greyscale;
Sharp.prototype.progressive = function(progressive) {
this.options.progressive = (typeof progressive === 'boolean') ? progressive : true;
return this;
};
Sharp.prototype.sequentialRead = function(sequentialRead) {
this.options.sequentialRead = (typeof sequentialRead === 'boolean') ? sequentialRead : true;
return this;
};
Sharp.prototype.quality = function(quality) {
if (!Number.isNaN(quality) && quality >= 1 && quality <= 100) {
this.options.quality = quality;
} else {
throw new Error('Invalid quality (1 to 100) ' + quality);
}
return this;
};
/*
zlib compression level for PNG output
*/
Sharp.prototype.compressionLevel = function(compressionLevel) {
if (!Number.isNaN(compressionLevel) && compressionLevel >= 0 && compressionLevel <= 9) {
this.options.compressionLevel = compressionLevel;
} else {
throw new Error('Invalid compressionLevel (0 to 9) ' + compressionLevel);
}
return this;
};
/*
Disable the use of adaptive row filtering for PNG output - requires libvips 7.41.0+
*/
Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
if (semver.gte(libvipsVersion, '7.41.0')) {
this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
} else {
console.error('withoutAdaptiveFiltering requires libvips 7.41.0+');
}
return this;
};
Sharp.prototype.withMetadata = function(withMetadata) {
this.options.withMetadata = (typeof withMetadata === 'boolean') ? withMetadata : true;
return this;
};
Sharp.prototype.resize = function(width, height) {
if (!width) {
this.options.width = -1;
} else {
if (typeof width === 'number' && !Number.isNaN(width)) {
this.options.width = width;
} else {
throw new Error('Invalid width ' + width);
}
}
if (!height) {
this.options.height = -1;
} else {
if (typeof height === 'number' && !Number.isNaN(height)) {
this.options.height = height;
} else {
throw new Error('Invalid height ' + height);
}
}
return this;
};
/*
Write output image data to a file
*/
Sharp.prototype.toFile = function(output, callback) {
if (!output || output.length === 0) {
var errOutputInvalid = new Error('Invalid output');
if (typeof callback === 'function') {
callback(errOutputInvalid);
} else {
return BluebirdPromise.reject(errOutputInvalid);
}
} else {
if (this.options.fileIn === output) {
var errOutputIsInput = new Error('Cannot use same file for input and output');
if (typeof callback === 'function') {
callback(errOutputIsInput);
} else {
return BluebirdPromise.reject(errOutputIsInput);
}
} else {
this.options.output = output;
return this._sharp(callback);
}
}
return this;
};
Sharp.prototype.toBuffer = function(callback) {
return this._sharp(callback);
};
Sharp.prototype.jpeg = function() {
this.options.output = '__jpeg';
return this;
};
Sharp.prototype.png = function() {
this.options.output = '__png';
return this;
};
Sharp.prototype.webp = function() {
this.options.output = '__webp';
return this;
};
/*
Used by a Writable Stream to notify that it is ready for data
*/
Sharp.prototype._read = function() {
if (!this.options.streamOut) {
this.options.streamOut = true;
this._sharp();
}
};
/*
Invoke the C++ image processing pipeline
Supports callback, stream and promise variants
*/
Sharp.prototype._sharp = function(callback) {
var that = this;
if (typeof callback === 'function') {
// output=file/buffer
if (this.options.streamIn) {
// output=file/buffer, input=stream
this.on('finish', function() {
sharp.resize(that.options, callback);
});
} else {
// output=file/buffer, input=file/buffer
sharp.resize(this.options, callback);
}
return this;
} else if (this.options.streamOut) {
// output=stream
if (this.options.streamIn) {
// output=stream, input=stream
this.on('finish', function() {
sharp.resize(that.options, function(err, data) {
if (err) {
that.emit('error', new Error(err));
} else {
that.push(data);
}
that.push(null);
});
});
} else {
// output=stream, input=file/buffer
sharp.resize(this.options, function(err, data) {
if (err) {
that.emit('error', new Error(err));
} else {
that.push(data);
}
that.push(null);
});
}
return this;
} else {
// output=promise
if (this.options.streamIn) {
// output=promise, input=stream
return new BluebirdPromise(function(resolve, reject) {
that.on('finish', function() {
sharp.resize(that.options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
});
} else {
// output=promise, input=file/buffer
return new BluebirdPromise(function(resolve, reject) {
sharp.resize(that.options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
}
};
/*
Reads the image header and returns metadata
Supports callback, stream and promise variants
*/
Sharp.prototype.metadata = function(callback) {
var that = this;
if (typeof callback === 'function') {
if (this.options.streamIn) {
this.on('finish', function() {
sharp.metadata(that.options, callback);
});
} else {
sharp.metadata(this.options, callback);
}
return this;
} else {
if (this.options.streamIn) {
return new BluebirdPromise(function(resolve, reject) {
that.on('finish', function() {
sharp.metadata(that.options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
});
} else {
return new BluebirdPromise(function(resolve, reject) {
sharp.metadata(that.options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
}
};
/*
Get and set cache memory and item limits
*/
module.exports.cache = function(memory, items) {
if (typeof memory !== 'number' || Number.isNaN(memory)) {
memory = null;
}
if (typeof items !== 'number' || Number.isNaN(items)) {
items = null;
}
return sharp.cache(memory, items);
};
/*
Get and set size of thread pool
*/
module.exports.concurrency = function(concurrency) {
if (typeof concurrency !== 'number' || Number.isNaN(concurrency)) {
concurrency = null;
}
return sharp.concurrency(concurrency);
};
/*
Get internal counters
*/
module.exports.counters = function() {
return sharp.counters();
};
/*
Get the version of the libvips library
*/
module.exports.libvipsVersion = function() {
return libvipsVersion;
};

113
lib/channel.js Normal file
View File

@@ -0,0 +1,113 @@
'use strict';
const is = require('./is');
/**
* Boolean operations for bandbool.
* @private
*/
const bool = {
and: 'and',
or: 'or',
eor: 'eor'
};
/**
* Extract a single channel from a multi-channel image.
*
* @example
* sharp(input)
* .extractChannel('green')
* .toFile('input_green.jpg', function(err, info) {
* // info.channels === 1
* // input_green.jpg contains the green channel of the input image
* });
*
* @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
*/
function extractChannel (channel) {
if (channel === 'red') {
channel = 0;
} else if (channel === 'green') {
channel = 1;
} else if (channel === 'blue') {
channel = 2;
}
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
this.options.extractChannel = channel;
} else {
throw new Error('Cannot extract invalid channel ' + channel);
}
return this;
}
/**
* Join one or more channels to the image.
* The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
* By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
* Channel ordering follows vips convention:
* - sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
* - CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
*
* 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 {Object} options - image options, see `sharp()` constructor.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function joinChannel (images, options) {
if (Array.isArray(images)) {
images.forEach(function (image) {
this.options.joinChannelIn.push(this._createInputDescriptor(image, options));
}, this);
} else {
this.options.joinChannelIn.push(this._createInputDescriptor(images, options));
}
return this;
}
/**
* Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
*
* @example
* sharp('3-channel-rgb-input.png')
* .bandbool(sharp.bool.and)
* .toFile('1-channel-output.png', function (err, info) {
* // The output will be a single channel image where each pixel `P = R & G & B`.
* // If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
* // 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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
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);
}
return this;
}
/**
* Decorate the Sharp prototype with channel-related functions.
* @private
*/
module.exports = function (Sharp) {
// Public instance functions
[
extractChannel,
joinChannel,
bandbool
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
// Class attributes
Sharp.bool = bool;
};

108
lib/colour.js Normal file
View File

@@ -0,0 +1,108 @@
'use strict';
const color = require('color');
const is = require('./is');
/**
* Colourspaces.
* @private
*/
const colourspace = {
multiband: 'multiband',
'b-w': 'b-w',
bw: 'b-w',
cmyk: 'cmyk',
srgb: 'srgb'
};
/**
* 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).
*
* @param {String|Object} rgba - 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 parameter
*/
function background (rgba) {
const colour = color(rgba);
this.options.background = [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
return this;
}
/**
* Convert to 8-bit greyscale; 256 shades of grey.
* This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
* By default the output image will be web-friendly sRGB and contain three (identical) color channels.
* 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.
* @param {Boolean} [greyscale=true]
* @returns {Sharp}
*/
function greyscale (greyscale) {
this.options.greyscale = is.bool(greyscale) ? greyscale : true;
return this;
}
/**
* Alternative spelling of `greyscale`.
* @param {Boolean} [grayscale=true]
* @returns {Sharp}
*/
function grayscale (grayscale) {
return this.greyscale(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)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColourspace (colourspace) {
if (!is.string(colourspace)) {
throw new Error('Invalid output colourspace ' + colourspace);
}
this.options.colourspace = colourspace;
return this;
}
/**
* Alternative spelling of `toColourspace`.
* @param {String} [colorspace] - output colorspace.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColorspace (colorspace) {
return this.toColourspace(colorspace);
}
/**
* Decorate the Sharp prototype with colour-related functions.
* @private
*/
module.exports = function (Sharp) {
// Public instance functions
[
background,
greyscale,
grayscale,
toColourspace,
toColorspace
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
// Class attributes
Sharp.colourspace = colourspace;
Sharp.colorspace = colourspace;
};

98
lib/composite.js Normal file
View File

@@ -0,0 +1,98 @@
'use strict';
const is = require('./is');
/**
* 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.
* If both `top` and `left` options are provided, they take precedence over `gravity`.
*
* @example
* sharp('input.png')
* .rotate(180)
* .resize(300)
* .flatten()
* .background('#ff6600')
* .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
* .sharpen()
* .withMetadata()
* .quality(90)
* .webp()
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains upside down, 300px wide, alpha channel flattened
* // onto orange background, composited with overlay.png with SE gravity,
* // 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.
* @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) && is.inRange(options.left, 0, this.constructor.maximum.width) &&
is.integer(options.top) && is.inRange(options.top, 0, this.constructor.maximum.height)
) {
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);
}
}
}
return this;
}
/**
* Decorate the Sharp prototype with composite-related functions.
* @private
*/
module.exports = function (Sharp) {
Sharp.prototype.overlayWith = overlayWith;
};

231
lib/constructor.js Normal file
View File

@@ -0,0 +1,231 @@
'use strict';
const path = require('path');
const util = require('util');
const stream = require('stream');
const events = require('events');
const semver = require('semver');
const sharp = require('../build/Release/sharp.node');
// 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);
}
// Include versions of dependencies, if present
try {
versions = require('../vendor/lib/versions.json');
} catch (err) {}
})();
/**
* @class Sharp
*
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
*
* JPEG, PNG or WebP format image data can be streamed out from this object.
* When using Stream based output, derived attributes are available from the `info` event.
*
* Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
*
* @example
* sharp('input.jpg')
* .resize(300, 200)
* .toFile('output.jpg', function(err) {
* // output.jpg is a 300 pixels wide and 200 pixels high image
* // containing a scaled and cropped version of input.jpg
* });
*
* @example
* // Read image data from readableStream,
* // resize to 300 pixels wide,
* // emit an 'info' event with calculated dimensions
* // and finally write image data to writableStream
* var transformer = sharp()
* .resize(300)
* .on('info', function(info) {
* console.log('Image height is ' + info.height);
* });
* readableStream.pipe(transformer).pipe(writableStream);
*
* @example
* // Create a blank 300x200 PNG image of semi-transluent red pixels
* sharp(null, {
* create: {
* width: 300,
* height: 200,
* channels: 4,
* background: { r: 255, g: 0, b: 0, alpha: 128 }
* }
* })
* .png()
* .toBuffer()
* .then( ... );
*
* @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.
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {Number} [options.density=72] - integral number representing the DPI for vector images.
* @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]
* @param {Number} [options.raw.channels] - 1-4
* @param {Object} [options.create] - describes a new image 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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
const Sharp = function (input, options) {
if (!(this instanceof Sharp)) {
return new Sharp(input, options);
}
stream.Duplex.call(this);
this.options = {
// input options
sequentialRead: false,
limitInputPixels: maximum.pixels,
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
widthPre: -1,
heightPre: -1,
topOffsetPost: -1,
leftOffsetPost: -1,
widthPost: -1,
heightPost: -1,
width: -1,
height: -1,
canvas: 'crop',
crop: 0,
angle: 0,
rotateBeforePreExtract: false,
flip: false,
flop: false,
extendTop: 0,
extendBottom: 0,
extendLeft: 0,
extendRight: 0,
withoutEnlargement: false,
kernel: 'lanczos3',
interpolator: 'bicubic',
centreSampling: false,
// operations
background: [0, 0, 0, 255],
flatten: false,
negate: false,
blurSigma: 0,
sharpenSigma: 0,
sharpenFlat: 1,
sharpenJagged: 2,
threshold: 0,
thresholdGrayscale: true,
trimTolerance: 0,
gamma: 0,
greyscale: false,
normalise: 0,
booleanBufferIn: null,
booleanFileIn: '',
joinChannelIn: [],
extractChannel: -1,
colourspace: 'srgb',
// overlay
overlayGravity: 0,
overlayXOffset: -1,
overlayYOffset: -1,
overlayTile: false,
overlayCutout: false,
// output
fileOut: '',
formatOut: 'input',
streamOut: false,
withMetadata: false,
withMetadataOrientation: -1,
resolveWithObject: false,
// output format
jpegQuality: 80,
jpegProgressive: false,
jpegChromaSubsampling: '4:2:0',
jpegTrellisQuantisation: false,
jpegOvershootDeringing: false,
jpegOptimiseScans: false,
pngProgressive: false,
pngCompressionLevel: 6,
pngAdaptiveFiltering: true,
webpQuality: 80,
webpAlphaQuality: 100,
webpLossless: false,
webpNearLossless: false,
tiffQuality: 80,
tiffCompression: 'jpeg',
tiffPredictor: 'none',
tileSize: 256,
tileOverlap: 0,
// Function to notify of queue length changes
queueListener: function (queueLength) {
queue.emit('change', queueLength);
}
};
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
return this;
};
util.inherits(Sharp, stream.Duplex);
/**
* Pixel limits.
* @member
* @private
*/
const maximum = {
width: 0x3FFF,
height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2)
};
Sharp.maximum = maximum;
/**
* 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();
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;
/**
* Export constructor.
* @private
*/
module.exports = Sharp;

BIN
lib/icc/cmyk.icm Normal file

Binary file not shown.

BIN
lib/icc/sRGB.icc Normal file

Binary file not shown.

17
lib/index.js Normal file
View File

@@ -0,0 +1,17 @@
'use strict';
const Sharp = require('./constructor');
[
'input',
'resize',
'composite',
'operation',
'colour',
'channel',
'output',
'utility'
].forEach(function (decorator) {
require('./' + decorator)(Sharp);
});
module.exports = Sharp;

283
lib/input.js Normal file
View File

@@ -0,0 +1,283 @@
'use strict';
const util = require('util');
const color = require('color');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* Create Object containing input and input-related options.
* @private
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = {};
if (is.string(input)) {
// filesystem
inputDescriptor.file = input;
} else if (is.buffer(input)) {
// Buffer
inputDescriptor.buffer = input;
} else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream
inputDescriptor.buffer = [];
} else {
throw new Error('Unsupported input ' + typeof input);
}
if (is.object(inputOptions)) {
// Density
if (is.defined(inputOptions.density)) {
if (is.integer(inputOptions.density) && is.inRange(inputOptions.density, 1, 2400)) {
inputDescriptor.density = inputOptions.density;
} else {
throw new Error('Invalid density (1 to 2400) ' + inputOptions.density);
}
}
// Raw pixel input
if (is.defined(inputOptions.raw)) {
if (
is.object(inputOptions.raw) &&
is.integer(inputOptions.raw.width) && is.inRange(inputOptions.raw.width, 1, this.constructor.maximum.width) &&
is.integer(inputOptions.raw.height) && is.inRange(inputOptions.raw.height, 1, this.constructor.maximum.height) &&
is.integer(inputOptions.raw.channels) && is.inRange(inputOptions.raw.channels, 1, 4)
) {
inputDescriptor.rawWidth = inputOptions.raw.width;
inputDescriptor.rawHeight = inputOptions.raw.height;
inputDescriptor.rawChannels = inputOptions.raw.channels;
} else {
throw new Error('Expected width, height and channels for raw pixel input');
}
}
// Create new image
if (is.defined(inputOptions.create)) {
if (
is.object(inputOptions.create) &&
is.integer(inputOptions.create.width) && is.inRange(inputOptions.create.width, 1, this.constructor.maximum.width) &&
is.integer(inputOptions.create.height) && is.inRange(inputOptions.create.height, 1, this.constructor.maximum.height) &&
is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) &&
is.defined(inputOptions.create.background)
) {
inputDescriptor.createWidth = inputOptions.create.width;
inputDescriptor.createHeight = inputOptions.create.height;
inputDescriptor.createChannels = inputOptions.create.channels;
const background = color(inputOptions.create.background);
inputDescriptor.createBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
delete inputDescriptor.buffer;
} else {
throw new Error('Expected width, height, channels and background to create a new input image');
}
}
} else if (is.defined(inputOptions)) {
throw new Error('Invalid input options ' + inputOptions);
}
return inputDescriptor;
}
/**
* Handle incoming Buffer chunk on Writable Stream.
* @private
* @param {Buffer} chunk
* @param {String} encoding - unused
* @param {Function} callback
*/
function _write (chunk, encoding, callback) {
/* istanbul ignore else */
if (Array.isArray(this.options.input.buffer)) {
/* 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.options.input.buffer.push(chunk);
callback();
} else {
callback(new Error('Non-Buffer data on Writable Stream'));
}
} else {
callback(new Error('Unexpected data on Writable Stream'));
}
}
/**
* Flattens the array of chunks accumulated in input.buffer.
* @private
*/
function _flattenBufferIn () {
if (this._isStreamInput()) {
this.options.input.buffer = Buffer.concat(this.options.input.buffer);
}
}
/**
* Are we expecting Stream-based input?
* @private
* @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();
util._extend(clone.options, this.options);
// Pass 'finish' event to clone for Stream-based input
this.on('finish', function () {
// Clone inherits input data
that._flattenBufferIn();
clone.options.bufferIn = that.options.bufferIn;
clone.emit('finish');
});
return clone;
}
/**
* Fast access to image metadata without decoding any compressed image data.
* A Promises/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#L568)
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* - `density`: Number of pixels per inch (DPI), if present
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
* - `orientation`: Number value of the EXIF Orientation header, if present
* - `exif`: Buffer containing raw EXIF data, if present
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
*
* @example
* const image = sharp(inputJpg);
* image
* .metadata()
* .then(function(metadata) {
* return image
* .resize(Math.round(metadata.width / 2))
* .webp()
* .toBuffer();
* })
* .then(function(data) {
* // data contains a WebP image half the width and height of the original JPEG
* });
*
* @param {Function} [callback] - called with the arguments `(err, metadata)`
* @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);
});
} else {
sharp.metadata(this.options, 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) {
if (err) {
reject(err);
} else {
resolve(metadata);
}
});
});
});
} else {
return new Promise(function (resolve, reject) {
sharp.metadata(that.options, function (err, metadata) {
if (err) {
reject(err);
} else {
resolve(metadata);
}
});
});
}
}
}
/**
* 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 = this.constructor.maximum.pixels;
}
if (is.integer(limit) && limit >= 0) {
this.options.limitInputPixels = limit;
} else {
throw new Error('Invalid pixel limit (0 to ' + this.constructor.maximum.pixels + ') ' + 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.
* @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) {
[
// Private
_createInputDescriptor,
_write,
_flattenBufferIn,
_isStreamInput,
// Public
clone,
metadata,
limitInputPixels,
sequentialRead
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
};

110
lib/is.js Normal file
View File

@@ -0,0 +1,110 @@
'use strict';
/**
* Is this value defined and not null?
* @private
*/
const defined = function (val) {
return typeof val !== 'undefined' && val !== null;
};
/**
* Is this value an object?
* @private
*/
const object = function (val) {
return typeof val === 'object';
};
/**
* Is this value a function?
* @private
*/
const fn = function (val) {
return typeof val === 'function';
};
/**
* Is this value a boolean?
* @private
*/
const bool = function (val) {
return typeof val === 'boolean';
};
/**
* Is this value a Buffer object?
* @private
*/
const buffer = function (val) {
return object(val) && val instanceof Buffer;
};
/**
* Is this value a non-empty string?
* @private
*/
const string = function (val) {
return typeof val === 'string' && val.length > 0;
};
/**
* Is this value a real number?
* @private
*/
const number = function (val) {
return typeof val === 'number' && !Number.isNaN(val);
};
/**
* Is this value an integer?
* @private
*/
const integer = function (val) {
return number(val) && val % 1 === 0;
};
/**
* Is this value within an inclusive given range?
* @private
*/
const inRange = function (val, min, max) {
return val >= min && val <= max;
};
/**
* Is this value within the elements of an array?
* @private
*/
const inArray = function (val, list) {
return list.indexOf(val) !== -1;
};
/**
* 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 {*} actual - the value received.
* @returns {Error} Containing the formatted message.
* @private
*/
const invalidParameterError = function (name, expected, actual) {
return new Error(
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
);
};
module.exports = {
defined: defined,
object: object,
fn: fn,
bool: bool,
buffer: buffer,
string: string,
number: number,
integer: integer,
inRange: inRange,
inArray: inArray,
invalidParameterError: invalidParameterError
};

431
lib/operation.js Normal file
View File

@@ -0,0 +1,431 @@
'use strict';
const is = require('./is');
/**
* Rotate the output image by either an explicit angle
* or auto-orient based on the EXIF `Orientation` tag.
*
* Use this method without angle to determine the angle from EXIF data.
* Mirroring is supported and may infer the use of a flip operation.
*
* The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
*
* Method order is important when both rotating and extracting regions,
* for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
*
* @example
* const pipeline = sharp()
* .rotate()
* .resize(null, 200)
* .toBuffer(function (err, outputBuffer, info) {
* // outputBuffer contains 200px high JPEG image data,
* // auto-rotated using EXIF Orientation tag
* // info.width and info.height contain the dimensions of the resized image
* });
* readableStream.pipe(pipeline);
*
* @param {Number} [angle=auto] 0, 90, 180 or 270.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function rotate (angle) {
if (!is.defined(angle)) {
this.options.angle = -1;
} else if (is.integer(angle) && is.inArray(angle, [0, 90, 180, 270])) {
this.options.angle = angle;
} else {
throw new Error('Unsupported angle (0, 90, 180, 270) ' + 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);
}
}, this);
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && this.options.angle !== 0) {
this.options.rotateBeforePreExtract = true;
}
return this;
}
/**
* 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.
* @param {Boolean} [flip=true]
* @returns {Sharp}
*/
function flip (flip) {
this.options.flip = is.bool(flip) ? flip : true;
return this;
}
/**
* 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.
* @param {Boolean} [flop=true]
* @returns {Sharp}
*/
function flop (flop) {
this.options.flop = is.bool(flop) ? flop : true;
return this;
}
/**
* Sharpen the image.
* When used without parameters, performs a fast, mild sharpen of the output image.
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
* Separate control over the level of sharpening in "flat" and "jagged" areas is available.
*
* @param {Number} [sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @param {Number} [flat=1.0] - the level of sharpening to apply to "flat" areas.
* @param {Number} [jagged=2.0] - the level of sharpening to apply to "jagged" areas.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function sharpen (sigma, flat, jagged) {
if (!is.defined(sigma)) {
// No arguments: default to mild sharpen
this.options.sharpenSigma = -1;
} else if (is.bool(sigma)) {
// Boolean argument: apply mild sharpen?
this.options.sharpenSigma = sigma ? -1 : 0;
} else if (is.number(sigma) && is.inRange(sigma, 0.01, 10000)) {
// Numeric argument: specific sigma
this.options.sharpenSigma = sigma;
// Control over flat areas
if (is.defined(flat)) {
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);
}
}
// Control over jagged areas
if (is.defined(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);
}
}
} else {
throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
}
return this;
}
/**
* 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`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function blur (sigma) {
if (!is.defined(sigma)) {
// No arguments: default to mild blur
this.options.blurSigma = -1;
} else if (is.bool(sigma)) {
// Boolean argument: apply mild blur?
this.options.blurSigma = sigma ? -1 : 0;
} else if (is.number(sigma) && is.inRange(sigma, 0.3, 1000)) {
// Numeric argument: specific sigma
this.options.blurSigma = sigma;
} else {
throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + 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]
* @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);
}
return this;
}
/**
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
* then increasing the encoding (brighten) post-resize at a factor of `gamma`.
* This can improve the perceived brightness of a resized image in non-linear colour spaces.
* 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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function gamma (gamma) {
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);
}
return this;
}
/**
* Produce the "negative" of the image.
* @param {Boolean} [negate=true]
* @returns {Sharp}
*/
function negate (negate) {
this.options.negate = is.bool(negate) ? negate : true;
return this;
}
/**
* Enhance output image contrast by stretching its luminance to cover the full dynamic range.
* @param {Boolean} [normalise=true]
* @returns {Sharp}
*/
function normalise (normalise) {
this.options.normalise = is.bool(normalise) ? normalise : true;
return this;
}
/**
* Alternative spelling of normalise.
* @param {Boolean} [normalize=true]
* @returns {Sharp}
*/
function normalize (normalize) {
return this.normalise(normalize);
}
/**
* Convolve the image with the specified kernel.
*
* @example
* sharp(input)
* .convolve({
* width: 3,
* height: 3,
* kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1]
* })
* .raw()
* .toBuffer(function(err, data, info) {
* // data contains the raw pixel data representing the convolution
* // of the input image with the horizontal Sobel operator
* });
*
* @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.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function convolve (kernel) {
if (!is.object(kernel) || !Array.isArray(kernel.kernel) ||
!is.integer(kernel.width) || !is.integer(kernel.height) ||
!is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) ||
kernel.height * kernel.width !== kernel.kernel.length
) {
// must pass in a kernel
throw new Error('Invalid convolution kernel');
}
// Default scale is sum of kernel values
if (!is.integer(kernel.scale)) {
kernel.scale = kernel.kernel.reduce(function (a, b) {
return a + b;
}, 0);
}
// Clip scale to a minimum value of 1
if (kernel.scale < 1) {
kernel.scale = 1;
}
if (!is.integer(kernel.offset)) {
kernel.offset = 0;
}
this.options.convKernel = kernel;
return this;
}
/**
* 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 {Object} [options]
* @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
* @param {Boolean} [options.grayscale=true] - alternative spelling for greyscale.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function threshold (threshold, options) {
if (!is.defined(threshold)) {
this.options.threshold = 128;
} else if (is.bool(threshold)) {
this.options.threshold = threshold ? 128 : 0;
} else if (is.integer(threshold) && is.inRange(threshold, 0, 255)) {
this.options.threshold = threshold;
} else {
throw new Error('Invalid threshold (0 to 255) ' + threshold);
}
if (!is.object(options) || options.greyscale === true || options.grayscale === true) {
this.options.thresholdGrayscale = true;
} else {
this.options.thresholdGrayscale = false;
}
return this;
}
/**
* 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.
*
* @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]
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function boolean (operand, operator, options) {
this.options.boolean = this._createInputDescriptor(operand, options);
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
this.options.booleanOp = operator;
} else {
throw new Error('Invalid boolean operator ' + operator);
}
return this;
}
/**
* Decorate the Sharp prototype with operation-related functions.
* @private
*/
module.exports = function (Sharp) {
[
rotate,
extract,
flip,
flop,
sharpen,
blur,
extend,
flatten,
trim,
gamma,
negate,
normalise,
normalize,
convolve,
threshold,
boolean
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
};

581
lib/output.js Normal file
View File

@@ -0,0 +1,581 @@
'use strict';
const util = require('util');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* Write output image data to a file.
*
* If an explicit output format is not selected, it will be inferred from the extension,
* with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
* Note that raw pixel data is only supported for buffer output.
*
* A Promises/A+ promise is returned when `callback` is not provided.
*
* @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` and `channels`.
* @returns {Promise<Object>} - when no callback is provided
* @throws {Error} Invalid parameters
*/
function toFile (fileOut, callback) {
if (!fileOut || fileOut.length === 0) {
const errOutputInvalid = new Error('Invalid output');
if (is.fn(callback)) {
callback(errOutputInvalid);
} else {
return Promise.reject(errOutputInvalid);
}
} else {
if (this.options.input.file === fileOut) {
const errOutputIsInput = new Error('Cannot use same file for input and output');
if (is.fn(callback)) {
callback(errOutputIsInput);
} else {
return Promise.reject(errOutputIsInput);
}
} else {
this.options.fileOut = fileOut;
return this._pipeline(callback);
}
}
return this;
}
/**
* Write output to a Buffer.
* JPEG, PNG, WebP, and RAW output are supported.
* By default, the format will match the input image, except GIF and SVG input which become PNG output.
*
* `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any.
* - `data` is the output image data.
* - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
* A Promise is returned when `callback` is not provided.
*
* @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 {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;
}
}
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.
* @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;
} else {
throw new Error('Invalid orientation (1 to 8) ' + withMetadata.orientation);
}
}
}
return this;
}
/**
* Use these JPEG options for output image.
* @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
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function jpeg (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
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);
}
}
if (is.defined(options.progressive)) {
this._setBooleanOption('jpegProgressive', options.progressive);
}
if (is.defined(options.chromaSubsampling)) {
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);
}
}
options.trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
if (is.defined(options.trellisQuantisation)) {
this._setBooleanOption('jpegTrellisQuantisation', options.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) {
this.options.jpegProgressive = true;
}
}
}
return this._updateFormatOut('jpeg', options);
}
/**
* Use these PNG options for output image.
* @param {Object} [options]
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {Number} [options.compressionLevel=6] - zlib compression level
* @param {Boolean} [options.adaptiveFiltering=true] - use adaptive row filtering
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function png (options) {
if (is.object(options)) {
if (is.defined(options.progressive)) {
this._setBooleanOption('pngProgressive', options.progressive);
}
if (is.defined(options.compressionLevel)) {
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);
}
}
if (is.defined(options.adaptiveFiltering)) {
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
}
}
return this._updateFormatOut('png', options);
}
/**
* Use these WebP options for output image.
* @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
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function webp (options) {
if (is.object(options) && is.defined(options.quality)) {
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);
}
}
if (is.object(options) && is.defined(options.alphaQuality)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 1, 100)) {
this.options.webpAlphaQuality = options.alphaQuality;
} else {
throw new Error('Invalid webp alpha quality (integer, 1-100) ' + options.alphaQuality);
}
}
if (is.object(options) && is.defined(options.lossless)) {
this._setBooleanOption('webpLossless', options.lossless);
}
if (is.object(options) && is.defined(options.nearLossless)) {
this._setBooleanOption('webpNearLossless', options.nearLossless);
}
return this._updateFormatOut('webp', options);
}
/**
* Use these TIFF options for output image.
* @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='none'] - compression predictor options: none, horizontal, float
* @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);
}
}
// 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);
}
}
// 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);
}
}
return this._updateFormatOut('tiff', options);
}
/**
* Force output to be raw, uncompressed uint8 pixel data.
* @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 (!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.
*
* @example
* sharp('input.tiff')
* .png()
* .tile({
* size: 512
* })
* .toFile('output.dz', function(err, info) {
* // output.dzi is the Deep Zoom XML definition
* // 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`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function tile (tile) {
if (is.object(tile)) {
// 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;
} else {
throw new Error('Invalid tile size (1 to 8192) ' + tile.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);
}
this.options.tileOverlap = tile.overlap;
} else {
throw new Error('Invalid tile overlap (0 to 8192) ' + tile.overlap);
}
}
// Container
if (is.defined(tile.container)) {
if (is.string(tile.container) && is.inArray(tile.container, ['fs', 'zip'])) {
this.options.tileContainer = tile.container;
} else {
throw new Error('Invalid tile container ' + tile.container);
}
}
// Layout
if (is.defined(tile.layout)) {
if (is.string(tile.layout) && is.inArray(tile.layout, ['dz', 'google', 'zoomify'])) {
this.options.tileLayout = tile.layout;
} else {
throw new Error('Invalid tile layout ' + tile.layout);
}
}
}
// 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);
}
return this._updateFormatOut('dz');
}
/**
* Update the output format unless options.force is false,
* in which case revert to input format.
* @private
* @param {String} formatOut
* @param {Object} [options]
* @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;
return this;
}
/**
* Update a Boolean attribute of the this.options Object.
* @private
* @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);
}
}
/**
* Called by a WriteableStream to notify us it is ready for data.
* @private
*/
function _read () {
if (!this.options.streamOut) {
this.options.streamOut = true;
this._pipeline();
}
}
/**
* Invoke the C++ image processing pipeline
* Supports callback, stream and promise variants
* @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);
});
} else {
// output=file/buffer, input=file/buffer
sharp.pipeline(this.options, callback);
}
return this;
} else if (this.options.streamOut) {
// output=stream
if (this._isStreamInput()) {
// output=stream, input=stream
if (this.streamInFinished) {
this._flattenBufferIn();
sharp.pipeline(this.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.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);
});
});
}
} else {
// output=stream, input=file/buffer
sharp.pipeline(this.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
}
that.push(null);
});
}
return this;
} else {
// 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) {
if (err) {
reject(err);
} else {
if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
}
}
});
});
});
} else {
// output=promise, input=file/buffer
return new Promise(function (resolve, reject) {
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
reject(err);
} else {
if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
}
}
});
});
}
}
}
// Deprecated output options
/* istanbul ignore next */
const quality = util.deprecate(function (quality) {
const formatOut = this.options.formatOut;
const options = { quality: quality };
this.jpeg(options).webp(options).tiff(options);
this.options.formatOut = formatOut;
return this;
}, 'quality: use jpeg({ quality: ... }), webp({ quality: ... }) and/or tiff({ quality: ... }) instead');
/* istanbul ignore next */
const progressive = util.deprecate(function (progressive) {
const formatOut = this.options.formatOut;
const options = { progressive: (progressive !== false) };
this.jpeg(options).png(options);
this.options.formatOut = formatOut;
return this;
}, 'progressive: use jpeg({ progressive: ... }) and/or png({ progressive: ... }) instead');
/* istanbul ignore next */
const compressionLevel = util.deprecate(function (compressionLevel) {
const formatOut = this.options.formatOut;
this.png({ compressionLevel: compressionLevel });
this.options.formatOut = formatOut;
return this;
}, 'compressionLevel: use png({ compressionLevel: ... }) instead');
/* istanbul ignore next */
const withoutAdaptiveFiltering = util.deprecate(function (withoutAdaptiveFiltering) {
const formatOut = this.options.formatOut;
this.png({ adaptiveFiltering: (withoutAdaptiveFiltering === false) });
this.options.formatOut = formatOut;
return this;
}, 'withoutAdaptiveFiltering: use png({ adaptiveFiltering: ... }) instead');
/* istanbul ignore next */
const withoutChromaSubsampling = util.deprecate(function (withoutChromaSubsampling) {
const formatOut = this.options.formatOut;
this.jpeg({ chromaSubsampling: (withoutChromaSubsampling === false) ? '4:2:0' : '4:4:4' });
this.options.formatOut = formatOut;
return this;
}, 'withoutChromaSubsampling: use jpeg({ chromaSubsampling: "4:4:4" }) instead');
/* istanbul ignore next */
const trellisQuantisation = util.deprecate(function (trellisQuantisation) {
const formatOut = this.options.formatOut;
this.jpeg({ trellisQuantisation: (trellisQuantisation !== false) });
this.options.formatOut = formatOut;
return this;
}, 'trellisQuantisation: use jpeg({ trellisQuantisation: ... }) instead');
/* istanbul ignore next */
const overshootDeringing = util.deprecate(function (overshootDeringing) {
const formatOut = this.options.formatOut;
this.jpeg({ overshootDeringing: (overshootDeringing !== false) });
this.options.formatOut = formatOut;
return this;
}, 'overshootDeringing: use jpeg({ overshootDeringing: ... }) instead');
/* istanbul ignore next */
const optimiseScans = util.deprecate(function (optimiseScans) {
const formatOut = this.options.formatOut;
this.jpeg({ optimiseScans: (optimiseScans !== false) });
this.options.formatOut = formatOut;
return this;
}, 'optimiseScans: use jpeg({ optimiseScans: ... }) instead');
/**
* Decorate the Sharp prototype with output-related functions.
* @private
*/
module.exports = function (Sharp) {
[
// Public
toFile,
toBuffer,
withMetadata,
jpeg,
png,
webp,
tiff,
raw,
toFormat,
tile,
// Private
_updateFormatOut,
_setBooleanOption,
_read,
_pipeline
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
// Deprecated
Sharp.prototype.quality = quality;
Sharp.prototype.progressive = progressive;
Sharp.prototype.compressionLevel = compressionLevel;
Sharp.prototype.withoutAdaptiveFiltering = withoutAdaptiveFiltering;
Sharp.prototype.withoutChromaSubsampling = withoutChromaSubsampling;
Sharp.prototype.trellisQuantisation = trellisQuantisation;
Sharp.prototype.trellisQuantization = trellisQuantisation;
Sharp.prototype.overshootDeringing = overshootDeringing;
Sharp.prototype.optimiseScans = optimiseScans;
Sharp.prototype.optimizeScans = optimiseScans;
};

306
lib/resize.js Normal file
View File

@@ -0,0 +1,306 @@
'use strict';
const is = require('./is');
/**
* Weighting to apply to image crop.
* @member
* @private
*/
const gravity = {
center: 0,
centre: 0,
north: 1,
east: 2,
south: 3,
west: 4,
northeast: 5,
southeast: 6,
southwest: 7,
northwest: 8
};
/**
* Strategies for automagic crop behaviour.
* @member
* @private
*/
const strategy = {
entropy: 16,
attention: 17
};
/**
* Reduction kernels.
* @member
* @private
*/
const kernel = {
nearest: 'nearest',
cubic: 'cubic',
lanczos2: 'lanczos2',
lanczos3: 'lanczos3'
};
/**
* Enlargement interpolators.
* @member
* @private
*/
const interpolator = {
nearest: 'nearest',
bilinear: 'bilinear',
bicubic: 'bicubic',
nohalo: 'nohalo',
lbb: 'lbb',
locallyBoundedBicubic: 'lbb',
vsqbs: 'vsqbs',
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
/**
* Resize image to `width` x `height`.
* By default, the resized image is centre cropped to the exact size specified.
*
* Possible reduction 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).
* - `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).
*
* Possible enlargement interpolators are:
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
* - `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), faster than bicubic but with less smooth results.
* - `vertexSplitQuadraticBasisSpline`: Use the smoother [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48) to prevent "staircasing" when enlarging.
* - `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default).
* - `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2.
* - `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance but typically reduces performance by a factor of 3.
*
* @example
* sharp(inputBuffer)
* .resize(200, 300, {
* kernel: sharp.kernel.lanczos2,
* interpolator: sharp.interpolator.nohalo
* })
* .background('white')
* .embed()
* .toFile('output.tiff')
* .then(function() {
* // output.tiff is a 200 pixels wide and 300 pixels high image
* // containing a lanczos2/nohalo scaled version, embedded on a white canvas,
* // of the image data in inputBuffer
* });
*
* @param {Number} [width] - pixels wide the resultant image should be, between 1 and 16383 (0x3FFF). Use `null` or `undefined` to auto-scale the width to match the height.
* @param {Number} [height] - pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {Object} [options]
* @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
* @param {String} [options.interpolator='bicubic'] - the interpolator to use for image enlargement.
* @param {Boolean} [options.centreSampling=false] - use *magick centre sampling convention instead of corner sampling.
* @param {Boolean} [options.centerSampling=false] - alternative spelling of centreSampling.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function resize (width, height, options) {
if (is.defined(width)) {
if (is.integer(width) && is.inRange(width, 1, this.constructor.maximum.width)) {
this.options.width = width;
} else {
throw is.invalidParameterError('width', `integer between 1 and ${this.constructor.maximum.width}`, width);
}
} else {
this.options.width = -1;
}
if (is.defined(height)) {
if (is.integer(height) && is.inRange(height, 1, this.constructor.maximum.height)) {
this.options.height = height;
} else {
throw is.invalidParameterError('height', `integer between 1 and ${this.constructor.maximum.height}`, height);
}
} else {
this.options.height = -1;
}
if (is.object(options)) {
// Kernel
if (is.defined(options.kernel)) {
if (is.string(kernel[options.kernel])) {
this.options.kernel = kernel[options.kernel];
} else {
throw is.invalidParameterError('kernel', 'valid kernel name', options.kernel);
}
}
// Interpolator
if (is.defined(options.interpolator)) {
if (is.string(interpolator[options.interpolator])) {
this.options.interpolator = interpolator[options.interpolator];
} else {
throw is.invalidParameterError('interpolator', 'valid interpolator name', options.interpolator);
}
}
// Centre sampling
options.centreSampling = is.bool(options.centerSampling) ? options.centerSampling : options.centreSampling;
if (is.defined(options.centreSampling)) {
this._setBooleanOption('centreSampling', options.centreSampling);
}
}
return this;
}
/**
* 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.
*
* @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);
*
* @param {String} [crop='centre'] - A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically.
* @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
* });
*
* @returns {Sharp}
*/
function embed () {
this.options.canvas = '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*".
* @param {Boolean} [withoutEnlargement=true]
* @returns {Sharp}
*/
function withoutEnlargement (withoutEnlargement) {
this.options.withoutEnlargement = is.bool(withoutEnlargement) ? withoutEnlargement : true;
return this;
}
/**
* Decorate the Sharp prototype with resize-related functions.
* @private
*/
module.exports = function (Sharp) {
[
resize,
crop,
embed,
max,
min,
ignoreAspectRatio,
withoutEnlargement
].forEach(function (f) {
Sharp.prototype[f.name] = f;
});
// Class attributes
Sharp.gravity = gravity;
Sharp.strategy = strategy;
Sharp.kernel = kernel;
Sharp.interpolator = interpolator;
};

116
lib/utility.js Normal file
View File

@@ -0,0 +1,116 @@
'use strict';
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* Gets, or when options are provided sets, the limits of _libvips'_ operation cache.
* Existing entries in the cache will be trimmed after any change in limits.
* This method always returns cache statistics,
* useful for determining how much working memory is required for a particular task.
*
* @example
* const stats = sharp.cache();
* @example
* sharp.cache( { items: 200 } );
* sharp.cache( { files: 0 } );
* sharp.cache(false);
*
* @param {Object|Boolean} options - 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) {
if (is.bool(options)) {
if (options) {
// Default cache settings of 50MB, 20 files, 100 items
return sharp.cache(50, 20, 100);
} else {
return sharp.cache(0, 0, 0);
}
} else if (is.object(options)) {
return sharp.cache(options.memory, options.files, options.items);
} else {
return sharp.cache();
}
}
cache(true);
/**
* Gets, or when a concurrency is provided sets,
* the number of threads _libvips'_ should create to process each image.
* The default value is the number of CPU cores.
* A value of `0` will reset to this default.
*
* The maximum number of images that can be processed in parallel
* is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
*
* This method always returns the current concurrency.
*
* @example
* const threads = sharp.concurrency(); // 4
* sharp.concurrency(2); // 2
* sharp.concurrency(0); // 4
*
* @param {Number} [concurrency]
* @returns {Number} concurrency
*/
function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}
/**
* 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.
*
* @example
* const counters = sharp.counters(); // { queue: 2, process: 4 }
*
* @returns {Object}
*/
function counters () {
return sharp.counters();
}
/**
* Get and set use of SIMD vector unit instructions.
* Requires libvips to have been compiled with liborc support.
*
* Improves the performance of `resize`, `blur` and `sharpen` operations
* by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
*
* 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
* @example
* const simd = sharp.simd(true);
* // attempts to enable the use of SIMD, returning true if available
*
* @param {Boolean} [simd=false]
* @returns {Boolean}
*/
function simd (simd) {
return sharp.simd(is.bool(simd) ? simd : null);
}
simd(false);
/**
* Decorate the Sharp class with utility-related functions.
* @private
*/
module.exports = function (Sharp) {
[
cache,
concurrency,
counters,
simd
].forEach(function (f) {
Sharp[f.name] = f;
});
};

25
mkdocs.yml Normal file
View File

@@ -0,0 +1,25 @@
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

85
package.json Executable file → Normal file
View File

@@ -1,7 +1,9 @@
{
"name": "sharp",
"version": "0.8.1",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.17.3",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
"Jonathan Ong <jonathanrichardong@gmail.com>",
@@ -11,13 +13,37 @@
"Julian Walker <julian@fiftythree.com>",
"Amit Pitaru <pitaru.amit@gmail.com>",
"Brandon Aaron <hello.brandon@aaron.sh>",
"Andreas Lind <andreas@one.com>"
"Andreas Lind <andreas@one.com>",
"Maurus Cuelenaere <mcuelenaere@gmail.com>",
"Linus Unnebäck <linus@folkdatorn.se>",
"Victor Mateevitsi <mvictoras@gmail.com>",
"Alaric Holloway <alaric.holloway@gmail.com>",
"Bernhard K. Weisshuhn <bkw@codingforce.com>",
"Chris Riley <criley@primedia.com>",
"David Carley <dacarley@gmail.com>",
"John Tobin <john@limelightmobileinc.com>",
"Kenton Gray <kentongray@gmail.com>",
"Felix Bünemann <Felix.Buenemann@gmail.com>",
"Samy Al Zahrani <samyalzahrany@gmail.com>",
"Chintan Thakkar <lemnisk8@gmail.com>",
"F. Orlando Galashan <frulo@gmx.de>",
"Kleis Auke Wolthuizen <info@kleisauke.nl>",
"Matt Hirsch <mhirsch@media.mit.edu>",
"Matthias Thoemmes <thoemmes@gmail.com>",
"Patrick Paskaris <patrick@paskaris.gr>",
"Jérémy Lal <kapouer@melix.org>",
"Rahul Nanwani <r.nanwani@gmail.com>",
"Alice Monday <alice0meta@gmail.com>",
"Kristo Jorgenson <kristo.jorgenson@gmail.com>"
],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
"scripts": {
"test": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=10000 ./test/unit/*.js"
"clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*",
"test": "semistandard && cc && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-leak": "./test/leak/leak.sh",
"test-packaging": "./packaging/test-linux-x64.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"
},
"main": "index.js",
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "git://github.com/lovell/sharp"
@@ -27,6 +53,10 @@
"png",
"webp",
"tiff",
"gif",
"svg",
"dzi",
"image",
"resize",
"thumbnail",
"crop",
@@ -34,19 +64,44 @@
"vips"
],
"dependencies": {
"bluebird": "^2.3.11",
"color": "^0.7.1",
"nan": "^1.4.1",
"semver": "^4.1.0"
"caw": "^2.0.0",
"color": "^1.0.3",
"got": "^6.7.1",
"nan": "^2.5.1",
"semver": "^5.3.0",
"tar": "^2.2.1"
},
"devDependencies": {
"mocha": "^2.0.1",
"mocha-jshint": "^0.0.9",
"istanbul": "^0.3.2",
"coveralls": "^2.11.2"
"async": "^2.2.0",
"bufferutil": "^3.0.0",
"cc": "^1.0.0",
"cross-env": "^4.0.0",
"documentation": "^4.0.0-beta.18",
"exif-reader": "^1.0.2",
"icc": "^1.0.0",
"mocha": "^3.2.0",
"nyc": "^10.2.0",
"rimraf": "^2.5.4",
"semistandard": "^10.0.0",
"unzip": "^0.1.11"
},
"license": "Apache-2.0",
"config": {
"libvips": "8.4.2"
},
"license": "Apache 2.0",
"engines": {
"node": ">=0.10"
"node": ">=4"
},
"semistandard": {
"env": [
"mocha"
]
},
"cc": {
"linelength": "120",
"filter": [
"build/include",
"runtime/indentation_namespace"
]
}
}

57
packaging/README.md Normal file
View File

@@ -0,0 +1,57 @@
# Packaging scripts
libvips and its dependencies are provided as pre-compiled shared libraries
for the most common operating systems and CPU architectures.
During `npm install`, these binaries are fetched as tarballs from
[Bintray](https://dl.bintray.com/lovell/sharp/) via HTTPS
and stored locally within `node_modules/sharp`.
## Using a custom tarball
A custom tarball stored on the local filesystem can be used instead.
Place it in the following location, where `x.y.z` is the libvips version,
`platform` is the value of `process.platform` and
`arch` is the value of `process.arch` (plus the version number for ARM).
`node_modules/sharp/packaging/libvips-x.y.z-platform-arch.tar.gz`
For example, for libvips v8.3.3 on an ARMv6 Linux machine, use:
`node_modules/sharp/packaging/libvips-8.3.3-linux-armv6.tar.gz`
Remove any `sharp/lib` and `sharp/include` directories
before running `npm install` again.
## Creating a tarball
Most people will not need to do this; proceed with caution.
The `packaging` directory contains the top-level [build script](build.sh).
### Linux
One [build script](build/lin.sh) is used to (cross-)compile
the same shared libraries within multiple containers.
* [x64](linux-x64/Dockerfile)
* [ARMv6](linux-armv6/Dockerfile)
* [ARMv7-A](linux-armv7/Dockerfile)
* [ARMv8-A](linux-armv8/Dockerfile)
The QEMU user mode emulation binaries are required to build for
the ARMv6 platform as the Debian armhf cross-compiler erroneously
generates unsupported Thumb 2 instructions.
```sh
sudo apt-get install qemu-user-static
```
### Windows
The output of libvips' [build-win64](https://github.com/jcupitt/build-win64)
"web" target is [post-processed](build/win.sh) within a [container](win32-x64/Dockerfile).
### OS X
See [package-libvips-darwin](https://github.com/lovell/package-libvips-darwin).

43
packaging/build.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/bin/sh
set -e
if [ $# -lt 1 ]; then
echo
echo "Usage: $0 VERSION [PLATFORM]"
echo "Build shared libraries for libvips and its dependencies via containers"
echo
echo "Please specify the libvips VERSION, e.g. 8.3.3"
echo
echo "Optionally build for only one PLATFORM, defaults to building for all"
echo "Possible values for PLATFORM are: win32-x64, linux-x64, linux-armv6,"
echo "linux-armv7, linux-armv8"
echo
exit 1
fi
VERSION_VIPS="$1"
PLATFORM="${2:-all}"
# Is docker available?
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
# Windows (x64)
if [ $PLATFORM = "all" ] || [ $PLATFORM = "win32-x64" ]; then
echo "Building win32-x64..."
docker build -t vips-dev-win32-x64 win32-x64
docker run --rm -e "VERSION_VIPS=${VERSION_VIPS}" -v $PWD:/packaging vips-dev-win32-x64 sh -c "/packaging/build/win.sh"
fi
# Linux (x64, ARMv6, ARMv7, ARMv8)
for flavour in linux-x64 linux-armv6 linux-armv7 linux-armv8; do
if [ $PLATFORM = "all" ] || [ $PLATFORM = $flavour ]; then
echo "Building $flavour..."
docker build -t vips-dev-$flavour $flavour
docker run --rm -e "VERSION_VIPS=${VERSION_VIPS}" -v $PWD:/packaging vips-dev-$flavour sh -c "/packaging/build/lin.sh"
fi
done
# Display checksums
sha256sum *.tar.gz

239
packaging/build/lin.sh Executable file
View File

@@ -0,0 +1,239 @@
#!/bin/sh
set -e
# Working directories
DEPS=/deps
TARGET=/target
mkdir ${DEPS}
mkdir ${TARGET}
# Common build paths and flags
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig"
export PATH="${PATH}:${TARGET}/bin"
export CPPFLAGS="-I${TARGET}/include"
export LDFLAGS="-L${TARGET}/lib"
export CFLAGS="${FLAGS}"
export CXXFLAGS="${FLAGS}"
# Dependency version numbers
VERSION_ZLIB=1.2.10
VERSION_FFI=3.2.1
VERSION_GLIB=2.50.1
VERSION_XML2=2.9.4
VERSION_GSF=1.14.40
VERSION_EXIF=0.6.21
VERSION_LCMS2=2.8
VERSION_JPEG=1.5.1
VERSION_PNG16=1.6.28
VERSION_WEBP=0.5.1
VERSION_TIFF=4.0.6
VERSION_ORC=0.4.26
VERSION_GDKPIXBUF=2.36.0
VERSION_FREETYPE=2.7
VERSION_FONTCONFIG=2.12.1
VERSION_HARFBUZZ=1.3.2
VERSION_PIXMAN=0.34.0
VERSION_CAIRO=1.14.6
VERSION_PANGO=1.40.3
VERSION_CROCO=0.6.11
VERSION_SVG=2.40.16
VERSION_GIF=5.1.4
# Least out-of-sync Sourceforge mirror
SOURCEFORGE_MIRROR=netix
mkdir ${DEPS}/zlib
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
cd ${DEPS}/zlib
./configure --prefix=${TARGET} --uname=linux
make install
rm ${TARGET}/lib/libz.a
mkdir ${DEPS}/ffi
curl -Ls ftp://sourceware.org/pub/libffi/libffi-${VERSION_FFI}.tar.gz | tar xzC ${DEPS}/ffi --strip-components=1
cd ${DEPS}/ffi
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir
make install-strip
mkdir ${DEPS}/glib
curl -Ls https://download.gnome.org/sources/glib/2.50/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
cd ${DEPS}/glib
echo glib_cv_stack_grows=no >>glib.cache
echo glib_cv_uscore=no >>glib.cache
./configure --cache-file=glib.cache --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal --disable-libmount
make install-strip
mkdir ${DEPS}/xml2
curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
cd ${DEPS}/xml2
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--without-python --without-debug --without-docbook --without-ftp --without-html --without-legacy \
--without-pattern --without-push --without-regexps --without-schemas --without-schematron --with-zlib=${TARGET}
make install-strip
mkdir ${DEPS}/gsf
curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1
cd ${DEPS}/gsf
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/exif
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
cd ${DEPS}/exif
autoreconf -fiv
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/lcms2
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
cd ${DEPS}/lcms2
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/jpeg
curl -Ls https://github.com/libjpeg-turbo/libjpeg-turbo/archive/${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
cd ${DEPS}/jpeg
autoreconf -fiv
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg
make install-strip
mkdir ${DEPS}/png16
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
cd ${DEPS}/png16
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/webp
curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
cd ${DEPS}/webp
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-neon
make install-strip
mkdir ${DEPS}/tiff
curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
cd ${DEPS}/tiff
# Apply patches for various libtiff security vulnerabilities reported since v4.0.6
VERSION_TIFF_GIT_MASTER_SHA=$(curl -Ls https://api.github.com/repos/vadz/libtiff/git/refs/heads/master | jq -r '.object.sha' | head -c7)
curl -Ls https://github.com/vadz/libtiff/compare/Release-v4-0-6...master.patch | patch -p1 -t || true
if [ -n "${CHOST}" ]; then autoreconf -fiv; fi
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-mdi --disable-pixarlog --disable-cxx
make install-strip
mkdir ${DEPS}/orc
curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
cd ${DEPS}/orc
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
cd ${TARGET}/lib
rm -rf liborc-test-*
mkdir ${DEPS}/gdkpixbuf
curl -Ls https://download.gnome.org/sources/gdk-pixbuf/2.36/gdk-pixbuf-${VERSION_GDKPIXBUF}.tar.xz | tar xJC ${DEPS}/gdkpixbuf --strip-components=1
cd ${DEPS}/gdkpixbuf
LD_LIBRARY_PATH=${TARGET}/lib \
./configure --cache-file=gdkpixbuf.cache --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-introspection --disable-modules --disable-gio-sniffing --without-libtiff --without-gdiplus --with-included-loaders=png,jpeg
make install-strip
mkdir ${DEPS}/freetype
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/freetype/freetype2/${VERSION_FREETYPE}/freetype-${VERSION_FREETYPE}.tar.gz | tar xzC ${DEPS}/freetype --strip-components=1
cd ${DEPS}/freetype
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static
make install
mkdir ${DEPS}/fontconfig
curl -Ls https://www.freedesktop.org/software/fontconfig/release/fontconfig-${VERSION_FONTCONFIG}.tar.bz2 | tar xjC ${DEPS}/fontconfig --strip-components=1
cd ${DEPS}/fontconfig
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --enable-libxml2 --sysconfdir=/etc
make install-strip
mkdir ${DEPS}/harfbuzz
curl -Ls https://www.freedesktop.org/software/harfbuzz/release/harfbuzz-${VERSION_HARFBUZZ}.tar.bz2 | tar xjC ${DEPS}/harfbuzz --strip-components=1
cd ${DEPS}/harfbuzz
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/pixman
curl -Ls http://cairographics.org/releases/pixman-${VERSION_PIXMAN}.tar.gz | tar xzC ${DEPS}/pixman --strip-components=1
cd ${DEPS}/pixman
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-libpng --disable-arm-iwmmxt
make install-strip
mkdir ${DEPS}/cairo
curl -Ls http://cairographics.org/releases/cairo-${VERSION_CAIRO}.tar.xz | tar xJC ${DEPS}/cairo --strip-components=1
cd ${DEPS}/cairo
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-xlib --disable-xcb --disable-quartz --disable-win32 --disable-egl --disable-glx --disable-wgl \
--disable-script --disable-ps --disable-gobject --disable-trace --disable-interpreter
make install-strip
mkdir ${DEPS}/pango
curl -Ls https://download.gnome.org/sources/pango/1.40/pango-${VERSION_PANGO}.tar.xz | tar xJC ${DEPS}/pango --strip-components=1
cd ${DEPS}/pango
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/croco
curl -Ls https://download.gnome.org/sources/libcroco/0.6/libcroco-${VERSION_CROCO}.tar.xz | tar xJC ${DEPS}/croco --strip-components=1
cd ${DEPS}/croco
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/svg
curl -Ls https://download.gnome.org/sources/librsvg/2.40/librsvg-${VERSION_SVG}.tar.xz | tar xJC ${DEPS}/svg --strip-components=1
cd ${DEPS}/svg
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-introspection --disable-tools --disable-pixbuf-loader
make install-strip
mkdir ${DEPS}/gif
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/giflib/giflib-${VERSION_GIF}.tar.gz | tar xzC ${DEPS}/gif --strip-components=1
cd ${DEPS}/gif
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip
mkdir ${DEPS}/vips
curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.4/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
cd ${DEPS}/vips
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw \
--without-magick --without-pangoft2 --without-ppm --without-analyze --without-radiance \
--with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib
make install-strip
# Remove the old C++ bindings
cd ${TARGET}/include
rm -rf vips/vipsc++.h vips/vipscpp.h
cd ${TARGET}/lib
rm -rf pkgconfig .libs *.la libvipsCC*
# Create JSON file of version numbers
cd ${TARGET}
echo "{\n\
\"cairo\": \"${VERSION_CAIRO}\",\n\
\"croco\": \"${VERSION_CROCO}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\
\"fontconfig\": \"${VERSION_FONTCONFIG}\",\n\
\"freetype\": \"${VERSION_FREETYPE}\",\n\
\"gdkpixbuf\": \"${VERSION_GDKPIXBUF}\",\n\
\"gif\": \"${VERSION_GIF}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\
\"harfbuzz\": \"${VERSION_HARFBUZZ}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\
\"pango\": \"${VERSION_PANGO}\",\n\
\"pixman\": \"${VERSION_PIXMAN}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"svg\": \"${VERSION_SVG}\",\n\
\"tiff\": \"${VERSION_TIFF}-${VERSION_TIFF_GIT_MASTER_SHA}\",\n\
\"vips\": \"${VERSION_VIPS}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"zlib\": \"${VERSION_ZLIB}\"\n\
}" >lib/versions.json
# Create .tar.gz
tar czf /packaging/libvips-${VERSION_VIPS}-${PLATFORM}.tar.gz include lib
advdef --recompress --shrink-insane /packaging/libvips-${VERSION_VIPS}-${PLATFORM}.tar.gz

22
packaging/build/win.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
set -e
# Fetch and unzip
mkdir /vips
cd /vips
curl -L -O https://github.com/lovell/build-win64/releases/download/v${VERSION_VIPS}/vips-dev-w64-web-${VERSION_VIPS}.zip
unzip vips-dev-w64-web-${VERSION_VIPS}.zip
# Clean and zip
cd /vips/vips-dev-8.4
rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll
cp bin/*.dll lib/
cp -r lib64/* lib/
# Temp patch for __declspec ordering
curl -L -o include/vips/VImage8.h https://raw.githubusercontent.com/lovell/libvips/e1aef0445bf123d2de000bc7f2ef97b9f788eea0/cplusplus/include/vips/VImage8.h
echo "Creating tarball"
tar czf /packaging/libvips-${VERSION_VIPS}-${PLATFORM}.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll
echo "Shrinking tarball"
advdef --recompress --shrink-insane /packaging/libvips-${VERSION_VIPS}-${PLATFORM}.tar.gz

View File

@@ -0,0 +1,15 @@
FROM socialdefect/raspbian-jessie-core
MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Rasbian-based container suitable for compiling Linux ARMv6 binaries
# Requires the QEMU user mode emulation binaries on the host machine
# Build dependencies
RUN \
apt-get update && \
apt-get install -y build-essential curl autoconf libtool nasm gtk-doc-tools texinfo advancecomp libglib2.0-dev jq
# Compiler settings
ENV \
PLATFORM=linux-armv6 \
FLAGS="-Os"

View File

@@ -0,0 +1,20 @@
FROM debian:jessie
MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Debian-based container suitable for cross-compiling Linux ARMv7-A binaries
# Build dependencies
RUN \
apt-get update && \
apt-get install -y curl && \
echo "deb http://emdebian.org/tools/debian/ jessie main" | tee /etc/apt/sources.list.d/crosstools.list && \
curl http://emdebian.org/tools/debian/emdebian-toolchain-archive.key | apt-key add - && \
dpkg --add-architecture armhf && \
apt-get update && \
apt-get install -y crossbuild-essential-armhf autoconf libtool nasm gtk-doc-tools texinfo advancecomp libglib2.0-dev jq
# Compiler settings
ENV \
PLATFORM=linux-armv7 \
CHOST=arm-linux-gnueabihf \
FLAGS="-marm -march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard -Os"

View File

@@ -0,0 +1,18 @@
FROM debian:stretch
MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Debian-based container suitable for cross-compiling Linux ARMv8-A binaries
# Build dependencies
RUN \
apt-get update && \
apt-get install -y curl && \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y crossbuild-essential-arm64 autoconf libtool nasm gtk-doc-tools texinfo advancecomp libglib2.0-dev jq
# Compiler settings
ENV \
PLATFORM=linux-armv8 \
CHOST=aarch64-linux-gnu \
FLAGS="-march=armv8-a -Os"

View File

@@ -0,0 +1,16 @@
FROM debian:wheezy
MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Debian-based container suitable for building Linux x64 binaries
# Build dependencies
RUN \
echo "deb http://ftp.debian.org/debian wheezy-backports main" | tee /etc/apt/sources.list.d/wheezy-backports.list && \
apt-get update && \
apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo advancecomp && \
apt-get -t wheezy-backports install -y jq
# Compiler settings
ENV \
PLATFORM="linux-x64" \
FLAGS="-O3"

28
packaging/test-linux-arm.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 IP"
echo "Test sharp on ARM using Docker, where IP is"
echo "the address of a Raspberry Pi running HypriotOS"
exit 1
fi
IP="$1"
echo "Verifying connectivity to $IP"
if ! ping -c 1 $IP; then
echo "Could not connect to $IP"
exit 1
fi
if ! type sshpass >/dev/null; then
echo "Please install sshpass"
exit 1
fi
export SSHPASS=hypriot
echo "Copying sharp source to device"
sshpass -e scp -o PreferredAuthentications=password -r ../../sharp pirate@${IP}:/home/pirate/sharp
echo "Compile and test within container"
sshpass -e ssh -o PreferredAuthentications=password -t pirate@${IP} "docker run --rm -v \${PWD}/sharp:/s hypriot/rpi-node:6 sh -c 'cd /s && npm install --unsafe-perm && npm test'"

56
packaging/test-linux-x64.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/sh
# Verify docker is available
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
version_node=6.3.0
test="npm run clean; npm install --unsafe-perm; npm test"
# Debian 7, 8
# Ubuntu 14.04
for dist in wheezy jessie trusty; do
echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/$dist:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; ./packaging/test/debian.sh; $test";
then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log
fi
done
# Centos 7
echo "Testing centos7..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/centos7:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; $test";
then echo "centos7 OK"
else echo "centos7 fail" && cat packaging/$dist.log
fi
# Fedora 22
echo "Testing fedora22..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/fedora22:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; $test";
then echo "fedora22 OK"
else echo "fedora22 fail" && cat packaging/$dist.log
fi
# openSUSE 13.2
echo "Testing opensuse..."
if docker run -i -t --rm -v $PWD:/v opensuse:13.2 >packaging/opensuse.log 2>&1 /bin/sh -c "cd /v; ./packaging/test/opensuse.sh; $test";
then echo "opensuse OK"
else echo "opensuse fail" && cat packaging/opensuse.log
fi
# Archlinux 2015.06.01
echo "Testing archlinux..."
if docker run -i -t --rm -v $PWD:/v pritunl/archlinux:latest >packaging/archlinux.log 2>&1 sh -c "cd /v; ./packaging/test/archlinux.sh; $test";
then echo "archlinux OK"
else echo "archlinux fail" && cat packaging/archlinux.log
fi
# Alpine
echo "Testing alpine..."
if docker run -i -t --rm -v $PWD:/v -e "SHARP_TEST_WITHOUT_CACHE=0" alpine:edge >packaging/alpine.log 2>&1 sh -c "cd /v; ./packaging/test/alpine.sh; $test";
then echo "alpine OK"
else echo "alpine fail" && cat packaging/alpine.log
fi

7
packaging/test/alpine.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
# Install build dependencies
apk add --update make gcc g++ python nodejs
# Install libvips from aports/testing
apk add --update --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing vips-dev

5
packaging/test/archlinux.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
# Install Node.js on Archlinux
pacman -Sy --noconfirm gcc make python2 nodejs npm | cat
ln -s /usr/bin/python2 /usr/bin/python

5
packaging/test/debian.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
# Install pkg-config on Debian/Ubuntu
apt-get update
apt-get install -y pkg-config

7
packaging/test/opensuse.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
# Install Node.js on openSUSE 13.2
zypper addrepo http://download.opensuse.org/repositories/devel:languages:nodejs/openSUSE_13.2/devel:languages:nodejs.repo
zypper --gpg-auto-import-keys refresh
zypper --non-interactive install gcc-c++ make nodejs-devel npm
npm install -g npm

View File

@@ -0,0 +1,8 @@
FROM debian:jessie
MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Debian-based container suitable for post-processing Windows x64 binaries
RUN apt-get update && apt-get install -y curl zip advancecomp
ENV PLATFORM=win32-x64

View File

@@ -1,151 +0,0 @@
#!/bin/sh
# Ensures libvips is installed and attempts to install it if not
# Currently supports:
# * Mac OS
# * Debian Linux
# * Debian 7, 8
# * Ubuntu 12.04, 14.04, 14.10, 15.04
# * Mint 13, 17
# * Red Hat Linux
# * RHEL/Centos/Scientific 6, 7
# * Fedora 21, 22
vips_version_minimum=7.40.0
vips_version_latest_major=7.42
vips_version_latest_minor=0
install_libvips_from_source() {
echo "Compiling libvips $vips_version_latest_major.$vips_version_latest_minor from source"
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major/vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
tar zvxf vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
cd vips-$vips_version_latest_major.$vips_version_latest_minor
./configure --enable-debug=no --enable-docs=no --enable-cxx=yes --without-python --without-orc --without-fftw --without-gsf $1
make
make install
cd ..
rm -rf vips-$vips_version_latest_major.$vips_version_latest_minor
rm vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
ldconfig
echo "Installed libvips $vips_version_latest_major.$vips_version_latest_minor"
}
sorry() {
echo "Sorry, I don't yet know how to install libvips on $1"
exit 1
}
# Is libvips already installed, and is it at least the minimum required version?
if ! type pkg-config >/dev/null; then
sorry "a system without pkg-config"
fi
pkg_config_path_homebrew=`which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR || true`
pkg_config_path="$pkg_config_path_homebrew:$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig"
PKG_CONFIG_PATH=$pkg_config_path pkg-config --exists vips
if [ $? -eq 0 ]; then
vips_version_found=$(PKG_CONFIG_PATH=$pkg_config_path pkg-config --modversion vips)
pkg-config --atleast-version=$vips_version_minimum vips
if [ $? -eq 0 ]; then
# Found suitable version of libvips
echo "Found libvips $vips_version_found"
exit 0
fi
echo "Found libvips $vips_version_found but require $vips_version_minimum"
else
echo "Could not find libvips using a PKG_CONFIG_PATH of '$pkg_config_path'"
fi
# Verify root/sudo access
if [ "$(id -u)" -ne "0" ]; then
echo "Sorry, I need root/sudo access to continue"
exit 1
fi
# OS-specific installations of libvips follows
case $(uname -s) in
*[Dd]arwin*)
# Mac OS
echo "Detected Mac OS"
if type "brew" > /dev/null; then
echo "Installing libvips via homebrew"
brew install homebrew/science/vips --with-webp --with-graphicsmagick
elif type "port" > /dev/null; then
echo "Installing libvips via MacPorts"
port install vips
else
sorry "Mac OS without homebrew or MacPorts"
fi
;;
*)
if [ -f /etc/debian_version ]; then
# Debian Linux
DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in
jessie|vivid)
# Debian 8, Ubuntu 15
echo "Installing libvips via apt-get"
apt-get install -y libvips-dev
;;
trusty|utopic|qiana|rebecca)
# Ubuntu 14, Mint 17
echo "Installing libvips dependencies via apt-get"
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev liblcms2-dev libxml2-dev swig libmagickwand-dev curl
install_libvips_from_source
;;
precise|wheezy|maya)
# Debian 7, Ubuntu 12.04, Mint 13
echo "Installing libvips dependencies via apt-get"
add-apt-repository -y ppa:lyrasis/precise-backports
apt-get update
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev liblcms2-dev libxml2-dev swig libmagickwand-dev curl
install_libvips_from_source
;;
*)
# Unsupported Debian-based OS
sorry "Debian-based $DISTRO"
;;
esac
elif [ -f /etc/redhat-release ]; then
# Red Hat Linux
RELEASE=$(cat /etc/redhat-release)
echo "Detected Red Hat Linux '$RELEASE'"
case $RELEASE in
"Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
# RHEL/CentOS 7
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr"
;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS 6
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel curl
yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
yum install -y --enablerepo=nux-dextop gobject-introspection-devel
yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
yum install -y --enablerepo=remi libwebp-devel
install_libvips_from_source "--prefix=/usr"
;;
"Fedora release 21 "*|"Fedora release 22 "*)
# Fedora 21, 22
echo "Installing libvips via yum"
yum install vips-devel
;;
*)
# Unsupported RHEL-based OS
sorry "$RELEASE"
;;
esac
else
# Unsupported OS
sorry "$(uname -a)"
fi
;;
esac

488
src/common.cc Executable file → Normal file
View File

@@ -1,11 +1,78 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib>
#include <string>
#include <string.h>
#include <vips/vips.h>
#include <vector>
#include <node.h>
#include <node_buffer.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
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();
}
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr) {
return *Nan::Utf8String(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked());
}
// 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();
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);
}
// Density for vector-based input
if (HasAttr(input, "density")) {
descriptor->density = AttrTo<uint32_t>(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");
}
// 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);
}
}
return descriptor;
}
// How many tasks are in the queue?
volatile int counterQueue = 0;
@@ -28,19 +95,38 @@ namespace sharp {
bool IsTiff(std::string const &str) {
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
}
bool IsDz(std::string const &str) {
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
}
bool IsDzZip(std::string const &str) {
return EndsWith(str, ".zip") || EndsWith(str, ".ZIP") || EndsWith(str, ".szi") || EndsWith(str, ".SZI");
}
bool IsV(std::string const &str) {
return EndsWith(str, ".v") || EndsWith(str, ".V") || EndsWith(str, ".vips") || EndsWith(str, ".VIPS");
}
// Buffer content checkers
unsigned char const MARKER_JPEG[] = {0xff, 0xd8};
unsigned char const MARKER_PNG[] = {0x89, 0x50};
unsigned char const MARKER_WEBP[] = {0x52, 0x49};
static bool buffer_is_tiff(char *buffer, size_t len) {
return (
len >= 4 && (
(buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == '\0' && (buffer[3] == '*' || buffer[3] == '+')) ||
(buffer[0] == 'I' && buffer[1] == 'I' && (buffer[2] == '*' || buffer[2] == '+') && buffer[3] == '\0')
)
);
/*
Provide a string identifier for the given image type.
*/
std::string ImageTypeId(ImageType const imageType) {
std::string id;
switch (imageType) {
case ImageType::JPEG: id = "jpeg"; break;
case ImageType::PNG: id = "png"; break;
case ImageType::WEBP: id = "webp"; break;
case ImageType::TIFF: id = "tiff"; break;
case ImageType::GIF: id = "gif"; break;
case ImageType::SVG: id = "svg"; 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::RAW: id = "raw"; break;
case ImageType::UNKNOWN: id = "unknown"; break;
}
return id;
}
/*
@@ -48,118 +134,356 @@ namespace sharp {
*/
ImageType DetermineImageType(void *buffer, size_t const length) {
ImageType imageType = ImageType::UNKNOWN;
if (length >= 4) {
if (memcmp(MARKER_JPEG, buffer, 2) == 0) {
char const *load = vips_foreign_find_load_buffer(buffer, length);
if (load != NULL) {
std::string const loader = load;
if (EndsWith(loader, "JpegBuffer")) {
imageType = ImageType::JPEG;
} else if (memcmp(MARKER_PNG, buffer, 2) == 0) {
} else if (EndsWith(loader, "PngBuffer")) {
imageType = ImageType::PNG;
} else if (memcmp(MARKER_WEBP, buffer, 2) == 0) {
} else if (EndsWith(loader, "WebpBuffer")) {
imageType = ImageType::WEBP;
} else if (buffer_is_tiff(static_cast<char*>(buffer), length)) {
} else if (EndsWith(loader, "TiffBuffer")) {
imageType = ImageType::TIFF;
} else if (EndsWith(loader, "GifBuffer")) {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgBuffer")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "PdfBuffer")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "MagickBuffer")) {
imageType = ImageType::MAGICK;
}
}
return imageType;
}
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(ImageType imageType, void *buffer, size_t const length, VipsAccess const access) {
VipsImage *image = NULL;
if (imageType == ImageType::JPEG) {
vips_jpegload_buffer(buffer, length, &image, "access", access, NULL);
} else if (imageType == ImageType::PNG) {
vips_pngload_buffer(buffer, length, &image, "access", access, NULL);
} else if (imageType == ImageType::WEBP) {
vips_webpload_buffer(buffer, length, &image, "access", access, NULL);
} else if (imageType == ImageType::TIFF) {
vips_tiffload_buffer(buffer, length, &image, "access", access, NULL);
}
return image;
}
/*
Inpect the first 2-4 bytes of a file to determine image format
Determine image format, reads the first few bytes of the file
*/
ImageType DetermineImageType(char const *file) {
ImageType imageType = ImageType::UNKNOWN;
if (vips_foreign_is_a("jpegload", file)) {
imageType = ImageType::JPEG;
} else if (vips_foreign_is_a("pngload", file)) {
imageType = ImageType::PNG;
} else if (vips_foreign_is_a("webpload", file)) {
imageType = ImageType::WEBP;
} else if (vips_foreign_is_a("tiffload", file)) {
imageType = ImageType::TIFF;
} else if(vips_foreign_is_a("magickload", file)) {
imageType = ImageType::MAGICK;
char const *load = vips_foreign_find_load(file);
if (load != nullptr) {
std::string const loader = load;
if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) {
imageType = ImageType::PNG;
} else if (EndsWith(loader, "WebpFile")) {
imageType = ImageType::WEBP;
} else if (EndsWith(loader, "Openslide")) {
imageType = ImageType::OPENSLIDE;
} else if (EndsWith(loader, "TiffFile")) {
imageType = ImageType::TIFF;
} else if (EndsWith(loader, "GifFile")) {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgFile")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "PdfFile")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "Ppm")) {
imageType = ImageType::PPM;
} else if (EndsWith(loader, "Fits")) {
imageType = ImageType::FITS;
} else if (EndsWith(loader, "Vips")) {
imageType = ImageType::VIPS;
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
imageType = ImageType::MAGICK;
}
}
return imageType;
}
/*
Initialise and return a VipsImage from a file.
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access) {
VipsImage *image = NULL;
if (imageType == ImageType::JPEG) {
vips_jpegload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::PNG) {
vips_pngload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::WEBP) {
vips_webpload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::TIFF) {
vips_tiffload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::MAGICK) {
vips_magickload(file, &image, "access", access, NULL);
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
VImage image;
ImageType imageType;
if (descriptor->buffer != nullptr) {
if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, VIPS_FORMAT_UCHAR);
if (descriptor->rawChannels < 3) {
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
} else {
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
}
imageType = ImageType::RAW;
} else {
// Compressed data
imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()->set("access", accessMethod);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density));
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
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);
}
} catch (...) {
throw vips::VError("Input buffer has corrupt header");
}
} else {
throw vips::VError("Input buffer contains unsupported image format");
}
}
} else {
if (descriptor->createChannels > 0) {
// Create new image
std::vector<double> background = {
descriptor->createBackground[0],
descriptor->createBackground[1],
descriptor->createBackground[2]
};
if (descriptor->createChannels == 4) {
background.push_back(descriptor->createBackground[3]);
}
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
imageType = ImageType::RAW;
} else {
// From filesystem
imageType = DetermineImageType(descriptor->file.data());
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()->set("access", accessMethod);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density));
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
}
} catch (...) {
throw vips::VError("Input file has corrupt header");
}
} else {
throw vips::VError("Input file is missing or of an unsupported image format");
}
}
}
return image;
return std::make_tuple(image, imageType);
}
/*
Does this image have an embedded profile?
*/
bool HasProfile(VipsImage *image) {
return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE;
bool HasProfile(VImage image) {
return (image.get_typeof(VIPS_META_ICC_NAME) != 0) ? TRUE : FALSE;
}
/*
Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this.
*/
bool HasAlpha(VipsImage *image) {
bool HasAlpha(VImage image) {
int const bands = image.bands();
VipsInterpretation const interpretation = image.interpretation();
return (
(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) ||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) ||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
);
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK));
}
/*
Get EXIF Orientation of image, if any.
*/
int ExifOrientation(VipsImage const *image) {
int ExifOrientation(VImage image) {
int orientation = 0;
const char *exif;
if (
vips_image_get_typeof(image, "exif-ifd0-Orientation") != 0 &&
!vips_image_get_string(image, "exif-ifd0-Orientation", &exif)
) {
orientation = atoi(&exif[0]);
if (image.get_typeof(VIPS_META_ORIENTATION) != 0) {
orientation = image.get_int(VIPS_META_ORIENTATION);
}
return orientation;
}
/*
Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation.
Set EXIF Orientation of image.
*/
int InterpolatorWindowSize(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name);
int window_size = vips_interpolate_get_window_size(interpolator);
g_object_unref(interpolator);
return window_size;
void SetExifOrientation(VImage image, int const orientation) {
image.set(VIPS_META_ORIENTATION, orientation);
}
} // namespace
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image) {
vips_image_remove(image.get_image(), VIPS_META_ORIENTATION);
}
/*
Does this image have a non-default density?
*/
bool HasDensity(VImage image) {
return image.xres() > 1.0;
}
/*
Get pixels/mm resolution as pixels/inch density.
*/
int GetDensity(VImage image) {
return static_cast<int>(round(image.xres() * 25.4));
}
/*
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");
}
/*
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);
}
}
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const gravity) {
int left = 0;
int top = 0;
switch (gravity) {
case 1:
// North
left = (inWidth - outWidth + 1) / 2;
break;
case 2:
// East
left = inWidth - outWidth;
top = (inHeight - outHeight + 1) / 2;
break;
case 3:
// South
left = (inWidth - outWidth + 1) / 2;
top = inHeight - outHeight;
break;
case 4:
// West
top = (inHeight - outHeight + 1) / 2;
break;
case 5:
// Northeast
left = inWidth - outWidth;
break;
case 6:
// Southeast
left = inWidth - outWidth;
top = inHeight - outHeight;
case 7:
// Southwest
top = inHeight - outHeight;
case 8:
// Northwest
break;
default:
// Centre
left = (inWidth - outWidth + 1) / 2;
top = (inHeight - outHeight + 1) / 2;
}
return std::make_tuple(left, top);
}
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given x and y offsets.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const x, int const y) {
// default values
int left = 0;
int top = 0;
// assign only if valid
if (x >= 0 && x < (inWidth - outWidth)) {
left = x;
} else if (x >= (inWidth - outWidth)) {
left = inWidth - outWidth;
}
if (y >= 0 && y < (inHeight - outHeight)) {
top = y;
} else if (y >= (inHeight - outHeight)) {
top = inHeight - outHeight;
}
// the resulting left and top could have been outside the image after calculation from bottom/right edges
if (left < 0) {
left = 0;
}
if (top < 0) {
top = 0;
}
return std::make_tuple(left, top);
}
/*
Are pixel values in this image 16-bit integer?
*/
bool Is16Bit(VipsInterpretation const interpretation) {
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
}
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation) {
return Is16Bit(interpretation) ? 65535.0 : 255.0;
}
/*
Get boolean operation type from string
*/
VipsOperationBoolean GetBooleanOperation(std::string const opStr) {
return static_cast<VipsOperationBoolean>(
vips_enum_from_nick(nullptr, VIPS_TYPE_OPERATION_BOOLEAN, opStr.data()));
}
/*
Get interpretation type from string
*/
VipsInterpretation GetInterpretation(std::string const typeStr) {
return static_cast<VipsInterpretation>(
vips_enum_from_nick(nullptr, VIPS_TYPE_INTERPRETATION, typeStr.data()));
}
/*
Convert RGBA value to another colourspace
*/
std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba, VipsInterpretation const interpretation) {
int const bands = static_cast<int>(rgba.size());
if (bands < 3 || interpretation == VIPS_INTERPRETATION_sRGB || interpretation == VIPS_INTERPRETATION_RGB) {
return rgba;
} else {
VImage pixel = VImage::new_matrix(1, 1);
pixel.set("bands", bands);
pixel = pixel.new_from_image(rgba);
pixel = pixel.colourspace(interpretation, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB));
return pixel(0, 0);
}
}
} // namespace sharp

201
src/common.h Executable file → Normal file
View File

@@ -1,15 +1,111 @@
#ifndef SHARP_COMMON_H
#define SHARP_COMMON_H
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_COMMON_H_
#define SRC_COMMON_H_
#include <string>
#include <tuple>
#include <vector>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 4))
#error libvips version 8.4.x required - see sharp.dimens.io/page/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.dimens.io/page/install#prerequisites
#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.dimens.io/page/install#prerequisites
#endif
#endif
using vips::VImage;
namespace sharp {
struct InputDescriptor {
std::string name;
std::string file;
char *buffer;
size_t bufferLength;
int density;
int rawChannels;
int rawWidth;
int rawHeight;
int createChannels;
int createWidth;
int createHeight;
double createBackground[4];
InputDescriptor():
buffer(nullptr),
bufferLength(0),
density(72),
rawChannels(0),
rawWidth(0),
rawHeight(0),
createChannels(0),
createWidth(0),
createHeight(0) {
createBackground[0] = 0.0;
createBackground[1] = 0.0;
createBackground[2] = 0.0;
createBackground[3] = 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();
}
// 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);
enum class ImageType {
UNKNOWN,
JPEG,
PNG,
WEBP,
TIFF,
MAGICK
GIF,
SVG,
PDF,
MAGICK,
OPENSLIDE,
PPM,
FITS,
VIPS,
RAW,
UNKNOWN
};
// How many tasks are in the queue?
@@ -23,6 +119,14 @@ namespace sharp {
bool IsPng(std::string const &str);
bool IsWebp(std::string const &str);
bool IsTiff(std::string const &str);
bool IsDz(std::string const &str);
bool IsDzZip(std::string const &str);
bool IsV(std::string const &str);
/*
Provide a string identifier for the given image type.
*/
std::string ImageTypeId(ImageType const imageType);
/*
Determine image format of a buffer.
@@ -35,37 +139,96 @@ namespace sharp {
ImageType DetermineImageType(char const *file);
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
VipsImage* InitImage(ImageType imageType, void *buffer, size_t const length, VipsAccess const access);
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access);
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod);
/*
Does this image have an embedded profile?
*/
bool HasProfile(VipsImage *image);
bool HasProfile(VImage image);
/*
Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this.
*/
bool HasAlpha(VipsImage *image);
bool HasAlpha(VImage image);
/*
Get EXIF Orientation of image, if any.
*/
int ExifOrientation(VipsImage const *image);
int ExifOrientation(VImage image);
/*
Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation.
Set EXIF Orientation of image.
*/
int InterpolatorWindowSize(char const *name);
void SetExifOrientation(VImage image, int const orientation);
} // namespace
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image);
#endif
/*
Does this image have a non-default density?
*/
bool HasDensity(VImage image);
/*
Get pixels/mm resolution as pixels/inch density.
*/
int GetDensity(VImage image);
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const int density);
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
void FreeCallback(char* data, void* hint);
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const gravity);
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given x and y offsets of the output image.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const x, int const y);
/*
Are pixel values in this image 16-bit integer?
*/
bool Is16Bit(VipsInterpretation const interpretation);
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation);
/*
Get boolean operation type from string
*/
VipsOperationBoolean GetBooleanOperation(std::string const opStr);
/*
Get interpretation type from string
*/
VipsInterpretation GetInterpretation(std::string const typeStr);
/*
Convert RGBA value to another colourspace
*/
std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba, VipsInterpretation const interpretation);
} // namespace sharp
#endif // SRC_COMMON_H_

View File

@@ -0,0 +1,52 @@
// Code for error type
/*
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 <iostream>
#include <vips/vips8>
VIPS_NAMESPACE_START
std::ostream &operator<<( std::ostream &file, const VError &err )
{
err.ostream_print( file );
return( file );
}
void VError::ostream_print( std::ostream &file ) const
{
file << _what;
}
VIPS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
/* Object part of VInterpolate 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
VInterpolate
VInterpolate::new_from_name( const char *name, VOption *options )
{
VipsInterpolate *interp;
if( !(interp = vips_interpolate_new( name )) ) {
delete options;
throw VError();
}
delete options;
VInterpolate out( interp );
return( out );
}
VOption *
VOption::set( const char *name, VInterpolate value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_INTERPOLATE );
g_value_set_object( &pair->value, value.get_interpolate() );
options.push_back( pair );
return( this );
}
VIPS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

208
src/metadata.cc Executable file → Normal file
View File

@@ -1,113 +1,135 @@
#include <node.h>
#include <vips/vips.h>
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#include <numeric>
#include <vector>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
#include "metadata.h"
using namespace v8;
using namespace sharp;
struct MetadataBaton {
// Input
std::string fileIn;
void* bufferIn;
size_t bufferInLength;
// Output
std::string format;
int width;
int height;
std::string space;
int channels;
bool hasProfile;
bool hasAlpha;
int orientation;
std::string err;
MetadataBaton():
bufferInLength(0),
orientation(0) {}
};
class MetadataWorker : public NanAsyncWorker {
class MetadataWorker : public Nan::AsyncWorker {
public:
MetadataWorker(NanCallback *callback, MetadataBaton *baton) : NanAsyncWorker(callback), baton(baton) {}
MetadataWorker(
Nan::Callback *callback, MetadataBaton *baton,
std::vector<v8::Local<v8::Object>> const buffersToPersist)
: Nan::AsyncWorker(callback), baton(baton), 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() {}
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&counterQueue);
g_atomic_int_dec_and_test(&sharp::counterQueue);
ImageType imageType = ImageType::UNKNOWN;
VipsImage *image = NULL;
if (baton->bufferInLength > 1) {
// From buffer
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (imageType != ImageType::UNKNOWN) {
image = InitImage(imageType, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
} else {
(baton->err).append("Input buffer contains unsupported image format");
}
} else {
// From file
imageType = DetermineImageType(baton->fileIn.c_str());
if (imageType != ImageType::UNKNOWN) {
image = InitImage(imageType, baton->fileIn.c_str(), VIPS_ACCESS_RANDOM);
} else {
(baton->err).append("File is of an unsupported image format");
}
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, VIPS_ACCESS_SEQUENTIAL);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
if (image != NULL && imageType != ImageType::UNKNOWN) {
if (imageType != sharp::ImageType::UNKNOWN) {
// Image type
switch (imageType) {
case ImageType::JPEG: baton->format = "jpeg"; break;
case ImageType::PNG: baton->format = "png"; break;
case ImageType::WEBP: baton->format = "webp"; break;
case ImageType::TIFF: baton->format = "tiff"; break;
case ImageType::MAGICK: baton->format = "magick"; break;
case ImageType::UNKNOWN: break;
}
baton->format = sharp::ImageTypeId(imageType);
// VipsImage attributes
baton->width = image->Xsize;
baton->height = image->Ysize;
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
baton->channels = image->Bands;
baton->hasProfile = HasProfile(image);
baton->width = image.width();
baton->height = image.height();
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
baton->channels = image.bands();
if (sharp::HasDensity(image)) {
baton->density = sharp::GetDensity(image);
}
baton->hasProfile = sharp::HasProfile(image);
// Derived attributes
baton->hasAlpha = HasAlpha(image);
baton->orientation = ExifOrientation(image);
// Drop image reference
g_object_unref(image);
baton->hasAlpha = sharp::HasAlpha(image);
baton->orientation = sharp::ExifOrientation(image);
// EXIF
if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
size_t exifLength;
void const *exif = image.get_blob(VIPS_META_EXIF_NAME, &exifLength);
baton->exif = static_cast<char*>(g_malloc(exifLength));
memcpy(baton->exif, exif, exifLength);
baton->exifLength = exifLength;
}
// ICC profile
if (image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
size_t iccLength;
void const *icc = image.get_blob(VIPS_META_ICC_NAME, &iccLength);
baton->icc = static_cast<char*>(g_malloc(iccLength));
memcpy(baton->icc, icc, iccLength);
baton->iccLength = iccLength;
}
}
// Clean up
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback () {
NanScope();
void HandleOKCallback() {
using Nan::New;
using Nan::Set;
Nan::HandleScope();
Handle<Value> argv[2] = { NanNull(), NanNull() };
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
// Error
argv[0] = Exception::Error(NanNew<String>(baton->err.data(), baton->err.size()));
argv[0] = Nan::Error(baton->err.data());
} else {
// Metadata Object
Local<Object> info = NanNew<Object>();
info->Set(NanNew<String>("format"), NanNew<String>(baton->format));
info->Set(NanNew<String>("width"), NanNew<Number>(baton->width));
info->Set(NanNew<String>("height"), NanNew<Number>(baton->height));
info->Set(NanNew<String>("space"), NanNew<String>(baton->space));
info->Set(NanNew<String>("channels"), NanNew<Number>(baton->channels));
info->Set(NanNew<String>("hasProfile"), NanNew<Boolean>(baton->hasProfile));
info->Set(NanNew<String>("hasAlpha"), NanNew<Boolean>(baton->hasAlpha));
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));
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) {
info->Set(NanNew<String>("orientation"), NanNew<Number>(baton->orientation));
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());
}
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;
// Return to JavaScript
@@ -116,33 +138,27 @@ class MetadataWorker : public NanAsyncWorker {
private:
MetadataBaton* baton;
std::vector<v8::Local<v8::Object>> buffersToPersist;
};
/*
metadata(options, callback)
*/
NAN_METHOD(metadata) {
NanScope();
// Input Buffers must not undergo GC compaction during processing
std::vector<v8::Local<v8::Object>> buffersToPersist;
// V8 objects are converted to non-V8 types held in the baton struct
MetadataBaton *baton = new MetadataBaton;
Local<Object> options = args[0]->ToObject();
v8::Local<v8::Object> options = info[0].As<v8::Object>();
// Input filename
baton->fileIn = *String::Utf8Value(options->Get(NanNew<String>("fileIn"))->ToString());
// Input Buffer object
if (options->Get(NanNew<String>("bufferIn"))->IsObject()) {
Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject();
baton->bufferInLength = node::Buffer::Length(buffer);
baton->bufferIn = node::Buffer::Data(buffer);
}
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
// Join queue for worker thread
NanCallback *callback = new NanCallback(args[1].As<v8::Function>());
NanAsyncQueueWorker(new MetadataWorker(callback, baton));
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, buffersToPersist));
// Increment queued task counter
g_atomic_int_inc(&counterQueue);
NanReturnUndefined();
g_atomic_int_inc(&sharp::counterQueue);
}

59
src/metadata.h Executable file → Normal file
View File

@@ -1,8 +1,59 @@
#ifndef SHARP_METADATA_H
#define SHARP_METADATA_H
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#ifndef SRC_METADATA_H_
#define SRC_METADATA_H_
#include <string>
#include <nan.h>
#include "./common.h"
struct MetadataBaton {
// Input
sharp::InputDescriptor *input;
// Output
std::string format;
int width;
int height;
std::string space;
int channels;
int density;
bool hasProfile;
bool hasAlpha;
int orientation;
char *exif;
size_t exifLength;
char *icc;
size_t iccLength;
std::string err;
MetadataBaton():
input(nullptr),
width(0),
height(0),
channels(0),
density(0),
hasProfile(false),
hasAlpha(false),
orientation(0),
exif(nullptr),
exifLength(0),
icc(nullptr),
iccLength(0) {}
};
NAN_METHOD(metadata);
#endif
#endif // SRC_METADATA_H_

508
src/operations.cc Normal file
View File

@@ -0,0 +1,508 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vector>
#include <vips/vips8>
#include "common.h"
#include "operations.h"
using vips::VImage;
using vips::VError;
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) {
if (IsInputValidForComposition(src, dst)) {
// Enlarge overlay src, if required
if (src.width() < dst.width() || src.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(), src.width(), src.height(), gravity);
// Embed onto transparent background
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background));
}
return CompositeImage(src, dst);
}
// If the input was not valid for composition the return the input image itself
return dst;
}
VImage Composite(VImage src, VImage dst, const int x, const int y) {
if (IsInputValidForComposition(src, dst)) {
// Enlarge overlay src, if required
if (src.width() < dst.width() || src.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(), src.width(), src.height(), x, y);
// Embed onto transparent background
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background));
}
return CompositeImage(src, dst);
}
// If the input was not valid for composition the return the input image itself
return dst;
}
bool IsInputValidForComposition(VImage src, VImage dst) {
using sharp::CalculateCrop;
using sharp::HasAlpha;
if (!HasAlpha(src)) {
throw VError("Overlay image must have an alpha channel");
}
if (!HasAlpha(dst)) {
throw VError("Image to be overlaid must have an alpha channel");
}
if (src.width() > dst.width() || src.height() > dst.height()) {
throw VError("Overlay image must have same dimensions or smaller");
}
return true;
}
VImage CompositeImage(VImage src, VImage dst) {
// 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);
}
/*
Cutout src over dst with given gravity.
*/
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");
}
if (!HasAlpha(dst)) {
throw VError("Image to be overlaid must have an alpha channel");
}
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()));
}
/*
* Stretch luminance to cover full dynamic range.
*/
VImage Normalise(VImage image) {
// Get original colourspace
VipsInterpretation typeBeforeNormalize = image.interpretation();
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
}
// Convert to LAB colourspace
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
// Extract luminance
VImage luminance = lab[0];
// Find luminance range
VImage stats = luminance.stats();
double min = stats(0, 0)[0];
double max = stats(1, 0)[0];
if (min != max) {
// Extract chroma
VImage chroma = lab.extract_band(1, VImage::option()->set("n", 2));
// Calculate multiplication factor and addition
double f = 100.0 / (max - min);
double a = -(min * f);
// Scale luminance, join to chroma, convert back to original colourspace
VImage normalized = luminance.linear(f, a).bandjoin(chroma).colourspace(typeBeforeNormalize);
// 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
return normalized.bandjoin(alpha);
} else {
return normalized;
}
}
return image;
}
/*
* Gamma encoding/decoding
*/
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);
} else {
return image.gamma(VImage::option()->set("exponent", exponent));
}
}
/*
* Gaussian blur. Use sigma of -1.0 for fast blur.
*/
VImage Blur(VImage image, double const sigma) {
if (sigma == -1.0) {
// Fast, mild blur - averages neighbouring pixels
VImage blur = VImage::new_matrixv(3, 3,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0);
blur.set("scale", 9.0);
return image.conv(blur);
} else {
// Slower, accurate Gaussian blur
return image.gaussblur(sigma);
}
}
/*
* Convolution with a kernel.
*/
VImage Convolve(VImage image, int const width, int const height,
double const scale, double const offset,
std::unique_ptr<double[]> const &kernel_v
) {
VImage kernel = VImage::new_from_memory(
kernel_v.get(),
width * height * sizeof(double),
width,
height,
1,
VIPS_FORMAT_DOUBLE);
kernel.set("scale", scale);
kernel.set("offset", offset);
return image.conv(kernel);
}
/*
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
if (sigma == -1.0) {
// Fast, mild sharpen
VImage sharpen = VImage::new_matrixv(3, 3,
-1.0, -1.0, -1.0,
-1.0, 32.0, -1.0,
-1.0, -1.0, -1.0);
sharpen.set("scale", 24.0);
return image.conv(sharpen);
} else {
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
VipsInterpretation colourspaceBeforeSharpen = image.interpretation();
if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
}
return image.sharpen(
VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged))
.colourspace(colourspaceBeforeSharpen);
}
}
/*
Calculate the Shannon entropy
*/
double EntropyStrategy::operator()(VImage image) {
return image.hist_find().hist_entropy();
}
/*
Calculate the intensity of edges, skin tone and saturation
*/
double AttentionStrategy::operator()(VImage image) {
// Flatten RGBA onto a mid-grey background
if (image.bands() == 4 && HasAlpha(image)) {
double const midgrey = sharp::Is16Bit(image.interpretation()) ? 32768.0 : 128.0;
std::vector<double> background { midgrey, midgrey, midgrey };
image = image.flatten(VImage::option()->set("background", background));
}
// Convert to LAB colourspace
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
VImage l = lab[0];
VImage a = lab[1];
VImage b = lab[2];
// Edge detect luminosity with the Sobel operator
VImage sobel = vips::VImage::new_matrixv(3, 3,
-1.0, 0.0, 1.0,
-2.0, 0.0, 2.0,
-1.0, 0.0, 1.0);
VImage edges = l.conv(sobel).abs() + l.conv(sobel.rot90()).abs();
// Skin tone chroma thresholds trained with http://humanae.tumblr.com/
VImage skin = (a >= 3) & (a <= 22) & (b >= 4) & (b <= 31);
// Chroma >~50% saturation
VImage lch = lab.colourspace(VIPS_INTERPRETATION_LCH);
VImage c = lch[1];
VImage saturation = c > 60;
// Find maximum in combined saliency mask
VImage mask = edges + skin + saturation;
return mask.max();
}
/*
Calculate crop area based on image entropy
*/
std::tuple<int, int> Crop(
VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy
) {
int left = 0;
int top = 0;
int const inWidth = image.width();
int const inHeight = image.height();
if (inWidth > outWidth) {
// Reduce width by repeated removing slices from edge with lowest score
int width = inWidth;
double leftScore = 0.0;
double rightScore = 0.0;
// Max width of each slice
int const maxSliceWidth = static_cast<int>(ceil((inWidth - outWidth) / 8.0));
while (width > outWidth) {
// Width of current slice
int const slice = std::min(width - outWidth, maxSliceWidth);
if (leftScore == 0.0) {
// Update score of left slice
leftScore = strategy(image.extract_area(left, 0, slice, inHeight));
}
if (rightScore == 0.0) {
// Update score of right slice
rightScore = strategy(image.extract_area(width - slice - 1, 0, slice, inHeight));
}
// Keep slice with highest score
if (leftScore >= rightScore) {
// Discard right slice
rightScore = 0.0;
} else {
// Discard left slice
leftScore = 0.0;
left = left + slice;
}
width = width - slice;
}
}
if (inHeight > outHeight) {
// Reduce height by repeated removing slices from edge with lowest score
int height = inHeight;
double topScore = 0.0;
double bottomScore = 0.0;
// Max height of each slice
int const maxSliceHeight = static_cast<int>(ceil((inHeight - outHeight) / 8.0));
while (height > outHeight) {
// Height of current slice
int const slice = std::min(height - outHeight, maxSliceHeight);
if (topScore == 0.0) {
// Update score of top slice
topScore = strategy(image.extract_area(0, top, inWidth, slice));
}
if (bottomScore == 0.0) {
// Update score of bottom slice
bottomScore = strategy(image.extract_area(0, height - slice - 1, inWidth, slice));
}
// Keep slice with highest score
if (topScore >= bottomScore) {
// Discard bottom slice
bottomScore = 0.0;
} else {
// Discard top slice
topScore = 0.0;
top = top + slice;
}
height = height - slice;
}
}
return std::make_tuple(left, top);
}
/*
Insert a tile cache to prevent over-computation of any previous operations in the pipeline
*/
VImage TileCache(VImage image, double const factor) {
int tile_width;
int tile_height;
int scanline_count;
vips_get_tile_size(image.get_image(), &tile_width, &tile_height, &scanline_count);
double const need_lines = 1.2 * scanline_count / factor;
return image.tilecache(VImage::option()
->set("tile_width", image.width())
->set("tile_height", 10)
->set("max_tiles", static_cast<int>(round(1.0 + need_lines / 10.0)))
->set("access", VIPS_ACCESS_SEQUENTIAL)
->set("threaded", TRUE));
}
VImage Threshold(VImage image, double const threshold, bool const thresholdGrayscale) {
if (!thresholdGrayscale) {
return image >= threshold;
}
return image.colourspace(VIPS_INTERPRETATION_B_W) >= threshold;
}
/*
Perform boolean/bitwise operation on image color channels - results in one channel image
*/
VImage Bandbool(VImage image, VipsOperationBoolean const boolean) {
image = image.bandbool(boolean);
return image.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));
}
/*
Perform bitwise boolean operation between images
*/
VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean) {
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");
}
// and now crop the original image
return image.extract_area(left, top, width, height);
}
} // namespace sharp

124
src/operations.h Normal file
View File

@@ -0,0 +1,124 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vips/vips8>
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);
/*
Alpha composite src over dst with given x and y offsets.
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
*/
VImage Composite(VImage src, VImage dst, const int x, const int y);
/*
Check if the src and dst Images for composition operation are valid
*/
bool IsInputValidForComposition(VImage src, VImage dst);
/*
Given a valid src and dst, returns the composite of the two images
*/
VImage CompositeImage(VImage src, VImage dst);
/*
Cutout src over dst with given gravity.
*/
VImage Cutout(VImage src, VImage dst, const int gravity);
/*
* Stretch luminance to cover full dynamic range.
*/
VImage Normalise(VImage image);
/*
* Gamma encoding/decoding
*/
VImage Gamma(VImage image, double const exponent);
/*
* Gaussian blur. Use sigma of -1.0 for fast blur.
*/
VImage Blur(VImage image, double const sigma);
/*
* Convolution with a kernel.
*/
VImage Convolve(VImage image, int const width, int const height,
double const scale, double const offset, std::unique_ptr<double[]> const &kernel_v);
/*
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
/*
Crop strategy functors
*/
struct EntropyStrategy {
double operator()(VImage image);
};
struct AttentionStrategy {
double operator()(VImage image);
};
/*
Calculate crop area based on given strategy (Entropy, Attention)
*/
std::tuple<int, int> Crop(
VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy);
/*
Insert a tile cache to prevent over-computation of any previous operations in the pipeline
*/
VImage TileCache(VImage image, double const factor);
/*
Threshold an image
*/
VImage Threshold(VImage image, double const threshold, bool const thresholdColor);
/*
Perform boolean/bitwise operation on image color channels - results in one channel image
*/
VImage Bandbool(VImage image, VipsOperationBoolean const boolean);
/*
Perform bitwise boolean operation between images
*/
VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean);
/*
Trim an image
*/
VImage Trim(VImage image, int const tolerance);
} // namespace sharp
#endif // SRC_OPERATIONS_H_

1251
src/pipeline.cc Normal file

File diff suppressed because it is too large Load Diff

201
src/pipeline.h Normal file
View File

@@ -0,0 +1,201 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_PIPELINE_H_
#define SRC_PIPELINE_H_
#include <memory>
#include <string>
#include <vector>
#include <nan.h>
#include <vips/vips8>
#include "./common.h"
NAN_METHOD(pipeline);
enum class Canvas {
CROP,
EMBED,
MAX,
MIN,
IGNORE_ASPECT
};
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<sharp::InputDescriptor *> joinChannelIn;
int topOffsetPre;
int leftOffsetPre;
int widthPre;
int heightPre;
int topOffsetPost;
int leftOffsetPost;
int widthPost;
int heightPost;
int width;
int height;
int channels;
Canvas canvas;
int crop;
int cropCalcLeft;
int cropCalcTop;
std::string kernel;
std::string interpolator;
bool centreSampling;
double background[4];
bool flatten;
bool negate;
double blurSigma;
double sharpenSigma;
double sharpenFlat;
double sharpenJagged;
int threshold;
bool thresholdGrayscale;
int trimTolerance;
double gamma;
bool greyscale;
bool normalise;
int angle;
bool rotateBeforePreExtract;
bool flip;
bool flop;
int extendTop;
int extendBottom;
int extendLeft;
int extendRight;
bool withoutEnlargement;
VipsAccess accessMethod;
int jpegQuality;
bool jpegProgressive;
std::string jpegChromaSubsampling;
bool jpegTrellisQuantisation;
bool jpegOvershootDeringing;
bool jpegOptimiseScans;
bool pngProgressive;
int pngCompressionLevel;
bool pngAdaptiveFiltering;
int webpQuality;
int webpAlphaQuality;
bool webpNearLossless;
bool webpLossless;
int tiffQuality;
VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor;
std::string err;
bool withMetadata;
int withMetadataOrientation;
std::unique_ptr<double[]> convKernel;
int convKernelWidth;
int convKernelHeight;
double convKernelScale;
double convKernelOffset;
sharp::InputDescriptor *boolean;
VipsOperationBoolean booleanOp;
VipsOperationBoolean bandBoolOp;
int extractChannel;
VipsInterpretation colourspace;
int tileSize;
int tileOverlap;
VipsForeignDzContainer tileContainer;
VipsForeignDzLayout tileLayout;
std::string tileFormat;
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),
cropCalcLeft(-1),
cropCalcTop(-1),
centreSampling(false),
flatten(false),
negate(false),
blurSigma(0.0),
sharpenSigma(0.0),
sharpenFlat(1.0),
sharpenJagged(2.0),
threshold(0),
thresholdGrayscale(true),
trimTolerance(0),
gamma(0.0),
greyscale(false),
normalise(false),
angle(0),
flip(false),
flop(false),
extendTop(0),
extendBottom(0),
extendLeft(0),
extendRight(0),
withoutEnlargement(false),
jpegQuality(80),
jpegProgressive(false),
jpegChromaSubsampling("4:2:0"),
jpegTrellisQuantisation(false),
jpegOvershootDeringing(false),
jpegOptimiseScans(false),
pngProgressive(false),
pngCompressionLevel(6),
pngAdaptiveFiltering(true),
webpQuality(80),
tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_NONE),
withMetadata(false),
withMetadataOrientation(-1),
convKernelWidth(0),
convKernelHeight(0),
convKernelScale(0.0),
convKernelOffset(0.0),
boolean(nullptr),
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
extractChannel(-1),
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;
}
};
#endif // SRC_PIPELINE_H_

View File

@@ -1,896 +0,0 @@
#include <tuple>
#include <algorithm>
#include <cmath>
#include <node.h>
#include <node_buffer.h>
#include <vips/vips.h>
#include "nan.h"
#include "common.h"
#include "resize.h"
using namespace v8;
using namespace sharp;
enum class Canvas {
CROP,
MAX,
EMBED
};
enum class Angle {
D0,
D90,
D180,
D270,
DLAST
};
struct ResizeBaton {
std::string fileIn;
void* bufferIn;
size_t bufferInLength;
std::string iccProfilePath;
std::string output;
std::string outputFormat;
void* bufferOut;
size_t bufferOutLength;
int topOffsetPre;
int leftOffsetPre;
int widthPre;
int heightPre;
int topOffsetPost;
int leftOffsetPost;
int widthPost;
int heightPost;
int width;
int height;
Canvas canvas;
int gravity;
std::string interpolator;
double background[4];
bool flatten;
double blurSigma;
int sharpenRadius;
double sharpenFlat;
double sharpenJagged;
double gamma;
bool greyscale;
int angle;
bool flip;
bool flop;
bool progressive;
bool withoutEnlargement;
VipsAccess accessMethod;
int quality;
int compressionLevel;
bool withoutAdaptiveFiltering;
std::string err;
bool withMetadata;
ResizeBaton():
bufferInLength(0),
outputFormat(""),
bufferOutLength(0),
topOffsetPre(-1),
topOffsetPost(-1),
canvas(Canvas::CROP),
gravity(0),
flatten(false),
blurSigma(0.0),
sharpenRadius(0),
sharpenFlat(1.0),
sharpenJagged(2.0),
gamma(0.0),
greyscale(false),
angle(0),
flip(false),
flop(false),
progressive(false),
withoutEnlargement(false),
quality(80),
compressionLevel(6),
withoutAdaptiveFiltering(false),
withMetadata(false) {
background[0] = 0.0;
background[1] = 0.0;
background[2] = 0.0;
background[3] = 255.0;
}
};
class ResizeWorker : public NanAsyncWorker {
public:
ResizeWorker(NanCallback *callback, ResizeBaton *baton) : NanAsyncWorker(callback), baton(baton) {}
~ResizeWorker() {}
/*
libuv worker
*/
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&counterQueue);
// Increment processing task counter
g_atomic_int_inc(&counterProcess);
// Hang image references from this hook object
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
// Input
ImageType inputImageType = ImageType::UNKNOWN;
VipsImage *image;
if (baton->bufferInLength > 1) {
// From buffer
inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (inputImageType != ImageType::UNKNOWN) {
image = InitImage(inputImageType, baton->bufferIn, baton->bufferInLength, baton->accessMethod);
} else {
(baton->err).append("Input buffer contains unsupported image format");
}
} else {
// From file
inputImageType = DetermineImageType(baton->fileIn.c_str());
if (inputImageType != ImageType::UNKNOWN) {
image = InitImage(inputImageType, baton->fileIn.c_str(), baton->accessMethod);
} else {
(baton->err).append("File is of an unsupported image format");
}
}
if (inputImageType == ImageType::UNKNOWN) {
return Error(baton, hook);
}
vips_object_local(hook, image);
// Pre extraction
if (baton->topOffsetPre != -1) {
VipsImage *extractedPre;
if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, extractedPre);
image = extractedPre;
}
// Get input image width and height
int inputWidth = image->Xsize;
int inputHeight = image->Ysize;
// Calculate angle of rotation, to be carried out later
Angle rotation;
bool flip;
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image);
if (rotation == Angle::D90 || rotation == Angle::D270) {
// Swap input output width and height when rotating by 90 or 270 degrees
int swap = inputWidth;
inputWidth = inputHeight;
inputHeight = swap;
}
if (flip && !baton->flip) {
// Add flip operation due to EXIF mirroring
baton->flip = TRUE;
}
// Get window size of interpolator, used for determining shrink vs affine
int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.c_str());
// Scaling calculations
double factor;
if (baton->width > 0 && baton->height > 0) {
// Fixed width and height
double xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
double yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height);
factor = (baton->canvas == Canvas::CROP) ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
// if max is set, we need to compute the real size of the thumb image
if (baton->canvas == Canvas::MAX) {
if (xfactor > yfactor) {
baton->height = round(static_cast<double>(inputHeight) / xfactor);
} else {
baton->width = round(static_cast<double>(inputWidth) / yfactor);
}
}
} else if (baton->width > 0) {
// Fixed width, auto height
factor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
baton->height = floor(static_cast<double>(inputHeight) / factor);
} else if (baton->height > 0) {
// Fixed height, auto width
factor = static_cast<double>(inputHeight) / static_cast<double>(baton->height);
baton->width = floor(static_cast<double>(inputWidth) / factor);
} else {
// Identity transform
factor = 1;
baton->width = inputWidth;
baton->height = inputHeight;
}
// Calculate integral box shrink
int shrink = 1;
if (factor >= 2 && interpolatorWindowSize > 3) {
// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
shrink = floor(factor * 3.0 / interpolatorWindowSize);
} else {
shrink = floor(factor);
}
if (shrink < 1) {
shrink = 1;
}
// Calculate residual float affine transformation
double residual = static_cast<double>(shrink) / factor;
// Do not enlarge the output if the input width *or* height are already less than the required dimensions
if (baton->withoutEnlargement) {
if (inputWidth < baton->width || inputHeight < baton->height) {
factor = 1;
shrink = 1;
residual = 0;
baton->width = inputWidth;
baton->height = inputHeight;
}
}
// Try to use libjpeg shrink-on-load, but not when applying gamma correction or pre-resize extract
int shrink_on_load = 1;
if (inputImageType == ImageType::JPEG && shrink >= 2 && baton->gamma == 0 && baton->topOffsetPre == -1) {
if (shrink >= 8) {
factor = factor / 8;
shrink_on_load = 8;
} else if (shrink >= 4) {
factor = factor / 4;
shrink_on_load = 4;
} else if (shrink >= 2) {
factor = factor / 2;
shrink_on_load = 2;
}
}
if (shrink_on_load > 1) {
// Recalculate integral shrink and double residual
factor = std::max(factor, 1.0);
if (factor >= 2 && interpolatorWindowSize > 3) {
shrink = floor(factor * 3.0 / interpolatorWindowSize);
} else {
shrink = floor(factor);
}
residual = static_cast<double>(shrink) / factor;
// Reload input using shrink-on-load
VipsImage *shrunkOnLoad;
if (baton->bufferInLength > 1) {
if (vips_jpegload_buffer(baton->bufferIn, baton->bufferInLength, &shrunkOnLoad, "shrink", shrink_on_load, NULL)) {
return Error(baton, hook);
}
} else {
if (vips_jpegload((baton->fileIn).c_str(), &shrunkOnLoad, "shrink", shrink_on_load, NULL)) {
return Error(baton, hook);
}
}
vips_object_local(hook, shrunkOnLoad);
image = shrunkOnLoad;
}
// Ensure we're using a device-independent colour space
if (HasProfile(image)) {
// Convert to sRGB using embedded profile
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
VipsImage *transformed;
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "embedded", TRUE, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, transformed);
image = transformed;
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
// Convert to sRGB using default "USWebCoatedSWOP" CMYK profile
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc";
VipsImage *transformed;
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "input_profile", cmykProfile.c_str(), NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, transformed);
image = transformed;
}
// Flatten image to remove alpha channel
if (baton->flatten && HasAlpha(image)) {
// Background colour
VipsArrayDouble *background = vips_array_double_newv(
3, // Ignore alpha channel as we're about to remove it
baton->background[0],
baton->background[1],
baton->background[2]
);
VipsImage *flattened;
if (vips_flatten(image, &flattened, "background", background, NULL)) {
vips_area_unref(reinterpret_cast<VipsArea*>(background));
return Error(baton, hook);
};
vips_area_unref(reinterpret_cast<VipsArea*>(background));
vips_object_local(hook, flattened);
image = flattened;
}
// Gamma encoding (darken)
if (baton->gamma >= 1 && baton->gamma <= 3) {
VipsImage *gammaEncoded;
if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, gammaEncoded);
image = gammaEncoded;
}
// Convert to greyscale (linear, therefore after gamma encoding, if any)
if (baton->greyscale) {
VipsImage *greyscale;
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, greyscale);
image = greyscale;
}
if (shrink > 1) {
VipsImage *shrunk;
// Use vips_shrink with the integral reduction
if (vips_shrink(image, &shrunk, shrink, shrink, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, shrunk);
image = shrunk;
// Recalculate residual float based on dimensions of required vs shrunk images
double shrunkWidth = shrunk->Xsize;
double shrunkHeight = shrunk->Ysize;
if (rotation == Angle::D90 || rotation == Angle::D270) {
// Swap input output width and height when rotating by 90 or 270 degrees
int swap = shrunkWidth;
shrunkWidth = shrunkHeight;
shrunkHeight = swap;
}
double residualx = static_cast<double>(baton->width) / static_cast<double>(shrunkWidth);
double residualy = static_cast<double>(baton->height) / static_cast<double>(shrunkHeight);
if (baton->canvas == Canvas::EMBED) {
residual = std::min(residualx, residualy);
} else {
residual = std::max(residualx, residualy);
}
}
// Use vips_affine with the remaining float part
if (residual != 0.0) {
// Apply Gaussian blur before large affine reductions
if (residual < 1.0) {
// Calculate standard deviation
double sigma = ((1.0 / residual) - 0.4) / 3.0;
if (sigma >= 0.3) {
// Create Gaussian function for standard deviation
VipsImage *gaussian;
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, gaussian);
// Apply Gaussian function
VipsImage *blurred;
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, blurred);
image = blurred;
}
}
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
vips_object_local(hook, interpolator);
// Perform affine transformation
VipsImage *affined;
if (vips_affine(image, &affined, residual, 0.0, 0.0, residual, "interpolate", interpolator, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, affined);
image = affined;
}
// Rotate
if (rotation != Angle::D0) {
VipsImage *rotated;
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, rotated);
image = rotated;
}
// Flip (mirror about Y axis)
if (baton->flip) {
VipsImage *flipped;
if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, flipped);
image = flipped;
}
// Flop (mirror about X axis)
if (baton->flop) {
VipsImage *flopped;
if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, flopped);
image = flopped;
}
// Crop/embed
if (image->Xsize != baton->width || image->Ysize != baton->height) {
if (baton->canvas == Canvas::EMBED) {
// Match background colour space, namely sRGB
if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Convert to sRGB colour space
VipsImage *colourspaced;
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, colourspaced);
image = colourspaced;
}
// Add non-transparent alpha channel, if required
if (baton->background[3] < 255.0 && !HasAlpha(image)) {
// Create single-channel transparency
VipsImage *black;
if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, black);
// Invert to become non-transparent
VipsImage *alpha;
if (vips_invert(black, &alpha, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, alpha);
// Append alpha channel to existing image
VipsImage *joined;
if (vips_bandjoin2(image, alpha, &joined, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, joined);
image = joined;
}
// Create background
VipsArrayDouble *background;
if (baton->background[3] < 255.0) {
background = vips_array_double_newv(
4, baton->background[0], baton->background[1], baton->background[2], baton->background[3]
);
} else {
background = vips_array_double_newv(
3, baton->background[0], baton->background[1], baton->background[2]
);
}
// Embed
int left = (baton->width - image->Xsize) / 2;
int top = (baton->height - image->Ysize) / 2;
VipsImage *embedded;
if (vips_embed(image, &embedded, left, top, baton->width, baton->height,
"extend", VIPS_EXTEND_BACKGROUND, "background", background, NULL
)) {
vips_area_unref(reinterpret_cast<VipsArea*>(background));
return Error(baton, hook);
}
vips_area_unref(reinterpret_cast<VipsArea*>(background));
vips_object_local(hook, embedded);
image = embedded;
} else {
// Crop/max
int left;
int top;
std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity);
int width = std::min(image->Xsize, baton->width);
int height = std::min(image->Ysize, baton->height);
VipsImage *extracted;
if (vips_extract_area(image, &extracted, left, top, width, height, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, extracted);
image = extracted;
}
}
// Post extraction
if (baton->topOffsetPost != -1) {
VipsImage *extractedPost;
if (vips_extract_area(image, &extractedPost, baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, extractedPost);
image = extractedPost;
}
// Blur
if (baton->blurSigma != 0.0) {
VipsImage *blurred;
if (baton->blurSigma < 0.0) {
// Fast, mild blur - averages neighbouring pixels
VipsImage *blur = vips_image_new_matrixv(3, 3,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0);
vips_image_set_double(blur, "scale", 9);
vips_object_local(hook, blur);
if (vips_conv(image, &blurred, blur, NULL)) {
return Error(baton, hook);
}
} else {
// Slower, accurate Gaussian blur
// Create Gaussian function for standard deviation
VipsImage *gaussian;
if (vips_gaussmat(&gaussian, baton->blurSigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, gaussian);
// Apply Gaussian function
VipsImage *blurred;
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
return Error(baton, hook);
}
}
vips_object_local(hook, blurred);
image = blurred;
}
// Sharpen
if (baton->sharpenRadius != 0) {
VipsImage *sharpened;
if (baton->sharpenRadius == -1) {
// Fast, mild sharpen
VipsImage *sharpen = vips_image_new_matrixv(3, 3,
-1.0, -1.0, -1.0,
-1.0, 32.0, -1.0,
-1.0, -1.0, -1.0);
vips_image_set_double(sharpen, "scale", 24);
vips_object_local(hook, sharpen);
if (vips_conv(image, &sharpened, sharpen, NULL)) {
return Error(baton, hook);
}
} else {
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
if (vips_sharpen(image, &sharpened, "radius", baton->sharpenRadius, "m1", baton->sharpenFlat, "m2", baton->sharpenJagged, NULL)) {
return Error(baton, hook);
}
}
vips_object_local(hook, sharpened);
image = sharpened;
}
// Gamma decoding (brighten)
if (baton->gamma >= 1 && baton->gamma <= 3) {
VipsImage *gammaDecoded;
if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, gammaDecoded);
image = gammaDecoded;
}
// Convert colour space to either sRGB or RGB-with-profile, if not already
if (image->Type != VIPS_INTERPRETATION_sRGB) {
VipsImage *rgb;
if (baton->withMetadata && HasProfile(image)) {
// Convert to device-dependent RGB using embedded profile of input
if (vips_icc_export(image, &rgb, NULL)) {
return Error(baton, hook);
}
} else {
// Convert to device-independent sRGB
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook);
}
}
vips_object_local(hook, rgb);
image = rgb;
}
#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
// Generate image tile cache when interlace output is required - no longer required as of libvips 7.40.5+
if (baton->progressive) {
VipsImage *cached;
if (vips_tilecache(image, &cached, "threaded", TRUE, "persistent", TRUE, "max_tiles", -1, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, cached);
image = cached;
}
#endif
// Output
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) {
// Write JPEG to buffer
if (vips_jpegsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
return Error(baton, hook);
}
baton->outputFormat = "jpeg";
} else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) {
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)
// Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
// Write PNG to buffer
if (vips_pngsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, "filter", filter, NULL)) {
return Error(baton, hook);
}
#else
// Write PNG to buffer
if (vips_pngsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
return Error(baton, hook);
}
#endif
baton->outputFormat = "png";
} else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == ImageType::WEBP)) {
// Write WEBP to buffer
if (vips_webpsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"Q", baton->quality, NULL)) {
return Error(baton, hook);
}
baton->outputFormat = "webp";
} else {
bool outputJpeg = IsJpeg(baton->output);
bool outputPng = IsPng(baton->output);
bool outputWebp = IsWebp(baton->output);
bool outputTiff = IsTiff(baton->output);
bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff);
if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
// Write JPEG to file
if (vips_jpegsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
return Error(baton, hook);
}
baton->outputFormat = "jpeg";
} else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) {
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41)
// Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
// Write PNG to file
if (vips_pngsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, "filter", filter, NULL)) {
return Error(baton, hook);
}
#else
// Write PNG to file
if (vips_pngsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
return Error(baton, hook);
}
#endif
baton->outputFormat = "png";
} else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) {
// Write WEBP to file
if (vips_webpsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"Q", baton->quality, NULL)) {
return Error(baton, hook);
}
baton->outputFormat = "webp";
} else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) {
// Write TIFF to file
if (vips_tiffsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", baton->quality, NULL)) {
return Error(baton, hook);
}
baton->outputFormat = "tiff";
} else {
(baton->err).append("Unsupported output " + baton->output);
return Error(baton, hook);
}
}
// Clean up any dangling image references
g_object_unref(hook);
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback () {
NanScope();
Handle<Value> argv[3] = { NanNull(), NanNull(), NanNull() };
if (!baton->err.empty()) {
// Error
argv[0] = Exception::Error(NanNew<String>(baton->err.data(), baton->err.size()));
} else {
int width = baton->width;
int height = baton->height;
if (baton->topOffsetPre != -1 && (baton->width == -1 || baton->height == -1)) {
width = baton->widthPre;
height = baton->heightPre;
}
if (baton->topOffsetPost != -1) {
width = baton->widthPost;
height = baton->heightPost;
}
// Info Object
Local<Object> info = NanNew<Object>();
info->Set(NanNew<String>("format"), NanNew<String>(baton->outputFormat));
info->Set(NanNew<String>("width"), NanNew<Number>(width));
info->Set(NanNew<String>("height"), NanNew<Number>(height));
if (baton->bufferOutLength > 0) {
// Buffer
argv[1] = NanNewBufferHandle(static_cast<char*>(baton->bufferOut), baton->bufferOutLength);
g_free(baton->bufferOut);
argv[2] = info;
} else {
// File
argv[1] = info;
}
}
delete baton;
// Decrement processing task counter
g_atomic_int_dec_and_test(&counterProcess);
// Return to JavaScript
callback->Call(3, argv);
}
private:
ResizeBaton* baton;
/*
Calculate the angle of rotation and need-to-flip for the output image.
In order of priority:
1. Use explicitly requested angle (supports 90, 180, 270)
2. Use input image EXIF Orientation header - supports mirroring
3. Otherwise default to zero, i.e. no rotation
*/
std::tuple<Angle, bool>
CalculateRotationAndFlip(int const angle, VipsImage const *input) {
Angle rotate = Angle::D0;
bool flip = FALSE;
if (angle == -1) {
switch(ExifOrientation(input)) {
case 6: rotate = Angle::D90; break;
case 3: rotate = Angle::D180; break;
case 8: rotate = Angle::D270; break;
case 2: flip = TRUE; break; // flip 1
case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6
case 4: flip = TRUE; rotate = Angle::D180; break; // flip 3
case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8
}
} else {
if (angle == 90) {
rotate = Angle::D90;
} else if (angle == 180) {
rotate = Angle::D180;
} else if (angle == 270) {
rotate = Angle::D270;
}
}
return std::make_tuple(rotate, flip);
}
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity.
*/
std::tuple<int, int>
CalculateCrop(int const inWidth, int const inHeight, int const outWidth, int const outHeight, int const gravity) {
int left = 0;
int top = 0;
switch (gravity) {
case 1: // North
left = (inWidth - outWidth + 1) / 2;
break;
case 2: // East
left = inWidth - outWidth;
top = (inHeight - outHeight + 1) / 2;
break;
case 3: // South
left = (inWidth - outWidth + 1) / 2;
top = inHeight - outHeight;
break;
case 4: // West
top = (inHeight - outHeight + 1) / 2;
break;
default: // Centre
left = (inWidth - outWidth + 1) / 2;
top = (inHeight - outHeight + 1) / 2;
}
return std::make_tuple(left, top);
}
/*
Copy then clear the error message.
Unref all transitional images on the hook.
Clear all thread-local data.
*/
void Error(ResizeBaton *baton, VipsObject *hook) {
// Get libvips' error message
(baton->err).append(vips_error_buffer());
// Clean up any dangling image references
g_object_unref(hook);
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown();
}
};
/*
resize(options, output, callback)
*/
NAN_METHOD(resize) {
NanScope();
// V8 objects are converted to non-V8 types held in the baton struct
ResizeBaton *baton = new ResizeBaton;
Local<Object> options = args[0]->ToObject();
// Input filename
baton->fileIn = *String::Utf8Value(options->Get(NanNew<String>("fileIn"))->ToString());
baton->accessMethod = options->Get(NanNew<String>("sequentialRead"))->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Input Buffer object
if (options->Get(NanNew<String>("bufferIn"))->IsObject()) {
Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject();
baton->bufferInLength = node::Buffer::Length(buffer);
baton->bufferIn = node::Buffer::Data(buffer);
}
// ICC profile to use when input CMYK image has no embedded profile
baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString());
// Extract image options
baton->topOffsetPre = options->Get(NanNew<String>("topOffsetPre"))->Int32Value();
baton->leftOffsetPre = options->Get(NanNew<String>("leftOffsetPre"))->Int32Value();
baton->widthPre = options->Get(NanNew<String>("widthPre"))->Int32Value();
baton->heightPre = options->Get(NanNew<String>("heightPre"))->Int32Value();
baton->topOffsetPost = options->Get(NanNew<String>("topOffsetPost"))->Int32Value();
baton->leftOffsetPost = options->Get(NanNew<String>("leftOffsetPost"))->Int32Value();
baton->widthPost = options->Get(NanNew<String>("widthPost"))->Int32Value();
baton->heightPost = options->Get(NanNew<String>("heightPost"))->Int32Value();
// Output image dimensions
baton->width = options->Get(NanNew<String>("width"))->Int32Value();
baton->height = options->Get(NanNew<String>("height"))->Int32Value();
// Canvas option
Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString();
if (canvas->Equals(NanNew<String>("c"))) {
baton->canvas = Canvas::CROP;
} else if (canvas->Equals(NanNew<String>("m"))) {
baton->canvas = Canvas::MAX;
} else if (canvas->Equals(NanNew<String>("e"))) {
baton->canvas = Canvas::EMBED;
}
// Background colour
Local<Array> background = Local<Array>::Cast(options->Get(NanNew<String>("background")));
for (int i = 0; i < 4; i++) {
baton->background[i] = background->Get(i)->NumberValue();
}
// Resize options
baton->withoutEnlargement = options->Get(NanNew<String>("withoutEnlargement"))->BooleanValue();
baton->gravity = options->Get(NanNew<String>("gravity"))->Int32Value();
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
// Operators
baton->flatten = options->Get(NanNew<String>("flatten"))->BooleanValue();
baton->blurSigma = options->Get(NanNew<String>("blurSigma"))->NumberValue();
baton->sharpenRadius = options->Get(NanNew<String>("sharpenRadius"))->Int32Value();
baton->sharpenFlat = options->Get(NanNew<String>("sharpenFlat"))->NumberValue();
baton->sharpenJagged = options->Get(NanNew<String>("sharpenJagged"))->NumberValue();
baton->gamma = options->Get(NanNew<String>("gamma"))->NumberValue();
baton->greyscale = options->Get(NanNew<String>("greyscale"))->BooleanValue();
baton->angle = options->Get(NanNew<String>("angle"))->Int32Value();
baton->flip = options->Get(NanNew<String>("flip"))->BooleanValue();
baton->flop = options->Get(NanNew<String>("flop"))->BooleanValue();
// Output options
baton->progressive = options->Get(NanNew<String>("progressive"))->BooleanValue();
baton->quality = options->Get(NanNew<String>("quality"))->Int32Value();
baton->compressionLevel = options->Get(NanNew<String>("compressionLevel"))->Int32Value();
baton->withoutAdaptiveFiltering = options->Get(NanNew<String>("withoutAdaptiveFiltering"))->BooleanValue();
baton->withMetadata = options->Get(NanNew<String>("withMetadata"))->BooleanValue();
// Output filename or __format for Buffer
baton->output = *String::Utf8Value(options->Get(NanNew<String>("output"))->ToString());
// Join queue for worker thread
NanCallback *callback = new NanCallback(args[1].As<v8::Function>());
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
// Increment queued task counter
g_atomic_int_inc(&counterQueue);
NanReturnUndefined();
}

View File

@@ -1,8 +0,0 @@
#ifndef SHARP_RESIZE_H
#define SHARP_RESIZE_H
#include "nan.h"
NAN_METHOD(resize);
#endif

63
src/sharp.cc Executable file → Normal file
View File

@@ -1,39 +1,48 @@
#include <node.h>
#include <vips/vips.h>
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
#include "metadata.h"
#include "resize.h"
#include "pipeline.h"
#include "utilities.h"
using namespace v8;
static void at_exit(void* arg) {
NanScope();
vips_shutdown();
}
extern "C" void init(Handle<Object> target) {
NanScope();
NAN_MODULE_INIT(init) {
vips_init("sharp");
node::AtExit(at_exit);
// Set libvips operation cache limits
vips_cache_set_max_mem(100 * 1048576); // 100 MB
vips_cache_set_max(500); // 500 operations
// Notify the V8 garbage collector of max cache size
NanAdjustExternalMemory(vips_cache_get_max_mem());
// Methods available to JavaScript
NODE_SET_METHOD(target, "metadata", metadata);
NODE_SET_METHOD(target, "resize", resize);
NODE_SET_METHOD(target, "cache", cache);
NODE_SET_METHOD(target, "concurrency", concurrency);
NODE_SET_METHOD(target, "counters", counters);
NODE_SET_METHOD(target, "libvipsVersion", libvipsVersion);
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());
}
NODE_MODULE(sharp, init)

272
src/utilities.cc Executable file → Normal file
View File

@@ -1,75 +1,271 @@
#include <node.h>
#include <vips/vips.h>
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#include <cmath>
#include <string>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include <vips/vector.h>
#include "common.h"
#include "operations.h"
#include "utilities.h"
using namespace v8;
using namespace sharp;
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 memory and item limits
Get and set cache limits
*/
NAN_METHOD(cache) {
NanScope();
HandleScope();
// Set cache memory limit
if (args[0]->IsInt32()) {
int newMax = args[0]->Int32Value() * 1048576;
int oldMax = vips_cache_get_max_mem();
vips_cache_set_max_mem(newMax);
// Notify the V8 garbage collector of delta in max cache size
NanAdjustExternalMemory(newMax - oldMax);
// Set memory limit
if (info[0]->IsInt32()) {
vips_cache_set_max_mem(To<int32_t>(info[0]).FromJust() * 1048576);
}
// Set file limit
if (info[1]->IsInt32()) {
vips_cache_set_max_files(To<int32_t>(info[1]).FromJust());
}
// Set items limit
if (info[2]->IsInt32()) {
vips_cache_set_max(To<int32_t>(info[2]).FromJust());
}
// Set cache items limit
if (args[1]->IsInt32()) {
vips_cache_set_max(args[1]->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))));
// 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()));
// Get cache statistics
Local<Object> cache = NanNew<Object>();
cache->Set(NanNew<String>("current"), NanNew<Number>(vips_tracked_get_mem() / 1048576));
cache->Set(NanNew<String>("high"), NanNew<Number>(vips_tracked_get_mem_highwater() / 1048576));
cache->Set(NanNew<String>("memory"), NanNew<Number>(vips_cache_get_max_mem() / 1048576));
cache->Set(NanNew<String>("items"), NanNew<Number>(vips_cache_get_max()));
NanReturnValue(cache);
// 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()));
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);
}
/*
Get and set size of thread pool
*/
NAN_METHOD(concurrency) {
NanScope();
HandleScope();
// Set concurrency
if (args[0]->IsInt32()) {
vips_concurrency_set(args[0]->Int32Value());
if (info[0]->IsInt32()) {
vips_concurrency_set(To<int32_t>(info[0]).FromJust());
}
// Get concurrency
NanReturnValue(NanNew<Number>(vips_concurrency_get()));
info.GetReturnValue().Set(New<Integer>(vips_concurrency_get()));
}
/*
Get internal counters (queued tasks, processing tasks)
*/
NAN_METHOD(counters) {
NanScope();
Local<Object> counters = NanNew<Object>();
counters->Set(NanNew<String>("queue"), NanNew<Number>(counterQueue));
counters->Set(NanNew<String>("process"), NanNew<Number>(counterProcess));
NanReturnValue(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);
}
/*
Get and set use of SIMD vector unit instructions
*/
NAN_METHOD(simd) {
HandleScope();
// Set state
if (info[0]->IsBoolean()) {
vips_vector_set_enabled(To<bool>(info[0]).FromJust());
}
// Get state
info.GetReturnValue().Set(New<Boolean>(vips_vector_isenabled()));
}
/*
Get libvips version
*/
NAN_METHOD(libvipsVersion) {
NanScope();
HandleScope();
char version[9];
snprintf(version, 9, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
NanReturnValue(NanNew<String>(version));
g_snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
info.GetReturnValue().Set(New(version).ToLocalChecked());
}
/*
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"
}) {
// 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);
// 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);
// 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);
// Add to set of formats
Set(format, formatId, 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);
info.GetReturnValue().Set(format);
}
/*
Synchronous, internal-only method used by some of the functional tests.
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();
// Open input files
VImage image1;
ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
if (imageType1 != ImageType::UNKNOWN) {
try {
image1 = VImage::new_from_file(*Utf8String(info[0]));
} catch (...) {
return ThrowError("Input file 1 has corrupt header");
}
} else {
return ThrowError("Input file 1 is of an unsupported image format");
}
VImage image2;
ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
if (imageType2 != ImageType::UNKNOWN) {
try {
image2 = VImage::new_from_file(*Utf8String(info[1]));
} catch (...) {
return ThrowError("Input file 2 has corrupt header");
}
} else {
return ThrowError("Input file 2 is of an unsupported image format");
}
// Ensure same number of channels
if (image1.bands() != image2.bands()) {
return ThrowError("mismatchedBands");
}
// Ensure same dimensions
if (image1.width() != image2.width() || image1.height() != image2.height()) {
return ThrowError("mismatchedDimensions");
}
double maxColourDistance;
try {
// Premultiply and remove alpha
if (HasAlpha(image1)) {
image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
}
if (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());
}
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown();
info.GetReturnValue().Set(New<Number>(maxColourDistance));
}

25
src/utilities.h Executable file → Normal file
View File

@@ -1,11 +1,28 @@
#ifndef SHARP_UTILITIES_H
#define SHARP_UTILITIES_H
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_
#include <nan.h>
NAN_METHOD(cache);
NAN_METHOD(concurrency);
NAN_METHOD(counters);
NAN_METHOD(simd);
NAN_METHOD(libvipsVersion);
NAN_METHOD(format);
NAN_METHOD(_maxColourDistance);
#endif
#endif // SRC_UTILITIES_H_

19
test/bench/package.json Executable file → Normal file
View File

@@ -5,18 +5,21 @@
"author": "Lovell Fuller <npm@lovell.info>",
"description": "Benchmark and performance tests for sharp",
"scripts": {
"test": "node perf && node random && node parallel"
"test": "VIPS_WARNING=0 node perf && node random && node parallel"
},
"devDependencies": {
"async": "^2.1.4",
"benchmark": "^2.1.2",
"gm": "^1.23.0",
"imagemagick": "^0.1.3",
"imagemagick-native": "^1.6.0",
"gm": "^1.17.0",
"async": "^0.9.0",
"semver": "^4.1.0",
"benchmark": "^1.0.0"
"imagemagick-native": "^1.9.3",
"jimp": "^0.2.27",
"lwip": "^0.0.9",
"mapnik": "^3.5.14",
"semver": "^5.3.0"
},
"license": "Apache 2.0",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
"node": ">=4"
}
}

33
test/bench/parallel.js Executable file → Normal file
View File

@@ -1,41 +1,44 @@
'use strict';
var assert = require('assert');
var async = require('async');
process.env.UV_THREADPOOL_SIZE = 64;
var sharp = require('../../index');
var fixtures = require('../fixtures');
const assert = require('assert');
const async = require('async');
var width = 720;
var height = 480;
const sharp = require('../../');
const fixtures = require('../fixtures');
const width = 720;
const height = 480;
sharp.concurrency(1);
sharp.simd(true);
var timer = setInterval(function() {
const timer = setInterval(function () {
console.dir(sharp.counters());
}, 100);
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64, 128], function(parallelism, next) {
var start = new Date().getTime();
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64], function (parallelism, next) {
const start = new Date().getTime();
async.times(parallelism,
function(id, callback) {
/*jslint unused: false */
sharp(fixtures.inputJpg).resize(width, height).toBuffer(function(err, buffer) {
function (id, callback) {
/* jslint unused: false */
sharp(fixtures.inputJpg).resize(width, height).toBuffer(function (err, buffer) {
buffer = null;
callback(err, new Date().getTime() - start);
});
},
function(err, ids) {
function (err, ids) {
assert(!err);
assert(ids.length === parallelism);
var mean = ids.reduce(function(a, b) {
const mean = ids.reduce(function (a, b) {
return a + b;
}) / ids.length;
console.log(parallelism + ' parallel calls: fastest=' + ids[0] + 'ms slowest=' + ids[ids.length - 1] + 'ms mean=' + mean + 'ms');
next();
}
);
}, function() {
}, function () {
clearInterval(timer);
console.dir(sharp.counters());
});

1173
test/bench/perf.js Executable file → Normal file

File diff suppressed because it is too large Load Diff

80
test/bench/random.js Executable file → Normal file
View File

@@ -1,30 +1,35 @@
'use strict';
var imagemagick = require('imagemagick');
var gm = require('gm');
var assert = require('assert');
var Benchmark = require('benchmark');
const imagemagick = require('imagemagick');
const gm = require('gm');
const assert = require('assert');
const Benchmark = require('benchmark');
var sharp = require('../../index');
var fixtures = require('../fixtures');
const sharp = require('../../');
const fixtures = require('../fixtures');
var min = 320;
var max = 960;
sharp.cache(false);
sharp.simd(true);
var randomDimension = function() {
return Math.random() * (max - min) + min;
const min = 320;
const max = 960;
const randomDimension = function () {
return Math.ceil((Math.random() * (max - min)) + min);
};
new Benchmark.Suite('random').add('imagemagick', {
defer: true,
fn: function(deferred) {
fn: function (deferred) {
imagemagick.resize({
srcPath: fixtures.inputJpg,
dstPath: fixtures.outputJpg,
quality: 0.8,
width: randomDimension(),
height: randomDimension()
}, function(err) {
height: randomDimension(),
format: 'jpg',
filter: 'Lanczos'
}, function (err) {
if (err) {
throw err;
} else {
@@ -34,32 +39,37 @@ new Benchmark.Suite('random').add('imagemagick', {
}
}).add('gm', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg).resize(randomDimension(), randomDimension()).quality(80).toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
fn: function (deferred) {
gm(fixtures.inputJpg)
.resize(randomDimension(), randomDimension())
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg).resize(randomDimension(), randomDimension()).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
fn: function (deferred) {
sharp(fixtures.inputJpg)
.resize(randomDimension(), randomDimension())
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).on('cycle', function(event) {
}).on('cycle', function (event) {
console.log(String(event.target));
}).on('complete', function() {
var winner = this.filter('fastest').pluck('name');
}).on('complete', function () {
const winner = this.filter('fastest').map('name');
assert.strictEqual('sharp', String(winner), 'sharp was slower than ' + winner);
console.dir(sharp.cache());
}).run();

BIN
test/fixtures/2x2_fdcce6.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

BIN
test/fixtures/5_webp_a.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
test/fixtures/Landscape_1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
test/fixtures/Landscape_2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test/fixtures/Landscape_4.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
test/fixtures/Landscape_6.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_7.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
test/fixtures/Portrait_1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
test/fixtures/Portrait_2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_4.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
test/fixtures/Portrait_5.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
test/fixtures/Portrait_6.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_7.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
test/fixtures/Portrait_8.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

BIN
test/fixtures/alpha-layer-1-fill.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

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