Compare commits

..

152 Commits

Author SHA1 Message Date
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
203 changed files with 11184 additions and 3707 deletions

5
.gitignore vendored
View File

@@ -4,8 +4,13 @@ coverage
test/bench/node_modules
test/fixtures/output*
test/leak/libvips.supp
test/saliency/report.json
test/saliency/Image*
test/saliency/[Uu]serData*
!test/saliency/userData.js
lib
include
packaging/libvips*
packaging/*.log
!packaging/build
.DS_Store

View File

@@ -1,3 +1,4 @@
node_modules
test/bench/node_modules
test/saliency/humanae/node_modules
coverage

View File

@@ -2,9 +2,10 @@
"strict": true,
"node": true,
"maxparams": 4,
"maxcomplexity": 13,
"maxcomplexity": 14,
"globals": {
"before": true,
"beforeEach": true,
"afterEach": true,
"describe": true,
"it": true
}

View File

@@ -13,3 +13,4 @@ mkdocs.yml
lib
include
packaging
preinstall.sh

View File

@@ -3,7 +3,10 @@ node_js:
- "0.10"
- "0.12"
- "4"
- "5"
- "6"
os:
- linux
- osx
sudo: false
addons:
apt:
@@ -11,7 +14,8 @@ addons:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
env:
CXX=g++-4.8
osx_image: xcode8
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi
after_success:
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

View File

@@ -41,8 +41,9 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch |
| ------: | :--------- |
| v0.12.0 | look |
| v0.13.0 | mind |
| v0.16.0 | pencil |
| v0.17.0 | quill |
| v0.18.0 | ridge |
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.

View File

@@ -1,23 +1,61 @@
# sharp
```sh
npm install sharp
```
The typical use case for this high speed Node.js module
is to convert large images of many formats to
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 faster than using the
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.
Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed.
Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most Windows (x64), Linux and ARMv6+ systems do not require
OS X, Windows (x64), Linux (x64, ARM) systems do not require
the installation of any external runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips`
to install the libvips dependency.
## Examples
```javascript
import sharp from 'sharp';
```
```javascript
sharp(inputBuffer)
.resize(320, 240)
.toFile('output.webp', (err, info) => ... );
```
```javascript
sharp('input.jpg')
.rotate()
.resize(200)
.toBuffer()
.then( data => ... )
.catch( err => ... );
```
```javascript
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);
```
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)

View File

@@ -7,9 +7,10 @@ environment:
matrix:
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "5"
- nodejs_version: "6"
install:
- ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@latest
- npm install
test_script:
- npm run-script test-win

View File

@@ -1,6 +1,54 @@
{
'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': [
'include',
'include/glib-2.0',
'lib/glib-2.0/include'
],
'libraries': [
'../lib/libvips.lib',
'../lib/libglib-2.0.lib',
'../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': {
@@ -17,7 +65,7 @@
'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 2>/dev/null || true)'
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips-cpp 2>/dev/null || true)'
}, {
'global_vips_version': ''
}]
@@ -49,60 +97,90 @@
'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 glib-2.0 | sed s\/-I//g)'],
'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)']
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
}, {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips)']
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
}]
],
'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 --libs-only-L vips-cpp | cut -c 3- | sed -e "s/^$/\/usr\/lib/")/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': [
'<(module_root_dir)/include',
'<(module_root_dir)/include/glib-2.0',
'<(module_root_dir)/lib/glib-2.0/include'
'include',
'include/glib-2.0',
'lib/glib-2.0/include'
],
'conditions': [
['OS == "win"', {
'defines': [
'_ALLOW_KEYWORD_MACROS'
],
'libraries': [
'../lib/libvips.lib',
'../lib/libglib-2.0.lib',
'../lib/libgobject-2.0.lib'
]
}],
['OS == "mac"', {
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'libraries': [
'<(module_root_dir)/lib/libvips.lib',
'<(module_root_dir)/lib/libglib-2.0.lib',
'<(module_root_dir)/lib/libgobject-2.0.lib'
'../lib/libvips-cpp.42.dylib',
'../lib/libvips.42.dylib',
'../lib/libglib-2.0.0.dylib',
'../lib/libgobject-2.0.0.dylib',
# Ensure runtime linking is relative to sharp.node
'-rpath \'@loader_path/../../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': [
'<(module_root_dir)/lib/libvips.so',
'<(module_root_dir)/lib/libglib-2.0.so',
'<(module_root_dir)/lib/libgobject-2.0.so',
'../lib/libvips-cpp.so',
'../lib/libvips.so',
'../lib/libglib-2.0.so',
'../lib/libgobject-2.0.so',
# Dependencies of dependencies, included for openSUSE support
'<(module_root_dir)/lib/libMagickCore-6.Q16.so',
'<(module_root_dir)/lib/libMagickWand-6.Q16.so',
'<(module_root_dir)/lib/libexif.so',
'<(module_root_dir)/lib/libgio-2.0.so',
'<(module_root_dir)/lib/libgmodule-2.0.so',
'<(module_root_dir)/lib/libgsf-1.so',
'<(module_root_dir)/lib/libjpeg.so',
'<(module_root_dir)/lib/libpng.so',
'<(module_root_dir)/lib/libtiff.so',
'<(module_root_dir)/lib/libwebp.so',
'<(module_root_dir)/lib/libz.so',
'<(module_root_dir)/lib/libffi.so',
'<(module_root_dir)/lib/libgthread-2.0.so',
'<(module_root_dir)/lib/liblcms2.so',
'<(module_root_dir)/lib/libpng16.so',
'<(module_root_dir)/lib/libxml2.so',
'<(module_root_dir)/lib/liborc-0.4.so',
'../lib/libcairo.so',
'../lib/libcroco-0.6.so',
'../lib/libexif.so',
'../lib/libffi.so',
'../lib/libfontconfig.so',
'../lib/libfreetype.so',
'../lib/libgdk_pixbuf-2.0.so',
'../lib/libgif.so',
'../lib/libgio-2.0.so',
'../lib/libgmodule-2.0.so',
'../lib/libgsf-1.so',
'../lib/libgthread-2.0.so',
'../lib/libharfbuzz.so',
'../lib/libjpeg.so',
'../lib/liblcms2.so',
'../lib/liborc-0.4.so',
'../lib/libpango-1.0.so',
'../lib/libpangocairo-1.0.so',
'../lib/libpangoft2-1.0.so',
'../lib/libpixman-1.so',
'../lib/libpng.so',
'../lib/librsvg-2.so',
'../lib/libtiff.so',
'../lib/libwebp.so',
'../lib/libxml2.so',
'../lib/libz.so',
# Ensure runtime linking is relative to sharp.node
'-Wl,-rpath=\'$${ORIGIN}/../../lib\''
'-Wl,--disable-new-dtags -Wl,-rpath=\'$${ORIGIN}/../../lib\''
]
}]
]
@@ -118,6 +196,8 @@
'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': [
'-fexceptions',
'-Wall',
@@ -130,7 +210,10 @@
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
}
},
'msvs_disabled_warnings': [
4275
]
}
},
}, {
@@ -143,46 +226,48 @@
['OS == "win"', {
# Windows lacks support for rpath
'copies': [{
'destination': '<(module_root_dir)/build/Release',
'destination': 'build/Release',
'files': [
'<(module_root_dir)/lib/GNU.Gettext.dll',
'<(module_root_dir)/lib/libMagickCore-6.Q16-2.dll',
'<(module_root_dir)/lib/libMagickWand-6.Q16-2.dll',
'<(module_root_dir)/lib/libasprintf-0.dll',
'<(module_root_dir)/lib/libcairo-2.dll',
'<(module_root_dir)/lib/libcairo-gobject-2.dll',
'<(module_root_dir)/lib/libcairo-script-interpreter-2.dll',
'<(module_root_dir)/lib/libexif-12.dll',
'<(module_root_dir)/lib/libexpat-1.dll',
'<(module_root_dir)/lib/libffi-6.dll',
'<(module_root_dir)/lib/libfftw3-3.dll',
'<(module_root_dir)/lib/libfontconfig-1.dll',
'<(module_root_dir)/lib/libfreetype-6.dll',
'<(module_root_dir)/lib/libgcc_s_seh-1.dll',
'<(module_root_dir)/lib/libgdk_pixbuf-2.0-0.dll',
'<(module_root_dir)/lib/libgio-2.0-0.dll',
'<(module_root_dir)/lib/libglib-2.0-0.dll',
'<(module_root_dir)/lib/libgmodule-2.0-0.dll',
'<(module_root_dir)/lib/libgobject-2.0-0.dll',
'<(module_root_dir)/lib/libgsf-1-114.dll',
'<(module_root_dir)/lib/libgthread-2.0-0.dll',
'<(module_root_dir)/lib/libintl-8.dll',
'<(module_root_dir)/lib/libjpeg-62.dll',
'<(module_root_dir)/lib/liblcms2-2.dll',
'<(module_root_dir)/lib/libopenjpeg-1.dll',
'<(module_root_dir)/lib/libopenslide-0.dll',
'<(module_root_dir)/lib/libpango-1.0-0.dll',
'<(module_root_dir)/lib/libpangocairo-1.0-0.dll',
'<(module_root_dir)/lib/libpangowin32-1.0-0.dll',
'<(module_root_dir)/lib/libpixman-1-0.dll',
'<(module_root_dir)/lib/libpng16-16.dll',
'<(module_root_dir)/lib/libquadmath-0.dll',
'<(module_root_dir)/lib/libsqlite3-0.dll',
'<(module_root_dir)/lib/libssp-0.dll',
'<(module_root_dir)/lib/libtiff-5.dll',
'<(module_root_dir)/lib/libvips-42.dll',
'<(module_root_dir)/lib/libxml2-2.dll',
'<(module_root_dir)/lib/zlib1.dll'
'lib/GNU.Gettext.dll',
'lib/libasprintf-0.dll',
'lib/libcairo-2.dll',
'lib/libcairo-gobject-2.dll',
'lib/libcairo-script-interpreter-2.dll',
'lib/libcharset-1.dll',
'lib/libcroco-0.6-3.dll',
'lib/libexif-12.dll',
'lib/libexpat-1.dll',
'lib/libffi-6.dll',
'lib/libfftw3-3.dll',
'lib/libfontconfig-1.dll',
'lib/libfreetype-6.dll',
'lib/libgcc_s_seh-1.dll',
'lib/libgdk_pixbuf-2.0-0.dll',
'lib/libgif-7.dll',
'lib/libgio-2.0-0.dll',
'lib/libglib-2.0-0.dll',
'lib/libgmodule-2.0-0.dll',
'lib/libgobject-2.0-0.dll',
'lib/libgsf-1-114.dll',
'lib/libgthread-2.0-0.dll',
'lib/libiconv-2.dll',
'lib/libintl-8.dll',
'lib/libjpeg-62.dll',
'lib/liblcms2-2.dll',
'lib/libpango-1.0-0.dll',
'lib/libpangocairo-1.0-0.dll',
'lib/libpangowin32-1.0-0.dll',
'lib/libpixman-1-0.dll',
'lib/libpng16-16.dll',
'lib/libquadmath-0.dll',
'lib/librsvg-2-2.dll',
'lib/libssp-0.dll',
'lib/libstdc++-6.dll',
'lib/libtiff-5.dll',
'lib/libvips-42.dll',
'lib/libwebp-6.dll',
'lib/libxml2-2.dll',
'lib/zlib1.dll'
]
}]
}]

View File

@@ -12,8 +12,21 @@ var tmp = require('os').tmpdir();
var distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
// Use NPM-provided environment variable where available, falling back to require-based method for Electron
var minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
var vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h');
var platform = process.env.npm_config_platform || process.platform;
var arch = process.env.npm_config_arch || process.arch;
var arm_version = process.env.npm_config_armv || process.config.variables.arm_version;
if (arch === 'arch64' || arch === 'armhf') {
arch = 'arm';
if (arch === 'arch64') arm_version = '8';
}
// -- Helpers
// Does this file exist?
@@ -43,6 +56,24 @@ var unpack = function(tarPath, done) {
.pipe(extractor);
};
var platformId = function() {
var id = [platform, arch].join('-');
if (arch === 'arm') {
switch(arm_version) {
case '8':
id = id + 'v8';
break;
case '7':
id = id + 'v7';
break;
default:
id = id + 'v6';
break;
}
}
return id;
};
// Error
var error = function(msg) {
if (msg instanceof Error) {
@@ -58,20 +89,23 @@ module.exports.download_vips = function() {
// Has vips been installed locally?
if (!isFile(vipsHeaderPath)) {
// Ensure Intel 64-bit or ARM
if (process.arch === 'ia32') {
if (arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
// Ensure libc >= 2.15
// Ensure glibc >= 2.15
var lddVersion = process.env.LDD_VERSION;
if (lddVersion) {
var libcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (libcVersion && semver.lt(libcVersion + '.0', '2.13.0')) {
error('libc version ' + libcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
if (/(glibc|gnu libc)/i.test(lddVersion)) {
var 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
var platform = (process.arch === 'arm') ? 'arm' : process.platform.substr(0, 3);
var tarFilename = ['libvips', process.env.npm_package_config_libvips, platform].join('-') + '.tar.gz';
var tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz';
var tarPath = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPath)) {
unpack(tarPath);
@@ -110,18 +144,8 @@ module.exports.use_global_vips = function() {
if (globalVipsVersion) {
useGlobalVips = semver.gte(
globalVipsVersion,
process.env.npm_package_config_libvips
minimumLibvipsVersion
);
}
if (process.platform === 'darwin' && !useGlobalVips) {
if (globalVipsVersion) {
error(
'Found libvips ' + globalVipsVersion + ' but require ' + process.env.npm_package_config_libvips +
'\nPlease upgrade libvips by running: brew update && brew upgrade'
);
} else {
error('Please install libvips by running: brew install homebrew/science/vips --with-webp --with-graphicsmagick');
}
}
process.stdout.write(useGlobalVips ? 'true' : 'false');
};

View File

@@ -3,4 +3,4 @@ machine:
- docker
test:
override:
- ./packaging/test.sh
- ./packaging/test-linux-x64.sh

View File

@@ -6,22 +6,28 @@ var sharp = require('sharp');
### Input
#### sharp([input])
#### sharp([input], [options])
Constructor to which further methods are chained. `input`, if present, can be one of:
Constructor to which further methods are chained.
* Buffer containing JPEG, PNG, WebP, GIF* or TIFF image data, or
* String containing the path to an image file, with most major formats supported.
`input`, if present, can be one of:
The object returned implements the
* Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* 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 `input` is `null` or `undefined`.
`options`, if present, is an Object with the following optional attributes:
* `density` an integral number representing the DPI for vector images, defaulting to 72.
* `raw` an Object containing `width`, `height` and `channels` when providing uncompressed data. See `raw()` for pixel ordering.
The object returned by the constructor implements the
[stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
JPEG, PNG, WebP, GIF* 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.
\* libvips 8.0.0+ is required for Buffer/Stream input of GIF and other `magick` formats.
When using Stream based output, derived attributes are available from the `info` event.
```javascript
sharp('input.jpg')
@@ -32,17 +38,31 @@ sharp('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);
```
#### 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`, `magick` and `openslide`)
* `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`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522)
* `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#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
@@ -93,11 +113,12 @@ This will reduce memory usage and can improve performance on some systems.
Do not process input images where the number of pixels (width * height) exceeds this limit.
`pixels` is the integral Number of pixels, with a value between 1 and the default 268402689 (0x3FFF * 0x3FFF).
`pixels` is either an integral Number of pixels, with a value between 1 and the default 268402689 (0x3FFF * 0x3FFF) or
a boolean. `false` will disable checking while `true` will revert to the default limit.
### Resizing
#### resize([width], [height])
#### resize([width], [height], [options])
Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified.
@@ -105,23 +126,72 @@ Scale output to `width` x `height`. By default, the resized image is cropped to
`height` is the integral Number of pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width.
#### crop([gravity])
`options` is an optional Object. If present, it can contain one or more of:
* `options.kernel`, the kernel to use for image reduction, defaulting to `lanczos3`.
* `options.interpolator`, the interpolator to use for image enlargement, defaulting to `bicubic`.
Possible kernels are:
* `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 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.
```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
});
```
#### crop([option])
Crop the resized image to the exact size specified, the default behaviour.
`gravity`, if present, is a String or an attribute of the `sharp.gravity` Object e.g. `sharp.gravity.north`.
`option`, if present, is an attribute of:
Possible values are `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` and `centre`.
The default gravity is `center`/`centre`.
* `sharp.gravity` e.g. `sharp.gravity.north`, to crop to an edge or corner, or
* `sharp.strategy` e.g. `sharp.strategy.entropy`, to crop dynamically.
Possible attributes of `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.
The default crop option is a `center`/`centre` gravity.
```javascript
var transformer = sharp()
.resize(300, 200)
.crop(sharp.gravity.north)
.resize(200, 200)
.crop(sharp.strategy.entropy)
.on('error', function(err) {
console.log(err);
});
// Read image data from readableStream, resize and write image data to writableStream
// Read image data from readableStream
// Write 200px square auto-cropped image data to writableStream
readableStream.pipe(transformer).pipe(writableStream);
```
@@ -197,37 +267,6 @@ if its width or height exceeds the geometry specification*".
Ignoring the aspect ratio of the input, stretch the image to the exact `width` and/or `height` provided via `resize`.
#### interpolateWith(interpolator)
Use the given interpolator for image resizing, where `interpolator` is an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.bicubic`.
The default interpolator is `bicubic`, providing a general-purpose interpolator that is both fast and of good quality.
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), 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.
[Compare the output of these interpolators](https://github.com/lovell/sharp/tree/master/test/interpolators)
```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 nohalo scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
### Operations
#### extract({ left: left, top: top, width: width, height: height })
@@ -258,9 +297,15 @@ sharp(input)
});
```
#### trim([tolerance])
Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
* `tolerance`, if present, is an integral Number between 1 and 99 representing the percentage similarity, defaulting to 10.
#### background(rgba)
Set the background for the `embed` and `flatten` operations.
Set the background for the `embed`, `flatten` and `extend` operations.
`rgba` is parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
@@ -272,6 +317,25 @@ The default background is `{r: 0, g: 0, b: 0, a: 1}`, black without transparency
Merge alpha transparency channel, if any, with `background`.
#### extend(extension)
Extends/pads the edges of the image with `background`, where `extension` is one of:
* a Number representing the pixel count to add to each edge, or
* an Object containing `top`, `left`, `bottom` and `right` attributes, each a Number of pixels to add to that edge.
This operation will always occur after resizing and extraction, if any.
```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, a: 0})
.extend({top: 10, bottom: 20, left: 10, right: 10})
...
```
#### negate()
Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc.
@@ -320,23 +384,49 @@ When used without parameters, performs a fast, mild blur of the output image. Th
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.
* `sigma`, if present, is a Number between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
#### sharpen([radius], [flat], [jagged])
#### convolve(kernel)
Convolve the image with the specified `kernel`, an Object with the following attributes:
* `width` is an integral Number representing the width of the kernel in pixels.
* `height` is an integral Number representing the width of the kernel in pixels.
* `kernel` is an Array of length `width*height` containing the kernel values.
* `scale`, if present, is a Number representing the scale of the kernel in pixels, defaulting to the sum of the kernel's values.
* `offset`, if present, is a Number representing the offset of the kernel in pixels, defaulting to 0.
```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
});
```
#### sharpen([sigma], [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%.
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. This typically reduces performance by 50%.
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels.
* `sigma`, if present, is a Number representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* `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.
#### threshold([threshold])
#### threshold([threshold], [options])
Converts all pixels in the image to greyscale white or black. Any pixel greather-than-or-equal-to the threshold (0..255) will be white. All others will be black.
Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
By default, the image will be converted to single channel greyscale before thresholding.
* `threshold`, if present, is a Number, representing the level above which pixels will be forced to white.
* `threshold`, if present, is a Number between 0 and 255, representing the level at which the threshold will be applied. The default threshold is 128.
* `options`, if present, is an Object containing a Boolean `greyscale` (or `grayscale`). When `false` each channel will have the threshold applied independently.
#### gamma([gamma])
@@ -354,19 +444,31 @@ 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.
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.
#### normalize() / normalise()
Enhance output image contrast by stretching its luminance to cover the full dynamic range. This typically reduces performance by 30%.
#### overlayWith(path)
#### overlayWith(image, [options])
_Experimental_
Overlay (composite) a image over the processed (resized, extracted etc.) image.
Alpha composite image at `path` over the processed (resized, extracted) image. The dimensions of the two images must match.
`image` is one of the following, and must be the same size or smaller than the processed image:
* `path` is a String containing the path to an image file with an alpha channel.
* Buffer containing image data, or
* String containing the path to an image file
`options`, if present, is an Object with the following optional attributes:
* `gravity` is a String or an attribute of the `sharp.gravity` Object e.g. `sharp.gravity.north` at which to place the overlay, defaulting to `center`/`centre`.
* `top` is an integral Number representing the pixel offset from the top edge.
* `left` is an integral Number representing the pixel offset from the left edge.
* `tile` is a Boolean, defaulting to `false`. When set to `true` repeats the overlay image across the entire image with the given `gravity`.
* `cutout` is a Boolean, defaulting to `false`. When set to `true` applies only the alpha channel of the overlay image to the image to be overlaid, giving the appearance of one image being cut out of another.
* `raw` an Object containing `width`, `height` and `channels` when providing uncompressed data.
If both `top` and `left` are provided, they take precedence over `gravity`.
```javascript
sharp('input.png')
@@ -374,7 +476,7 @@ sharp('input.png')
.resize(300)
.flatten()
.background('#ff6600')
.overlayWith('overlay.png')
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
.sharpen()
.withMetadata()
.quality(90)
@@ -382,33 +484,109 @@ sharp('input.png')
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png, sharpened,
// with metadata, 90% quality WebP image data. Phew!
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});
```
#### toColourspace(colourspace) / toColorspace(colorspace)
Set the output colourspace. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
`colourspace` is a string or `sharp.colourspace` enum that identifies an output colourspace. String arguments comprise vips colour space interpretation names e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
#### extractChannel(channel)
Extract a single channel from a multi-channel image.
`channel` is a zero-indexed integral Number representing the band number to extract. `red`, `green` or `blue` are also accepted as an alternative to `0`, `1` or `2` respectively.
```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
});
```
#### joinChannel(channels, [options])
Join a data channel 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.
`channels` is one of
* a single file path
* an array of file paths
* a single buffer
* an array of buffers
Note that 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. In the case of a RAW buffer, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor. See `sharp()` for details. See `raw()` for pixel ordering.
#### bandbool(operation)
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
`operation` is a string containing the name of the bitwise operator to be appled to image channels, which can be one of:
* `and` performs a bitwise and operation, like the c-operator `&`.
* `or` performs a bitwise or operation, like the c-operator `|`.
* `eor` performs a bitwise exclusive or operation, like the c-operator `^`.
```javascript
sharp('input.png')
.bandbool(sharp.bool.and)
.toFile('output.png')
```
In the above example if `input.png` is a 3 channel RGB image, `output.png` will be a 1 channel grayscale image where each pixel `P = R & G & B`.
For example, if `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]` then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
#### boolean(image, operation, [options])
Perform a bitwise boolean operation with `image`, where `image` is one of the following:
* Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* String containing the path to an image file
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.
The boolean operation can be one of the following:
* `and` performs a bitwise and operation, like the c-operator `&`.
* `or` performs a bitwise or operation, like the c-operator `|`.
* `eor` performs a bitwise exclusive or operation, like the c-operator `^`.
`options`, if present, is an Object with the following optional attributes:
* `raw` an Object containing `width`, `height` and `channels` when providing uncompressed data.
### Output
#### toFile(path, [callback])
`path` is a String containing the path to write the image data to. The format is inferred from the extension, with JPEG, PNG, WebP, TIFF and DZI supported.
`path` is a String containing the path to write the image data to.
If an explicit output format is not selected, it will be inferred from the extension, with JPEG, PNG, WebP, TIFF, DZI, and VIPS V format supported. Note that RAW format is only supported for buffer output.
`callback`, if present, is called with two arguments `(err, info)` where:
* `err` contains an error message, if any.
* `info` contains the output image `format`, `size` (bytes), `width` and `height`.
* `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
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.
Write image data to a Buffer, the format of which will match the input image by default. JPEG, PNG, WebP, and RAW 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`, `size` (bytes), `width` and `height`.
* `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
A Promises/A+ promise is returned when `callback` is not provided.
@@ -459,23 +637,33 @@ This will also convert to and add the latest web-friendly v2 sRGB ICC profile.
The optional `metadata` parameter, if present, is an Object with the attributes to update.
New attributes cannot be inserted, only existing attributes updated.
* `orientation` is an integral Number between 0 and 7, used to update the value of the EXIF `Orientation` tag.
* `orientation` is an integral Number between 1 and 8, used to update the value of the EXIF `Orientation` tag.
This has no effect if the input image does not have an EXIF `Orientation` tag.
The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
#### tile([size], [overlap])
#### tile(options)
The size and overlap, in pixels, of square Deep Zoom image pyramid tiles.
The size, overlap, container and directory layout to use when generating square Deep Zoom image pyramid tiles.
`options` is an Object with one or more of the following attributes:
* `size` is an integral Number between 1 and 8192. The default value is 256 pixels.
* `overlap` is an integral Number between 0 and 8192. The default value is 0 pixels.
* `container` is a String, with value `fs` or `zip`. The default value is `fs`.
* `layout` is a String, with value `dz`, `zoomify` or `google`. The default value is `dz`.
You can also use the file extension `.zip` or `.szi` to write to a compressed archive file format.
```javascript
sharp('input.tiff').tile(256).toFile('output.dzi', function(err, info) {
// The output.dzi file is the XML format Deep Zoom definition
// The output_files directory contains 256x256 pixel tiles grouped by zoom level
});
sharp('input.tiff')
.tile({
size: 512
})
.toFile('output.dzi', function(err, info) {
// output.dzi is the Deep Zoom XML definition
// output_files contains 512x512 tiles grouped by zoom level
});
```
#### withoutChromaSubsampling()
@@ -544,9 +732,6 @@ for example:
tiff: { id: 'tiff',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: false, stream: false } },
magick: { id: 'magick',
input: { file: true, buffer: true, stream: true },
output: { file: false, buffer: false, stream: false } },
raw: { id: 'raw',
input: { file: false, buffer: false, stream: false },
output: { file: false, buffer: true, stream: true } } }
@@ -570,39 +755,33 @@ sharp.queue.on('change', function(queueLength) {
An Object containing the version numbers of libvips and, on Linux, its dependencies.
```javascript
> console.log(sharp.versions);
{ zlib: '1.2.8',
ffi: '3.2.1',
glib: '2.46.2',
xml: '2.9.2',
gsf: '1.14.34',
exif: '0.6.21',
jpeg: '1.4.2',
png: '1.6.19',
lcms: '2.7',
webp: '0.4.4',
tiff: '4.0.6',
magick: '6.9.2-6',
orc: '0.4.24',
vips: '8.1.1' }
console.log(sharp.versions);
```
### Utilities
#### sharp.cache([memory], [items])
#### sharp.cache([options])
If `memory` or `items` are provided, set the limits of _libvips'_ operation cache.
If `options` is provided, sets 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
* `options.memory` is the maximum memory in MB to use for this cache, with a default value of 50
* `options.files` is the maximum number of files to hold open, with a default value of 20
* `options.items` is the maximum number of operations to cache, with a default value of 100
`options` can also be a boolean, where `true` enables the default cache settings and `false` disables all caching.
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.
```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}
var stats = sharp.cache();
```
```javascript
sharp.cache( { items: 200 } );
sharp.cache( { files: 0 } );
sharp.cache(false);
```
#### sharp.concurrency([threads])

View File

@@ -1,7 +1,280 @@
# Changelog
### v0.16 - "*pencil*"
Requires libvips v8.3.3
#### 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.

View File

@@ -1,30 +1,26 @@
# sharp
The typical use case for this high speed Node.js module
is to convert large images of many formats to
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 faster than using
the quickest ImageMagick and GraphicsMagick settings.
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.
Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed.
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.
64-bit Windows and recent Linux systems do not require
OS X, Windows (x64), Linux (x64, ARM) systems do not require
the installation of any external runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips`
to install the libvips dependency.
[![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, OpenSlide,
GIF and other libmagick-supported 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.
@@ -89,12 +85,21 @@ the help and code contributions of the following people:
* [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)
Thank you!
### Licence
Copyright 2013, 2014, 2015 Lovell Fuller and contributors.
Copyright 2013, 2014, 2015, 2016 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.

View File

@@ -6,69 +6,65 @@ npm install sharp
### Prerequisites
* C++11 compatible compiler such as gcc 4.6+ (Node v4+ requires gcc 4.8+), clang 3.0+ or MSVC 2013
* [node-gyp](https://github.com/TooTallNate/node-gyp#installation)
* 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
### 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` during `npm install`.
This involves an automated HTTPS download of approximately 6MB.
libvips and its dependencies are fetched and stored within `node_modules/sharp/lib` during `npm install`.
This involves an automated HTTPS download of approximately 6.5MB.
Most recent Linux-based operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.:
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, 14.10, 15.04, 15.10
* Ubuntu 12.04, 14.04, 16.04
* Centos 7
* Fedora 21, 22, 23
* Fedora 23, 24
* openSUSE 13.2
* Archlinux 2015.06.01
* 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.
Preference will be given to an existing globally-installed (via `pkg-config`)
version of libvips that meets the minimum version requirement.
This allows the use of newer versions of libvips with older versions of sharp.
For older Linux-based operating systems and 32-bit Intel CPUs,
For 32-bit Intel CPUs and older Linux-based operating systems such as Centos 6,
a system-wide installation of the most suitable version of
libvips and its dependencies can be achieved by running
the following command as a user with `sudo` access
(requires `curl` and `pkg-config`):
```sh
# WARNING: This script is deprecated. You probably don't need to run it. Please read above.
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
```
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-osx-ci.png?branch=master)](https://travis-ci.org/lovell/sharp-osx-ci)
[![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
libvips must be installed before `npm install` is run.
This can be achieved via homebrew:
libvips and its dependencies are fetched and stored within `node_modules/sharp/lib` during `npm install`.
This involves an automated HTTPS download of approximately 6.5MB.
```sh
brew install homebrew/science/vips
```
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`.
For GIF input and WebP output suppport use:
```sh
brew install homebrew/science/vips --with-imagemagick --with-webp
```
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`.
### Windows
### Windows x64
[![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
@@ -76,7 +72,6 @@ libvips and its dependencies are fetched and stored within `node_modules\sharp`
This involves an automated HTTPS download of approximately 9MB.
Only 64-bit (x64) `node.exe` is supported.
The WebP format is currently unavailable on Windows.
### FreeBSD
@@ -109,8 +104,113 @@ docker pull marcbachmann/libvips
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)
* [gulp-sharp](https://www.npmjs.com/package/gulp-sharp)
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)
### 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) |

View File

@@ -2,42 +2,45 @@
### Test environment
* AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @2.90GHz)
* Amazon Linux 2015.09.1
* Node.js v5.1.0
* AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @ 2.90GHz)
* Amazon Linux AMI 2016.03.1 (HVM), SSD Volume Type
* Node.js v6.2.0
### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.19 - Image processing in pure JavaScript. Bilinear interpolation only.
* [lwip](https://www.npmjs.com/package/lwip) v0.0.8 - Wrapper around CImg, compiles dependencies from source.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) @5ab570e - Wrapper around libmagick++, supports Buffers only.
* [jimp](https://www.npmjs.com/package/jimp) v0.2.24 - Image processing in pure JavaScript. Bilinear interpolation only.
* [lwip](https://www.npmjs.com/package/lwip) v0.0.9 - Wrapper around CImg, compiles dependencies from source.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) v1.9.2 - 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.21.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.12.0 / libvips v8.1.1 - Caching within libvips disabled to ensure a fair comparison.
* [gm](https://www.npmjs.com/package/gm) v1.22.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.15.0 / libvips v8.3.1 - Caching within libvips disabled to ensure a fair comparison.
### The task
Decompress a 2725x2225 JPEG image, resize to 720x480 using bicubic interpolation (where available), then compress to JPEG.
Decompress a 2725x2225 JPEG image,
resize to 720x480 using Lanczos 3 resampling (where available),
then compress to JPEG.
### Results
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp | file | file | 0.99 | 1.0 |
| jimp | buffer | buffer | 1.05 | 1.1 |
| lwip | file | file | 1.13 | 1.1 |
| lwip | buffer | buffer | 1.13 | 1.1 |
| imagemagick-native | buffer | buffer | 1.67 | 1.7 |
| imagemagick | file | file | 5.19 | 5.2 |
| gm | buffer | buffer | 5.56 | 5.6 |
| gm | file | file | 5.59 | 5.6 |
| sharp | stream | stream | 21.91 | 22.1 |
| sharp | file | file | 22.79 | 23.0 |
| sharp | file | buffer | 22.91 | 23.1 |
| sharp | buffer | file | 23.03 | 23.3 |
| sharp | buffer | buffer | 23.15 | 23.4 |
| jimp (bilinear) | file | file | 0.94 | 1.0 |
| jimp (bilinear) | buffer | buffer | 0.98 | 1.0 |
| lwip | file | file | 1.14 | 1.2 |
| lwip | buffer | buffer | 1.14 | 1.2 |
| imagemagick-native | buffer | buffer | 1.66 | 1.8 |
| imagemagick | file | file | 5.08 | 5.4 |
| gm | buffer | buffer | 5.43 | 5.7 |
| gm | file | file | 5.46 | 5.8 |
| sharp | stream | stream | 26.52 | 28.2 |
| sharp | file | file | 28.16 | 30.0 |
| sharp | file | buffer | 28.27 | 30.1 |
| sharp | buffer | file | 28.42 | 30.2 |
| sharp | buffer | buffer | 28.42 | 30.2 |
Greater performance can be expected with caching enabled (default) and using 8+ core machines.
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.

Binary file not shown.

BIN
icc/cmyk.icm Normal file

Binary file not shown.

BIN
icc/sRGB.icc Normal file

Binary file not shown.

745
index.js

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
site_name: sharp
site_url: http://sharp.dimens.io/
repo_url: https://github.com/lovell/sharp
site_description: The fastest Node.js module for resizing JPEG, PNG, WebP and TIFF images. Uses the libvips library.
site_description: High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images
copyright: <a href="https://dimens.io/">dimens.io</a>
google_analytics: ['UA-13034748-12', 'sharp.dimens.io']
theme: readthedocs

View File

@@ -1,6 +1,6 @@
{
"name": "sharp",
"version": "0.12.2",
"version": "0.16.1",
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
@@ -18,15 +18,24 @@
"Alaric Holloway <alaric.holloway@gmail.com>",
"Bernhard K. Weisshuhn <bkw@codingforce.com>",
"Chris Riley <criley@primedia.com>",
"David Carley <dacarley@gmail.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>"
],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"scripts": {
"clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-leak": "./test/leak/leak.sh",
"test-packaging": "./packaging/test.sh",
"test-packaging": "./packaging/test-linux-x64.sh",
"test-clean": "rm -rf coverage/ test/fixtures/output.* && npm install && npm test"
},
"main": "index.js",
@@ -39,7 +48,10 @@
"png",
"webp",
"tiff",
"gif",
"svg",
"dzi",
"image",
"resize",
"thumbnail",
"crop",
@@ -47,28 +59,29 @@
"vips"
],
"dependencies": {
"bluebird": "^3.1.1",
"color": "^0.11.1",
"nan": "^2.2.0",
"semver": "^5.1.0",
"request": "^2.67.0",
"bluebird": "^3.4.6",
"color": "^0.11.3",
"nan": "^2.4.0",
"semver": "^5.3.0",
"request": "^2.75.0",
"tar": "^2.2.1"
},
"devDependencies": {
"async": "^1.5.2",
"coveralls": "^2.11.6",
"exif-reader": "^1.0.0",
"async": "^2.1.0",
"bufferutil": "^1.2.1",
"coveralls": "^2.11.14",
"exif-reader": "^1.0.1",
"icc": "^0.0.2",
"istanbul": "^0.4.2",
"mocha": "^2.3.4",
"mocha-jshint": "^2.2.6",
"istanbul": "^0.4.5",
"mocha": "^3.1.2",
"mocha-jshint": "^2.3.1",
"node-cpplint": "^0.4.0",
"rimraf": "^2.5.0",
"bufferutil": "^1.2.1"
"rimraf": "^2.5.4",
"unzip": "^0.1.11"
},
"license": "Apache-2.0",
"config": {
"libvips": "8.2.0"
"libvips": "8.3.3"
},
"engines": {
"node": ">=0.10"

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).

View File

@@ -1,34 +0,0 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 IP"
echo "Build libvips for 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 arm/Dockerfile and arm/build.sh to device"
sshpass -e scp -o PreferredAuthentications=password -r arm root@${IP}:/root
echo "Building Raspbian-based container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker build -t vips-dev-arm arm"
echo "Running arm/build.sh within container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker run -i -t --rm -v \${PWD}/arm:/arm vips-dev-arm sh -c 'cd /arm && ./build.sh' | tee arm/build.log"
echo "Copying resultant tar.gz file from device"
sshpass -e scp -o PreferredAuthentications=password root@${IP}:/root/arm/*.tar.gz .

View File

@@ -1,5 +0,0 @@
FROM resin/rpi-raspbian:jessie
MAINTAINER Lovell Fuller <npm@lovell.info>
# Build dependencies
RUN apt-get update && apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo curl

View File

@@ -1,135 +0,0 @@
#!/bin/sh
# To be run inside a Raspbian container
# 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"
# Dependency version numbers
VERSION_ZLIB=1.2.8
VERSION_FFI=3.2.1
VERSION_GLIB=2.46.2
VERSION_XML2=2.9.3
VERSION_GSF=1.14.34
VERSION_EXIF=0.6.21
VERSION_JPEG=1.4.2
VERSION_PNG16=1.6.20
VERSION_LCMS2=2.7
VERSION_WEBP=0.5.0
VERSION_TIFF=4.0.6
VERSION_MAGICK=6.9.2-10
VERSION_ORC=0.4.24
VERSION_VIPS=8.2.0
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} && 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 --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
mkdir ${DEPS}/glib
curl -Ls http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
cd ${DEPS}/glib
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && 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 --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip
mkdir ${DEPS}/gsf
curl -Ls http://ftp.gnome.org/pub/GNOME/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1
cd ${DEPS}/gsf
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/exif
curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
cd ${DEPS}/exif
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/jpeg
curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
cd ${DEPS}/jpeg
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
mkdir ${DEPS}/png16
curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
cd ${DEPS}/png16
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/lcms2
curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
cd ${DEPS}/lcms2
./configure --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 --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/tiff
curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
cd ${DEPS}/tiff
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
rm ${TARGET}/lib/libtiffxx*
mkdir ${DEPS}/magick
curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1
cd ${DEPS}/magick
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && 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 --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.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
cd ${DEPS}/vips
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw \
--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 C++ bindings
cd ${TARGET}/include
rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h
cd ${TARGET}/lib
rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.*
# Create JSON file of version numbers
cd ${TARGET}
echo "{\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"magick\": \"${VERSION_MAGICK}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\
}" >lib/versions.json
# Create .tar.gz
GZIP=-9 tar czf /arm/libvips-${VERSION_VIPS}-arm.tar.gz include lib

View File

@@ -1,28 +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
# TODO: docker v1.9.0 will allow build-time args - https://github.com/docker/docker/pull/15182
# 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
# Windows
docker build -t vips-dev-win win
WIN_CONTAINER_ID=$(docker run -d vips-dev-win)
docker cp $WIN_CONTAINER_ID:/libvips-8.2.0-win.tar.gz .
docker rm $WIN_CONTAINER_ID
# Linux
docker build -t vips-dev-lin lin
LIN_CONTAINER_ID=$(docker run -d vips-dev-lin)
docker cp $LIN_CONTAINER_ID:/libvips-8.2.0-lin.tar.gz .
docker rm $LIN_CONTAINER_ID
# Checksums
# 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

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

@@ -0,0 +1,236 @@
#!/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.8
VERSION_FFI=3.2.1
VERSION_GLIB=2.49.4
VERSION_XML2=2.9.4
VERSION_GSF=1.14.39
VERSION_EXIF=0.6.21
VERSION_LCMS2=2.8
VERSION_JPEG=1.5.0
VERSION_PNG16=1.6.25
VERSION_WEBP=0.5.1
VERSION_TIFF=4.0.6
VERSION_ORC=0.4.25
VERSION_GDKPIXBUF=2.35.2
VERSION_FREETYPE=2.6.5
VERSION_FONTCONFIG=2.12.0
VERSION_HARFBUZZ=1.3.0
VERSION_PIXMAN=0.34.0
VERSION_CAIRO=1.14.6
VERSION_PANGO=1.40.1
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.49/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
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
if [ -n "${CHOST}" ]; then autoreconf -fiv; fi
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-mdi --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.35/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-libpng --without-libjpeg --without-libtiff --without-gdiplus --with-included-loaders=
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
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.3/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}\",\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

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

@@ -0,0 +1,19 @@
#!/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.3
rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll
cp bin/*.dll lib/
cp -r lib64/* lib/
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

@@ -1,137 +0,0 @@
FROM debian:wheezy
MAINTAINER Lovell Fuller <npm@lovell.info>
# Build dependencies
RUN apt-get update && apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo
# Create working directories
ENV DEPS=/deps \
TARGET=/target
RUN mkdir ${DEPS} && mkdir ${TARGET}
# Common build paths and flags
ENV PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig \
PATH=${PATH}:${TARGET}/bin \
CPPFLAGS=-I${TARGET}/include \
LDFLAGS=-L${TARGET}/lib
# Dependency version numbers
ENV VERSION_ZLIB=1.2.8 \
VERSION_FFI=3.2.1 \
VERSION_GLIB=2.46.2 \
VERSION_XML2=2.9.3 \
VERSION_GSF=1.14.34 \
VERSION_EXIF=0.6.21 \
VERSION_JPEG=1.4.2 \
VERSION_PNG16=1.6.20 \
VERSION_LCMS2=2.7 \
VERSION_WEBP=0.5.0 \
VERSION_TIFF=4.0.6 \
VERSION_MAGICK=6.9.2-10 \
VERSION_ORC=0.4.24 \
VERSION_VIPS=8.2.0
RUN mkdir ${DEPS}/zlib
RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
WORKDIR ${DEPS}/zlib
RUN ./configure --prefix=${TARGET} && make install
RUN rm ${TARGET}/lib/libz.a
RUN mkdir ${DEPS}/ffi
RUN curl -Ls ftp://sourceware.org/pub/libffi/libffi-${VERSION_FFI}.tar.gz | tar xzC ${DEPS}/ffi --strip-components=1
WORKDIR ${DEPS}/ffi
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
RUN mkdir ${DEPS}/glib
RUN curl -Ls http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
WORKDIR ${DEPS}/glib
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/xml2
RUN curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
WORKDIR ${DEPS}/xml2
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip
RUN mkdir ${DEPS}/gsf
RUN curl -Ls http://ftp.gnome.org/pub/GNOME/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1
WORKDIR ${DEPS}/gsf
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/exif
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
WORKDIR ${DEPS}/exif
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/jpeg
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
WORKDIR ${DEPS}/jpeg
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
RUN mkdir ${DEPS}/png16
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
WORKDIR ${DEPS}/png16
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/lcms2
RUN curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
WORKDIR ${DEPS}/lcms2
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/webp
RUN curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
WORKDIR ${DEPS}/webp
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/tiff
RUN curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
WORKDIR ${DEPS}/tiff
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN rm ${TARGET}/lib/libtiffxx*
RUN mkdir ${DEPS}/magick
RUN curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1
WORKDIR ${DEPS}/magick
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip
RUN mkdir ${DEPS}/orc
RUN curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
WORKDIR ${DEPS}/orc
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/vips
RUN curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
WORKDIR ${DEPS}/vips
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw \
--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 C++ bindings
WORKDIR ${TARGET}/include
RUN rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h
WORKDIR ${TARGET}/lib
RUN rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.*
# Create JSON file of version numbers
WORKDIR ${TARGET}
RUN echo "{\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"magick\": \"${VERSION_MAGICK}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\
}" >lib/versions.json
# Create .tar.gz
WORKDIR ${TARGET}
RUN GZIP=-9 tar czf /libvips-${VERSION_VIPS}-lin.tar.gz include lib

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
# 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
# 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
# Compiler settings
ENV \
PLATFORM=linux-armv8 \
CHOST=aarch64-linux-gnu \
FLAGS="-march=armv8-a -Os -D_GLIBCXX_USE_CXX11_ABI=0"

View File

@@ -0,0 +1,14 @@
FROM debian:wheezy
MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Debian-based container suitable for building Linux x64 binaries
# Build dependencies
RUN \
apt-get update && \
apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo advancecomp
# Compiler settings
ENV \
PLATFORM="linux-x64" \
FLAGS="-O3"

View File

@@ -22,7 +22,7 @@ fi
export SSHPASS=hypriot
echo "Copying sharp source to device"
sshpass -e scp -o PreferredAuthentications=password -r ../../sharp root@${IP}:/root/sharp
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 root@${IP} "docker run -i -t --rm -v \${PWD}/sharp:/s hypriot/rpi-node:5 sh -c 'cd /s && npm install --unsafe-perm && npm test'"
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

View File

@@ -1,50 +0,0 @@
#!/bin/sh
# Verify docker is available
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
test="npm run clean; NODE_ENV=development npm install --unsafe-perm; npm test"
# Debian 7, 8
# Ubuntu 12.04, 14.04
for dist in wheezy jessie precise trusty; do
echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v nodesource/$dist:0.12 >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 6
echo "Testing centos6..."
if docker run -i -t --rm -v $PWD:/v nodesource/centos6:0.12 >packaging/centos6.log 2>&1 sh -c "cd /v; source ./packaging/test/centos6.sh; ./preinstall.sh; $test";
then echo "centos6 OK"
else echo "centos6 fail" && cat packaging/centos6.log
fi
# Centos 7
# Fedora 20, 21
for dist in centos7 fedora20 fedora21; do
echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v nodesource/$dist:0.12 >packaging/$dist.log 2>&1 sh -c "cd /v; $test";
then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log
fi
done
# 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 base/archlinux:2015.06.01 >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

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

View File

@@ -1,8 +0,0 @@
#!/bin/sh
# Install C++11 compatible version of g++ on Centos 6
curl -o /etc/yum.repos.d/devtools-1.1.repo http://people.centos.org/tru/devtools-1.1/devtools-1.1.repo
yum install -y devtoolset-1.1
export CC=/opt/centos/devtoolset-1.1/root/usr/bin/gcc
export CPP=/opt/centos/devtoolset-1.1/root/usr/bin/cpp
export CXX=/opt/centos/devtoolset-1.1/root/usr/bin/c++

View File

@@ -3,5 +3,5 @@
# 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 nodejs-npm
zypper --non-interactive install gcc-c++ make nodejs-devel npm
npm install -g npm

View File

@@ -1,17 +0,0 @@
FROM ubuntu:precise
MAINTAINER Lovell Fuller <npm@lovell.info>
RUN apt-get update && apt-get install -y curl zip
# Fetch and unzip
RUN mkdir /vips
WORKDIR /vips
RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.zip
RUN unzip vips-dev-w64-8.2.zip
# Clean and zip
WORKDIR /vips/vips-dev-8.2
RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll bin/libstdc++-6.dll
RUN cp bin/*.dll lib/
RUN cp -r lib64/* lib/
RUN GZIP=-9 tar czf /libvips-8.2.0-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll

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,21 +1,22 @@
#!/bin/sh
# Ensures libvips is installed and attempts to install it if not
# Currently supports:
# * Debian Linux
# * Debian 7, 8
# * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
# * Mint 13, 17
# * Elementary 0.3
# * Red Hat Linux
# * RHEL/Centos/Scientific 6, 7
# * Fedora 21, 22, 23
# * Amazon Linux 2015.03, 2015.09
# * OpenSuse 13
# Use of this script is deprecated
vips_version_minimum=8.2.0
vips_version_latest_major_minor=8.2
vips_version_latest_patch=1
echo
echo "WARNING: This script will stop working at the end of 2016"
echo
echo "WARNING: This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
echo
echo "See http://sharp.dimens.io/page/install#linux"
echo
echo "If you really, really need this script, it will attempt"
echo "to globally install libvips if not already available."
echo
sleep 5
vips_version_minimum=8.3.3
vips_version_latest_major_minor=8.3
vips_version_latest_patch=3
openslide_version_minimum=3.4.0
openslide_version_latest_major_minor=3.4
@@ -26,7 +27,7 @@ install_libvips_from_source() {
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch
./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
make
make install
cd ..
@@ -119,6 +120,11 @@ if [ "$(id -u)" -ne "0" ]; then
exit 1
fi
# Deprecation warning
if [ "$(arch)" == "x86_64" ]; then
echo "This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
fi
# OS-specific installations of libopenslide follows
# Either openslide does not exist, or vips is installed without openslide support
if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then
@@ -127,7 +133,7 @@ if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_ex
DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in
jessie|vivid|wily)
jessie|vivid|wily|xenial)
# Debian 8, Ubuntu 15
echo "Installing libopenslide via apt-get"
apt-get install -y libopenslide-dev

View File

@@ -1,30 +1,53 @@
#include <cstdlib>
#include <string>
#include <string.h>
#include <vips/vips.h>
#include <node.h>
#include <node_buffer.h>
#include <vips/vips8>
#include "nan.h"
#include "common.h"
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 1 && VIPS_PATCH_VERSION < 1))
#error libvips version 8.1.1+ required - see http://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 http://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 http://sharp.dimens.io/page/install#prerequisites
#endif
#endif
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
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 {
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");
}
return descriptor;
}
// How many tasks are in the queue?
volatile int counterQueue = 0;
@@ -50,6 +73,36 @@ namespace sharp {
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");
}
/*
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;
}
/*
Determine image format of a buffer.
@@ -58,7 +111,7 @@ namespace sharp {
ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load_buffer(buffer, length);
if (load != NULL) {
std::string loader = load;
std::string const loader = load;
if (EndsWith(loader, "JpegBuffer")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "PngBuffer")) {
@@ -67,6 +120,12 @@ namespace sharp {
imageType = ImageType::WEBP;
} 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;
}
@@ -74,13 +133,6 @@ namespace sharp {
return imageType;
}
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access) {
return vips_image_new_from_buffer(buffer, length, nullptr, "access", access, nullptr);
}
/*
Determine image format, reads the first few bytes of the file
*/
@@ -88,7 +140,7 @@ namespace sharp {
ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load(file);
if (load != nullptr) {
std::string loader = load;
std::string const loader = load;
if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) {
@@ -99,6 +151,18 @@ namespace sharp {
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;
}
@@ -107,42 +171,103 @@ namespace sharp {
}
/*
Initialise and return a VipsImage from a file.
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
VipsImage* InitImage(char const *file, VipsAccess const access) {
return vips_image_new_from_file(file, "access", access, nullptr);
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
VImage image;
ImageType imageType;
if (descriptor->buffer != nullptr) {
// From buffer
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 {
// 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 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(EXIF_IFD0_ORIENTATION) != 0) {
char const *exif = image.get_string(EXIF_IFD0_ORIENTATION);
if (exif != nullptr) {
orientation = atoi(&exif[0]);
}
}
return orientation;
}
@@ -150,31 +275,41 @@ namespace sharp {
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VipsImage *image, int const orientation) {
void SetExifOrientation(VImage image, int const orientation) {
char exif[3];
g_snprintf(exif, sizeof(exif), "%d", orientation);
vips_image_set_string(image, EXIF_IFD0_ORIENTATION, exif);
image.set(EXIF_IFD0_ORIENTATION, exif);
}
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VipsImage *image) {
void RemoveExifOrientation(VImage image) {
SetExifOrientation(image, 0);
}
/*
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.
Does this image have a non-default density?
*/
int InterpolatorWindowSize(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name);
if (interpolator == nullptr) {
return -1;
}
int window_size = vips_interpolate_get_window_size(interpolator);
g_object_unref(interpolator);
return window_size;
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");
}
/*
@@ -186,4 +321,122 @@ namespace sharp {
}
}
} // namespace sharp
/*
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())
);
}
} // namespace sharp

View File

@@ -2,17 +2,87 @@
#define SRC_COMMON_H_
#include <string>
#include <tuple>
#include <node.h>
#include <vips/vips8>
#include "nan.h"
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 3))
#error libvips version 8.3.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
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
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;
InputDescriptor():
buffer(nullptr),
bufferLength(0),
density(72),
rawChannels(0),
rawWidth(0),
rawHeight(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,
GIF,
SVG,
PDF,
MAGICK,
OPENSLIDE
OPENSLIDE,
PPM,
FITS,
VIPS,
RAW,
UNKNOWN
};
// How many tasks are in the queue?
@@ -27,6 +97,13 @@ namespace sharp {
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.
@@ -39,52 +116,91 @@ 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(void *buffer, size_t const length, VipsAccess const access);
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(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);
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VipsImage *image, int const orientation);
void SetExifOrientation(VImage image, int const orientation);
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VipsImage *image);
void RemoveExifOrientation(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.
Does this image have a non-default density?
*/
int InterpolatorWindowSize(char const *name);
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);
} // 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

View File

@@ -1,199 +1,124 @@
#include <numeric>
#include <node.h>
#include <vips/vips.h>
#include <vips/vips8>
#include "nan.h"
#include "common.h"
#include "metadata.h"
using v8::Handle;
using v8::Local;
using v8::Value;
using v8::Object;
using v8::Number;
using v8::String;
using v8::Boolean;
using v8::Function;
using v8::Exception;
using Nan::AsyncQueueWorker;
using Nan::AsyncWorker;
using Nan::Callback;
using Nan::HandleScope;
using Nan::Utf8String;
using Nan::Has;
using Nan::Get;
using Nan::Set;
using Nan::New;
using Nan::NewBuffer;
using Nan::Null;
using Nan::Error;
using sharp::ImageType;
using sharp::DetermineImageType;
using sharp::InitImage;
using sharp::HasProfile;
using sharp::HasAlpha;
using sharp::ExifOrientation;
using sharp::FreeCallback;
using sharp::counterQueue;
struct MetadataBaton {
// Input
std::string fileIn;
char *bufferIn;
size_t bufferInLength;
// Output
std::string format;
int width;
int height;
std::string space;
int channels;
bool hasProfile;
bool hasAlpha;
int orientation;
char *exif;
size_t exifLength;
char *icc;
size_t iccLength;
std::string err;
MetadataBaton():
bufferInLength(0),
orientation(0),
exifLength(0),
iccLength(0) {}
};
class MetadataWorker : public AsyncWorker {
class MetadataWorker : public Nan::AsyncWorker {
public:
MetadataWorker(Callback *callback, MetadataBaton *baton, const Local<Object> &bufferIn) :
AsyncWorker(callback), baton(baton) {
if (baton->bufferInLength > 0) {
SaveToPersistent("bufferIn", bufferIn);
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 = nullptr;
if (baton->bufferInLength > 0) {
// From buffer
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (imageType != ImageType::UNKNOWN) {
image = InitImage(baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
if (image == nullptr) {
(baton->err).append("Input buffer has corrupt header");
imageType = ImageType::UNKNOWN;
}
} else {
(baton->err).append("Input buffer contains unsupported image format");
}
} else {
// From file
imageType = DetermineImageType(baton->fileIn.data());
if (imageType != ImageType::UNKNOWN) {
image = InitImage(baton->fileIn.data(), VIPS_ACCESS_RANDOM);
if (image == nullptr) {
(baton->err).append("Input file has corrupt header");
imageType = ImageType::UNKNOWN;
}
} else {
(baton->err).append("Input 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 != nullptr && 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::OPENSLIDE: baton->format = "openslide"; 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);
baton->hasAlpha = sharp::HasAlpha(image);
baton->orientation = sharp::ExifOrientation(image);
// EXIF
if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
void* exif;
if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
size_t exifLength;
if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) {
baton->exifLength = exifLength;
baton->exif = static_cast<char*>(g_malloc(exifLength));
memcpy(baton->exif, exif, 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 (vips_image_get_typeof(image, VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
void* icc;
if (image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
size_t iccLength;
if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) {
baton->iccLength = iccLength;
baton->icc = static_cast<char*>(g_malloc(iccLength));
memcpy(baton->icc, icc, 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;
}
// Drop image reference
g_object_unref(image);
}
// Clean up
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback () {
HandleScope();
using Nan::New;
using Nan::Set;
Nan::HandleScope();
Local<Value> argv[2] = { Null(), Null() };
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
// Error
argv[0] = Error(baton->err.data());
argv[0] = Nan::Error(baton->err.data());
} else {
// Metadata Object
Local<Object> info = New<Object>();
Set(info, New("format").ToLocalChecked(), New<String>(baton->format).ToLocalChecked());
Set(info, New("width").ToLocalChecked(), New<Number>(baton->width));
Set(info, New("height").ToLocalChecked(), New<Number>(baton->height));
Set(info, New("space").ToLocalChecked(), New<String>(baton->space).ToLocalChecked());
Set(info, New("channels").ToLocalChecked(), New<Number>(baton->channels));
Set(info, New("hasProfile").ToLocalChecked(), New<Boolean>(baton->hasProfile));
Set(info, New("hasAlpha").ToLocalChecked(), New<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) {
Set(info, New("orientation").ToLocalChecked(), New<Number>(baton->orientation));
Set(info, New("orientation").ToLocalChecked(), New<v8::Uint32>(baton->orientation));
}
if (baton->exifLength > 0) {
Set(info,
New("exif").ToLocalChecked(),
NewBuffer(baton->exif, baton->exifLength, FreeCallback, nullptr).ToLocalChecked()
Nan::NewBuffer(baton->exif, baton->exifLength, sharp::FreeCallback, nullptr).ToLocalChecked()
);
}
if (baton->iccLength > 0) {
Set(info,
New("icc").ToLocalChecked(),
NewBuffer(baton->icc, baton->iccLength, FreeCallback, nullptr).ToLocalChecked()
Nan::NewBuffer(baton->icc, baton->iccLength, sharp::FreeCallback, nullptr).ToLocalChecked()
);
}
argv[1] = info;
}
// Dispose of Persistent wrapper around input Buffer so it can be garbage collected
if (baton->bufferInLength > 0) {
GetFromPersistent("bufferIn");
}
// 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
@@ -202,32 +127,27 @@ class MetadataWorker : public AsyncWorker {
private:
MetadataBaton* baton;
std::vector<v8::Local<v8::Object>> buffersToPersist;
};
/*
metadata(options, callback)
*/
NAN_METHOD(metadata) {
HandleScope();
// 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 = info[0].As<Object>();
v8::Local<v8::Object> options = info[0].As<v8::Object>();
// Input filename
baton->fileIn = *Utf8String(Get(options, New("fileIn").ToLocalChecked()).ToLocalChecked());
// Input Buffer object
Local<Object> bufferIn;
if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) {
bufferIn = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>();
baton->bufferInLength = node::Buffer::Length(bufferIn);
baton->bufferIn = node::Buffer::Data(bufferIn);
}
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
// Join queue for worker thread
Callback *callback = new Callback(info[1].As<Function>());
AsyncQueueWorker(new MetadataWorker(callback, baton, bufferIn));
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);
g_atomic_int_inc(&sharp::counterQueue);
}

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

@@ -2,6 +2,41 @@
#define SRC_METADATA_H_
#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);

View File

@@ -1,66 +1,89 @@
#include <vips/vips.h>
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vips/vips8>
#include "common.h"
#include "operations.h"
using vips::VImage;
using vips::VError;
namespace sharp {
/*
Alpha composite src over dst
Assumes alpha channels are already premultiplied and will be unpremultiplied after
Alpha composite src over dst with given gravity.
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
*/
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out) {
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;
// Split src into non-alpha and alpha
VipsImage *srcWithoutAlpha;
if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, nullptr))
return -1;
vips_object_local(context, srcWithoutAlpha);
VipsImage *srcAlpha;
if (vips_extract_band(src, &srcAlpha, src->Bands - 1, "n", 1, nullptr))
return -1;
vips_object_local(context, srcAlpha);
// Split dst into non-alpha and alpha channels
VipsImage *dstWithoutAlpha;
VipsImage *dstAlpha;
if (HasAlpha(dst)) {
// Non-alpha: extract all-but-last channel
if (vips_extract_band(dst, &dstWithoutAlpha, 0, "n", dst->Bands - 1, nullptr)) {
return -1;
}
vips_object_local(context, dstWithoutAlpha);
// Alpha: Extract last channel
if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, nullptr)) {
return -1;
}
vips_object_local(context, dstAlpha);
} else {
// Non-alpha: Copy reference
dstWithoutAlpha = dst;
// Alpha: Use blank, opaque (0xFF) image
VipsImage *black;
if (vips_black(&black, dst->Xsize, dst->Ysize, nullptr)) {
return -1;
}
vips_object_local(context, black);
if (vips_invert(black, &dstAlpha, nullptr)) {
return -1;
}
vips_object_local(context, dstAlpha);
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");
}
// Compute normalized input alpha channels:
VipsImage *srcAlphaNormalized;
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
return -1;
vips_object_local(context, srcAlphaNormalized);
return true;
}
VipsImage *dstAlphaNormalized;
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
return -1;
vips_object_local(context, dstAlphaNormalized);
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:
@@ -72,22 +95,8 @@ namespace sharp {
// out_a = src_a + dst_a * (1 - src_a)
// ^^^^^^^^^^^
// t0
// ^^^^^^^^^^^^^^^^^^^
// t1
VipsImage *t0;
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, nullptr))
return -1;
vips_object_local(context, t0);
VipsImage *t1;
if (vips_multiply(dstAlphaNormalized, t0, &t1, nullptr))
return -1;
vips_object_local(context, t1);
VipsImage *outAlphaNormalized;
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, nullptr))
return -1;
vips_object_local(context, outAlphaNormalized);
VImage t0 = srcAlpha.linear(-1.0, 1.0);
VImage outAlphaNormalized = srcAlpha + dstAlpha * t0;
//
// Compute output RGB channels:
@@ -101,182 +110,382 @@ namespace sharp {
// premultiplied RGBA image as reversal of premultiplication is handled
// externally.
//
VipsImage *t2;
if (vips_multiply(dstWithoutAlpha, t0, &t2, nullptr))
return -1;
vips_object_local(context, t2);
VipsImage *outRGBPremultiplied;
if (vips_add(srcWithoutAlpha, t2, &outRGBPremultiplied, nullptr))
return -1;
vips_object_local(context, outRGBPremultiplied);
// Denormalize output alpha channel:
VipsImage *outAlpha;
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, nullptr))
return -1;
vips_object_local(context, outAlpha);
VImage outRGBPremultiplied = srcWithoutAlpha + dstWithoutAlpha * t0;
// Combine RGB and alpha channel into output image:
return vips_bandjoin2(outRGBPremultiplied, outAlpha, out, nullptr);
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.
*/
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) {
VImage Normalize(VImage image) {
// Get original colourspace
VipsInterpretation typeBeforeNormalize = image->Type;
VipsInterpretation typeBeforeNormalize = image.interpretation();
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
}
// Convert to LAB colourspace
VipsImage *lab;
if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, nullptr)) {
return -1;
}
vips_object_local(context, lab);
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
// Extract luminance
VipsImage *luminance;
if (vips_extract_band(lab, &luminance, 0, "n", 1, nullptr)) {
return -1;
}
vips_object_local(context, luminance);
// Extract chroma
VipsImage *chroma;
if (vips_extract_band(lab, &chroma, 1, "n", 2, nullptr)) {
return -1;
}
vips_object_local(context, chroma);
VImage luminance = lab[0];
// Find luminance range
VipsImage *stats;
if (vips_stats(luminance, &stats, nullptr)) {
return -1;
}
vips_object_local(context, stats);
double min = *VIPS_MATRIX(stats, 0, 0);
double max = *VIPS_MATRIX(stats, 1, 0);
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
VipsImage *luminance100;
if (vips_linear1(luminance, &luminance100, f, a, nullptr)) {
return -1;
}
vips_object_local(context, luminance100);
// Join scaled luminance to chroma
VipsImage *normalizedLab;
if (vips_bandjoin2(luminance100, chroma, &normalizedLab, nullptr)) {
return -1;
}
vips_object_local(context, normalizedLab);
// Convert to original colourspace
VipsImage *normalized;
if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, nullptr)) {
return -1;
}
vips_object_local(context, normalized);
// 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
VipsImage *alpha;
if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, nullptr)) {
return -1;
}
vips_object_local(context, alpha);
VImage alpha = image[image.bands() - 1];
// Join alpha channel to normalised image
VipsImage *normalizedAlpha;
if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, nullptr)) {
return -1;
}
vips_object_local(context, normalizedAlpha);
*out = normalizedAlpha;
return normalized.bandjoin(alpha);
} else {
*out = normalized;
return normalized;
}
} else {
// Cannot normalise zero-range image
*out = image;
}
return 0;
return image;
}
/*
* Gaussian blur (use sigma <0 for fast blur)
* Gamma encoding/decoding
*/
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma) {
VipsImage *blurred;
if (sigma < 0.0) {
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
VipsImage *blur = vips_image_new_matrixv(3, 3,
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);
vips_image_set_double(blur, "scale", 9);
vips_object_local(context, blur);
if (vips_conv(image, &blurred, blur, nullptr)) {
return -1;
}
blur.set("scale", 9.0);
return image.conv(blur);
} else {
// Slower, accurate Gaussian blur
// Create Gaussian function for standard deviation
VipsImage *gaussian;
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, nullptr)) {
return -1;
}
vips_object_local(context, gaussian);
// Apply Gaussian function
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, nullptr)) {
return -1;
}
return image.gaussblur(sigma);
}
vips_object_local(context, blurred);
*out = blurred;
return 0;
}
/*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
* Convolution with a kernel.
*/
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged) {
VipsImage *sharpened;
if (radius == -1) {
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
VipsImage *sharpen = vips_image_new_matrixv(3, 3,
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);
vips_image_set_double(sharpen, "scale", 24);
vips_object_local(context, sharpen);
if (vips_conv(image, &sharpened, sharpen, nullptr)) {
return -1;
}
sharpen.set("scale", 24.0);
return image.conv(sharpen);
} else {
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
if (vips_sharpen(image, &sharpened, "radius", radius, "m1", flat, "m2", jagged, nullptr)) {
return -1;
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) {
// 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;
}
}
vips_object_local(context, sharpened);
*out = sharpened;
return 0;
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);
}
int Threshold(VipsObject *context, VipsImage *image, VipsImage **out, int threshold) {
VipsImage *greyscale;
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, nullptr)) {
return -1;
}
vips_object_local(context, greyscale);
image = greyscale;
VipsImage *thresholded;
if (vips_moreeq_const1(image, &thresholded, threshold, nullptr)) {
return -1;
}
vips_object_local(context, thresholded);
*out = thresholded;
return 0;
/*
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

99
src/operations.h Executable file → Normal file
View File

@@ -1,34 +1,111 @@
#ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vips/vips8>
using vips::VImage;
namespace sharp {
/*
Composite images `src` and `dst` with premultiplied alpha channel and output
image with premultiplied alpha.
Alpha composite src over dst with given gravity.
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
*/
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out);
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.
*/
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out);
VImage Normalize(VImage image);
/*
* Gaussian blur. Use sigma of -1 for fast blur.
* Gamma encoding/decoding
*/
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma);
VImage Gamma(VImage image, double const exponent);
/*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
* Gaussian blur. Use sigma of -1.0 for fast blur.
*/
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged);
VImage Blur(VImage image, double const sigma);
/*
* Perform thresholding on an image. If the image is not greyscale, will convert before thresholding.
* Pixels with a greyscale value greater-than-or-equal-to `threshold` will be pure white. All others will be pure black.
* Convolution with a kernel.
*/
int Threshold(VipsObject *context, VipsImage *image, VipsImage **out, int threshold);
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_

File diff suppressed because it is too large Load Diff

161
src/pipeline.h Executable file → Normal file
View File

@@ -1,8 +1,169 @@
#ifndef SRC_PIPELINE_H_
#define SRC_PIPELINE_H_
#include <memory>
#include <vips/vips8>
#include "nan.h"
#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;
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 normalize;
int angle;
bool rotateBeforePreExtract;
bool flip;
bool flop;
int extendTop;
int extendBottom;
int extendLeft;
int extendRight;
bool progressive;
bool withoutEnlargement;
VipsAccess accessMethod;
int quality;
int compressionLevel;
bool withoutAdaptiveFiltering;
bool withoutChromaSubsampling;
bool trellisQuantisation;
bool overshootDeringing;
bool optimiseScans;
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;
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),
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),
normalize(false),
angle(0),
flip(false),
flop(false),
extendTop(0),
extendBottom(0),
extendLeft(0),
extendRight(0),
progressive(false),
withoutEnlargement(false),
quality(80),
compressionLevel(6),
withoutAdaptiveFiltering(false),
withoutChromaSubsampling(false),
trellisQuantisation(false),
overshootDeringing(false),
optimiseScans(false),
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,5 +1,5 @@
#include <node.h>
#include <vips/vips.h>
#include <vips/vips8>
#include "nan.h"
@@ -11,10 +11,6 @@
NAN_MODULE_INIT(init) {
vips_init("sharp");
// Set libvips operation cache limits
vips_cache_set_max_mem(100 * 1024 * 1024); // 100 MB
vips_cache_set_max(500); // 500 operations
// Methods available to JavaScript
Nan::Set(target, Nan::New("metadata").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked());

View File

@@ -1,6 +1,6 @@
#include <cmath>
#include <node.h>
#include <vips/vips.h>
#include <vips/vips8>
#include <vips/vector.h>
#include "nan.h"
@@ -24,33 +24,49 @@ using Nan::To;
using Nan::Utf8String;
/*
Get and set cache memory and item limits
Get and set cache limits
*/
NAN_METHOD(cache) {
HandleScope();
// Set cache memory limit
// Set memory limit
if (info[0]->IsInt32()) {
vips_cache_set_max_mem(To<int32_t>(info[0]).FromJust() * 1048576);
}
// Set cache items limit
// Set file limit
if (info[1]->IsInt32()) {
vips_cache_set_max(To<int32_t>(info[1]).FromJust());
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());
}
// Get cache statistics
Local<Object> cache = New<Object>();
Set(cache, New("current").ToLocalChecked(),
// 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(cache, New("high").ToLocalChecked(),
Set(memory, New("high").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576)))
);
Set(cache, New("memory").ToLocalChecked(),
Set(memory, New("max").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576)))
);
Set(cache, New("items").ToLocalChecked(), New<Integer>(vips_cache_get_max()));
// 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 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);
}
@@ -122,7 +138,9 @@ NAN_METHOD(format) {
// 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"}) {
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()));
@@ -156,19 +174,17 @@ NAN_METHOD(format) {
Local<String> rawId = New("raw").ToLocalChecked();
Set(raw, attrId, rawId);
Set(format, rawId, raw);
// No support for raw input yet, so always false
Local<Boolean> supported = New<Boolean>(true);
Local<Boolean> unsupported = New<Boolean>(false);
Local<Object> rawInput = New<Object>();
Set(rawInput, attrFile, unsupported);
Set(rawInput, attrBuffer, unsupported);
Set(rawInput, attrStream, unsupported);
Set(rawInput, attrBuffer, supported);
Set(rawInput, attrStream, supported);
Set(raw, attrInput, rawInput);
// Raw output via Buffer/Stream is available in libvips >= 7.42.0
Local<Boolean> hasOutputBufferRaw = New<Boolean>(vips_version(0) >= 8 || (vips_version(0) == 7 && vips_version(1) >= 42));
Local<Object> rawOutput = New<Object>();
Set(rawOutput, attrFile, unsupported);
Set(rawOutput, attrBuffer, hasOutputBufferRaw);
Set(rawOutput, attrStream, hasOutputBufferRaw);
Set(rawOutput, attrBuffer, supported);
Set(rawOutput, attrStream, supported);
Set(raw, attrOutput, rawOutput);
info.GetReturnValue().Set(format);
@@ -180,101 +196,64 @@ NAN_METHOD(format) {
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::InitImage;
using sharp::HasAlpha;
HandleScope();
// Create "hook" VipsObject to hang image references from
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
// Open input files
VipsImage *image1 = nullptr;
VImage image1;
ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
if (imageType1 != ImageType::UNKNOWN) {
image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL);
if (image1 == nullptr) {
g_object_unref(hook);
try {
image1 = VImage::new_from_file(*Utf8String(info[0]));
} catch (...) {
return ThrowError("Input file 1 has corrupt header");
} else {
vips_object_local(hook, image1);
}
} else {
g_object_unref(hook);
return ThrowError("Input file 1 is of an unsupported image format");
}
VipsImage *image2 = nullptr;
VImage image2;
ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
if (imageType2 != ImageType::UNKNOWN) {
image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL);
if (image2 == nullptr) {
g_object_unref(hook);
try {
image2 = VImage::new_from_file(*Utf8String(info[1]));
} catch (...) {
return ThrowError("Input file 2 has corrupt header");
} else {
vips_object_local(hook, image2);
}
} else {
g_object_unref(hook);
return ThrowError("Input file 2 is of an unsupported image format");
}
// Ensure same number of channels
if (image1->Bands != image2->Bands) {
g_object_unref(hook);
if (image1.bands() != image2.bands()) {
return ThrowError("mismatchedBands");
}
// Ensure same dimensions
if (image1->Xsize != image2->Xsize || image1->Ysize != image2->Ysize) {
g_object_unref(hook);
if (image1.width() != image2.width() || image1.height() != image2.height()) {
return ThrowError("mismatchedDimensions");
}
// Premultiply and remove alpha
if (HasAlpha(image1)) {
VipsImage *imagePremultiplied1;
if (vips_premultiply(image1, &imagePremultiplied1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultiplied1);
VipsImage *imagePremultipliedNoAlpha1;
if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha1);
image1 = imagePremultipliedNoAlpha1;
}
if (HasAlpha(image2)) {
VipsImage *imagePremultiplied2;
if (vips_premultiply(image2, &imagePremultiplied2, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultiplied2);
VipsImage *imagePremultipliedNoAlpha2;
if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha2);
image2 = imagePremultipliedNoAlpha2;
}
// Calculate colour distance
VipsImage *difference;
if (vips_dE00(image1, image2, &difference, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, difference);
// Extract maximum distance
double maxColourDistance;
if (vips_max(difference, &maxColourDistance, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
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());
}
g_object_unref(hook);
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown();
info.GetReturnValue().Set(New<Number>(maxColourDistance));
}

View File

@@ -5,17 +5,17 @@
"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": "^1.5.0",
"benchmark": "^1.0.0",
"gm": "^1.21.0",
"async": "^2.1.1",
"benchmark": "^2.1.1",
"gm": "^1.23.0",
"imagemagick": "^0.1.3",
"imagemagick-native": "elad/node-imagemagick-native",
"jimp": "^0.2.20",
"lwip": "^0.0.8",
"semver": "^5.1.0"
"imagemagick-native": "^1.9.3",
"jimp": "^0.2.27",
"lwip": "^0.0.9",
"semver": "^5.3.0"
},
"license": "Apache-2.0",
"engines": {

View File

@@ -30,18 +30,15 @@ var fixtures = require('../fixtures');
var width = 720;
var height = 480;
var magickFilterBilinear = 'Triangle';
var magickFilterBicubic = 'Lanczos';
// Disable libvips cache to ensure tests are as fair as they can be
sharp.cache(0);
sharp.cache(false);
// Enable use of SIMD
sharp.simd(true);
async.series({
'jpeg-linear': function(callback) {
'jpeg': function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg-linear');
var jpegSuite = new Benchmark.Suite('jpeg');
// jimp
jpegSuite.add('jimp-buffer-buffer', {
defer: true,
@@ -93,7 +90,7 @@ async.series({
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
@@ -113,7 +110,7 @@ async.series({
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
@@ -140,7 +137,7 @@ async.series({
width: width,
height: height,
format: 'jpg',
filter: magickFilterBilinear
filter: 'Lanczos'
}, function(err) {
if (err) {
throw err;
@@ -161,7 +158,7 @@ async.series({
width: width,
height: height,
format: 'JPEG',
filter: magickFilterBilinear
filter: 'Lanczos'
}, function (err, buffer) {
if (err) {
throw err;
@@ -179,7 +176,7 @@ async.series({
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
@@ -194,7 +191,7 @@ async.series({
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
@@ -210,7 +207,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
@@ -225,7 +222,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
@@ -243,7 +240,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
@@ -257,7 +253,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -272,7 +267,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
@@ -290,8 +284,7 @@ async.series({
deferred.resolve();
});
var pipeline = sharp()
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear);
.resize(width, height);
readable.pipe(pipeline).pipe(writable);
}
}).add('sharp-file-buffer', {
@@ -299,7 +292,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -314,19 +306,27 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer()
.then(function(buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
}
}).add('sharp-sharpen-mild', {
}).on('cycle', function(event) {
console.log('jpeg ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
// Effect of applying operations
operations: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var operationsSuite = new Benchmark.Suite('operations');
operationsSuite.add('sharp-sharpen-mild', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sharpen()
.toBuffer(function(err, buffer) {
if (err) {
@@ -342,7 +342,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sharpen(3, 1, 3)
.toBuffer(function(err, buffer) {
if (err) {
@@ -358,7 +357,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.blur()
.toBuffer(function(err, buffer) {
if (err) {
@@ -374,7 +372,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.blur(3)
.toBuffer(function(err, buffer) {
if (err) {
@@ -390,7 +387,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.gamma()
.toBuffer(function(err, buffer) {
if (err) {
@@ -406,7 +402,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.normalise()
.toBuffer(function(err, buffer) {
if (err) {
@@ -422,7 +417,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.greyscale()
.toBuffer(function(err, buffer) {
if (err) {
@@ -438,7 +432,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.gamma()
.greyscale()
.toBuffer(function(err, buffer) {
@@ -455,7 +448,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.progressive()
.toBuffer(function(err, buffer) {
if (err) {
@@ -471,7 +463,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.withoutChromaSubsampling()
.toBuffer(function(err, buffer) {
if (err) {
@@ -487,7 +478,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.rotate(90)
.interpolateWith(sharp.interpolator.bilinear)
.resize(width, height)
.toBuffer(function(err, buffer) {
if (err) {
@@ -503,8 +493,6 @@ async.series({
fn: function(deferred) {
sharp.simd(false);
sharp(inputJpgBuffer)
.rotate(90)
.interpolateWith(sharp.interpolator.bilinear)
.resize(width, height)
.toBuffer(function(err, buffer) {
sharp.simd(true);
@@ -520,9 +508,38 @@ async.series({
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sequentialRead()
.resize(width, height)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-crop-entropy', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.crop(sharp.strategy.entropy)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-crop-attention', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.crop(sharp.strategy.attention)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -533,189 +550,19 @@ async.series({
});
}
}).on('cycle', function(event) {
console.log('jpeg-linear ' + String(event.target));
console.log('operations ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
callback(null, this.filter('fastest').map('name'));
}).run();
},
'jpeg-cubic': function(callback) {
// Comparitive speed of kernels
kernels: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg-cubic');
// lwip
if (typeof lwip !== 'undefined') {
jpegSuite.add('lwip-file-file', {
defer: true,
fn: function(deferred) {
lwip.open(fixtures.inputJpg, function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.writeFile(fixtures.outputJpg, {quality: 80}, function (err) {
if (err) {
throw err;
}
deferred.resolve();
});
});
});
}
}).add('lwip-buffer-buffer', {
defer: true,
fn: function(deferred) {
lwip.open(inputJpgBuffer, 'jpg', function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.toBuffer('jpg', {quality: 80}, function (err, buffer) {
if (err) {
throw err;
}
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
});
});
}
});
}
// imagemagick
jpegSuite.add('imagemagick-file-file', {
defer: true,
fn: function(deferred) {
imagemagick.resize({
srcPath: fixtures.inputJpg,
dstPath: fixtures.outputJpg,
quality: 0.8,
width: width,
height: height,
format: 'jpg',
filter: magickFilterBicubic
}, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
// imagemagick-native
if (typeof imagemagickNative !== 'undefined') {
jpegSuite.add('imagemagick-native-buffer-buffer', {
defer: true,
fn: function(deferred) {
imagemagickNative.convert({
srcData: inputJpgBuffer,
quality: 80,
width: width,
height: height,
format: 'JPEG',
filter: magickFilterBicubic
}, function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
}
// gm
jpegSuite.add('gm-buffer-file', {
defer: true,
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-buffer-buffer', {
defer: true,
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('gm-file-file', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-file-buffer', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// sharp
jpegSuite.add('sharp-buffer-file', {
(new Benchmark.Suite('kernels')).add('sharp-cubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-buffer-buffer', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.resize(width, height, { kernel: 'cubic' })
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -725,39 +572,11 @@ async.series({
}
});
}
}).add('sharp-file-file', {
}).add('sharp-lanczos2', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-stream-stream', {
defer: true,
fn: function(deferred) {
var readable = fs.createReadStream(fixtures.inputJpg);
var writable = fs.createWriteStream(fixtures.outputJpg);
writable.on('finish', function() {
deferred.resolve();
});
var pipeline = sharp()
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic);
readable.pipe(pipeline).pipe(writable);
}
}).add('sharp-file-buffer', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos2' })
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -767,109 +586,11 @@ async.series({
}
});
}
}).add('sharp-promise', {
}).add('sharp-lanczos3', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer()
.then(function(buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
}
}).on('cycle', function(event) {
console.log('jpeg-cubic ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
}).run();
},
// Comparitive speed of pixel interpolators
interpolators: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
(new Benchmark.Suite('interpolators')).add('sharp-nearest-neighbour', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.nearest)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-bilinear', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-vertexSplitQuadraticBasisSpline', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-bicubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-locallyBoundedBicubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.locallyBoundedBicubic)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-nohalo', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.nohalo)
.resize(width, height, { kernel: 'lanczos3' })
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -880,12 +601,12 @@ async.series({
});
}
}).on('cycle', function(event) {
console.log('interpolators ' + String(event.target));
console.log('kernels ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
callback(null, this.filter('fastest').map('name'));
}).run();
},
// PNG
png: function(callback) {
var inputPngBuffer = fs.readFileSync(fixtures.inputPng);
var pngSuite = new Benchmark.Suite('png');
@@ -938,7 +659,7 @@ async.series({
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
@@ -963,7 +684,7 @@ async.series({
dstPath: fixtures.outputPng,
width: width,
height: height,
filter: magickFilterBilinear
filter: 'Lanczos'
}, function(err) {
if (err) {
throw err;
@@ -983,7 +704,7 @@ async.series({
width: width,
height: height,
format: 'PNG',
filter: magickFilterBilinear
filter: 'Lanczos'
});
deferred.resolve();
}
@@ -995,7 +716,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputPng)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.write(fixtures.outputPng, function (err) {
if (err) {
throw err;
@@ -1009,7 +730,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputPng)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.toBuffer(function (err, buffer) {
if (err) {
throw err;
@@ -1026,7 +747,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputPng, function(err) {
if (err) {
throw err;
@@ -1040,7 +760,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1055,7 +774,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputPng)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputPng, function(err) {
if (err) {
throw err;
@@ -1069,7 +787,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputPng)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1084,7 +801,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.progressive()
.toBuffer(function(err, buffer) {
if (err) {
@@ -1100,7 +816,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.withoutAdaptiveFiltering()
.toBuffer(function(err, buffer) {
if (err) {
@@ -1115,10 +830,10 @@ async.series({
pngSuite.on('cycle', function(event) {
console.log(' png ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
callback(null, this.filter('fastest').map('name'));
}).run();
},
// WebP
webp: function(callback) {
var inputWebPBuffer = fs.readFileSync(fixtures.inputWebP);
(new Benchmark.Suite('webp')).add('sharp-buffer-file', {
@@ -1126,7 +841,6 @@ async.series({
fn: function(deferred) {
sharp(inputWebPBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputWebP, function(err) {
if (err) {
throw err;
@@ -1140,7 +854,6 @@ async.series({
fn: function(deferred) {
sharp(inputWebPBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1155,7 +868,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputWebP)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputWebP, function(err) {
if (err) {
throw err;
@@ -1169,7 +881,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputWebp)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1182,7 +893,7 @@ async.series({
}).on('cycle', function(event) {
console.log('webp ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
callback(null, this.filter('fastest').map('name'));
}).run();
}
}, function(err, results) {

View File

@@ -8,14 +8,12 @@ var Benchmark = require('benchmark');
var sharp = require('../../index');
var fixtures = require('../fixtures');
sharp.cache(false);
sharp.simd(true);
var min = 320;
var max = 960;
// Nearest equivalent to bilinear
var magickFilter = 'Triangle';
var randomDimension = function() {
return Math.ceil(Math.random() * (max - min) + min);
};
@@ -30,7 +28,7 @@ new Benchmark.Suite('random').add('imagemagick', {
width: randomDimension(),
height: randomDimension(),
format: 'jpg',
filter: magickFilter
filter: 'Lanczos'
}, function(err) {
if (err) {
throw err;
@@ -44,7 +42,7 @@ new Benchmark.Suite('random').add('imagemagick', {
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(randomDimension(), randomDimension())
.filter(magickFilter)
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
@@ -58,19 +56,20 @@ new Benchmark.Suite('random').add('imagemagick', {
}).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();
}
});
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) {
console.log(String(event.target));
}).on('complete', function() {
var winner = this.filter('fastest').pluck('name');
var winner = this.filter('fastest').map('name');
assert.strictEqual('sharp', String(winner), 'sharp was slower than ' + winner);
console.dir(sharp.cache());
}).run();

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
id="Wikimedia logo"
viewBox="-599 -599 1198 1198" width="1024" height="1024">
<defs>
<clipPath id="mask">
<path d="M 47.5,-87.5 v 425 h -95 v -425 l -552,-552 v 1250 h 1199 v -1250 z" />
</clipPath>
</defs>
<g clip-path="url(#mask)">
<circle id="green parts" fill="#396" r="336.5"/>
<circle id="blue arc" fill="none" stroke="#069" r="480.25" stroke-width="135.5" />
</g>
<circle fill="#900" cy="-379.5" r="184.5" id="red circle"/>
</svg>

Before

Width:  |  Height:  |  Size: 692 B

BIN
test/fixtures/bandbool.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
test/fixtures/booleanTest.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

3
test/fixtures/check.svg vendored Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M30,76q6-14,13-26q6-12,14-23q8-12,13-17q3-4,6-6q1-1,5-2q8-1,12-1q1,0,1,1q0,1-1,2q-13,11-27,33q-14,21-24,44q-4,9-5,11q-1,2-9,2q-5,0-6-1q-1-1-5-6q-5-8-12-15q-3-4-3-6q0-2,4-5q3-2,6-2q3,0,8,3q5,4,10,14z" fill="green" />
</svg>

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
test/fixtures/expected/conv-1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

BIN
test/fixtures/expected/conv-2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
test/fixtures/expected/crop-strategy.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
test/fixtures/expected/crop-strategy.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 827 B

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

BIN
test/fixtures/expected/extend-equal.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
test/fixtures/expected/extract-blue.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
test/fixtures/expected/extract-green.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
test/fixtures/expected/extract-red.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 B

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

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